aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md17
-rw-r--r--tests/functional/sokol_gl_test.c13
-rw-r--r--util/sokol_gl.h140
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;
}
}