textures working on web, separation of vertex and index buffers (actually matters)

This commit is contained in:
veclavtalica 2025-02-21 23:34:01 +03:00
parent d76ea06470
commit 814269ab0c
5 changed files with 150 additions and 71 deletions

View File

@ -18,13 +18,9 @@ void game_tick(void) {
struct state *state = ctx.udata; struct state *state = ctx.udata;
++state->counter; ++state->counter;
m_sprite("nothing!", m_sprite("twn.png",
(Rect) { (Rect) { .w = 128, .h = 64, },
.x = 0, m_opt(stretch, true)
.y = 0,
.w = 32,
.h = 32,
}
); );
} }

View File

@ -39,12 +39,18 @@ enum {
typedef uint32_t VertexBuffer; typedef uint32_t VertexBuffer;
typedef uint32_t IndexBuffer;
typedef struct VertexBufferBuilder { typedef struct VertexBufferBuilder {
size_t size; size_t size;
void *base; void *base;
} VertexBufferBuilder; } VertexBufferBuilder;
typedef struct IndexBufferBuilder {
size_t size;
void *base;
} IndexBufferBuilder;
typedef struct SpritePrimitive { typedef struct SpritePrimitive {
Rect rect; Rect rect;
@ -268,19 +274,19 @@ void text_cache_reset_arena(TextCache *cache);
/* vertex buffer */ /* vertex buffer */
VertexBuffer create_vertex_buffer(void); VertexBuffer create_vertex_buffer(void);
void restart_scratch_vertex_arrays(void); void restart_scratch_vertex_arrays(void);
VertexBuffer get_scratch_vertex_array(void); VertexBuffer get_scratch_vertex_array(void);
void delete_vertex_buffer(VertexBuffer buffer); void delete_vertex_buffer(VertexBuffer buffer);
void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes); void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes);
VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes); VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes);
void finish_vertex_builder(VertexBufferBuilder *builder); void finish_vertex_builder(VertexBufferBuilder *builder);
IndexBuffer create_index_buffer(void);
void delete_index_buffer(IndexBuffer buffer);
void specify_index_buffer(IndexBuffer buffer, void const *data, size_t bytes);
IndexBufferBuilder build_index_buffer(IndexBuffer buffer, size_t bytes);
void finish_index_builder(IndexBufferBuilder *builder);
/* 2d */ /* 2d */
/* fills two existing arrays with the geometry data of a circle */ /* fills two existing arrays with the geometry data of a circle */
@ -304,9 +310,8 @@ struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len);
void render_sprite_batch(const Primitive2D primitives[], struct QuadBatch batch); void render_sprite_batch(const Primitive2D primitives[], struct QuadBatch batch);
void render_rect_batch(const Primitive2D primitives[], struct QuadBatch batch); void render_rect_batch(const Primitive2D primitives[], struct QuadBatch batch);
VertexBuffer get_quad_element_buffer(void); IndexBuffer get_quad_element_buffer(void);
IndexBuffer get_circle_element_buffer(void);
VertexBuffer get_circle_element_buffer(void);
void render_circle(const CirclePrimitive *circle); void render_circle(const CirclePrimitive *circle);

View File

@ -50,8 +50,9 @@ bool render_init(void) {
log_info("OpenGL context: %s\n", glGetString(GL_VERSION)); log_info("OpenGL context: %s\n", glGetString(GL_VERSION));
#ifndef __EMSCRIPTEN__
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST); glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
#ifndef __EMSCRIPTEN__
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glHint(GL_FOG_HINT, GL_FASTEST); glHint(GL_FOG_HINT, GL_FASTEST);
@ -196,59 +197,79 @@ static void finally_use_2d_pipeline(void) {
pipeline_last_used = PIPELINE_2D; pipeline_last_used = PIPELINE_2D;
} }
static void setup_ghostly_texture_mode(void) {
#ifndef __EMSCRIPTEN__
static GLuint list = 0;
if (!list) {
list = glGenLists(1);
glNewList(list, GL_COMPILE);
#endif
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
glDepthMask(GL_FALSE);
glDisable(GL_ALPHA_TEST);
#ifndef __EMSCRIPTEN__
glEndList();
}
glCallList(list);
#endif
}
static void setup_seethrough_texture_mode(void) {
#ifndef __EMSCRIPTEN__
static GLuint list = 0;
if (!list) {
list = glGenLists(1);
glNewList(list, GL_COMPILE);
#endif
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_EQUAL, 1.0f);
#ifndef __EMSCRIPTEN__
glEndList();
}
glCallList(list);
#endif
}
static void setup_opaque_texture_mode(void) {
#ifndef __EMSCRIPTEN__
static GLuint list = 0;
if (!list) {
list = glGenLists(1);
glNewList(list, GL_COMPILE);
#endif
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glDisable(GL_ALPHA_TEST);
#ifndef __EMSCRIPTEN__
glEndList();
}
glCallList(list);
#endif
}
/* TODO: ensure we minimize depth func switching to enable Hi-Z (hierarchical depth) optimizations */ /* TODO: ensure we minimize depth func switching to enable Hi-Z (hierarchical depth) optimizations */
static void finally_use_texture_mode(TextureMode mode) { static void finally_use_texture_mode(TextureMode mode) {
if (texture_mode_last_used == mode) if (texture_mode_last_used == mode)
return; return;
#ifndef __EMSCRIPTEN__
static GLuint lists = 0;
if (!lists) {
lists = glGenLists(3);
/* ghostly */
glNewList(lists + 0, GL_COMPILE); {
#endif
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
glDepthMask(GL_FALSE);
glDisable(GL_ALPHA_TEST);
#ifndef __EMSCRIPTEN__
} glEndList();
/* seethrough */
glNewList(lists + 1, GL_COMPILE); {
#endif
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_EQUAL, 1.0f);
#ifndef __EMSCRIPTEN__
} glEndList();
/* opaque */
glNewList(lists + 2, GL_COMPILE); {
#endif
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glDisable(GL_ALPHA_TEST);
#ifndef __EMSCRIPTEN__
} glEndList();
}
if (mode == TEXTURE_MODE_GHOSTLY) { if (mode == TEXTURE_MODE_GHOSTLY) {
glCallList(lists + 0); setup_ghostly_texture_mode();
} else if (mode == TEXTURE_MODE_SEETHROUGH) { } else if (mode == TEXTURE_MODE_SEETHROUGH) {
glCallList(lists + 1); setup_seethrough_texture_mode();
} else { } else {
glCallList(lists + 2); setup_opaque_texture_mode();
} }
#endif
texture_mode_last_used = mode; texture_mode_last_used = mode;
} }
@ -287,6 +308,40 @@ void finish_vertex_builder(VertexBufferBuilder *builder) {
} }
IndexBufferBuilder build_index_buffer(IndexBuffer buffer, size_t bytes) {
SDL_assert(bytes != 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW);
#ifndef __EMSCRIPTEN__
void *mapping = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
#else
void *mapping = SDL_malloc(bytes);
#endif
if (!mapping)
CRY("build_vertex_buffer", "Error mapping a vertex array buffer");
return (IndexBufferBuilder) {
.base = mapping,
.size = bytes,
};
}
void finish_index_builder(IndexBufferBuilder *builder) {
#ifndef __EMSCRIPTEN__
if (!glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER))
CRY("finish_vertex_builder", "Error unmapping a vertex array buffer");
#else
glBufferData(GL_ELEMENT_ARRAY_BUFFER, builder->size, builder->base, GL_STREAM_DRAW);
SDL_free(builder->base);
#endif
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
builder->base = 0;
builder->size = 0;
}
static void load_cubemap_side(const char *path, GLenum target) { static void load_cubemap_side(const char *path, GLenum target) {
SDL_Surface *surface = textures_load_surface(path); SDL_Surface *surface = textures_load_surface(path);
/* TODO: sanity check whether all of them have same dimensions? */ /* TODO: sanity check whether all of them have same dimensions? */

View File

@ -29,6 +29,18 @@ void delete_vertex_buffer(VertexBuffer buffer) {
} }
IndexBuffer create_index_buffer(void) {
GLuint result;
glGenBuffers(1, &result);
return result;
}
void delete_index_buffer(IndexBuffer buffer) {
glDeleteBuffers(1, &buffer);
}
void specify_vertex_buffer(VertexBuffer buffer, void const *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);
@ -36,14 +48,21 @@ void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes)
} }
VertexBuffer get_quad_element_buffer(void) { void specify_index_buffer(IndexBuffer buffer, void const *data, size_t bytes) {
static VertexBuffer buffer = 0; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bytes, data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
IndexBuffer get_quad_element_buffer(void) {
static IndexBuffer buffer = 0;
/* it's only generated once at runtime */ /* it's only generated once at runtime */
/* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */ /* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */
if (buffer == 0) { if (buffer == 0) {
buffer = create_vertex_buffer(); buffer = create_index_buffer();
VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 );
for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) {
((GLshort *)builder.base)[i * 6 + 0] = (GLshort)(i * 4 + 0); ((GLshort *)builder.base)[i * 6 + 0] = (GLshort)(i * 4 + 0);
@ -54,7 +73,7 @@ VertexBuffer get_quad_element_buffer(void) {
((GLshort *)builder.base)[i * 6 + 5] = (GLshort)(i * 4 + 0); ((GLshort *)builder.base)[i * 6 + 5] = (GLshort)(i * 4 + 0);
} }
finish_vertex_builder(&builder); finish_index_builder(&builder);
} }
SDL_assert_always(buffer); SDL_assert_always(buffer);
@ -63,12 +82,12 @@ VertexBuffer get_quad_element_buffer(void) {
} }
VertexBuffer get_circle_element_buffer(void) { IndexBuffer get_circle_element_buffer(void) {
static VertexBuffer buffer = 0; static IndexBuffer buffer = 0;
if (buffer == 0) { if (buffer == 0) {
buffer = create_vertex_buffer(); buffer = create_index_buffer();
VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * (CIRCLE_VERTICES_MAX - 2) * 3); IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLshort) * (CIRCLE_VERTICES_MAX - 2) * 3);
for (size_t i = 1; i < CIRCLE_VERTICES_MAX - 1; ++i) { for (size_t i = 1; i < CIRCLE_VERTICES_MAX - 1; ++i) {
/* first one is center point index, always zero */ /* first one is center point index, always zero */
@ -79,7 +98,7 @@ VertexBuffer get_circle_element_buffer(void) {
((GLshort *)builder.base)[(i - 1) * 3 + 2] = (GLshort)i + 1; ((GLshort *)builder.base)[(i - 1) * 3 + 2] = (GLshort)i + 1;
} }
finish_vertex_builder(&builder); finish_index_builder(&builder);
} }
SDL_assert_always(buffer); SDL_assert_always(buffer);
@ -128,7 +147,11 @@ GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps, int c
SDL_assert(width > 0 && height > 0); SDL_assert(width > 0 && height > 0);
SDL_assert(channels > 0 && channels <= 4); SDL_assert(channels > 0 && channels <= 4);
#ifndef __EMSCRIPTEN__
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps);
#else
(void)generate_mipmaps;
#endif
if (filter == TEXTURE_FILTER_NEAREAST) { if (filter == TEXTURE_FILTER_NEAREAST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

View File

@ -881,7 +881,7 @@ int enter_loop(int argc, char **argv) {
profile_end("startup"); profile_end("startup");
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
emscripten_set_main_loop(main_loop, ctx.desired_frametime, true); emscripten_set_main_loop(main_loop, 0, true);
#else #else
while (ctx.is_running) while (ctx.is_running)
main_loop(); main_loop();