From 20e33fe30dbdc62d2e97d34f3221fbec552ace03 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 16:06:47 +0300 Subject: [PATCH] rendering.c: sprite batches with no color information when appropriate --- src/private/rendering.h | 22 ---- src/rendering.c | 13 +-- src/rendering/sprites.h | 229 +++++++++++++++++++++++++--------------- 3 files changed, 142 insertions(+), 122 deletions(-) diff --git a/src/private/rendering.h b/src/private/rendering.h index d4073e0..d15467c 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -20,28 +20,6 @@ struct sprite_primitive { bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */ }; -/* interleaved vertex array data */ -/* TODO: use int16_t for uvs */ -/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ -struct sprite_primitive_payload { - /* upper-left */ - t_fvec2 v0; - t_fvec2 uv0; - t_color c0; - /* bottom-left */ - t_fvec2 v1; - t_fvec2 uv1; - t_color c1; - /* bottom-right */ - t_fvec2 v2; - t_fvec2 uv2; - t_color c2; - /* upper-right */ - t_fvec2 v3; - t_fvec2 uv3; - t_color c3; -}; - struct rect_primitive { t_frect rect; t_color color; diff --git a/src/rendering.c b/src/rendering.c index 27bd18d..e3e51ee 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -88,18 +88,7 @@ static void render_2d(void) { glDepthRange((double)batch_count / UINT16_MAX, 1.0); - if (batch.blend) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthFunc(GL_ALWAYS); - - render_sprites(current, batch.size, false); - } else { - glDisable(GL_BLEND); - glDepthFunc(GL_LESS); - - render_sprites(current, batch.size, true); - } + render_sprites(current, batch); i += batch.size - 1; ++batch_count; break; diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 45cd429..1ea3c98 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -13,6 +13,44 @@ #include #include +/* interleaved vertex array data */ +/* TODO: use int16_t for uvs */ +/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ +struct sprite_primitive_payload { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + t_color c0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + t_color c1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + t_color c2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; + t_color c3; +}; + + +struct sprite_primitive_payload_without_color { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; +}; + /* * an implementation note: * try to avoid doing expensive work in the push functions, @@ -63,8 +101,9 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { static struct sprite_batch { int atlas_id; - size_t size; /* how many primitives are in current batch */ - bool blend; /* whether it's blended or not */ + size_t size; /* how many primitives are in current batch */ + bool blend; /* whether it's blended or not */ + bool colored; /* whether color inmformation is needed to be passed */ } collect_sprite_batch(const struct primitive_2d *primitives, size_t len) { /* assumes that first primitive is already a sprite */ struct sprite_batch result = { @@ -93,6 +132,13 @@ static struct sprite_batch { != result.atlas_id) break; + /* if all are effectively without modulation we can skip sending color data */ + if (!result.colored && (current->sprite.color.r != 255 || + current->sprite.color.g != 255 || + current->sprite.color.b != 255 || + current->sprite.color.a != 255 )) + result.colored = true; + ++result.size; } @@ -102,17 +148,31 @@ static struct sprite_batch { /* assumes that orthogonal matrix setup is done already */ static void render_sprites(const struct primitive_2d primitives[], - const size_t len, - const bool reversed) + const struct sprite_batch batch) { /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ static GLuint vertex_array = 0; if (vertex_array == 0) glGenBuffers(1, &vertex_array); + if (batch.blend) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_ALWAYS); + } else { + glDisable(GL_BLEND); + glDepthFunc(GL_LESS); + } + + size_t payload_size; + if (batch.colored) + payload_size = sizeof (struct sprite_primitive_payload); + else + payload_size = sizeof (struct sprite_primitive_payload_without_color); + glBindBuffer(GL_ARRAY_BUFFER, vertex_array); glBufferData(GL_ARRAY_BUFFER, - sizeof (struct sprite_primitive_payload) * len, + payload_size * batch.size, NULL, GL_STREAM_DRAW); @@ -122,11 +182,10 @@ static void render_sprites(const struct primitive_2d primitives[], /* vertex population over a mapped buffer */ { /* TODO: check errors, ensure alignment ? */ - struct sprite_primitive_payload *const payload = - glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + void *const payload = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - for (size_t i = 0; i < len; ++i) { - const size_t cur = reversed ? len - i - 1: i; + for (size_t i = 0; i < batch.size; ++i) { + const size_t cur = !batch.blend ? batch.size - i - 1: i; /* render opaques front to back */ const struct sprite_primitive sprite = primitives[cur].sprite; const t_rect srcrect = @@ -137,49 +196,25 @@ static void render_sprites(const struct primitive_2d primitives[], const float xr = (float)srcrect.x / (float)dims.w; const float yr = (float)srcrect.y / (float)dims.h; - /* non-rotated case */ + t_fvec2 uv0 = { xr + wr * sprite.flip_x, yr + hr * sprite.flip_y }; + t_fvec2 uv1 = { xr + wr * sprite.flip_x, yr + hr * !sprite.flip_y }; + t_fvec2 uv2 = { xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y }; + t_fvec2 uv3 = { xr + wr * !sprite.flip_x, yr + hr * sprite.flip_y }; + + t_fvec2 v0, v1, v2, v3; + + /* todo: fast PI/2 degree divisible rotations? */ if (sprite.rotation == 0.0f) { - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - sprite.rect.x, - sprite.rect.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + /* non-rotated case */ - /* bottom-left */ - .v1 = { - (sprite.rect.x), - (sprite.rect.y + sprite.rect.h) }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + v0 = (t_fvec2){ sprite.rect.x, sprite.rect.y }; + v1 = (t_fvec2){ sprite.rect.x, sprite.rect.y + sprite.rect.h }; + v2 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h }; + v3 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y }; - /* bottom-right */ - .v2 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y + sprite.rect.h) }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + } else if (sprite.rect.w == sprite.rect.h) { + /* rotated square case */ - /* upper-right */ - .v3 = { - (sprite.rect.x + sprite.rect.w), - (sprite.rect.y) }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, - - /* equal for all (flat shaded) */ - .c0 = sprite.color, - .c1 = sprite.color, - .c2 = sprite.color, - .c3 = sprite.color, - }; - } else { - /* rotated case */ const t_fvec2 c = frect_center(sprite.rect); const t_fvec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); const t_fvec2 d = { @@ -187,38 +222,28 @@ static void render_sprites(const struct primitive_2d primitives[], .y = t.y * sprite.rect.h * (float)M_SQRT1_2, }; - payload[i] = (struct sprite_primitive_payload) { - /* upper-left */ - .v0 = { - c.x - d.x, - c.y - d.y }, - .uv0 = { - xr + wr * sprite.flip_x, - yr + hr * sprite.flip_y, }, + v0 = (t_fvec2){ c.x - d.x, c.y - d.y }; + v1 = (t_fvec2){ c.x - d.y, c.y + d.x }; + v2 = (t_fvec2){ c.x + d.x, c.y + d.y }; + v3 = (t_fvec2){ c.x + d.y, c.y - d.x }; - /* bottom-left */ - .v1 = { - c.x - d.y, - c.y + d.x }, - .uv1 = { - xr + wr * sprite.flip_x, - yr + hr * !sprite.flip_y, }, + } else { + /* rotated non-square case*/ - /* bottom-right */ - .v2 = { - c.x + d.x, - c.y + d.y }, - .uv2 = { - xr + wr * !sprite.flip_x, - yr + hr * !sprite.flip_y, }, + CRY("Rotation", "Unimplemented"); + } - /* upper-right */ - .v3 = { - c.x + d.y, - c.y - d.x }, - .uv3 = { - xr + wr * !sprite.flip_x, - yr + hr * sprite.flip_y, }, + if (batch.colored) + ((struct sprite_primitive_payload *)payload)[i] = (struct sprite_primitive_payload) { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .v3 = v3, + + .uv0 = uv0, + .uv1 = uv1, + .uv2 = uv2, + .uv3 = uv3, /* equal for all (flat shaded) */ .c0 = sprite.color, @@ -226,37 +251,65 @@ static void render_sprites(const struct primitive_2d primitives[], .c2 = sprite.color, .c3 = sprite.color, }; - } + else + ((struct sprite_primitive_payload_without_color *)payload)[i] = (struct sprite_primitive_payload_without_color) { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .v3 = v3, + + .uv0 = uv0, + .uv1 = uv1, + .uv2 = uv2, + .uv3 = uv3, + }; } glUnmapBuffer(GL_ARRAY_BUFFER); } + GLsizei off; + GLsizei voff; + GLsizei uvoff; + + if (batch.colored) { + off = offsetof(struct sprite_primitive_payload, v1); + voff = offsetof(struct sprite_primitive_payload, v0); + uvoff = offsetof(struct sprite_primitive_payload, uv0); + } else { + off = offsetof(struct sprite_primitive_payload_without_color, v1); + voff = offsetof(struct sprite_primitive_payload_without_color, v0); + uvoff = offsetof(struct sprite_primitive_payload_without_color, uv0); + } + /* vertex specification */ glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, - offsetof(struct sprite_primitive_payload, v1), - (void *)offsetof(struct sprite_primitive_payload, v0)); + off, + (void *)(size_t)voff); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, GL_FLOAT, - offsetof(struct sprite_primitive_payload, v1), - (void *)offsetof(struct sprite_primitive_payload, uv0)); + off, + (void *)(size_t)uvoff); - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(4, - GL_UNSIGNED_BYTE, - offsetof(struct sprite_primitive_payload, v1), - (void *)offsetof(struct sprite_primitive_payload, c0)); + if (batch.colored) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + off, + (void *)offsetof(struct sprite_primitive_payload, c0)); + } else + glColor4ub(255, 255, 255, 255); textures_bind(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D); bind_quad_element_buffer(); - glDrawElements(GL_TRIANGLES, 6 * (GLsizei)len, GL_UNSIGNED_SHORT, NULL); + glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL); /* clear the state */ glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);