Browse Source

Merge pull request #744 from floooh/sokol-debugtext-layers

Implement layered rendering for sokol_debugtext.h

...also some code cleanup in sokol_gl.h and sokol_spine.h.
Andre Weissflog 2 years ago
parent
commit
02f39b3f3f
7 changed files with 529 additions and 244 deletions
  1. 8 0
      CHANGELOG.md
  2. 1 1
      README.md
  3. 92 17
      tests/functional/sokol_debugtext_test.c
  4. 48 48
      tests/functional/sokol_gl_test.c
  5. 261 63
      util/sokol_debugtext.h
  6. 75 71
      util/sokol_gl.h
  7. 44 44
      util/sokol_spine.h

+ 8 - 0
CHANGELOG.md

@@ -1,5 +1,13 @@
 ## Updates
 ## Updates
 
 
+- **16-Nov-2022**: Render layer support has been added to sokol_debugtext.h,
+  same general changes as in sokol_gl.h with two new functions:
+  sdtx_layer(layer_id) to select the layer to record text into, and
+  sdtx_draw_layer(layer_id) to draw the recorded text in that layer inside a
+  sokol-gfx render pass. The new sample [debugtext-layers-sapp](https://floooh.github.io/sokol-html5/debugtext-layers-sapp) demonstrates the feature together with
+  sokol-gl.
+
+
 - **11-Nov-2022**: sokol_gl.h has 2 new public API functions which enable
 - **11-Nov-2022**: sokol_gl.h has 2 new public API functions which enable
   layered rendering: sgl_layer(), sgl_draw_layer() (technically it's three
   layered rendering: sgl_layer(), sgl_draw_layer() (technically it's three
   functions: there's also sgl_context_draw_layer(), but that's just a variant of
   functions: there's also sgl_context_draw_layer(), but that's just a variant of

+ 1 - 1
README.md

@@ -4,7 +4,7 @@ Simple
 [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
 [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
 cross-platform libraries for C and C++, written in C.
 cross-platform libraries for C and C++, written in C.
 
 
-[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**11-Nov-2022** sokol_gl.h learned layered rendering)
+[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**16-Nov-2022** sokol_debugtext.h learned layered rendering)
 
 
 [![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)
 [![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)
 
 

+ 92 - 17
tests/functional/sokol_debugtext_test.c

@@ -57,11 +57,12 @@ UTEST(sokol_debugtext, default_init_shutdown) {
     T(_sdtx.cur_ctx->desc.color_format == 0);
     T(_sdtx.cur_ctx->desc.color_format == 0);
     T(_sdtx.cur_ctx->desc.depth_format == 0);
     T(_sdtx.cur_ctx->desc.depth_format == 0);
     T(_sdtx.cur_ctx->desc.sample_count == 0);
     T(_sdtx.cur_ctx->desc.sample_count == 0);
-    T(_sdtx.cur_ctx->cur_vertex_ptr);
-    T(_sdtx.cur_ctx->max_vertex_ptr);
-    T(_sdtx.cur_ctx->vertices);
-    T(_sdtx.cur_ctx->vertices == _sdtx.cur_ctx->cur_vertex_ptr);
-    T(_sdtx.cur_ctx->max_vertex_ptr == (_sdtx.cur_ctx->vertices + _SDTX_DEFAULT_CHAR_BUF_SIZE * 6));
+    T(_sdtx.cur_ctx->vertices.cap == _SDTX_DEFAULT_CHAR_BUF_SIZE * 6);
+    T(_sdtx.cur_ctx->vertices.next == 0);
+    T(_sdtx.cur_ctx->vertices.ptr);
+    T(_sdtx.cur_ctx->commands.cap == _SDTX_DEFAULT_MAX_COMMANDS);
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr);
     T(_sdtx.cur_ctx->vbuf.id != 0);
     T(_sdtx.cur_ctx->vbuf.id != 0);
     T(_sdtx.cur_ctx->pip.id != 0);
     T(_sdtx.cur_ctx->pip.id != 0);
     TFLT(_sdtx.cur_ctx->canvas_size.x, 640.0f);
     TFLT(_sdtx.cur_ctx->canvas_size.x, 640.0f);
@@ -112,7 +113,7 @@ UTEST(sokol_debugtext, init_with_params) {
     T(_sdtx.cur_ctx->desc.color_format == SG_PIXELFORMAT_RGBA8);
     T(_sdtx.cur_ctx->desc.color_format == SG_PIXELFORMAT_RGBA8);
     T(_sdtx.cur_ctx->desc.depth_format == SG_PIXELFORMAT_DEPTH_STENCIL);
     T(_sdtx.cur_ctx->desc.depth_format == SG_PIXELFORMAT_DEPTH_STENCIL);
     T(_sdtx.cur_ctx->desc.sample_count == 4);
     T(_sdtx.cur_ctx->desc.sample_count == 4);
-    T(_sdtx.cur_ctx->max_vertex_ptr == (_sdtx.cur_ctx->vertices + 256 * 6));
+    T(_sdtx.cur_ctx->vertices.cap == (256 * 6));
     TFLT(_sdtx.cur_ctx->canvas_size.x, 320.0f);
     TFLT(_sdtx.cur_ctx->canvas_size.x, 320.0f);
     TFLT(_sdtx.cur_ctx->canvas_size.y, 200.0f);
     TFLT(_sdtx.cur_ctx->canvas_size.y, 200.0f);
     TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 320.0f);
     TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 320.0f);
@@ -144,9 +145,9 @@ UTEST(sokol_debugtext, make_destroy_context) {
     T(ctx->desc.color_format == SG_PIXELFORMAT_RGBA32F);
     T(ctx->desc.color_format == SG_PIXELFORMAT_RGBA32F);
     T(ctx->desc.depth_format == 0);
     T(ctx->desc.depth_format == 0);
     T(ctx->desc.sample_count == 2);
     T(ctx->desc.sample_count == 2);
-    T(ctx->vertices);
-    T(ctx->cur_vertex_ptr == ctx->vertices);
-    T(ctx->max_vertex_ptr == ctx->vertices + 64 * 6);
+    T(ctx->vertices.ptr);
+    T(ctx->vertices.next == 0);
+    T(ctx->vertices.cap == (64 * 6));
     TFLT(ctx->canvas_size.x, 1024.0f);
     TFLT(ctx->canvas_size.x, 1024.0f);
     TFLT(ctx->canvas_size.y, 768.0f);
     TFLT(ctx->canvas_size.y, 768.0f);
     TFLT(ctx->glyph_size.x, 8.0f / 1024.0f);
     TFLT(ctx->glyph_size.x, 8.0f / 1024.0f);
@@ -155,7 +156,7 @@ UTEST(sokol_debugtext, make_destroy_context) {
     sdtx_destroy_context(ctx_id);
     sdtx_destroy_context(ctx_id);
     T(0 == _sdtx_lookup_context(ctx_id.id));
     T(0 == _sdtx_lookup_context(ctx_id.id));
     T(ctx->desc.char_buf_size == 0);
     T(ctx->desc.char_buf_size == 0);
-    T(ctx->vertices == 0);
+    T(ctx->vertices.ptr == 0);
     shutdown();
     shutdown();
 }
 }
 
 
@@ -336,7 +337,7 @@ UTEST(sokol_debugtext, vertex_overflow) {
     sdtx_puts("1234567890");
     sdtx_puts("1234567890");
     sdtx_putr("1234567890", 5);
     sdtx_putr("1234567890", 5);
     sdtx_printf("Hello World %d!\n", 12);
     sdtx_printf("Hello World %d!\n", 12);
-    T(_sdtx.cur_ctx->cur_vertex_ptr == _sdtx.cur_ctx->max_vertex_ptr);
+    T(_sdtx.cur_ctx->vertices.next == _sdtx.cur_ctx->vertices.cap);
     shutdown();
     shutdown();
 }
 }
 
 
@@ -399,7 +400,7 @@ UTEST(sokol_debugtext, rewind_after_draw) {
     sdtx_font(3);
     sdtx_font(3);
     T(_sdtx.cur_ctx->cur_font == 3);
     T(_sdtx.cur_ctx->cur_font == 3);
     sdtx_printf("Hello World!\n");
     sdtx_printf("Hello World!\n");
-    T(_sdtx.cur_ctx->cur_vertex_ptr != _sdtx.cur_ctx->vertices);
+    T(_sdtx.cur_ctx->vertices.next != 0);
     sg_begin_default_pass(&(sg_pass_action){ 0 }, 256, 256);
     sg_begin_default_pass(&(sg_pass_action){ 0 }, 256, 256);
     sdtx_draw();
     sdtx_draw();
     sg_end_pass();
     sg_end_pass();
@@ -411,21 +412,21 @@ UTEST(sokol_debugtext, rewind_after_draw) {
     TFLT(_sdtx.cur_ctx->pos.x, 0);
     TFLT(_sdtx.cur_ctx->pos.x, 0);
     TFLT(_sdtx.cur_ctx->pos.x, 0);
     TFLT(_sdtx.cur_ctx->pos.x, 0);
     T(_sdtx.cur_ctx->cur_font == 0);
     T(_sdtx.cur_ctx->cur_font == 0);
-    T(_sdtx.cur_ctx->cur_vertex_ptr == _sdtx.cur_ctx->vertices);
+    T(_sdtx.cur_ctx->vertices.next == 0);
     shutdown();
     shutdown();
 }
 }
 
 
 UTEST(sokol_debugtext, putr) {
 UTEST(sokol_debugtext, putr) {
     // test if sdtx_putr() draws the right amount of characters
     // test if sdtx_putr() draws the right amount of characters
     init();
     init();
-    _sdtx_vertex_t* start_ptr = _sdtx.cur_ctx->cur_vertex_ptr;
+    int start_index = _sdtx.cur_ctx->vertices.next;
     sdtx_putr("Hello World!", 5);
     sdtx_putr("Hello World!", 5);
-    T((5 * 6) == (_sdtx.cur_ctx->cur_vertex_ptr - start_ptr));
+    T((5 * 6) == (_sdtx.cur_ctx->vertices.next - start_index));
 
 
-    start_ptr = _sdtx.cur_ctx->cur_vertex_ptr;
+    start_index = _sdtx.cur_ctx->vertices.next;
     sdtx_putr("Hello!\n\n\n\n\n\n\n\n\n\n\n", 10);
     sdtx_putr("Hello!\n\n\n\n\n\n\n\n\n\n\n", 10);
     // NOTE: the \n's don't result in rendered vertices
     // NOTE: the \n's don't result in rendered vertices
-    T((6 * 6) == (_sdtx.cur_ctx->cur_vertex_ptr - start_ptr));
+    T((6 * 6) == (_sdtx.cur_ctx->vertices.next - start_index));
     shutdown();
     shutdown();
 }
 }
 
 
@@ -434,3 +435,77 @@ UTEST(sokol_debugtext, default_context) {
     T(sdtx_default_context().id == SDTX_DEFAULT_CONTEXT.id);
     T(sdtx_default_context().id == SDTX_DEFAULT_CONTEXT.id);
     shutdown();
     shutdown();
 }
 }
+
+// switching layers without any text inbetween should not advance the current draw command
+UTEST(sokol_debug_text, empty_layers) {
+    init();
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
+    sdtx_layer(1);
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 1);
+    sdtx_layer(2);
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 2);
+    sdtx_layer(0);
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
+    shutdown();
+}
+
+// switching layers with text inbetween should advance the current draw command
+UTEST(sokol_debug_text, non_empty_layers) {
+    init();
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
+    T(_sdtx.cur_ctx->commands.ptr[0].first_vertex == 0);
+    T(_sdtx.cur_ctx->commands.ptr[0].num_vertices == 0);
+    sdtx_puts("123");
+    T(_sdtx.cur_ctx->commands.next == 1);
+    T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
+    T(_sdtx.cur_ctx->commands.ptr[0].first_vertex == 0);
+    T(_sdtx.cur_ctx->commands.ptr[0].num_vertices == (3 * 6));
+    sdtx_layer(1);
+    sdtx_puts("1234");
+    T(_sdtx.cur_ctx->commands.next == 2);
+    T(_sdtx.cur_ctx->commands.ptr[1].layer_id == 1);
+    T(_sdtx.cur_ctx->commands.ptr[1].first_vertex == (3 * 6));
+    T(_sdtx.cur_ctx->commands.ptr[1].num_vertices == (4 * 6));
+    // switching to same layer should not start a new draw commands
+    sdtx_layer(1);
+    sdtx_puts("12345");
+    T(_sdtx.cur_ctx->commands.next == 2);
+    T(_sdtx.cur_ctx->commands.ptr[1].layer_id == 1);
+    T(_sdtx.cur_ctx->commands.ptr[1].first_vertex == (3 * 6));
+    T(_sdtx.cur_ctx->commands.ptr[1].num_vertices == (9 * 6));
+    sdtx_layer(0);
+    sdtx_puts("123456");
+    T(_sdtx.cur_ctx->commands.next == 3);
+    T(_sdtx.cur_ctx->commands.ptr[2].layer_id == 0);
+    T(_sdtx.cur_ctx->commands.ptr[2].first_vertex == (12 * 6));
+    T(_sdtx.cur_ctx->commands.ptr[2].num_vertices == (6 * 6));
+    shutdown();
+}
+
+UTEST(sokol_debug_text, command_buffer_overflow) {
+    init_with(&(sdtx_desc_t){
+        .context = {
+            .max_commands = 4
+        }
+    });
+    sdtx_puts("0");
+    T(_sdtx.cur_ctx->commands.next == 1);
+    sdtx_layer(1);
+    sdtx_puts("1");
+    T(_sdtx.cur_ctx->commands.next == 2);
+    sdtx_layer(2);
+    sdtx_puts("2");
+    T(_sdtx.cur_ctx->commands.next == 3);
+    sdtx_layer(3);
+    sdtx_puts("3");
+    T(_sdtx.cur_ctx->commands.next == 4);
+    // from here on should fail
+    sdtx_layer(4);
+    sdtx_puts("4");
+    T(_sdtx.cur_ctx->commands.next == 4);
+}

+ 48 - 48
tests/functional/sokol_gl_test.c

@@ -26,15 +26,15 @@ UTEST(sokol_gl, default_init_shutdown) {
     T(_sgl.def_ctx_id.id == SGL_DEFAULT_CONTEXT.id);
     T(_sgl.def_ctx_id.id == SGL_DEFAULT_CONTEXT.id);
     T(_sgl.cur_ctx_id.id == _sgl.def_ctx_id.id);
     T(_sgl.cur_ctx_id.id == _sgl.def_ctx_id.id);
     T(_sgl.cur_ctx);
     T(_sgl.cur_ctx);
-    T(_sgl.cur_ctx->num_vertices == 65536);
-    T(_sgl.cur_ctx->num_commands == 16384);
-    T(_sgl.cur_ctx->num_uniforms == 16384);
-    T(_sgl.cur_ctx->cur_vertex == 0);
-    T(_sgl.cur_ctx->cur_command == 0);
-    T(_sgl.cur_ctx->cur_uniform == 0);
-    T(_sgl.cur_ctx->vertices != 0);
-    T(_sgl.cur_ctx->uniforms != 0);
-    T(_sgl.cur_ctx->commands != 0);
+    T(_sgl.cur_ctx->vertices.cap == 65536);
+    T(_sgl.cur_ctx->commands.cap == 16384);
+    T(_sgl.cur_ctx->uniforms.cap == 16384);
+    T(_sgl.cur_ctx->vertices.next == 0);
+    T(_sgl.cur_ctx->commands.next == 0);
+    T(_sgl.cur_ctx->uniforms.next == 0);
+    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 == SGL_NO_ERROR);
     T(!_sgl.cur_ctx->in_begin);
     T(!_sgl.cur_ctx->in_begin);
     T(_sgl.cur_ctx->def_pip.id != SG_INVALID_ID);
     T(_sgl.cur_ctx->def_pip.id != SG_INVALID_ID);
@@ -49,42 +49,42 @@ UTEST(sokol_gl, default_init_shutdown) {
 UTEST(sokol_gl, viewport) {
 UTEST(sokol_gl, viewport) {
     init();
     init();
     sgl_viewport(1, 2, 3, 4, true);
     sgl_viewport(1, 2, 3, 4, true);
-    T(_sgl.cur_ctx->cur_command == 1);
-    T(_sgl.cur_ctx->commands[0].cmd == SGL_COMMAND_VIEWPORT);
-    T(_sgl.cur_ctx->commands[0].args.viewport.x == 1);
-    T(_sgl.cur_ctx->commands[0].args.viewport.y == 2);
-    T(_sgl.cur_ctx->commands[0].args.viewport.w == 3);
-    T(_sgl.cur_ctx->commands[0].args.viewport.h == 4);
-    T(_sgl.cur_ctx->commands[0].args.viewport.origin_top_left);
+    T(_sgl.cur_ctx->commands.next == 1);
+    T(_sgl.cur_ctx->commands.ptr[0].cmd == SGL_COMMAND_VIEWPORT);
+    T(_sgl.cur_ctx->commands.ptr[0].args.viewport.x == 1);
+    T(_sgl.cur_ctx->commands.ptr[0].args.viewport.y == 2);
+    T(_sgl.cur_ctx->commands.ptr[0].args.viewport.w == 3);
+    T(_sgl.cur_ctx->commands.ptr[0].args.viewport.h == 4);
+    T(_sgl.cur_ctx->commands.ptr[0].args.viewport.origin_top_left);
     sgl_viewport(5, 6, 7, 8, false);
     sgl_viewport(5, 6, 7, 8, false);
-    T(_sgl.cur_ctx->cur_command == 2);
-    T(_sgl.cur_ctx->commands[1].cmd == SGL_COMMAND_VIEWPORT);
-    T(_sgl.cur_ctx->commands[1].args.viewport.x == 5);
-    T(_sgl.cur_ctx->commands[1].args.viewport.y == 6);
-    T(_sgl.cur_ctx->commands[1].args.viewport.w == 7);
-    T(_sgl.cur_ctx->commands[1].args.viewport.h == 8);
-    T(!_sgl.cur_ctx->commands[1].args.viewport.origin_top_left);
+    T(_sgl.cur_ctx->commands.next == 2);
+    T(_sgl.cur_ctx->commands.ptr[1].cmd == SGL_COMMAND_VIEWPORT);
+    T(_sgl.cur_ctx->commands.ptr[1].args.viewport.x == 5);
+    T(_sgl.cur_ctx->commands.ptr[1].args.viewport.y == 6);
+    T(_sgl.cur_ctx->commands.ptr[1].args.viewport.w == 7);
+    T(_sgl.cur_ctx->commands.ptr[1].args.viewport.h == 8);
+    T(!_sgl.cur_ctx->commands.ptr[1].args.viewport.origin_top_left);
     shutdown();
     shutdown();
 }
 }
 
 
 UTEST(sokol_gl, scissor_rect) {
 UTEST(sokol_gl, scissor_rect) {
     init();
     init();
     sgl_scissor_rect(10, 20, 30, 40, true);
     sgl_scissor_rect(10, 20, 30, 40, true);
-    T(_sgl.cur_ctx->cur_command == 1);
-    T(_sgl.cur_ctx->commands[0].cmd == SGL_COMMAND_SCISSOR_RECT);
-    T(_sgl.cur_ctx->commands[0].args.scissor_rect.x == 10);
-    T(_sgl.cur_ctx->commands[0].args.scissor_rect.y == 20);
-    T(_sgl.cur_ctx->commands[0].args.scissor_rect.w == 30);
-    T(_sgl.cur_ctx->commands[0].args.scissor_rect.h == 40);
-    T(_sgl.cur_ctx->commands[0].args.scissor_rect.origin_top_left);
+    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);
+    T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.y == 20);
+    T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.w == 30);
+    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);
     sgl_scissor_rect(50, 60, 70, 80, false);
-    T(_sgl.cur_ctx->cur_command == 2);
-    T(_sgl.cur_ctx->commands[1].cmd == SGL_COMMAND_SCISSOR_RECT);
-    T(_sgl.cur_ctx->commands[1].args.scissor_rect.x == 50);
-    T(_sgl.cur_ctx->commands[1].args.scissor_rect.y == 60);
-    T(_sgl.cur_ctx->commands[1].args.scissor_rect.w == 70);
-    T(_sgl.cur_ctx->commands[1].args.scissor_rect.h == 80);
-    T(!_sgl.cur_ctx->commands[1].args.scissor_rect.origin_top_left);
+    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);
+    T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.y == 60);
+    T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.w == 70);
+    T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.h == 80);
+    T(!_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.origin_top_left);
     shutdown();
     shutdown();
 }
 }
 
 
@@ -111,14 +111,14 @@ UTEST(sokol_gl, begin_end) {
     sgl_v3f(7.0f, 8.0f, 9.0f);
     sgl_v3f(7.0f, 8.0f, 9.0f);
     sgl_end();
     sgl_end();
     T(_sgl.cur_ctx->base_vertex == 0);
     T(_sgl.cur_ctx->base_vertex == 0);
-    T(_sgl.cur_ctx->cur_vertex == 3);
-    T(_sgl.cur_ctx->cur_command == 1);
-    T(_sgl.cur_ctx->cur_uniform == 1);
-    T(_sgl.cur_ctx->commands[0].cmd == SGL_COMMAND_DRAW);
-    T(_sgl.cur_ctx->commands[0].args.draw.pip.id == _sgl_pipeline_at(_sgl.cur_ctx->def_pip.id)->pip[SGL_PRIMITIVETYPE_TRIANGLES].id);
-    T(_sgl.cur_ctx->commands[0].args.draw.base_vertex == 0);
-    T(_sgl.cur_ctx->commands[0].args.draw.num_vertices == 3);
-    T(_sgl.cur_ctx->commands[0].args.draw.uniform_index == 0);
+    T(_sgl.cur_ctx->vertices.next == 3);
+    T(_sgl.cur_ctx->commands.next == 1);
+    T(_sgl.cur_ctx->uniforms.next == 1);
+    T(_sgl.cur_ctx->commands.ptr[0].cmd == SGL_COMMAND_DRAW);
+    T(_sgl.cur_ctx->commands.ptr[0].args.draw.pip.id == _sgl_pipeline_at(_sgl.cur_ctx->def_pip.id)->pip[SGL_PRIMITIVETYPE_TRIANGLES].id);
+    T(_sgl.cur_ctx->commands.ptr[0].args.draw.base_vertex == 0);
+    T(_sgl.cur_ctx->commands.ptr[0].args.draw.num_vertices == 3);
+    T(_sgl.cur_ctx->commands.ptr[0].args.draw.uniform_index == 0);
     shutdown();
     shutdown();
 }
 }
 
 
@@ -223,9 +223,9 @@ UTEST(sokol_gl, make_destroy_contexts) {
     // creating a context should not change the current context
     // creating a context should not change the current context
     T(ctx.id != _sgl.cur_ctx_id.id);
     T(ctx.id != _sgl.cur_ctx_id.id);
     sgl_set_context(ctx);
     sgl_set_context(ctx);
-    T(_sgl.cur_ctx->num_vertices == 1024);
-    T(_sgl.cur_ctx->num_commands == 256);
-    T(_sgl.cur_ctx->num_uniforms == 256);
+    T(_sgl.cur_ctx->vertices.cap == 1024);
+    T(_sgl.cur_ctx->commands.cap == 256);
+    T(_sgl.cur_ctx->uniforms.cap == 256);
     T(ctx.id == _sgl.cur_ctx_id.id);
     T(ctx.id == _sgl.cur_ctx_id.id);
     T(sgl_get_context().id == ctx.id);
     T(sgl_get_context().id == ctx.id);
     sgl_set_context(SGL_DEFAULT_CONTEXT);
     sgl_set_context(SGL_DEFAULT_CONTEXT);

+ 261 - 63
util/sokol_debugtext.h

@@ -93,6 +93,11 @@
             be active right after sdtx_setup(), or when calling
             be active right after sdtx_setup(), or when calling
             sdtx_set_context(SDTX_DEFAULT_CONTEXT):
             sdtx_set_context(SDTX_DEFAULT_CONTEXT):
 
 
+            .max_commands (default: 4096)
+                The max number of render commands that can be recorded
+                into the internal command buffer. This directly translates
+                to the number of render layer changes in a single frame.
+
             .char_buf_size (default: 4096)
             .char_buf_size (default: 4096)
                 The number of characters that can be rendered per frame in this
                 The number of characters that can be rendered per frame in this
                 context, defines the size of an internal fixed-size vertex
                 context, defines the size of an internal fixed-size vertex
@@ -220,14 +225,36 @@
             \n  - carriage return + line feed (same as stdx_crlf())
             \n  - carriage return + line feed (same as stdx_crlf())
             \t  - a tab character
             \t  - a tab character
 
 
+    --- You can 'record' text into render layers, this allows to mix/interleave
+        sokol-debugtext rendering with other rendering operations inside
+        sokol-gfx render passes. To start recording text into a different render
+        layer, call:
+
+            sdtx_layer(int layer_id)
+
+        ...outside a sokol-gfx render pass.
+
     --- finally, from within a sokol-gfx render pass, call:
     --- finally, from within a sokol-gfx render pass, call:
 
 
             sdtx_draw()
             sdtx_draw()
 
 
-        ...to actually render the text. Calling sdtx_draw() will also rewind
-        the text context:
+        ...for non-layered rendering, or to draw a specific layer:
+
+            sdtx_draw_layer(int layer_id)
+
+        NOTE that sdtx_draw() is equivalent to:
 
 
-            - the internal vertex buffer pointer is reset to the beginning
+            sdtx_draw_layer(0)
+
+        ...so sdtx_draw() will *NOT* render all text layers, instead it will
+        only render the 'default layer' 0.
+
+    --- at the end of a frame (defined by the call to sg_commit()), sokol-debugtext
+        will rewind all contexts:
+
+            - the internal vertex index is set to 0
+            - the internal command index is set to 0
+            - the current layer id is set to 0
             - the current font is set to 0
             - the current font is set to 0
             - the cursor position is reset
             - the cursor position is reset
 
 
@@ -268,7 +295,8 @@
         - the origin position
         - the origin position
         - the current cursor position
         - the current cursor position
         - the current tab width
         - the current tab width
-        - and the current color
+        - the current color
+        - and the current layer-id
 
 
     You can get the currently active context with:
     You can get the currently active context with:
 
 
@@ -291,6 +319,12 @@
     If a context is set as active that no longer exists, all sokol-debugtext
     If a context is set as active that no longer exists, all sokol-debugtext
     functions that require an active context will silently fail.
     functions that require an active context will silently fail.
 
 
+    You can directly draw the recorded text in a specific context without
+    setting the active context:
+
+        sdtx_context_draw(ctx)
+        sdtx_context_draw_layer(ctx, layer_id)
+
     USING YOUR OWN FONT DATA
     USING YOUR OWN FONT DATA
     ========================
     ========================
 
 
@@ -540,6 +574,7 @@ typedef struct sdtx_font_desc_t {
     of text.
     of text.
 */
 */
 typedef struct sdtx_context_desc_t {
 typedef struct sdtx_context_desc_t {
+    int max_commands;                       // max number of draw commands, each layer transition counts as a command, default: 4096
     int char_buf_size;                      // max number of characters rendered in one frame, default: 4096
     int char_buf_size;                      // max number of characters rendered in one frame, default: 4096
     float canvas_width;                     // the initial virtual canvas width, default: 640
     float canvas_width;                     // the initial virtual canvas width, default: 640
     float canvas_height;                    // the initial virtual canvas height, default: 400
     float canvas_height;                    // the initial virtual canvas height, default: 400
@@ -618,8 +653,14 @@ SOKOL_DEBUGTEXT_API_DECL void sdtx_set_context(sdtx_context ctx);
 SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_get_context(void);
 SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_get_context(void);
 SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_default_context(void);
 SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_default_context(void);
 
 
-/* draw and rewind the current context */
+/* drawing functions (call inside sokol-gfx render pass) */
 SOKOL_DEBUGTEXT_API_DECL void sdtx_draw(void);
 SOKOL_DEBUGTEXT_API_DECL void sdtx_draw(void);
+SOKOL_DEBUGTEXT_API_DECL void sdtx_context_draw(sdtx_context ctx);
+SOKOL_DEBUGTEXT_API_DECL void sdtx_draw_layer(int layer_id);
+SOKOL_DEBUGTEXT_API_DECL void sdtx_context_draw_layer(sdtx_context ctx, int layer_id);
+
+/* switch render layer */
+SOKOL_DEBUGTEXT_API_DECL void sdtx_layer(int layer_id);
 
 
 /* switch to a different font */
 /* switch to a different font */
 SOKOL_DEBUGTEXT_API_DECL void sdtx_font(int font_index);
 SOKOL_DEBUGTEXT_API_DECL void sdtx_font(int font_index);
@@ -718,9 +759,10 @@ inline sdtx_context sdtx_make_context(const sdtx_context_desc_t& desc) { return
 #define _sdtx_def(val, def) (((val) == 0) ? (def) : (val))
 #define _sdtx_def(val, def) (((val) == 0) ? (def) : (val))
 #define _SDTX_INIT_COOKIE (0xACBAABCA)
 #define _SDTX_INIT_COOKIE (0xACBAABCA)
 
 
+#define _SDTX_DEFAULT_MAX_COMMANDS (4096)
 #define _SDTX_DEFAULT_CONTEXT_POOL_SIZE (8)
 #define _SDTX_DEFAULT_CONTEXT_POOL_SIZE (8)
-#define _SDTX_DEFAULT_CHAR_BUF_SIZE (1<<12)
-#define _SDTX_DEFAULT_PRINTF_BUF_SIZE (1<<12)
+#define _SDTX_DEFAULT_CHAR_BUF_SIZE (4096)
+#define _SDTX_DEFAULT_PRINTF_BUF_SIZE (4096)
 #define _SDTX_DEFAULT_CANVAS_WIDTH (640)
 #define _SDTX_DEFAULT_CANVAS_WIDTH (640)
 #define _SDTX_DEFAULT_CANVAS_HEIGHT (480)
 #define _SDTX_DEFAULT_CANVAS_HEIGHT (480)
 #define _SDTX_DEFAULT_TAB_WIDTH (4)
 #define _SDTX_DEFAULT_TAB_WIDTH (4)
@@ -3478,15 +3520,31 @@ typedef struct {
     uint32_t color;
     uint32_t color;
 } _sdtx_vertex_t;
 } _sdtx_vertex_t;
 
 
+typedef struct {
+    int layer_id;
+    int first_vertex;
+    int num_vertices;
+} _sdtx_command_t;
+
 typedef struct {
 typedef struct {
     _sdtx_slot_t slot;
     _sdtx_slot_t slot;
     sdtx_context_desc_t desc;
     sdtx_context_desc_t desc;
-    _sdtx_vertex_t* cur_vertex_ptr;
-    const _sdtx_vertex_t* max_vertex_ptr;
-    _sdtx_vertex_t* vertices;
+    uint32_t frame_id;
+    uint32_t update_frame_id;
+    struct {
+        int cap;
+        int next;
+        _sdtx_vertex_t* ptr;
+    } vertices;
+    struct {
+        int cap;
+        int next;
+        _sdtx_command_t* ptr;
+    } commands;
     sg_buffer vbuf;
     sg_buffer vbuf;
     sg_pipeline pip;
     sg_pipeline pip;
     int cur_font;
     int cur_font;
+    int cur_layer_id;
     _sdtx_float2_t canvas_size;
     _sdtx_float2_t canvas_size;
     _sdtx_float2_t glyph_size;
     _sdtx_float2_t glyph_size;
     _sdtx_float2_t origin;
     _sdtx_float2_t origin;
@@ -3704,6 +3762,7 @@ static sdtx_context _sdtx_alloc_context(void) {
 
 
 static sdtx_context_desc_t _sdtx_context_desc_defaults(const sdtx_context_desc_t* desc) {
 static sdtx_context_desc_t _sdtx_context_desc_defaults(const sdtx_context_desc_t* desc) {
     sdtx_context_desc_t res = *desc;
     sdtx_context_desc_t res = *desc;
+    res.max_commands = _sdtx_def(res.max_commands, _SDTX_DEFAULT_MAX_COMMANDS);
     res.char_buf_size = _sdtx_def(res.char_buf_size, _SDTX_DEFAULT_CHAR_BUF_SIZE);
     res.char_buf_size = _sdtx_def(res.char_buf_size, _SDTX_DEFAULT_CHAR_BUF_SIZE);
     res.canvas_width = _sdtx_def(res.canvas_width, _SDTX_DEFAULT_CANVAS_WIDTH);
     res.canvas_width = _sdtx_def(res.canvas_width, _SDTX_DEFAULT_CANVAS_WIDTH);
     res.canvas_height = _sdtx_def(res.canvas_height, _SDTX_DEFAULT_CANVAS_HEIGHT);
     res.canvas_height = _sdtx_def(res.canvas_height, _SDTX_DEFAULT_CANVAS_HEIGHT);
@@ -3715,6 +3774,30 @@ static sdtx_context_desc_t _sdtx_context_desc_defaults(const sdtx_context_desc_t
     return res;
     return res;
 }
 }
 
 
+static void _sdtx_set_layer(_sdtx_context_t* ctx, int layer_id);
+static void _sdtx_rewind(_sdtx_context_t* ctx) {
+    SOKOL_ASSERT(ctx);
+    ctx->frame_id++;
+    ctx->vertices.next = 0;
+    ctx->commands.next = 0;
+    _sdtx_set_layer(ctx, 0);
+    ctx->cur_font = 0;
+    ctx->pos.x = 0.0f;
+    ctx->pos.y = 0.0f;
+}
+
+static void _sdtx_commit_listener(void* userdata) {
+    _sdtx_context_t* ctx = _sdtx_lookup_context((uint32_t)(uintptr_t)userdata);
+    if (ctx) {
+        _sdtx_rewind(ctx);
+    }
+}
+
+static sg_commit_listener _sdtx_make_commit_listener(_sdtx_context_t* ctx) {
+    sg_commit_listener listener = { _sdtx_commit_listener, (void*)(uintptr_t)(ctx->slot.id) };
+    return listener;
+}
+
 static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* in_desc) {
 static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* in_desc) {
     sg_push_debug_group("sokol-debugtext");
     sg_push_debug_group("sokol-debugtext");
 
 
@@ -3722,12 +3805,16 @@ static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* i
     _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
     _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
     SOKOL_ASSERT(ctx);
     SOKOL_ASSERT(ctx);
     ctx->desc = _sdtx_context_desc_defaults(in_desc);
     ctx->desc = _sdtx_context_desc_defaults(in_desc);
+    // NOTE: frame_id must be non-zero, so that updates trigger in first frame
+    ctx->frame_id = 1;
+
+    ctx->vertices.cap = 6 * ctx->desc.char_buf_size;
+    const size_t vbuf_size = (size_t)ctx->vertices.cap * sizeof(_sdtx_vertex_t);
+    ctx->vertices.ptr = (_sdtx_vertex_t*) _sdtx_malloc(vbuf_size);
 
 
-    const int max_vertices = 6 * ctx->desc.char_buf_size;
-    const size_t vbuf_size = (size_t)max_vertices * sizeof(_sdtx_vertex_t);
-    ctx->vertices = (_sdtx_vertex_t*) _sdtx_malloc(vbuf_size);
-    ctx->cur_vertex_ptr = ctx->vertices;
-    ctx->max_vertex_ptr = ctx->vertices + max_vertices;
+    ctx->commands.cap = ctx->desc.max_commands;
+    ctx->commands.ptr = (_sdtx_command_t*) _sdtx_malloc((size_t)ctx->commands.cap * sizeof(_sdtx_command_t));
+    _sdtx_set_layer(ctx, 0);
 
 
     sg_buffer_desc vbuf_desc;
     sg_buffer_desc vbuf_desc;
     _sdtx_clear(&vbuf_desc, sizeof(vbuf_desc));
     _sdtx_clear(&vbuf_desc, sizeof(vbuf_desc));
@@ -3765,21 +3852,33 @@ static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* i
     ctx->tab_width = (float) ctx->desc.tab_width;
     ctx->tab_width = (float) ctx->desc.tab_width;
     ctx->color = _SDTX_DEFAULT_COLOR;
     ctx->color = _SDTX_DEFAULT_COLOR;
 
 
+    if (!sg_add_commit_listener(_sdtx_make_commit_listener(ctx))) {
+        // FIXME: this should actually result in an invalid context,
+        // fix this when proper error logging/reporting is added
+        SDTX_LOG("sokol_debugtext.h: failed to add sokol-gfx commit listener");
+    }
     sg_pop_debug_group();
     sg_pop_debug_group();
 }
 }
 
 
 static void _sdtx_destroy_context(sdtx_context ctx_id) {
 static void _sdtx_destroy_context(sdtx_context ctx_id) {
     _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
     _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
     if (ctx) {
     if (ctx) {
-        if (ctx->vertices) {
-            _sdtx_free(ctx->vertices);
-            ctx->vertices = 0;
-            ctx->cur_vertex_ptr = 0;
-            ctx->max_vertex_ptr = 0;
+        if (ctx->vertices.ptr) {
+            _sdtx_free(ctx->vertices.ptr);
+            ctx->vertices.ptr = 0;
+            ctx->vertices.cap = 0;
+            ctx->vertices.next = 0;
+        }
+        if (ctx->commands.ptr) {
+            _sdtx_free(ctx->commands.ptr);
+            ctx->commands.ptr = 0;
+            ctx->commands.cap = 0;
+            ctx->commands.next = 0;
         }
         }
         sg_push_debug_group("sokol_debugtext");
         sg_push_debug_group("sokol_debugtext");
         sg_destroy_buffer(ctx->vbuf);
         sg_destroy_buffer(ctx->vbuf);
         sg_destroy_pipeline(ctx->pip);
         sg_destroy_pipeline(ctx->pip);
+        sg_remove_commit_listener(_sdtx_make_commit_listener(ctx));
         sg_pop_debug_group();
         sg_pop_debug_group();
         _sdtx_clear(ctx, sizeof(*ctx));
         _sdtx_clear(ctx, sizeof(*ctx));
         _sdtx_pool_free_index(&_sdtx.context_pool.pool, _sdtx_slot_index(ctx_id.id));
         _sdtx_pool_free_index(&_sdtx.context_pool.pool, _sdtx_slot_index(ctx_id.id));
@@ -3869,7 +3968,7 @@ static void _sdtx_setup_common(void) {
 
 
     /* unpack font data */
     /* unpack font data */
     memset(_sdtx.font_pixels, 0xFF, sizeof(_sdtx.font_pixels));
     memset(_sdtx.font_pixels, 0xFF, sizeof(_sdtx.font_pixels));
-    const int unpacked_font_size = 256 * 8 * 8;
+    const int unpacked_font_size = (int) (sizeof(_sdtx.font_pixels) / SDTX_MAX_FONTS);
     for (int i = 0; i < SDTX_MAX_FONTS; i++) {
     for (int i = 0; i < SDTX_MAX_FONTS; i++) {
         if (_sdtx.desc.fonts[i].data.ptr) {
         if (_sdtx.desc.fonts[i].data.ptr) {
             _sdtx_unpack_font(&_sdtx.desc.fonts[i], &_sdtx.font_pixels[i * unpacked_font_size]);
             _sdtx_unpack_font(&_sdtx.desc.fonts[i], &_sdtx.font_pixels[i * unpacked_font_size]);
@@ -3887,6 +3986,7 @@ static void _sdtx_setup_common(void) {
     img_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE;
     img_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE;
     img_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
     img_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
     img_desc.data.subimage[0][0] = SG_RANGE(_sdtx.font_pixels);
     img_desc.data.subimage[0][0] = SG_RANGE(_sdtx.font_pixels);
+    img_desc.label = "sdtx-font-texture";
     _sdtx.font_img = sg_make_image(&img_desc);
     _sdtx.font_img = sg_make_image(&img_desc);
     SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_img.id);
     SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_img.id);
 
 
@@ -3904,17 +4004,17 @@ static void _sdtx_discard_common(void) {
     sg_pop_debug_group();
     sg_pop_debug_group();
 }
 }
 
 
-static inline uint32_t _sdtx_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+static uint32_t _sdtx_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
     return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r);
     return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r);
 }
 }
 
 
-static inline float _sdtx_clamp(float v, float lo, float hi) {
+static float _sdtx_clamp(float v, float lo, float hi) {
     if (v < lo) return lo;
     if (v < lo) return lo;
     else if (v > hi) return hi;
     else if (v > hi) return hi;
     else return v;
     else return v;
 }
 }
 
 
-static inline uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) {
+static uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) {
     uint8_t r_u8 = (uint8_t) (_sdtx_clamp(r, 0.0f, 1.0f) * 255.0f);
     uint8_t r_u8 = (uint8_t) (_sdtx_clamp(r, 0.0f, 1.0f) * 255.0f);
     uint8_t g_u8 = (uint8_t) (_sdtx_clamp(g, 0.0f, 1.0f) * 255.0f);
     uint8_t g_u8 = (uint8_t) (_sdtx_clamp(g, 0.0f, 1.0f) * 255.0f);
     uint8_t b_u8 = (uint8_t) (_sdtx_clamp(b, 0.0f, 1.0f) * 255.0f);
     uint8_t b_u8 = (uint8_t) (_sdtx_clamp(b, 0.0f, 1.0f) * 255.0f);
@@ -3922,7 +4022,7 @@ static inline uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) {
     return _sdtx_pack_rgbab(r_u8, g_u8, b_u8, a_u8);
     return _sdtx_pack_rgbab(r_u8, g_u8, b_u8, a_u8);
 }
 }
 
 
-static inline void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) {
+static void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) {
     switch (c) {
     switch (c) {
         case '\r':
         case '\r':
             ctx->pos.x = 0.0f;
             ctx->pos.x = 0.0f;
@@ -3940,31 +4040,88 @@ static inline void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) {
     }
     }
 }
 }
 
 
-static inline void _sdtx_draw_char(_sdtx_context_t* ctx, uint8_t c) {
-    if ((ctx->cur_vertex_ptr + 6) <= ctx->max_vertex_ptr) {
+static _sdtx_vertex_t* _sdtx_next_vertex(_sdtx_context_t* ctx) {
+    if ((ctx->vertices.next + 6) <= ctx->vertices.cap) {
+        _sdtx_vertex_t* vx = &ctx->vertices.ptr[ctx->vertices.next];
+        ctx->vertices.next += 6;
+        return vx;
+    }
+    else {
+        return 0;
+    }
+}
+
+static _sdtx_command_t* _sdtx_cur_command(_sdtx_context_t* ctx) {
+    if (ctx->commands.next > 0) {
+        return &ctx->commands.ptr[ctx->commands.next - 1];
+    }
+    else {
+        return 0;
+    }
+}
+
+static _sdtx_command_t* _sdtx_next_command(_sdtx_context_t* ctx) {
+    if (ctx->commands.next < ctx->commands.cap) {
+        return &ctx->commands.ptr[ctx->commands.next++];
+    }
+    else {
+        SDTX_LOG("sokol_debugtext.h: command buffer full");
+        return 0;
+    }
+}
+
+static void _sdtx_set_layer(_sdtx_context_t* ctx, int layer_id) {
+    ctx->cur_layer_id = layer_id;
+    _sdtx_command_t* cur_cmd = _sdtx_cur_command(ctx);
+    if (cur_cmd) {
+        if ((cur_cmd->num_vertices == 0) || (cur_cmd->layer_id == layer_id)) {
+            // no vertices recorded in current draw command, or layer hasn't changed, can just reuse this
+            cur_cmd->layer_id = layer_id;
+        }
+        else {
+            // layer has changed, need to start a new draw command
+            _sdtx_command_t* next_cmd = _sdtx_next_command(ctx);
+            if (next_cmd) {
+                next_cmd->layer_id = layer_id;
+                next_cmd->first_vertex = cur_cmd->first_vertex + cur_cmd->num_vertices;
+                next_cmd->num_vertices = 0;
+            }
+        }
+    }
+    else {
+        // first draw command in frame
+        _sdtx_command_t* next_cmd = _sdtx_next_command(ctx);
+        if (next_cmd) {
+            next_cmd->layer_id = layer_id;
+            next_cmd->first_vertex = 0;
+            next_cmd->num_vertices = 0;
+        }
+    }
+}
+
+static void _sdtx_render_char(_sdtx_context_t* ctx, uint8_t c) {
+    _sdtx_vertex_t* vx = _sdtx_next_vertex(ctx);
+    _sdtx_command_t* cmd = _sdtx_cur_command(ctx);
+    if (vx && cmd) {
+        // update vertex count in current draw command
+        cmd->num_vertices += 6;
+
         const float x0 = (ctx->origin.x + ctx->pos.x) * ctx->glyph_size.x;
         const float x0 = (ctx->origin.x + ctx->pos.x) * ctx->glyph_size.x;
         const float y0 = (ctx->origin.y + ctx->pos.y) * ctx->glyph_size.y;
         const float y0 = (ctx->origin.y + ctx->pos.y) * ctx->glyph_size.y;
         const float x1 = x0 + ctx->glyph_size.x;
         const float x1 = x0 + ctx->glyph_size.x;
         const float y1 = y0 + ctx->glyph_size.y;
         const float y1 = y0 + ctx->glyph_size.y;
 
 
         // glyph width and heigth in font texture space
         // glyph width and heigth in font texture space
+        // NOTE: the '+1' and '-2' fixes texture bleeding into the neighboring font texture cell
         const uint16_t uvw = 0x10000 / 0x100;
         const uint16_t uvw = 0x10000 / 0x100;
         const uint16_t uvh = 0x10000 / SDTX_MAX_FONTS;
         const uint16_t uvh = 0x10000 / SDTX_MAX_FONTS;
-        const uint16_t u0 = ((uint16_t)c) * uvw;
-        const uint16_t v0 = ((uint16_t)ctx->cur_font) * uvh;
-        uint16_t u1 = u0 + uvw;
-        uint16_t v1 = v0 + uvh;
-        if (u1 == 0x0000) {
-            u1 = 0xFFFF;
-        }
-        if (v1 == 0x0000) {
-            v1 = 0xFFFF;
-        }
+        const uint16_t u0 = (((uint16_t)c) * uvw) + 1;
+        const uint16_t v0 = (((uint16_t)ctx->cur_font) * uvh) + 1;
+        uint16_t u1 = (u0 + uvw) - 2;
+        uint16_t v1 = (v0 + uvh) - 2;
         const uint32_t color = ctx->color;
         const uint32_t color = ctx->color;
 
 
         // write 6 vertices
         // write 6 vertices
-        _sdtx_vertex_t* vx = ctx->cur_vertex_ptr;
-
         vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++;
         vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++;
         vx->x=x1; vx->y=y0; vx->u = u1; vx->v = v0; vx->color = color; vx++;
         vx->x=x1; vx->y=y0; vx->u = u1; vx->v = v0; vx->color = color; vx++;
         vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++;
         vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++;
@@ -3972,22 +4129,51 @@ static inline void _sdtx_draw_char(_sdtx_context_t* ctx, uint8_t c) {
         vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++;
         vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++;
         vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++;
         vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++;
         vx->x=x0; vx->y=y1; vx->u = u0; vx->v = v1; vx->color = color; vx++;
         vx->x=x0; vx->y=y1; vx->u = u0; vx->v = v1; vx->color = color; vx++;
-
-        ctx->cur_vertex_ptr = vx;
     }
     }
     ctx->pos.x += 1.0f;
     ctx->pos.x += 1.0f;
 }
 }
 
 
-static inline void _sdtx_put_char(_sdtx_context_t* ctx, char c) {
+static void _sdtx_put_char(_sdtx_context_t* ctx, char c) {
     uint8_t c_u8 = (uint8_t)c;
     uint8_t c_u8 = (uint8_t)c;
     if (c_u8 <= 32) {
     if (c_u8 <= 32) {
         _sdtx_ctrl_char(ctx, c_u8);
         _sdtx_ctrl_char(ctx, c_u8);
     }
     }
     else {
     else {
-        _sdtx_draw_char(ctx, c_u8);
+        _sdtx_render_char(ctx, c_u8);
     }
     }
 }
 }
 
 
+SOKOL_API_IMPL void _sdtx_draw_layer(_sdtx_context_t* ctx, int layer_id) {
+    SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
+    SOKOL_ASSERT(ctx);
+    if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) {
+        sg_push_debug_group("sokol-debugtext");
+
+        if (ctx->update_frame_id != ctx->frame_id) {
+            ctx->update_frame_id = ctx->frame_id;
+            const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sdtx_vertex_t) };
+            sg_update_buffer(ctx->vbuf, &range);
+        }
+
+        sg_apply_pipeline(ctx->pip);
+        sg_bindings bindings;
+        _sdtx_clear(&bindings, sizeof(bindings));
+        bindings.vertex_buffers[0] = ctx->vbuf;
+        bindings.fs_images[0] = _sdtx.font_img;
+        sg_apply_bindings(&bindings);
+        for (int cmd_index = 0; cmd_index < ctx->commands.next; cmd_index++) {
+            const _sdtx_command_t* cmd = &ctx->commands.ptr[cmd_index];
+            if (cmd->layer_id != layer_id) {
+                continue;
+            }
+            SOKOL_ASSERT((cmd->num_vertices % 6) == 0);
+            sg_draw(cmd->first_vertex, cmd->num_vertices, 1);
+        }
+        sg_pop_debug_group();
+    }
+}
+
+
 static sdtx_desc_t _sdtx_desc_defaults(const sdtx_desc_t* desc) {
 static sdtx_desc_t _sdtx_desc_defaults(const sdtx_desc_t* desc) {
     SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
     SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
     sdtx_desc_t res = *desc;
     sdtx_desc_t res = *desc;
@@ -4106,6 +4292,14 @@ SOKOL_API_IMPL sdtx_context sdtx_default_context(void) {
     return SDTX_DEFAULT_CONTEXT;
     return SDTX_DEFAULT_CONTEXT;
 }
 }
 
 
+SOKOL_API_IMPL void sdtx_layer(int layer_id) {
+    SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
+    _sdtx_context_t* ctx = _sdtx.cur_ctx;
+    if (ctx) {
+        _sdtx_set_layer(ctx, layer_id);
+    }
+}
+
 SOKOL_API_IMPL void sdtx_font(int font_index) {
 SOKOL_API_IMPL void sdtx_font(int font_index) {
     SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
     SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
     SOKOL_ASSERT((font_index >= 0) && (font_index < SDTX_MAX_FONTS));
     SOKOL_ASSERT((font_index >= 0) && (font_index < SDTX_MAX_FONTS));
@@ -4308,27 +4502,31 @@ SOKOL_API_IMPL void sdtx_draw(void) {
     SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
     SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
     _sdtx_context_t* ctx = _sdtx.cur_ctx;
     _sdtx_context_t* ctx = _sdtx.cur_ctx;
     if (ctx) {
     if (ctx) {
-        const int num_verts = (int) (ctx->cur_vertex_ptr - ctx->vertices);
-        if (num_verts > 0) {
-            SOKOL_ASSERT((num_verts % 6) == 0);
-            sg_push_debug_group("sokol-debugtext");
-            const sg_range range = { ctx->vertices, (size_t)num_verts * sizeof(_sdtx_vertex_t) };
-            int vbuf_offset = sg_append_buffer(ctx->vbuf, &range);
-            sg_apply_pipeline(ctx->pip);
-            sg_bindings bindings;
-            _sdtx_clear(&bindings, sizeof(bindings));
-            bindings.vertex_buffers[0] = ctx->vbuf;
-            bindings.vertex_buffer_offsets[0] = vbuf_offset;
-            bindings.fs_images[0] = _sdtx.font_img;
-            sg_apply_bindings(&bindings);
-            sg_draw(0, num_verts, 1);
-            sg_pop_debug_group();
-        }
-        ctx->cur_vertex_ptr = ctx->vertices;
-        ctx->cur_font = 0;
-        ctx->pos.x = 0.0f;
-        ctx->pos.y = 0.0f;
+        _sdtx_draw_layer(ctx, 0);
+    }
+}
+
+SOKOL_API_IMPL void sdtx_context_draw(sdtx_context ctx_id) {
+    SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
+    _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
+    if (ctx) {
+        _sdtx_draw_layer(ctx, 0);
     }
     }
 }
 }
 
 
+SOKOL_API_IMPL void sdtx_draw_layer(int layer_id) {
+    SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
+    _sdtx_context_t* ctx = _sdtx.cur_ctx;
+    if (ctx) {
+        _sdtx_draw_layer(ctx, layer_id);
+    }
+}
+
+SOKOL_API_IMPL void sdtx_context_draw_layer(sdtx_context ctx_id, int layer_id) {
+    SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie);
+    _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
+    if (ctx) {
+        _sdtx_draw_layer(ctx, layer_id);
+    }
+}
 #endif /* SOKOL_DEBUGTEXT_IMPL */
 #endif /* SOKOL_DEBUGTEXT_IMPL */

+ 75 - 71
util/sokol_gl.h

@@ -2321,16 +2321,21 @@ typedef struct {
     sgl_context_desc_t desc;
     sgl_context_desc_t desc;
     uint32_t frame_id;
     uint32_t frame_id;
     uint32_t update_frame_id;
     uint32_t update_frame_id;
-
-    int num_vertices;
-    int num_uniforms;
-    int num_commands;
-    int cur_vertex;
-    int cur_uniform;
-    int cur_command;
-    _sgl_vertex_t* vertices;
-    _sgl_uniform_t* uniforms;
-    _sgl_command_t* commands;
+    struct {
+        int cap;
+        int next;
+        _sgl_vertex_t* ptr;
+    } vertices;
+    struct {
+        int cap;
+        int next;
+        _sgl_uniform_t* ptr;
+    } uniforms;
+    struct {
+        int cap;
+        int next;
+        _sgl_command_t* ptr;
+    } commands;
 
 
     /* state tracking */
     /* state tracking */
     int base_vertex;
     int base_vertex;
@@ -2483,9 +2488,9 @@ static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) {
 
 
 static void _sgl_reset_context(_sgl_context_t* ctx) {
 static void _sgl_reset_context(_sgl_context_t* ctx) {
     SOKOL_ASSERT(ctx);
     SOKOL_ASSERT(ctx);
-    SOKOL_ASSERT(0 == ctx->vertices);
-    SOKOL_ASSERT(0 == ctx->uniforms);
-    SOKOL_ASSERT(0 == ctx->commands);
+    SOKOL_ASSERT(0 == ctx->vertices.ptr);
+    SOKOL_ASSERT(0 == ctx->uniforms.ptr);
+    SOKOL_ASSERT(0 == ctx->commands.ptr);
     _sgl_clear(ctx, sizeof(_sgl_context_t));
     _sgl_clear(ctx, sizeof(_sgl_context_t));
 }
 }
 
 
@@ -2763,18 +2768,18 @@ static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_d
     ctx->cur_img = _sgl.def_img;
     ctx->cur_img = _sgl.def_img;
 
 
     // allocate buffers and pools
     // allocate buffers and pools
-    ctx->num_vertices = ctx->desc.max_vertices;
-    ctx->num_commands = ctx->num_uniforms = ctx->desc.max_commands;
-    ctx->vertices = (_sgl_vertex_t*) _sgl_malloc((size_t)ctx->num_vertices * sizeof(_sgl_vertex_t));
-    ctx->uniforms = (_sgl_uniform_t*) _sgl_malloc((size_t)ctx->num_uniforms * sizeof(_sgl_uniform_t));
-    ctx->commands = (_sgl_command_t*) _sgl_malloc((size_t)ctx->num_commands * sizeof(_sgl_command_t));
+    ctx->vertices.cap = ctx->desc.max_vertices;
+    ctx->commands.cap = ctx->uniforms.cap = ctx->desc.max_commands;
+    ctx->vertices.ptr = (_sgl_vertex_t*) _sgl_malloc((size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t));
+    ctx->uniforms.ptr = (_sgl_uniform_t*) _sgl_malloc((size_t)ctx->uniforms.cap * sizeof(_sgl_uniform_t));
+    ctx->commands.ptr = (_sgl_command_t*) _sgl_malloc((size_t)ctx->commands.cap * sizeof(_sgl_command_t));
 
 
     // create sokol-gfx resource objects
     // create sokol-gfx resource objects
     sg_push_debug_group("sokol-gl");
     sg_push_debug_group("sokol-gl");
 
 
     sg_buffer_desc vbuf_desc;
     sg_buffer_desc vbuf_desc;
     _sgl_clear(&vbuf_desc, sizeof(vbuf_desc));
     _sgl_clear(&vbuf_desc, sizeof(vbuf_desc));
-    vbuf_desc.size = (size_t)ctx->num_vertices * sizeof(_sgl_vertex_t);
+    vbuf_desc.size = (size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t);
     vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
     vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
     vbuf_desc.usage = SG_USAGE_STREAM;
     vbuf_desc.usage = SG_USAGE_STREAM;
     vbuf_desc.label = "sgl-vertex-buffer";
     vbuf_desc.label = "sgl-vertex-buffer";
@@ -2818,17 +2823,16 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) {
 static void _sgl_destroy_context(sgl_context ctx_id) {
 static void _sgl_destroy_context(sgl_context ctx_id) {
     _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
     _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
     if (ctx) {
     if (ctx) {
-        SOKOL_ASSERT(ctx->vertices);
-        SOKOL_ASSERT(ctx->uniforms);
-        SOKOL_ASSERT(ctx->commands);
-
-        _sgl_free(ctx->vertices);
-        _sgl_free(ctx->uniforms);
-        _sgl_free(ctx->commands);
+        SOKOL_ASSERT(ctx->vertices.ptr);
+        SOKOL_ASSERT(ctx->uniforms.ptr);
+        SOKOL_ASSERT(ctx->commands.ptr);
 
 
-        ctx->vertices = 0;
-        ctx->uniforms = 0;
-        ctx->commands = 0;
+        _sgl_free(ctx->vertices.ptr);
+        _sgl_free(ctx->uniforms.ptr);
+        _sgl_free(ctx->commands.ptr);
+        ctx->vertices.ptr = 0;
+        ctx->uniforms.ptr = 0;
+        ctx->commands.ptr = 0;
 
 
         sg_push_debug_group("sokol-gl");
         sg_push_debug_group("sokol-gl");
         sg_destroy_buffer(ctx->vbuf);
         sg_destroy_buffer(ctx->vbuf);
@@ -2841,18 +2845,18 @@ static void _sgl_destroy_context(sgl_context ctx_id) {
     }
     }
 }
 }
 
 
-static inline void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) {
+static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) {
     ctx->in_begin = true;
     ctx->in_begin = true;
-    ctx->base_vertex = ctx->cur_vertex;
+    ctx->base_vertex = ctx->vertices.next;
     ctx->vtx_count = 0;
     ctx->vtx_count = 0;
     ctx->cur_prim_type = mode;
     ctx->cur_prim_type = mode;
 }
 }
 
 
 static void _sgl_rewind(_sgl_context_t* ctx) {
 static void _sgl_rewind(_sgl_context_t* ctx) {
     ctx->frame_id++;
     ctx->frame_id++;
-    ctx->cur_vertex = 0;
-    ctx->cur_uniform = 0;
-    ctx->cur_command = 0;
+    ctx->vertices.next = 0;
+    ctx->uniforms.next = 0;
+    ctx->commands.next = 0;
     ctx->base_vertex = 0;
     ctx->base_vertex = 0;
     ctx->error = SGL_NO_ERROR;
     ctx->error = SGL_NO_ERROR;
     ctx->layer_id = 0;
     ctx->layer_id = 0;
@@ -2872,9 +2876,9 @@ static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx) {
     return listener;
     return listener;
 }
 }
 
 
-static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
-    if (ctx->cur_vertex < ctx->num_vertices) {
-        return &ctx->vertices[ctx->cur_vertex++];
+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 {
     else {
         ctx->error = SGL_ERROR_VERTICES_FULL;
         ctx->error = SGL_ERROR_VERTICES_FULL;
@@ -2882,9 +2886,9 @@ static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
     }
     }
 }
 }
 
 
-static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
-    if (ctx->cur_uniform < ctx->num_uniforms) {
-        return &ctx->uniforms[ctx->cur_uniform++];
+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 {
     else {
         ctx->error = SGL_ERROR_UNIFORMS_FULL;
         ctx->error = SGL_ERROR_UNIFORMS_FULL;
@@ -2892,18 +2896,18 @@ static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
     }
     }
 }
 }
 
 
-static inline _sgl_command_t* _sgl_prev_command(_sgl_context_t* ctx) {
-    if (ctx->cur_command > 0) {
-        return &ctx->commands[ctx->cur_command - 1];
+static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) {
+    if (ctx->commands.next > 0) {
+        return &ctx->commands.ptr[ctx->commands.next - 1];
     }
     }
     else {
     else {
         return 0;
         return 0;
     }
     }
 }
 }
 
 
-static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
-    if (ctx->cur_command < ctx->num_commands) {
-        return &ctx->commands[ctx->cur_command++];
+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 {
     else {
         ctx->error = SGL_ERROR_COMMANDS_FULL;
         ctx->error = SGL_ERROR_COMMANDS_FULL;
@@ -2911,17 +2915,17 @@ static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
     }
     }
 }
 }
 
 
-static inline uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+static uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
     return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r);
     return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r);
 }
 }
 
 
-static inline float _sgl_clamp(float v, float lo, float hi) {
+static float _sgl_clamp(float v, float lo, float hi) {
     if (v < lo) return lo;
     if (v < lo) return lo;
     else if (v > hi) return hi;
     else if (v > hi) return hi;
     else return v;
     else return v;
 }
 }
 
 
-static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
+static uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
     uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f);
     uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f);
     uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f);
     uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f);
     uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f);
     uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f);
@@ -2929,7 +2933,7 @@ static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
     return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8);
     return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8);
 }
 }
 
 
-static inline void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) {
+static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) {
     SOKOL_ASSERT(ctx->in_begin);
     SOKOL_ASSERT(ctx->in_begin);
     _sgl_vertex_t* vtx;
     _sgl_vertex_t* vtx;
     /* handle non-native primitive types */
     /* handle non-native primitive types */
@@ -3143,22 +3147,22 @@ static void _sgl_lookat(_sgl_matrix_t* dst,
 }
 }
 
 
 /* current top-of-stack projection matrix */
 /* current top-of-stack projection matrix */
-static inline _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) {
+static _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) {
     return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->matrix_tos[SGL_MATRIXMODE_PROJECTION]];
     return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->matrix_tos[SGL_MATRIXMODE_PROJECTION]];
 }
 }
 
 
 /* get top-of-stack modelview matrix */
 /* get top-of-stack modelview matrix */
-static inline _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) {
+static _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) {
     return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->matrix_tos[SGL_MATRIXMODE_MODELVIEW]];
     return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->matrix_tos[SGL_MATRIXMODE_MODELVIEW]];
 }
 }
 
 
 /* get top-of-stack texture matrix */
 /* get top-of-stack texture matrix */
-static inline _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) {
+static _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) {
     return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->matrix_tos[SGL_MATRIXMODE_TEXTURE]];
     return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->matrix_tos[SGL_MATRIXMODE_TEXTURE]];
 }
 }
 
 
 /* get pointer to current top-of-stack of current matrix mode */
 /* get pointer to current top-of-stack of current matrix mode */
-static inline _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) {
+static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) {
     return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]];
     return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]];
 }
 }
 
 
@@ -3272,7 +3276,7 @@ static bool _sgl_is_default_context(sgl_context ctx_id) {
 
 
 static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
 static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
     SOKOL_ASSERT(ctx);
     SOKOL_ASSERT(ctx);
-    if ((ctx->error == SGL_NO_ERROR) && (ctx->cur_vertex > 0) && (ctx->cur_command > 0)) {
+    if ((ctx->error == SGL_NO_ERROR) && (ctx->vertices.next > 0) && (ctx->commands.next > 0)) {
         sg_push_debug_group("sokol-gl");
         sg_push_debug_group("sokol-gl");
 
 
         uint32_t cur_pip_id = SG_INVALID_ID;
         uint32_t cur_pip_id = SG_INVALID_ID;
@@ -3281,12 +3285,12 @@ static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
 
 
         if (ctx->update_frame_id != ctx->frame_id) {
         if (ctx->update_frame_id != ctx->frame_id) {
             ctx->update_frame_id = ctx->frame_id;
             ctx->update_frame_id = ctx->frame_id;
-            const sg_range range = { ctx->vertices, (size_t)ctx->cur_vertex * sizeof(_sgl_vertex_t) };
+            const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sgl_vertex_t) };
             sg_update_buffer(ctx->vbuf, &range);
             sg_update_buffer(ctx->vbuf, &range);
         }
         }
 
 
-        for (int i = 0; i < ctx->cur_command; i++) {
-            const _sgl_command_t* cmd = &ctx->commands[i];
+        for (int i = 0; i < ctx->commands.next; i++) {
+            const _sgl_command_t* cmd = &ctx->commands.ptr[i];
             if (cmd->layer_id != layer_id) {
             if (cmd->layer_id != layer_id) {
                 continue;
                 continue;
             }
             }
@@ -3319,7 +3323,7 @@ static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
                             cur_img_id = args->img.id;
                             cur_img_id = args->img.id;
                         }
                         }
                         if (cur_uniform_index != args->uniform_index) {
                         if (cur_uniform_index != args->uniform_index) {
-                            const sg_range ub_range = { &ctx->uniforms[args->uniform_index], sizeof(_sgl_uniform_t) };
+                            const sg_range ub_range = { &ctx->uniforms.ptr[args->uniform_index], sizeof(_sgl_uniform_t) };
                             sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range);
                             sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range);
                             cur_uniform_index = args->uniform_index;
                             cur_uniform_index = args->uniform_index;
                         }
                         }
@@ -3698,7 +3702,7 @@ SOKOL_API_IMPL void sgl_end(void) {
         return;
         return;
     }
     }
     SOKOL_ASSERT(ctx->in_begin);
     SOKOL_ASSERT(ctx->in_begin);
-    SOKOL_ASSERT(ctx->cur_vertex >= ctx->base_vertex);
+    SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex);
     ctx->in_begin = false;
     ctx->in_begin = false;
     bool matrix_dirty = ctx->matrix_dirty;
     bool matrix_dirty = ctx->matrix_dirty;
     if (matrix_dirty) {
     if (matrix_dirty) {
@@ -3709,39 +3713,39 @@ SOKOL_API_IMPL void sgl_end(void) {
             uni->tm = *_sgl_matrix_texture(ctx);
             uni->tm = *_sgl_matrix_texture(ctx);
         }
         }
     }
     }
-    /* check if command can be merged with previous command */
+    /* 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_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;
     sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img;
-    _sgl_command_t* prev_cmd = _sgl_prev_command(ctx);
+    _sgl_command_t* cur_cmd = _sgl_cur_command(ctx);
     bool merge_cmd = false;
     bool merge_cmd = false;
-    if (prev_cmd) {
-        if ((prev_cmd->cmd == SGL_COMMAND_DRAW) &&
-            (prev_cmd->layer_id == ctx->layer_id) &&
+    if (cur_cmd) {
+        if ((cur_cmd->cmd == SGL_COMMAND_DRAW) &&
+            (cur_cmd->layer_id == ctx->layer_id) &&
             (ctx->cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) &&
             (ctx->cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) &&
             (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) &&
             (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) &&
             !matrix_dirty &&
             !matrix_dirty &&
-            (prev_cmd->args.draw.img.id == img.id) &&
-            (prev_cmd->args.draw.pip.id == pip.id))
+            (cur_cmd->args.draw.img.id == img.id) &&
+            (cur_cmd->args.draw.pip.id == pip.id))
         {
         {
             merge_cmd = true;
             merge_cmd = true;
         }
         }
     }
     }
     if (merge_cmd) {
     if (merge_cmd) {
         /* draw command can be merged with the previous command */
         /* draw command can be merged with the previous command */
-        prev_cmd->args.draw.num_vertices += ctx->cur_vertex - ctx->base_vertex;
+        cur_cmd->args.draw.num_vertices += ctx->vertices.next - ctx->base_vertex;
     }
     }
     else {
     else {
         /* append a new draw command */
         /* append a new draw command */
         _sgl_command_t* cmd = _sgl_next_command(ctx);
         _sgl_command_t* cmd = _sgl_next_command(ctx);
         if (cmd) {
         if (cmd) {
-            SOKOL_ASSERT(ctx->cur_uniform > 0);
+            SOKOL_ASSERT(ctx->uniforms.next > 0);
             cmd->cmd = SGL_COMMAND_DRAW;
             cmd->cmd = SGL_COMMAND_DRAW;
             cmd->layer_id = ctx->layer_id;
             cmd->layer_id = ctx->layer_id;
             cmd->args.draw.img = img;
             cmd->args.draw.img = img;
             cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
             cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
             cmd->args.draw.base_vertex = ctx->base_vertex;
             cmd->args.draw.base_vertex = ctx->base_vertex;
-            cmd->args.draw.num_vertices = ctx->cur_vertex - ctx->base_vertex;
-            cmd->args.draw.uniform_index = ctx->cur_uniform - 1;
+            cmd->args.draw.num_vertices = ctx->vertices.next - ctx->base_vertex;
+            cmd->args.draw.uniform_index = ctx->uniforms.next - 1;
         }
         }
     }
     }
 }
 }

+ 44 - 44
util/sokol_spine.h

@@ -2619,7 +2619,7 @@ typedef struct {
     spSkeletonData* sp_skel_data;
     spSkeletonData* sp_skel_data;
     spAnimationStateData* sp_anim_data;
     spAnimationStateData* sp_anim_data;
     struct {
     struct {
-        int num;
+        int cap;
         sspine_vec2* ptr;
         sspine_vec2* ptr;
     } tform_buf;
     } tform_buf;
 } _sspine_skeleton_t;
 } _sspine_skeleton_t;
@@ -2696,20 +2696,20 @@ typedef struct {
     _sspine_slot_t slot;
     _sspine_slot_t slot;
     float transform[16];
     float transform[16];
     struct {
     struct {
-        int num;
-        int cur;
+        int cap;
+        int next;
         uint32_t rewind_frame_id;
         uint32_t rewind_frame_id;
         _sspine_vertex_t* ptr;
         _sspine_vertex_t* ptr;
     } vertices;
     } vertices;
     struct {
     struct {
-        int num;
-        int cur;
+        int cap;
+        int next;
         uint32_t rewind_frame_id;
         uint32_t rewind_frame_id;
         uint32_t* ptr;
         uint32_t* ptr;
     } indices;
     } indices;
     struct {
     struct {
-        int num;
-        int cur;
+        int cap;
+        int next;
         uint32_t rewind_frame_id;
         uint32_t rewind_frame_id;
         _sspine_command_t* ptr;
         _sspine_command_t* ptr;
     } commands;
     } commands;
@@ -3114,13 +3114,13 @@ static sspine_resource_state _sspine_init_context(_sspine_context_t* ctx, const
     SOKOL_ASSERT(desc);
     SOKOL_ASSERT(desc);
 
 
     // setup vertex, index and command storage
     // setup vertex, index and command storage
-    ctx->vertices.num = desc->max_vertices;
-    ctx->indices.num = ctx->vertices.num * 3;
-    ctx->commands.num = desc->max_commands;
+    ctx->vertices.cap = desc->max_vertices;
+    ctx->indices.cap = ctx->vertices.cap * 3;
+    ctx->commands.cap = desc->max_commands;
 
 
-    const size_t vbuf_size = (size_t)ctx->vertices.num * sizeof(_sspine_vertex_t);
-    const size_t ibuf_size = (size_t)ctx->indices.num * sizeof(uint32_t);
-    const size_t cbuf_size = (size_t)ctx->commands.num * sizeof(_sspine_command_t);
+    const size_t vbuf_size = (size_t)ctx->vertices.cap * sizeof(_sspine_vertex_t);
+    const size_t ibuf_size = (size_t)ctx->indices.cap * sizeof(uint32_t);
+    const size_t cbuf_size = (size_t)ctx->commands.cap * sizeof(_sspine_command_t);
 
 
     ctx->vertices.ptr = (_sspine_vertex_t*) _sspine_malloc(vbuf_size);
     ctx->vertices.ptr = (_sspine_vertex_t*) _sspine_malloc(vbuf_size);
     ctx->indices.ptr = (uint32_t*) _sspine_malloc(ibuf_size);
     ctx->indices.ptr = (uint32_t*) _sspine_malloc(ibuf_size);
@@ -3483,8 +3483,8 @@ static sspine_resource_state _sspine_init_skeleton(_sspine_skeleton_t* skeleton,
     }
     }
 
 
     // allocate a shared vertex transform buffer (big enough to hold vertices for biggest mesh attachment)
     // allocate a shared vertex transform buffer (big enough to hold vertices for biggest mesh attachment)
-    skeleton->tform_buf.num = max_vertex_count;
-    skeleton->tform_buf.ptr = (sspine_vec2*) _sspine_malloc((size_t)skeleton->tform_buf.num * sizeof(sspine_vec2));
+    skeleton->tform_buf.cap = max_vertex_count;
+    skeleton->tform_buf.ptr = (sspine_vec2*) _sspine_malloc((size_t)skeleton->tform_buf.cap * sizeof(sspine_vec2));
 
 
     return SSPINE_RESOURCESTATE_VALID;
     return SSPINE_RESOURCESTATE_VALID;
 }
 }
@@ -4035,15 +4035,15 @@ static void _sspine_init_image_info(const _sspine_atlas_t* atlas, int index, ssp
 
 
 static void _sspine_check_rewind_commands(_sspine_context_t* ctx) {
 static void _sspine_check_rewind_commands(_sspine_context_t* ctx) {
     if (_sspine.frame_id != ctx->commands.rewind_frame_id) {
     if (_sspine.frame_id != ctx->commands.rewind_frame_id) {
-        ctx->commands.cur = 0;
+        ctx->commands.next = 0;
         ctx->commands.rewind_frame_id = _sspine.frame_id;
         ctx->commands.rewind_frame_id = _sspine.frame_id;
     }
     }
 }
 }
 
 
 static _sspine_command_t* _sspine_next_command(_sspine_context_t* ctx) {
 static _sspine_command_t* _sspine_next_command(_sspine_context_t* ctx) {
     _sspine_check_rewind_commands(ctx);
     _sspine_check_rewind_commands(ctx);
-    if ((ctx->commands.cur + 1) <= ctx->commands.num) {
-        return &(ctx->commands.ptr[ctx->commands.cur++]);
+    if (ctx->commands.next < ctx->commands.cap) {
+        return &(ctx->commands.ptr[ctx->commands.next++]);
     }
     }
     else {
     else {
         _SSPINE_ERROR(COMMAND_BUFFER_OVERFLOW);
         _SSPINE_ERROR(COMMAND_BUFFER_OVERFLOW);
@@ -4051,10 +4051,10 @@ static _sspine_command_t* _sspine_next_command(_sspine_context_t* ctx) {
     }
     }
 }
 }
 
 
-static _sspine_command_t* _sspine_prev_command(_sspine_context_t* ctx) {
+static _sspine_command_t* _sspine_cur_command(_sspine_context_t* ctx) {
     _sspine_check_rewind_commands(ctx);
     _sspine_check_rewind_commands(ctx);
-    if ((ctx->commands.cur > 0) && (ctx->commands.cur <= ctx->commands.num)) {
-        return &ctx->commands.ptr[ctx->commands.cur - 1];
+    if (ctx->commands.next > 0) {
+        return &ctx->commands.ptr[ctx->commands.next - 1];
     }
     }
     else {
     else {
         return 0;
         return 0;
@@ -4063,7 +4063,7 @@ static _sspine_command_t* _sspine_prev_command(_sspine_context_t* ctx) {
 
 
 static void _sspine_check_rewind_vertices(_sspine_context_t* ctx) {
 static void _sspine_check_rewind_vertices(_sspine_context_t* ctx) {
     if (_sspine.frame_id != ctx->vertices.rewind_frame_id) {
     if (_sspine.frame_id != ctx->vertices.rewind_frame_id) {
-        ctx->vertices.cur = 0;
+        ctx->vertices.next = 0;
         ctx->vertices.rewind_frame_id = _sspine.frame_id;
         ctx->vertices.rewind_frame_id = _sspine.frame_id;
     }
     }
 }
 }
@@ -4072,10 +4072,10 @@ static _sspine_alloc_vertices_result_t _sspine_alloc_vertices(_sspine_context_t*
     _sspine_check_rewind_vertices(ctx);
     _sspine_check_rewind_vertices(ctx);
     _sspine_alloc_vertices_result_t res;
     _sspine_alloc_vertices_result_t res;
     _sspine_clear(&res, sizeof(res));
     _sspine_clear(&res, sizeof(res));
-    if ((ctx->vertices.cur + num) <= ctx->vertices.num) {
-        res.ptr = &(ctx->vertices.ptr[ctx->vertices.cur]);
-        res.index = ctx->vertices.cur;
-        ctx->vertices.cur += num;
+    if ((ctx->vertices.next + num) <= ctx->vertices.cap) {
+        res.ptr = &(ctx->vertices.ptr[ctx->vertices.next]);
+        res.index = ctx->vertices.next;
+        ctx->vertices.next += num;
     }
     }
     else {
     else {
         _SSPINE_ERROR(VERTEX_BUFFER_OVERFLOW);
         _SSPINE_ERROR(VERTEX_BUFFER_OVERFLOW);
@@ -4085,7 +4085,7 @@ static _sspine_alloc_vertices_result_t _sspine_alloc_vertices(_sspine_context_t*
 
 
 static void _sspine_check_rewind_indices(_sspine_context_t* ctx) {
 static void _sspine_check_rewind_indices(_sspine_context_t* ctx) {
     if (_sspine.frame_id != ctx->indices.rewind_frame_id) {
     if (_sspine.frame_id != ctx->indices.rewind_frame_id) {
-        ctx->indices.cur = 0;
+        ctx->indices.next = 0;
         ctx->indices.rewind_frame_id = _sspine.frame_id;
         ctx->indices.rewind_frame_id = _sspine.frame_id;
     }
     }
 }
 }
@@ -4094,10 +4094,10 @@ static _sspine_alloc_indices_result_t _sspine_alloc_indices(_sspine_context_t* c
     _sspine_check_rewind_indices(ctx);
     _sspine_check_rewind_indices(ctx);
     _sspine_alloc_indices_result_t res;
     _sspine_alloc_indices_result_t res;
     _sspine_clear(&res, sizeof(res));
     _sspine_clear(&res, sizeof(res));
-    if ((ctx->indices.cur + num) <= ctx->indices.num) {
-        res.ptr = &(ctx->indices.ptr[ctx->indices.cur]);
-        res.index = ctx->indices.cur;
-        ctx->indices.cur += num;
+    if ((ctx->indices.next + num) <= ctx->indices.cap) {
+        res.ptr = &(ctx->indices.ptr[ctx->indices.next]);
+        res.index = ctx->indices.next;
+        ctx->indices.next += num;
     }
     }
     else {
     else {
         _SSPINE_ERROR(INDEX_BUFFER_OVERFLOW);
         _SSPINE_ERROR(INDEX_BUFFER_OVERFLOW);
@@ -4114,7 +4114,7 @@ static void _sspine_draw_instance(_sspine_context_t* ctx, _sspine_instance_t* in
     // see: https://github.com/EsotericSoftware/spine-runtimes/blob/4.1/spine-sdl/src/spine-sdl-c.c
     // see: https://github.com/EsotericSoftware/spine-runtimes/blob/4.1/spine-sdl/src/spine-sdl-c.c
     const spSkeleton* sp_skel = instance->sp_skel;
     const spSkeleton* sp_skel = instance->sp_skel;
     float* tform_buf = (float*)instance->skel.ptr->tform_buf.ptr;
     float* tform_buf = (float*)instance->skel.ptr->tform_buf.ptr;
-    const int max_tform_buf_verts = instance->skel.ptr->tform_buf.num;
+    const int max_tform_buf_verts = instance->skel.ptr->tform_buf.cap;
     SOKOL_UNUSED(max_tform_buf_verts); // only used in asserts
     SOKOL_UNUSED(max_tform_buf_verts); // only used in asserts
     const int tform_buf_stride = 2; // each element is 2 floats
     const int tform_buf_stride = 2; // each element is 2 floats
     spSkeletonClipping* sp_clip = instance->sp_clip;
     spSkeletonClipping* sp_clip = instance->sp_clip;
@@ -4254,11 +4254,11 @@ static void _sspine_draw_instance(_sspine_context_t* ctx, _sspine_instance_t* in
                 break;
                 break;
         }
         }
 
 
-        // write new draw command, or merge with previous draw command
-        _sspine_command_t* prev_cmd = _sspine_prev_command(ctx);
-        if (prev_cmd && (prev_cmd->layer == layer) && (prev_cmd->pip.id == pip.id) && (prev_cmd->img.id == img.id) && (prev_cmd->pma == pma)) {
-            // merge with previous command
-            prev_cmd->num_elements += num_indices;
+        // write new draw command, or merge with current draw command
+        _sspine_command_t* cur_cmd = _sspine_cur_command(ctx);
+        if (cur_cmd && (cur_cmd->layer == layer) && (cur_cmd->pip.id == pip.id) && (cur_cmd->img.id == img.id) && (cur_cmd->pma == pma)) {
+            // merge with current command
+            cur_cmd->num_elements += num_indices;
         }
         }
         else {
         else {
             // record a new command
             // record a new command
@@ -4311,14 +4311,14 @@ static _sspine_vsparams_t _sspine_compute_vsparams(const sspine_layer_transform*
 }
 }
 
 
 static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_layer_transform* tform) {
 static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_layer_transform* tform) {
-    if ((ctx->vertices.cur > 0) && (ctx->commands.cur > 0)) {
+    if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) {
         sg_push_debug_group("sokol-spine");
         sg_push_debug_group("sokol-spine");
 
 
         if (ctx->update_frame_id != _sspine.frame_id) {
         if (ctx->update_frame_id != _sspine.frame_id) {
             ctx->update_frame_id = _sspine.frame_id;
             ctx->update_frame_id = _sspine.frame_id;
-            const sg_range vtx_range = { ctx->vertices.ptr, (size_t)ctx->vertices.cur * sizeof(_sspine_vertex_t) };
+            const sg_range vtx_range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sspine_vertex_t) };
             sg_update_buffer(ctx->vbuf, &vtx_range);
             sg_update_buffer(ctx->vbuf, &vtx_range);
-            const sg_range idx_range = { ctx->indices.ptr, (size_t)ctx->indices.cur * sizeof(uint32_t) };
+            const sg_range idx_range = { ctx->indices.ptr, (size_t)ctx->indices.next * sizeof(uint32_t) };
             sg_update_buffer(ctx->ibuf, &idx_range);
             sg_update_buffer(ctx->ibuf, &idx_range);
         }
         }
 
 
@@ -4331,7 +4331,7 @@ static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_l
         uint32_t cur_pip_id = SG_INVALID_ID;
         uint32_t cur_pip_id = SG_INVALID_ID;
         uint32_t cur_img_id = SG_INVALID_ID;
         uint32_t cur_img_id = SG_INVALID_ID;
         float cur_pma = -1.0f;
         float cur_pma = -1.0f;
-        for (int i = 0; i < ctx->commands.cur; i++) {
+        for (int i = 0; i < ctx->commands.next; i++) {
             const _sspine_command_t* cmd = &ctx->commands.ptr[i];
             const _sspine_command_t* cmd = &ctx->commands.ptr[i];
             if ((layer == cmd->layer) && (sg_query_image_state(cmd->img) == SG_RESOURCESTATE_VALID)) {
             if ((layer == cmd->layer) && (sg_query_image_state(cmd->img) == SG_RESOURCESTATE_VALID)) {
                 if (cur_pip_id != cmd->pip.id) {
                 if (cur_pip_id != cmd->pip.id) {
@@ -4542,9 +4542,9 @@ SOKOL_API_IMPL sspine_context_info sspine_get_context_info(sspine_context ctx_id
     _sspine_clear(&res, sizeof(res));
     _sspine_clear(&res, sizeof(res));
     const _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id);
     const _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id);
     if (ctx) {
     if (ctx) {
-        res.num_vertices = ctx->vertices.cur;
-        res.num_indices  = ctx->indices.cur;
-        res.num_commands = ctx->commands.cur;
+        res.num_vertices = ctx->vertices.next;
+        res.num_indices  = ctx->indices.next;
+        res.num_commands = ctx->commands.next;
     }
     }
     return res;
     return res;
 }
 }