rendering: use sprite batching techniques for rect primitives, unite their render path

This commit is contained in:
veclav talica 2024-10-14 11:46:07 +03:00
parent 82bad550e5
commit b295c5920c
10 changed files with 308 additions and 116 deletions

View File

@ -104,6 +104,7 @@ set(TWN_NONOPT_SOURCE_FILES
src/rendering/twn_draw.c src/rendering/twn_draw_c.h src/rendering/twn_draw.c src/rendering/twn_draw_c.h
src/rendering/twn_sprites.c src/rendering/twn_sprites.c
src/rendering/twn_rects.c
src/rendering/twn_text.c src/rendering/twn_text.c
src/rendering/twn_triangles.c src/rendering/twn_triangles.c
src/rendering/twn_circles.c src/rendering/twn_circles.c

View File

@ -1,6 +1,9 @@
#ifndef TWN_TEXTURES_MODES_H #ifndef TWN_TEXTURES_MODES_H
#define TWN_TEXTURES_MODES_H #define TWN_TEXTURES_MODES_H
/* TODO: rename, as it doesn't have to be about textures only, but blending */
/* TODO: move from public /include/ tree */
/* alpha channel information */ /* alpha channel information */
typedef enum TextureMode { typedef enum TextureMode {
TEXTURE_MODE_OPAQUE, /* all pixels are solid */ TEXTURE_MODE_OPAQUE, /* all pixels are solid */

View File

@ -35,22 +35,6 @@ void render_queue_clear(void) {
} }
/* rectangle */
void draw_rectangle(Rect rect, Color color) {
RectPrimitive rectangle = {
.rect = rect,
.color = color,
};
Primitive2D primitive = {
.type = PRIMITIVE_2D_RECT,
.rect = rectangle,
};
arrput(ctx.render_queue_2d, primitive);
}
void draw_9slice(const char *texture_path, int texture_w, int texture_h, int border_thickness, Rect rect, Color color) { void draw_9slice(const char *texture_path, int texture_w, int texture_h, int border_thickness, Rect rect, Color color) {
const float bt = (float)border_thickness; /* i know! */ const float bt = (float)border_thickness; /* i know! */
const float bt2 = bt * 2; /* combined size of the two borders in an axis */ const float bt2 = bt * 2; /* combined size of the two borders in an axis */
@ -204,19 +188,27 @@ static void render_2d(void) {
switch (current->type) { switch (current->type) {
case PRIMITIVE_2D_SPRITE: { case PRIMITIVE_2D_SPRITE: {
const struct SpriteBatch batch = const struct QuadBatch batch =
collect_sprite_batch(current, render_queue_len - i); collect_sprite_batch(current, render_queue_len - i);
/* TODO: what's even the point? just use OR_EQUAL comparison */ /* TODO: what's even the point? just use OR_EQUAL comparison */
set_depth_range((double)batch_count / UINT16_MAX, 1.0); set_depth_range((double)batch_count / UINT16_MAX, 1.0);
render_sprites(current, batch); render_sprite_batch(current, batch);
i += batch.size - 1; ++batch_count; i += batch.size - 1; ++batch_count;
break; break;
} }
case PRIMITIVE_2D_RECT: case PRIMITIVE_2D_RECT: {
render_rectangle(&current->rect); const struct QuadBatch batch =
collect_rect_batch(current, render_queue_len - i);
/* TODO: what's even the point? just use OR_EQUAL comparison */
set_depth_range((double)batch_count / UINT16_MAX, 1.0);
render_rect_batch(current, batch);
i += batch.size - 1; ++batch_count;
break; break;
}
case PRIMITIVE_2D_CIRCLE: case PRIMITIVE_2D_CIRCLE:
render_circle(&current->circle); render_circle(&current->circle);
break; break;

View File

@ -133,15 +133,18 @@ void create_circle_geometry(Vec2 position,
SDL_Vertex **vertices_out, SDL_Vertex **vertices_out,
int **indices_out); int **indices_out);
struct SpriteBatch { struct QuadBatch {
size_t size; /* how many primitives are in current batch */ size_t size; /* how many primitives are in current batch */
TextureMode mode; TextureMode mode; /* how color should be applied */
bool constant_colored; /* whether colored batch is uniformly colored */ bool constant_colored; /* whether colored batch is uniformly colored */
bool repeat; /* whether repeat is needed */ bool repeat; /* whether repeat is needed */
} collect_sprite_batch(const Primitive2D primitives[], size_t len); bool textured;
} collect_quad_batch(const Primitive2D primitives[], size_t len);
void render_sprites(const Primitive2D primitives[], void render_quad_batch(const Primitive2D primitives[], struct QuadBatch batch);
const struct SpriteBatch batch); struct QuadBatch collect_sprite_batch(const Primitive2D primitives[], size_t len);
struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len);
void render_sprite_batch(const Primitive2D primitives[], struct QuadBatch batch);
void render_rect_batch(const Primitive2D primitives[], struct QuadBatch batch);
void draw_uncolored_space_traingle_batch(MeshBatch *batch, void draw_uncolored_space_traingle_batch(MeshBatch *batch,
TextureKey texture_key); TextureKey texture_key);
@ -162,14 +165,14 @@ VertexBuffer create_vertex_buffer(void);
void delete_vertex_buffer(VertexBuffer buffer); void delete_vertex_buffer(VertexBuffer buffer);
void specify_vertex_buffer(VertexBuffer buffer, void *data, size_t bytes); void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes);
/* uses present in 1.5 buffer mapping feature */ /* uses present in 1.5 buffer mapping feature */
VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes); VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes);
/* collects bytes for sending to the gpu until all is pushed, which is when false is returned */ /* collects bytes for sending to the gpu until all is pushed, which is when false is returned */
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder, bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
void *bytes, void const *bytes,
size_t size); size_t size);
/* state */ /* state */
@ -194,15 +197,13 @@ void use_2d_pipeline(void);
void use_texture_mode(TextureMode mode); void use_texture_mode(TextureMode mode);
void upload_quad_vertices(Rect rect); void finally_render_quads(Primitive2D const primitives[],
struct QuadBatch batch,
void finally_render_sprites(Primitive2D const primitives[],
struct SpriteBatch batch,
VertexBuffer buffer); VertexBuffer buffer);
size_t get_sprite_payload_size(struct SpriteBatch batch); size_t get_quad_payload_size(struct QuadBatch batch);
bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch, bool push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch,
VertexBufferBuilder *builder, VertexBufferBuilder *builder,
Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3, Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3, Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,

View File

@ -3,6 +3,7 @@
#include "twn_util_c.h" #include "twn_util_c.h"
#include "twn_engine_context_c.h" #include "twn_engine_context_c.h"
#include "twn_text_c.h" #include "twn_text_c.h"
#include "twn_types.h"
#include <glad/glad.h> #include <glad/glad.h>
#include <stb_ds.h> #include <stb_ds.h>
@ -48,6 +49,34 @@ typedef struct ElementIndexedQuadWithoutColor {
} ElementIndexedQuadWithoutColor; } ElementIndexedQuadWithoutColor;
typedef struct ElementIndexedQuadWithoutTexture {
/* upper-left */
Vec2 v0;
Color c0;
/* bottom-left */
Vec2 v1;
Color c1;
/* bottom-right */
Vec2 v2;
Color c2;
/* upper-right */
Vec2 v3;
Color c3;
} ElementIndexedQuadWithoutTexture;
typedef struct ElementIndexedQuadWithoutColorWithoutTexture {
/* upper-left */
Vec2 v0;
/* bottom-left */
Vec2 v1;
/* bottom-right */
Vec2 v2;
/* upper-right */
Vec2 v3;
} ElementIndexedQuadWithoutColorWithoutTexture;
typedef enum { typedef enum {
PIPELINE_NO, PIPELINE_NO,
PIPELINE_SPACE, PIPELINE_SPACE,
@ -136,33 +165,6 @@ void use_2d_pipeline(void) {
} }
void upload_quad_vertices(Rect rect) {
/* client memory needs to be reachable on glDraw*, so */
static float vertices[6 * 2];
vertices[0] = rect.x; vertices[1] = rect.y;
vertices[2] = rect.x; vertices[3] = rect.y + rect.h;
vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h;
vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h;
vertices[8] = rect.x + rect.w; vertices[9] = rect.y;
vertices[10] = rect.x; vertices[11] = rect.y;
glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices);
}
void render_rectangle(const RectPrimitive *rectangle) {
glColor4ub(rectangle->color.r, rectangle->color.g,
rectangle->color.b, rectangle->color.a);
glEnableClientState(GL_VERTEX_ARRAY);
upload_quad_vertices(rectangle->rect);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableClientState(GL_VERTEX_ARRAY);
}
void render_circle(const CirclePrimitive *circle) { void render_circle(const CirclePrimitive *circle) {
SDL_Vertex *vertices = NULL; SDL_Vertex *vertices = NULL;
int *indices = NULL; int *indices = NULL;
@ -258,7 +260,7 @@ VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) {
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder, bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
void *bytes, size_t size) { void const *bytes, size_t size) {
if (builder->bytes_left == 0) if (builder->bytes_left == 0)
return false; return false;
@ -277,24 +279,30 @@ bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
} }
void finally_render_sprites(const Primitive2D primitives[], void finally_render_quads(const Primitive2D primitives[],
const struct SpriteBatch batch, const struct QuadBatch batch,
const VertexBuffer buffer) const VertexBuffer buffer)
{ {
(void)buffer; (void)buffer;
GLsizei off; GLsizei off = 0, voff = 0, uvoff = 0, coff = 0;
GLsizei voff;
GLsizei uvoff;
if (!batch.constant_colored) { if (!batch.constant_colored && batch.textured) {
off = offsetof(ElementIndexedQuad, v1); off = offsetof(ElementIndexedQuad, v1);
voff = offsetof(ElementIndexedQuad, v0); voff = offsetof(ElementIndexedQuad, v0);
uvoff = offsetof(ElementIndexedQuad, uv0); uvoff = offsetof(ElementIndexedQuad, uv0);
} else { coff = offsetof(ElementIndexedQuad, c0);
} else if (batch.constant_colored && batch.textured) {
off = offsetof(ElementIndexedQuadWithoutColor, v1); off = offsetof(ElementIndexedQuadWithoutColor, v1);
voff = offsetof(ElementIndexedQuadWithoutColor, v0); voff = offsetof(ElementIndexedQuadWithoutColor, v0);
uvoff = offsetof(ElementIndexedQuadWithoutColor, uv0); uvoff = offsetof(ElementIndexedQuadWithoutColor, uv0);
} else if (!batch.constant_colored && !batch.textured) {
off = offsetof(ElementIndexedQuadWithoutTexture, v1);
voff = offsetof(ElementIndexedQuadWithoutTexture, v0);
coff = offsetof(ElementIndexedQuad, c0);
} else if (batch.constant_colored && !batch.textured) {
off = offsetof(ElementIndexedQuadWithoutColorWithoutTexture, v1);
voff = offsetof(ElementIndexedQuadWithoutColorWithoutTexture, v0);
} }
/* vertex specification */ /* vertex specification */
@ -304,65 +312,82 @@ void finally_render_sprites(const Primitive2D primitives[],
off, off,
(void *)(size_t)voff); (void *)(size_t)voff);
if (batch.textured) {
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0); glClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, glTexCoordPointer(2,
GL_FLOAT, GL_FLOAT,
off, off,
(void *)(size_t)uvoff); (void *)(size_t)uvoff);
}
if (!batch.constant_colored) { if (!batch.constant_colored) {
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, glColorPointer(4,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
off, off,
(void *)offsetof(ElementIndexedQuad, c0)); (void *)(size_t)coff);
} else } else
glColor4ub(primitives[0].sprite.color.r, glColor4ub(primitives[0].sprite.color.r,
primitives[0].sprite.color.g, primitives[0].sprite.color.g,
primitives[0].sprite.color.b, primitives[0].sprite.color.b,
primitives[0].sprite.color.a); primitives[0].sprite.color.a);
if (batch.textured) {
if (!batch.repeat) if (!batch.repeat)
textures_bind(&ctx.texture_cache, primitives->sprite.texture_key); textures_bind(&ctx.texture_cache, primitives->sprite.texture_key);
else else
textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key); textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key);
}
bind_quad_element_buffer(); bind_quad_element_buffer();
glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL); glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL);
/* clear the state */ /* clear the state */
{
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
if (batch.textured)
glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
if (!batch.constant_colored) if (!batch.constant_colored)
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
if (batch.textured)
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
} }
size_t get_sprite_payload_size(struct SpriteBatch batch) { size_t get_quad_payload_size(struct QuadBatch batch) {
if (batch.constant_colored) if (batch.constant_colored && batch.textured)
return sizeof (ElementIndexedQuadWithoutColor); return sizeof (ElementIndexedQuadWithoutColor);
else else if (!batch.constant_colored && batch.textured)
return sizeof (ElementIndexedQuad); return sizeof (ElementIndexedQuad);
else if (batch.constant_colored && !batch.textured)
return sizeof (ElementIndexedQuadWithoutTexture);
else if (!batch.constant_colored && !batch.textured)
return sizeof (ElementIndexedQuadWithoutColorWithoutTexture);
SDL_assert(false);
return 0;
} }
bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch, bool push_quad_payload_to_vertex_buffer_builder(struct QuadBatch batch,
VertexBufferBuilder *builder, VertexBufferBuilder *builder,
Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3, Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3, Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
Color color) Color color)
{ {
if (!batch.constant_colored) { if (!batch.constant_colored && batch.textured) {
ElementIndexedQuad buffer_element = { ElementIndexedQuad const buffer_element = {
.v0 = v0, .v0 = v0,
.v1 = v1, .v1 = v1,
.v2 = v2, .v2 = v2,
@ -382,8 +407,8 @@ bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
} else { } else if (batch.constant_colored && batch.textured) {
ElementIndexedQuadWithoutColor buffer_element = { ElementIndexedQuadWithoutColor const buffer_element = {
.v0 = v0, .v0 = v0,
.v1 = v1, .v1 = v1,
.v2 = v2, .v2 = v2,
@ -395,8 +420,37 @@ bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
.uv3 = uv3, .uv3 = uv3,
}; };
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
} else if (!batch.constant_colored && !batch.textured) {
ElementIndexedQuadWithoutTexture const buffer_element = {
.v0 = v0,
.v1 = v1,
.v2 = v2,
.v3 = v3,
/* equal for all (flat shaded) */
.c0 = color,
.c1 = color,
.c2 = color,
.c3 = color,
};
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
} else if (batch.constant_colored && !batch.textured) {
ElementIndexedQuadWithoutColorWithoutTexture const buffer_element = {
.v0 = v0,
.v1 = v1,
.v2 = v2,
.v3 = v3,
};
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
} }
SDL_assert(false);
return false;
} }

View File

@ -26,7 +26,7 @@ void delete_vertex_buffer(VertexBuffer buffer) {
} }
void specify_vertex_buffer(VertexBuffer buffer, void *data, size_t bytes) { void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes) {
glBindBuffer(GL_ARRAY_BUFFER, buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW);
} }

30
src/rendering/twn_quads.c Normal file
View File

@ -0,0 +1,30 @@
#include "twn_draw_c.h"
#include <stddef.h>
struct QuadBatch collect_quad_batch(const Primitive2D primitives[], size_t len) {
if (primitives[0].type == PRIMITIVE_2D_SPRITE)
return collect_sprite_batch(primitives, len);
else if (primitives[0].type == PRIMITIVE_2D_RECT)
return collect_rect_batch(primitives, len);
else
SDL_assert(false);
return (struct QuadBatch){0};
}
/* assumes that orthogonal matrix setup is done already */
void render_quad_batch(const Primitive2D primitives[],
const struct QuadBatch batch)
{
if (primitives[0].type == PRIMITIVE_2D_SPRITE)
render_sprite_batch(primitives, batch);
else if (primitives[0].type == PRIMITIVE_2D_RECT)
render_rect_batch(primitives, batch);
else
SDL_assert(false);
return (struct QuadBatch){0};
}

104
src/rendering/twn_rects.c Normal file
View File

@ -0,0 +1,104 @@
#include "twn_draw.h"
#include "twn_draw_c.h"
#include "twn_engine_context_c.h"
#include "twn_util.h"
#include "twn_util_c.h"
#include "twn_textures_c.h"
#include "twn_option.h"
#include <stb_ds.h>
#include <stdbool.h>
#include <stddef.h>
void draw_rectangle(Rect rect, Color color) {
RectPrimitive rectangle = {
.rect = rect,
.color = color,
};
Primitive2D primitive = {
.type = PRIMITIVE_2D_RECT,
.rect = rectangle,
};
arrput(ctx.render_queue_2d, primitive);
}
struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len) {
SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
SDL_assert(primitives && len != 0);
struct QuadBatch batch = {
.mode = primitives[0].rect.color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY,
.constant_colored = true,
};
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].rect.color;
/* batch size is clamped so that reallocated short indices could be used */
if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
len = QUAD_ELEMENT_BUFFER_LENGTH;
for (size_t i = 0; i < len; ++i) {
const Primitive2D *const current = &primitives[i];
/* don't touch things other than rectangles */
if (current->type != PRIMITIVE_2D_RECT)
break;
/* only collect the same blend modes */
if ((current->rect.color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY) != batch.mode)
break;
/* if all are modulated the same we can skip sending the color data */
if (*(const uint32_t *)&current->rect.color != uniform_color)
batch.constant_colored = false;
++batch.size;
}
return batch;
}
/* assumes that orthogonal matrix setup is done already */
void render_rect_batch(const Primitive2D primitives[],
const struct QuadBatch batch)
{
SDL_assert(primitives && batch.size != 0);
SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */
static VertexBuffer vertex_array = 0;
if (vertex_array == 0)
vertex_array = create_vertex_buffer();
use_texture_mode(batch.mode);
/* vertex population over a vertex buffer builder interface */
{
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);
for (size_t i = 0; i < batch.size; ++i) {
/* render opaques front to back, to gain benefit of an early z rejection */
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
const RectPrimitive rect = primitives[cur].rect;
Vec2 v0 = { rect.rect.x, rect.rect.y };
Vec2 v1 = { rect.rect.x, rect.rect.y + rect.rect.h };
Vec2 v2 = { rect.rect.x + rect.rect.w, rect.rect.y + rect.rect.h };
Vec2 v3 = { rect.rect.x + rect.rect.w, rect.rect.y };
push_quad_payload_to_vertex_buffer_builder(
batch, &payload,
v0, v1, v2, v3,
(Vec2){0}, (Vec2){0}, (Vec2){0}, (Vec2){0},
rect.color);
}
}
finally_render_quads(primitives, batch, vertex_array);
}

View File

@ -60,15 +60,18 @@ void draw_sprite_args(const DrawSpriteArgs args) {
} }
struct SpriteBatch collect_sprite_batch(const Primitive2D primitives[], size_t len) { struct QuadBatch collect_sprite_batch(const Primitive2D primitives[], size_t len) {
/* assumes that first primitive is already a sprite */ SDL_assert(primitives[0].type == PRIMITIVE_2D_SPRITE);
SDL_assert(primitives && len != 0);
const uint16_t texture_key_id = primitives[0].sprite.texture_key.id; const uint16_t texture_key_id = primitives[0].sprite.texture_key.id;
const int atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key); const int atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key);
struct SpriteBatch batch = { struct QuadBatch batch = {
.mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key), .mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key),
.constant_colored = true, .constant_colored = true,
.repeat = primitives[0].sprite.repeat, .repeat = primitives[0].sprite.repeat,
.textured = true,
}; };
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].sprite.color; const uint32_t uniform_color = *(const uint32_t *)&primitives[0].sprite.color;
@ -116,9 +119,12 @@ struct SpriteBatch collect_sprite_batch(const Primitive2D primitives[], size_t l
/* assumes that orthogonal matrix setup is done already */ /* assumes that orthogonal matrix setup is done already */
void render_sprites(const Primitive2D primitives[], void render_sprite_batch(const Primitive2D primitives[],
const struct SpriteBatch batch) const struct QuadBatch batch)
{ {
SDL_assert(primitives && batch.size != 0);
SDL_assert(primitives[0].type == PRIMITIVE_2D_SPRITE);
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */ /* single vertex array is used for every batch with NULL glBufferData() trick at the end */
static VertexBuffer vertex_array = 0; static VertexBuffer vertex_array = 0;
if (vertex_array == 0) if (vertex_array == 0)
@ -131,10 +137,10 @@ void render_sprites(const Primitive2D primitives[],
/* vertex population over a vertex buffer builder interface */ /* vertex population over a vertex buffer builder interface */
{ {
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_sprite_payload_size(batch) * batch.size); VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);
for (size_t i = 0; i < batch.size; ++i) { for (size_t i = 0; i < batch.size; ++i) {
/* render opaques front to back */ /* render opaques front to back, to gain benefit of an early z rejection */
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1; const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
const SpritePrimitive sprite = primitives[cur].sprite; const SpritePrimitive sprite = primitives[cur].sprite;
@ -233,9 +239,9 @@ void render_sprites(const Primitive2D primitives[],
v3 = (Vec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y }; v3 = (Vec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y };
} }
push_sprite_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color); push_quad_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color);
} }
} }
finally_render_sprites(primitives, batch, vertex_array); finally_render_quads(primitives, batch, vertex_array);
} }

View File

@ -15,5 +15,6 @@
#include "rendering/twn_fog.c" #include "rendering/twn_fog.c"
#include "rendering/twn_skybox.c" #include "rendering/twn_skybox.c"
#include "rendering/twn_sprites.c" #include "rendering/twn_sprites.c"
#include "rendering/twn_rects.c"
#include "rendering/twn_text.c" #include "rendering/twn_text.c"
#include "rendering/twn_triangles.c" #include "rendering/twn_triangles.c"