diff options
| -rw-r--r-- | CHANGELOG.md | 17 | ||||
| -rw-r--r-- | tests/functional/sokol_gl_test.c | 13 | ||||
| -rw-r--r-- | util/sokol_gl.h | 140 |
3 files changed, 133 insertions, 37 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 362b29a1..65cd3fda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ ## Updates +### 26-Aug-2024 + +A small behaviour update for sokol_gl.h (may be breaking if you call `sgl_error()`): + +- Instead of skipping rendering completely for the current frame if an error is encountered + (for instance the vertex- or command-buffer running full), sokol-gl will now + render all successfully recorded draw commands before the error was recorded. +- Minor breaking change: `sgl_error` has been changed from an error code enum to + a struct with a boolean flag per error type, that way no error information is + lost if multiple error happen in the same frame. +- Two new functions to query the current number of recorded vertices and commands + in the current frame: + - `int sgl_num_vertices(void)` + - `int sgl_num_commands(void)` + +Also see ticket #1092 and PR (fixme) for details! + ### 14-Aug-2024 The previously 'inofficial' Jai bindings at https://github.com/colinbellino/sokol-jai diff --git a/tests/functional/sokol_gl_test.c b/tests/functional/sokol_gl_test.c index 4fe4f455..24d4cd6e 100644 --- a/tests/functional/sokol_gl_test.c +++ b/tests/functional/sokol_gl_test.c @@ -35,7 +35,7 @@ UTEST(sokol_gl, default_init_shutdown) { T(_sgl.cur_ctx->vertices.ptr != 0); T(_sgl.cur_ctx->uniforms.ptr != 0); T(_sgl.cur_ctx->commands.ptr != 0); - T(_sgl.cur_ctx->error == SGL_NO_ERROR); + T(_sgl.cur_ctx->error.any == false); T(!_sgl.cur_ctx->in_begin); T(_sgl.cur_ctx->def_pip.id != SG_INVALID_ID); T(_sgl.pip_pool.pool.size == (_SGL_DEFAULT_PIPELINE_POOL_SIZE + 1)); @@ -43,6 +43,8 @@ UTEST(sokol_gl, default_init_shutdown) { TFLT(_sgl.cur_ctx->v, 0.0f, FLT_MIN); T(_sgl.cur_ctx->rgba == 0xFFFFFFFF); T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + T(sgl_num_commands() == 0); + T(sgl_num_vertices() == 0); shutdown(); } @@ -70,6 +72,7 @@ UTEST(sokol_gl, viewport) { UTEST(sokol_gl, scissor_rect) { init(); sgl_scissor_rect(10, 20, 30, 40, true); + T(sgl_num_commands() == 1); T(_sgl.cur_ctx->commands.next == 1); T(_sgl.cur_ctx->commands.ptr[0].cmd == SGL_COMMAND_SCISSOR_RECT); T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.x == 10); @@ -78,6 +81,7 @@ UTEST(sokol_gl, scissor_rect) { T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.h == 40); T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.origin_top_left); sgl_scissor_rect(50, 60, 70, 80, false); + T(sgl_num_commands() == 2); T(_sgl.cur_ctx->commands.next == 2); T(_sgl.cur_ctx->commands.ptr[1].cmd == SGL_COMMAND_SCISSOR_RECT); T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.x == 50); @@ -141,11 +145,15 @@ UTEST(sokol_gl, texture_noimage_nosampler) { } UTEST(sokol_gl, begin_end) { init(); + T(sgl_num_commands() == 0); + T(sgl_num_vertices() == 0); sgl_begin_triangles(); sgl_v3f(1.0f, 2.0f, 3.0f); sgl_v3f(4.0f, 5.0f, 6.0f); sgl_v3f(7.0f, 8.0f, 9.0f); sgl_end(); + T(sgl_num_commands() == 1); + T(sgl_num_vertices() == 3); T(_sgl.cur_ctx->base_vertex == 0); T(_sgl.cur_ctx->vertices.next == 3); T(_sgl.cur_ctx->commands.next == 1); @@ -282,7 +290,8 @@ UTEST(sokol_gl, destroy_active_context) { sgl_set_context(ctx); sgl_destroy_context(ctx); T(_sgl.cur_ctx == 0); - T(sgl_error() == SGL_ERROR_NO_CONTEXT); + T(sgl_error().no_context); + T(sgl_error().any); shutdown(); } diff --git a/util/sokol_gl.h b/util/sokol_gl.h index 15b38808..654cb0f6 100644 --- a/util/sokol_gl.h +++ b/util/sokol_gl.h @@ -372,8 +372,8 @@ the last call to sgl_draw() through sokol-gfx, and will 'rewind' the internal vertex-, uniform- and command-buffers. - --- each sokol-gl context tracks an internal error code, to query the - current error code for the currently active context call: + --- each sokol-gl context tracks internal error states which can + be obtains via: sgl_error_t sgl_error() @@ -381,18 +381,28 @@ sgl_error_t sgl_context_error(ctx); - ...which can return the following error codes: + ...this returns a struct with the following booleans: - SGL_NO_ERROR - all OK, no error occurred since last sgl_draw() - SGL_ERROR_VERTICES_FULL - internal vertex buffer is full (checked in sgl_end()) - SGL_ERROR_UNIFORMS_FULL - the internal uniforms buffer is full (checked in sgl_end()) - SGL_ERROR_COMMANDS_FULL - the internal command buffer is full (checked in sgl_end()) - SGL_ERROR_STACK_OVERFLOW - matrix- or pipeline-stack overflow - SGL_ERROR_STACK_UNDERFLOW - matrix- or pipeline-stack underflow - SGL_ERROR_NO_CONTEXT - the active context no longer exists + .any - true if any of the below errors is true + .vertices_full - internal vertex buffer is full (checked in sgl_end()) + .uniforms_full - the internal uniforms buffer is full (checked in sgl_end()) + .commands_full - the internal command buffer is full (checked in sgl_end()) + .stack_overflow - matrix- or pipeline-stack overflow + .stack_underflow - matrix- or pipeline-stack underflow + .no_context - the active context no longer exists - ...if sokol-gl is in an error-state, sgl_draw() will skip any rendering, - and reset the error code to SGL_NO_ERROR. + ...depending on the above error state, sgl_draw() may skip rendering + completely, or only draw partial geometry + + --- you can get the number of recorded vertices and draw commands in the current + frame and active sokol-gl context via: + + int sgl_num_vertices() + int sgl_num_commands() + + ...this allows you to check whether the vertex or command pools are running + full before the overflow actually happens (in this case you could also + check the error booleans in the result of sgl_error()). RENDER LAYERS ============= @@ -762,14 +772,14 @@ typedef struct sgl_context { uint32_t id; } sgl_context; Errors are reset each frame after calling sgl_draw(), get the last error code with sgl_error() */ -typedef enum sgl_error_t { - SGL_NO_ERROR = 0, - SGL_ERROR_VERTICES_FULL, - SGL_ERROR_UNIFORMS_FULL, - SGL_ERROR_COMMANDS_FULL, - SGL_ERROR_STACK_OVERFLOW, - SGL_ERROR_STACK_UNDERFLOW, - SGL_ERROR_NO_CONTEXT, +typedef struct sgl_error_t { + bool any; + bool vertices_full; + bool uniforms_full; + bool commands_full; + bool stack_overflow; + bool stack_underflow; + bool no_context; } sgl_error_t; /* @@ -832,6 +842,10 @@ SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx); SOKOL_GL_API_DECL sgl_context sgl_get_context(void); SOKOL_GL_API_DECL sgl_context sgl_default_context(void); +/* get information about recorded vertices and commands in current context */ +SOKOL_GL_API_DECL int sgl_num_vertices(void); +SOKOL_GL_API_DECL int sgl_num_commands(void); + /* draw recorded commands (call inside a sokol-gfx render pass) */ SOKOL_GL_API_DECL void sgl_draw(void); SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx); @@ -2342,7 +2356,7 @@ typedef struct { /* state tracking */ int base_vertex; - int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ + int quad_vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ sgl_error_t error; bool in_begin; int layer_id; @@ -2903,10 +2917,24 @@ static void _sgl_destroy_context(sgl_context ctx_id) { // ██ ██ ██ ███████ ██████ // // >>misc + +static sgl_error_t _sgl_error_defaults(void) { + sgl_error_t defaults = {0}; + return defaults; +} + +static int _sgl_num_vertices(_sgl_context_t* ctx) { + return ctx->vertices.next; +} + +static int _sgl_num_commands(_sgl_context_t* ctx) { + return ctx->commands.next; +} + static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) { ctx->in_begin = true; ctx->base_vertex = ctx->vertices.next; - ctx->vtx_count = 0; + ctx->quad_vtx_count = 0; ctx->cur_prim_type = mode; } @@ -2916,7 +2944,7 @@ static void _sgl_rewind(_sgl_context_t* ctx) { ctx->uniforms.next = 0; ctx->commands.next = 0; ctx->base_vertex = 0; - ctx->error = SGL_NO_ERROR; + ctx->error = _sgl_error_defaults(); ctx->layer_id = 0; ctx->matrix_dirty = true; } @@ -2938,7 +2966,8 @@ static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { if (ctx->vertices.next < ctx->vertices.cap) { return &ctx->vertices.ptr[ctx->vertices.next++]; } else { - ctx->error = SGL_ERROR_VERTICES_FULL; + ctx->error.vertices_full = true; + ctx->error.any = true; return 0; } } @@ -2947,7 +2976,8 @@ static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { if (ctx->uniforms.next < ctx->uniforms.cap) { return &ctx->uniforms.ptr[ctx->uniforms.next++]; } else { - ctx->error = SGL_ERROR_UNIFORMS_FULL; + ctx->error.uniforms_full = true; + ctx->error.any = true; return 0; } } @@ -2964,7 +2994,8 @@ static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { if (ctx->commands.next < ctx->commands.cap) { return &ctx->commands.ptr[ctx->commands.next++]; } else { - ctx->error = SGL_ERROR_COMMANDS_FULL; + ctx->error.commands_full = true; + ctx->error.any = true; return 0; } } @@ -2991,7 +3022,7 @@ static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, fl SOKOL_ASSERT(ctx->in_begin); _sgl_vertex_t* vtx; /* handle non-native primitive types */ - if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->vtx_count & 3) == 3)) { + if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->quad_vtx_count & 3) == 3)) { /* for quads, before writing the last quad vertex, reuse the first and third vertex to start the second triangle in the quad */ @@ -3007,7 +3038,7 @@ static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, fl vtx->rgba = rgba; vtx->psize = ctx->point_size; } - ctx->vtx_count++; + ctx->quad_vtx_count++; } static void _sgl_identity(_sgl_matrix_t* m) { @@ -3342,7 +3373,7 @@ static bool _sgl_is_default_context(sgl_context ctx_id) { static void _sgl_draw(_sgl_context_t* ctx, int layer_id) { SOKOL_ASSERT(ctx); - if ((ctx->error == SGL_NO_ERROR) && (ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) { sg_push_debug_group("sokol-gl"); uint32_t cur_pip_id = SG_INVALID_ID; @@ -3356,6 +3387,8 @@ static void _sgl_draw(_sgl_context_t* ctx, int layer_id) { sg_update_buffer(ctx->vbuf, &range); } + // render all successfully recorded commands (this may be less than the + // issued commands if we're in an error state) for (int i = 0; i < ctx->commands.next; i++) { const _sgl_command_t* cmd = &ctx->commands.ptr[i]; if (cmd->layer_id != layer_id) { @@ -3463,7 +3496,10 @@ SOKOL_API_IMPL sgl_error_t sgl_error(void) { if (ctx) { return ctx->error; } else { - return SGL_ERROR_NO_CONTEXT; + sgl_error_t err = _sgl_error_defaults(); + err.no_context = true; + err.any = true; + return err; } } @@ -3472,7 +3508,10 @@ SOKOL_API_IMPL sgl_error_t sgl_context_error(sgl_context ctx_id) { if (ctx) { return ctx->error; } else { - return SGL_ERROR_NO_CONTEXT; + sgl_error_t err = _sgl_error_defaults(); + err.no_context = true; + err.any = true; + return err; } } @@ -3521,6 +3560,26 @@ SOKOL_API_IMPL sgl_context sgl_default_context(void) { return SGL_DEFAULT_CONTEXT; } +SOKOL_API_IMPL int sgl_num_vertices(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return _sgl_num_vertices(ctx); + } else { + return 0; + } +} + +SOKOL_API_IMPL int sgl_num_commands(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return _sgl_num_commands(ctx); + } else { + return 0; + } +} + SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); _sgl_context_t* ctx = _sgl.cur_ctx; @@ -3576,7 +3635,8 @@ SOKOL_API_IMPL void sgl_push_pipeline(void) { ctx->pip_tos++; ctx->pip_stack[ctx->pip_tos] = ctx->pip_stack[ctx->pip_tos-1]; } else { - ctx->error = SGL_ERROR_STACK_OVERFLOW; + ctx->error.stack_overflow = true; + ctx->error.any = true; } } @@ -3589,7 +3649,8 @@ SOKOL_API_IMPL void sgl_pop_pipeline(void) { if (ctx->pip_tos > 0) { ctx->pip_tos--; } else { - ctx->error = SGL_ERROR_STACK_UNDERFLOW; + ctx->error.stack_underflow = true; + ctx->error.any = true; } } @@ -3778,6 +3839,7 @@ SOKOL_API_IMPL void sgl_end(void) { SOKOL_ASSERT(ctx->in_begin); SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex); ctx->in_begin = false; + bool matrix_dirty = ctx->matrix_dirty; if (matrix_dirty) { ctx->matrix_dirty = false; @@ -3787,6 +3849,12 @@ SOKOL_API_IMPL void sgl_end(void) { uni->tm = *_sgl_matrix_texture(ctx); } } + + // don't record any new commands when we're in an error state + if (ctx->error.any) { + return; + } + // check if command can be merged with current command sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img; @@ -4205,7 +4273,8 @@ SOKOL_GL_API_DECL void sgl_push_matrix(void) { _sgl_matrix_t* dst = _sgl_matrix(ctx); *dst = *src; } else { - ctx->error = SGL_ERROR_STACK_OVERFLOW; + ctx->error.stack_overflow = true; + ctx->error.any = true; } } @@ -4220,7 +4289,8 @@ SOKOL_GL_API_DECL void sgl_pop_matrix(void) { if (ctx->matrix_tos[ctx->cur_matrix_mode] > 0) { ctx->matrix_tos[ctx->cur_matrix_mode]--; } else { - ctx->error = SGL_ERROR_STACK_UNDERFLOW; + ctx->error.stack_underflow = true; + ctx->error.any = true; } } |