From bdf2a541076e089c21504b665af685d4b0b4aa6e Mon Sep 17 00:00:00 2001 From: wanp Date: Mon, 15 Jul 2024 23:31:54 -0300 Subject: [PATCH] replace categorized, sorted render queues with a single ordered 2d queue --- src/context.h | 6 +- src/main.c | 19 +++-- src/private/rendering.h | 17 ++++- src/rendering.c | 151 ++++++++++++---------------------------- src/rendering.h | 1 - 5 files changed, 71 insertions(+), 123 deletions(-) diff --git a/src/context.h b/src/context.h index d26746f..b046c97 100644 --- a/src/context.h +++ b/src/context.h @@ -20,9 +20,7 @@ typedef struct context { struct texture_cache texture_cache; struct input_state input; - struct sprite_primitive *render_queue_sprites; - struct rect_primitive *render_queue_rectangles; - struct circle_primitive *render_queue_circles; + struct primitive_2d *render_queue_2d; struct mesh_batch_item *uncolored_mesh_batches; struct audio_channel_item *audio_channels; @@ -38,7 +36,7 @@ typedef struct context { int64_t prev_frame_time; int64_t desired_frametime; /* how long one tick should be */ int64_t frame_accumulator; - int64_t delta_averager_residual; + int64_t delta_averager_residual; int64_t time_averager[4]; int64_t delta_time; /* preserves real time frame delta with no manipilation */ uint64_t tick_count; diff --git a/src/main.c b/src/main.c index 38db7f4..33aae14 100644 --- a/src/main.c +++ b/src/main.c @@ -28,7 +28,7 @@ static void poll_events(void) { case SDL_QUIT: ctx.is_running = false; break; - + case SDL_WINDOWEVENT: if (e.window.windowID != ctx.window_id) break; @@ -72,7 +72,7 @@ void main_loop(void) { emscripten_cancel_main_loop(); } */ - + /* frame timer */ int64_t current_frame_time = SDL_GetPerformanceCounter(); int64_t delta_time = current_frame_time - ctx.prev_frame_time; @@ -130,7 +130,7 @@ void main_loop(void) { ctx.frame_accumulator += delta_time; /* spiral of death protection */ - if (ctx.frame_accumulator > ctx.desired_frametime * 8) { + if (ctx.frame_accumulator > ctx.desired_frametime * 8) { ctx.resync_flag = true; } @@ -203,7 +203,7 @@ static bool initialize(void) { /* might need this to have multiple windows */ ctx.window_id = SDL_GetWindowID(ctx.window); - + glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); /* TODO: */ @@ -238,14 +238,14 @@ static bool initialize(void) { } /* filesystem time */ - /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ + /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ if (!PHYSFS_init(ctx.argv[0]) || !PHYSFS_setSaneConfig(ORGANIZATION_NAME, APP_NAME, PACKAGE_EXTENSION, false, true)) { CRY_PHYSFS("Filesystem initialization failed."); goto fail; } - + /* you could change this at runtime if you wanted */ ctx.update_multiplicity = 1; @@ -287,9 +287,7 @@ static bool initialize(void) { /* rendering */ /* these are dynamic arrays and will be allocated lazily by stb_ds */ - ctx.render_queue_sprites = NULL; - ctx.render_queue_rectangles = NULL; - ctx.render_queue_circles = NULL; + ctx.render_queue_2d = NULL; ctx.circle_radius_hash = NULL; textures_cache_init(&ctx.texture_cache, ctx.window); @@ -324,8 +322,7 @@ static void clean_up(void) { input_state_deinit(&ctx.input); - arrfree(ctx.render_queue_sprites); - arrfree(ctx.render_queue_rectangles); + arrfree(ctx.render_queue_2d); textures_cache_deinit(&ctx.texture_cache); PHYSFS_deinit(); diff --git a/src/private/rendering.h b/src/private/rendering.h index 5c3326e..73a9cc2 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -16,7 +16,6 @@ struct sprite_primitive { double rotation; SDL_BlendMode blend_mode; t_texture_key texture_key; - int layer; bool flip_x; bool flip_y; }; @@ -32,6 +31,22 @@ struct circle_primitive { t_fvec2 position; }; +enum primitive_2d_type { + PRIMITIVE_2D_SPRITE, + PRIMITIVE_2D_RECT, + PRIMITIVE_2D_CIRCLE, +}; + +struct primitive_2d { + enum primitive_2d_type type; + + union { + struct sprite_primitive sprite; + struct rect_primitive rect; + struct circle_primitive circle; + }; +}; + /* union for in-place recalculation of texture coordinates */ union uncolored_space_triangle { /* pending for sending, uvs are not final as texture atlases could update */ diff --git a/src/rendering.c b/src/rendering.c index eeed4ea..17d88ae 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -16,9 +16,7 @@ void render_queue_clear(void) { /* since i don't intend to free the queues, */ /* it's faster and simpler to just "start over" */ /* and start overwriting the existing data */ - arrsetlen(ctx.render_queue_sprites, 0); - arrsetlen(ctx.render_queue_rectangles, 0); - arrsetlen(ctx.render_queue_circles, 0); + arrsetlen(ctx.render_queue_2d, 0); for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0); @@ -31,7 +29,6 @@ void render_queue_clear(void) { * because they will be called multiple times in the main loop * before anything is really rendered */ - /* sprite */ void push_sprite(char *path, t_frect rect) { struct sprite_primitive sprite = { @@ -40,12 +37,16 @@ void push_sprite(char *path, t_frect rect) { .rotation = 0.0, .blend_mode = SDL_BLENDMODE_BLEND, .texture_key = textures_get_key(&ctx.texture_cache, path), - .layer = 0, .flip_x = false, .flip_y = false, }; - arrput(ctx.render_queue_sprites, sprite); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -55,13 +56,17 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { .color = args.color, .rotation = args.rotation, .blend_mode = args.blend_mode, - .texture_key = textures_get_key(&ctx.texture_cache, args.path), - .layer = args.layer, + .texture_key = textures_get_key(&ctx.texture_cache, args.path), .flip_x = args.flip_x, .flip_y = args.flip_y, }; - arrput(ctx.render_queue_sprites, sprite); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -72,7 +77,12 @@ void push_rectangle(t_frect rect, t_color color) { .color = color, }; - arrput(ctx.render_queue_rectangles, rectangle); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_RECT, + .rect = rectangle, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -84,7 +94,12 @@ void push_circle(t_fvec2 position, float radius, t_color color) { .position = position, }; - arrput(ctx.render_queue_circles, circle); + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_CIRCLE, + .circle = circle, + }; + + arrput(ctx.render_queue_2d, primitive); } @@ -123,76 +138,6 @@ void unfurl_triangle(const char *path, } -/* compare functions for the sort in render_sprites */ -static int cmp_atlases(const void *a, const void *b) { - int index_a = ((const struct sprite_primitive *)a)->texture_key.id; - int index_b = ((const struct sprite_primitive *)b)->texture_key.id; - - return (index_a > index_b) - (index_a < index_b); -} - - -static int cmp_blend_modes(const void *a, const void *b) { - SDL_BlendMode mode_a = ((const struct sprite_primitive *)a)->blend_mode; - SDL_BlendMode mode_b = ((const struct sprite_primitive *)b)->blend_mode; - - return (mode_a > mode_b) - (mode_a < mode_b); -} - - -static int cmp_colors(const void *a, const void *b) { - t_color color_a = ((const struct sprite_primitive *)a)->color; - t_color color_b = ((const struct sprite_primitive *)b)->color; - - /* check reds */ - if (color_a.r < color_b.r) - return -1; - else if (color_a.r > color_b.r) - return 1; - - /* reds were equal, check greens */ - else if (color_a.g < color_b.g) - return -1; - else if (color_a.g > color_b.g) - return 1; - - /* greens were equal, check blues */ - else if (color_a.b < color_b.b) - return -1; - else if (color_a.b > color_b.b) - return 1; - - /* blues were equal, check alphas */ - else if (color_a.a < color_b.a) - return -1; - else if (color_a.a > color_b.a) - return 1; - - /* entirely equal */ - else - return 0; -} - - -static int cmp_layers(const void *a, const void *b) { - int layer_a = ((const struct sprite_primitive *)a)->layer; - int layer_b = ((const struct sprite_primitive *)b)->layer; - - return (layer_a > layer_b) - (layer_a < layer_b); -} - - -/* TODO: not that we're SDL free we need to implement batching ourselves */ -/* necessary to allow the renderer to draw in batches in the best case */ -static void sort_sprites(struct sprite_primitive *sprites) { - size_t sprites_len = arrlenu(sprites); - qsort(sprites, sprites_len, sizeof *sprites, cmp_atlases); - qsort(sprites, sprites_len, sizeof *sprites, cmp_blend_modes); - qsort(sprites, sprites_len, sizeof *sprites, cmp_colors); - qsort(sprites, sprites_len, sizeof *sprites, cmp_layers); -} - - static void upload_quad_vertices(t_frect rect) { /* client memory needs to be reachable on glDraw*, so */ static float vertices[6 * 2]; @@ -277,7 +222,7 @@ static void create_circle_geometry(t_fvec2 position, /* the angle (in radians) to rotate by on each iteration */ float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); - + vertices[0].position.x = (float)position.x; vertices[0].position.y = (float)position.y; vertices[0].color.r = color.r; @@ -366,25 +311,21 @@ static void render_circle(struct circle_primitive *circle) { } -static void render_sprites(void) { - sort_sprites(ctx.render_queue_sprites); +static void render_2d(void) { + for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { + struct primitive_2d *current = &ctx.render_queue_2d[i]; - for (size_t i = 0; i < arrlenu(ctx.render_queue_sprites); ++i) { - render_sprite(&ctx.render_queue_sprites[i]); - } -} - - -static void render_rectangles(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_rectangles); ++i) { - render_rectangle(&ctx.render_queue_rectangles[i]); - } -} - - -static void render_circles(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_circles); ++i) { - render_circle(&ctx.render_queue_circles[i]); + switch (current->type) { + case PRIMITIVE_2D_SPRITE: + render_sprite(¤t->sprite); + break; + case PRIMITIVE_2D_RECT: + render_rectangle(¤t->rect); + break; + case PRIMITIVE_2D_CIRCLE: + render_circle(¤t->circle); + break; + } } } @@ -516,7 +457,7 @@ void render(void) { glDepthFunc(GL_ALWAYS); /* fill depth buffer with ones, 2d view is always in front */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); @@ -525,10 +466,8 @@ void render(void) { glLoadIdentity(); glEnable(GL_TEXTURE_2D); - - render_sprites(); - render_rectangles(); - render_circles(); + + render_2d(); } { @@ -537,7 +476,7 @@ void render(void) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - + glEnable(GL_CULL_FACE); glDepthFunc(GL_LESS); glEnable(GL_BLEND); diff --git a/src/rendering.h b/src/rendering.h index 5f348e4..099e595 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -9,7 +9,6 @@ typedef struct push_sprite_args { char *path; - int layer; t_color color; double rotation; SDL_BlendMode blend_mode;