Procházet zdrojové kódy

Merge branch 'master' into sgfx-wgpu

Andre Weissflog před 1 rokem
rodič
revize
9a1421dd1e

+ 1 - 1
.github/workflows/gen_bindings.yml

@@ -227,7 +227,7 @@ jobs:
       - uses: actions/download-artifact@v3
       - uses: actions/download-artifact@v3
         with:
         with:
           name: ignore-me-rust
           name: ignore-me-rust
-          path: src/sokol
+          path: src
       - uses: dtolnay/rust-toolchain@master
       - uses: dtolnay/rust-toolchain@master
         with:
         with:
           toolchain: stable
           toolchain: stable

+ 64 - 0
CHANGELOG.md

@@ -1,5 +1,69 @@
 ## Updates
 ## Updates
 
 
+#### 03-Oct-2023
+
+- sokol_app.h win/gl: PR https://github.com/floooh/sokol/pull/886 has been merged, this makes
+  GL context initialization on Windows slightly more efficient. Many thanks to @dtrebilco!
+
+#### 25-Sep-2023
+
+- The allocator callback functions in all headers that support custom allocators have been renamed
+  from `alloc` and `free` to `alloc_fn` and `free_fn`, this is because the symbol `free` is quite
+  likely to collide with a preprocessor macro of the same name if the standard C allocator is
+  replaced with a custom allocator.
+
+  This is a breaking change only if you've been providing your own allocator functions to
+  the sokol headers.
+
+  See issue https://github.com/floooh/sokol/issues/903 and PR https://github.com/floooh/sokol/pull/908
+  for details.
+
+#### 23-Sep-2023
+
+- sokol_gfx.h gl: Allow to inject an external GL framebuffer id into the sokol-gfx default
+  pass. See PR https://github.com/floooh/sokol/pull/899 and issue https://github.com/floooh/sokol/issues/892
+  for details. Many thanks to @danielchasehooper for the discussion and PR!
+
+  Further down the road I want to make the whole topic more flexible while at the same time
+  simplifying the sokol-gfx API, see here: https://github.com/floooh/sokol/issues/904
+
+#### 22-Sep-2023
+
+- sokol_gfx.h: Fixed a Metal validation error on Intel Macs when creating textures (Intel Macs
+  have unified memory, but don't support textures in shared storage mode). This was a regression
+  in the image/sampler split update in mid-July 2023. Fixes issue https://github.com/floooh/sokol/issues/905
+  via PR https://github.com/floooh/sokol/pull/907.
+
+#### 19-Sep-2023
+
+- sokol_fetch.h: fixed a minor issue where a request that was cancelled before it was dispatched
+  had an incomplete response state set in the response callback (the `finished`, `failed` and
+  `error_code` fields were not set). This fixes issue https://github.com/floooh/sokol/issues/882
+  via PR https://github.com/floooh/sokol/pull/898
+
+#### 18-Sep-2023
+
+- PR https://github.com/floooh/sokol/pull/893 has been merged, this fixes a minor issue
+  in the GL backend when using an injected texture as framebuffer attachment.
+- Issue https://github.com/floooh/sokol/issues/884 has been fixed via PR https://github.com/floooh/sokol/pull/894,
+  this adds missing error code paths in the Metal backend when Metal object creation fails.
+- Clarified `sapp_run()` behaviour in the sokol_app.h documentation header (search for `OPTIONAL: DON'T HIJACK main()`)
+- sokol_args.h now fully supports "key-only args", see issue https://github.com/floooh/sokol/issues/876 for details,
+  fixed via PR https://github.com/floooh/sokol/pull/896
+
+#### 17-Sep-2023
+
+- The sokol-gfx Metal backend now adds debug labels to Metal resource objects and
+  also passes through the `sg_push/pop_debug_group()` calls. If you use the push/pop
+  debug group calls, please be aware of the following limitations:
+
+  - a push inside a render pass must have an associated pop inside the same render pass
+  - a push outside any render pass must have an associated pop outside any render pass
+  - Metal will ignore any push/pop calls outside render passes (this is because in Metal
+    these are MTLCommandEncoder methods)
+
+  Associated issue: https://github.com/floooh/sokol/issues/889, and PR: https://github.com/floooh/sokol/pull/890.
+
 #### 09-Sep-2023
 #### 09-Sep-2023
 
 
 - a small PR has been merged which fixes a redundant glBindFramebuffer() in the GLES3 backend
 - a small PR has been merged which fixes a redundant glBindFramebuffer() in the GLES3 backend

+ 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) (**26-Jul-2023** proper image/sampler pair support in sokol_nuklear.h)
+[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**25-Sep-2023** POTENTIALLY BREAKING: allocator callbacks have been renamed)
 
 
 [![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)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/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)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)
 
 

+ 2 - 0
bindgen/gen_rust.py

@@ -355,6 +355,8 @@ def funcptr_result_c(field_type):
     res_type = field_type[: field_type.index("(*)")].strip()
     res_type = field_type[: field_type.index("(*)")].strip()
     if res_type == "void":
     if res_type == "void":
         return ""
         return ""
+    elif is_prim_type(res_type):
+        return f" -> {as_rust_prim_type(res_type)}"
     elif util.is_const_void_ptr(res_type):
     elif util.is_const_void_ptr(res_type):
         return " -> *const core::ffi::c_void"
         return " -> *const core::ffi::c_void"
     elif util.is_void_ptr(res_type):
     elif util.is_void_ptr(res_type):

+ 2 - 0
bindgen/gen_zig.py

@@ -271,6 +271,8 @@ def funcptr_result_c(field_type):
     res_type = field_type[:field_type.index('(*)')].strip()
     res_type = field_type[:field_type.index('(*)')].strip()
     if res_type == 'void':
     if res_type == 'void':
         return 'void'
         return 'void'
+    elif is_prim_type(res_type):
+        return as_zig_prim_type(res_type)
     elif util.is_const_void_ptr(res_type):
     elif util.is_const_void_ptr(res_type):
         return '?*const anyopaque'
         return '?*const anyopaque'
     elif util.is_void_ptr(res_type):
     elif util.is_void_ptr(res_type):

+ 97 - 35
sokol_app.h

@@ -951,9 +951,11 @@
 
 
     OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
     OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
     ======================================================
     ======================================================
+    NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android.
+
     In its default configuration, sokol_app.h "hijacks" the platform's
     In its default configuration, sokol_app.h "hijacks" the platform's
     standard main() function. This was done because different platforms
     standard main() function. This was done because different platforms
-    have different main functions which are not compatible with
+    have different entry point conventions which are not compatible with
     C's main() (for instance WinMain on Windows has completely different
     C's main() (for instance WinMain on Windows has completely different
     arguments). However, this "main hijacking" posed a problem for
     arguments). However, this "main hijacking" posed a problem for
     usage scenarios like integrating sokol_app.h with other languages than
     usage scenarios like integrating sokol_app.h with other languages than
@@ -965,12 +967,30 @@
     - instead provide the standard main() function of the platform
     - instead provide the standard main() function of the platform
     - from the main function, call the function ```sapp_run()``` which
     - from the main function, call the function ```sapp_run()``` which
       takes a pointer to an ```sapp_desc``` structure.
       takes a pointer to an ```sapp_desc``` structure.
-    - ```sapp_run()``` takes over control and calls the provided init-, frame-,
-      shutdown- and event-callbacks just like in the default model, it
-      will only return when the application quits (or not at all on some
-      platforms, like emscripten)
+    - from here on```sapp_run()``` takes over control and calls the provided
+      init-, frame-, event- and cleanup-callbacks just like in the default model.
+
+    sapp_run() behaves differently across platforms:
+
+        - on some platforms, sapp_run() will return when the application quits
+        - on other platforms, sapp_run() will never return, even when the
+          application quits (the operating system is free to simply terminate
+          the application at any time)
+        - on Emscripten specifically, sapp_run() will return immediately while
+          the frame callback keeps being called
+
+    This different behaviour of sapp_run() essentially means that there shouldn't
+    be any code *after* sapp_run(), because that may either never be called, or in
+    case of Emscripten will be called at an unexpected time (at application start).
 
 
-    NOTE: SOKOL_NO_ENTRY is currently not supported on Android.
+    An application also should not depend on the cleanup-callback being called
+    when cross-platform compatibility is required.
+
+    Since sapp_run() returns immediately on Emscripten you shouldn't activate
+    the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling
+    for the browser target), since the C/C++ exit runtime would be called immediately at
+    application start, causing any global objects to be destroyed and global
+    variables to be zeroed.
 
 
     WINDOWS CONSOLE OUTPUT
     WINDOWS CONSOLE OUTPUT
     ======================
     ======================
@@ -1019,8 +1039,8 @@
             return (sapp_desc){
             return (sapp_desc){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...,
                     .user_data = ...,
                 }
                 }
             };
             };
@@ -1473,12 +1493,12 @@ typedef struct sapp_icon_desc {
 
 
     Used in sapp_desc to provide custom memory-alloc and -free functions
     Used in sapp_desc to provide custom memory-alloc and -free functions
     to sokol_app.h. If memory management should be overridden, both the
     to sokol_app.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fb and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sapp_allocator {
 typedef struct sapp_allocator {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sapp_allocator;
 } sapp_allocator;
 
 
@@ -2857,10 +2877,9 @@ _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) {
 _SOKOL_PRIVATE void* _sapp_malloc(size_t size) {
 _SOKOL_PRIVATE void* _sapp_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_sapp.desc.allocator.alloc) {
-        ptr = _sapp.desc.allocator.alloc(size, _sapp.desc.allocator.user_data);
-    }
-    else {
+    if (_sapp.desc.allocator.alloc_fn) {
+        ptr = _sapp.desc.allocator.alloc_fn(size, _sapp.desc.allocator.user_data);
+    } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
     if (0 == ptr) {
     if (0 == ptr) {
@@ -2876,8 +2895,8 @@ _SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) {
 }
 }
 
 
 _SOKOL_PRIVATE void _sapp_free(void* ptr) {
 _SOKOL_PRIVATE void _sapp_free(void* ptr) {
-    if (_sapp.desc.allocator.free) {
-        _sapp.desc.allocator.free(ptr, _sapp.desc.allocator.user_data);
+    if (_sapp.desc.allocator.free_fn) {
+        _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data);
     }
     }
     else {
     else {
         free(ptr);
         free(ptr);
@@ -2981,7 +3000,7 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) {
 }
 }
 
 
 _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) {
 _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) {
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     sapp_desc res = *desc;
     sapp_desc res = *desc;
     res.sample_count = _sapp_def(res.sample_count, 1);
     res.sample_count = _sapp_def(res.sample_count, 1);
     res.swap_interval = _sapp_def(res.swap_interval, 1);
     res.swap_interval = _sapp_def(res.swap_interval, 1);
@@ -6626,11 +6645,54 @@ _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) {
     return value;
     return value;
 }
 }
 
 
+_SOKOL_PRIVATE void _sapp_wgl_attribiv(int pixel_format, int num_attribs, const int* attribs, int* results) {
+    SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
+    if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, num_attribs, attribs, results)) {
+        _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED);
+    }
+}
+
 _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
 _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
     SOKOL_ASSERT(_sapp.win32.dc);
     SOKOL_ASSERT(_sapp.win32.dc);
     SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
     SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
     const _sapp_gl_fbconfig* closest;
     const _sapp_gl_fbconfig* closest;
 
 
+    #define _sapp_wgl_num_query_tags (12)
+    const int query_tags[_sapp_wgl_num_query_tags] = {
+      WGL_SUPPORT_OPENGL_ARB,
+      WGL_DRAW_TO_WINDOW_ARB,
+      WGL_PIXEL_TYPE_ARB,
+      WGL_ACCELERATION_ARB,
+      WGL_DOUBLE_BUFFER_ARB,
+      WGL_RED_BITS_ARB,
+      WGL_GREEN_BITS_ARB,
+      WGL_BLUE_BITS_ARB,
+      WGL_ALPHA_BITS_ARB,
+      WGL_DEPTH_BITS_ARB,
+      WGL_STENCIL_BITS_ARB,
+      WGL_SAMPLES_ARB,
+    };
+    const int result_support_opengl_index = 0;
+    const int result_draw_to_window_index = 1;
+    const int result_pixel_type_index = 2;
+    const int result_acceleration_index = 3;
+    const int result_double_buffer_index = 4;
+    const int result_red_bits_index = 5;
+    const int result_green_bits_index = 6;
+    const int result_blue_bits_index = 7;
+    const int result_alpha_bits_index = 8;
+    const int result_depth_bits_index = 9;
+    const int result_stencil_bits_index = 10;
+    const int result_samples_index = 11;
+
+    int query_results[_sapp_wgl_num_query_tags] = {0};
+    // Drop the last item if multisample extension is not supported.
+    //  If in future querying with multiple extensions, will have to shuffle index values to have active extensions on the end.
+    int query_count = _sapp_wgl_num_query_tags;
+    if (!_sapp.wgl.arb_multisample) {
+        query_count = _sapp_wgl_num_query_tags - 1;
+    }
+
     int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
     int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
     _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig));
     _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig));
     SOKOL_ASSERT(usable_configs);
     SOKOL_ASSERT(usable_configs);
@@ -6639,27 +6701,27 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
         const int n = i + 1;
         const int n = i + 1;
         _sapp_gl_fbconfig* u = usable_configs + usable_count;
         _sapp_gl_fbconfig* u = usable_configs + usable_count;
         _sapp_gl_init_fbconfig(u);
         _sapp_gl_init_fbconfig(u);
-        if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) {
-            continue;
-        }
-        if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) {
-            continue;
-        }
-        if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) {
+        _sapp_wgl_attribiv(n, query_count, query_tags, query_results);
+
+        if (query_results[result_support_opengl_index] == 0
+          || query_results[result_draw_to_window_index] == 0
+          || query_results[result_pixel_type_index] != WGL_TYPE_RGBA_ARB
+          || query_results[result_acceleration_index] == WGL_NO_ACCELERATION_ARB)
+        {
             continue;
             continue;
         }
         }
-        u->red_bits     = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB);
-        u->green_bits   = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB);
-        u->blue_bits    = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB);
-        u->alpha_bits   = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB);
-        u->depth_bits   = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB);
-        u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB);
-        if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) {
+        u->red_bits     = query_results[result_red_bits_index];
+        u->green_bits   = query_results[result_green_bits_index];
+        u->blue_bits    = query_results[result_blue_bits_index];
+        u->alpha_bits   = query_results[result_alpha_bits_index];
+        u->depth_bits   = query_results[result_depth_bits_index];
+        u->stencil_bits = query_results[result_stencil_bits_index];
+        if (query_results[result_double_buffer_index]) {
             u->doublebuffer = true;
             u->doublebuffer = true;
         }
         }
-        if (_sapp.wgl.arb_multisample) {
-            u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB);
-        }
+
+        u->samples = query_results[result_samples_index]; // NOTE: If arb_multisample is not supported  - just takes the default 0
+
         u->handle = (uintptr_t)n;
         u->handle = (uintptr_t)n;
         usable_count++;
         usable_count++;
     }
     }

+ 75 - 53
sokol_args.h

@@ -35,11 +35,22 @@
 
 
     When running as WebAssembly app, arguments are taken from the page URL:
     When running as WebAssembly app, arguments are taken from the page URL:
 
 
-    https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc
+        https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc
 
 
     The same arguments provided to a command line app:
     The same arguments provided to a command line app:
 
 
-    kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc
+        kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc
+
+    You can also use standalone keys without value:
+
+        https://floooh.github.io/tiny8bit/kc85.html?bla&blub
+
+    On the command line:
+
+        kc85 bla blub
+
+    Such value-less keys are reported as the value being an empty string, but they
+    can be tested with `sapp_exists("bla")` or `sapp_boolean("blub")`.
 
 
     ARGUMENT FORMATTING
     ARGUMENT FORMATTING
     ===================
     ===================
@@ -57,6 +68,12 @@
 
 
         key=value
         key=value
 
 
+    or
+
+        key
+
+    When a key has no value, the value will be assigned an empty string.
+
     Key/value pairs are separated by 'whitespace', valid whitespace
     Key/value pairs are separated by 'whitespace', valid whitespace
     characters are space and tab.
     characters are space and tab.
 
 
@@ -71,9 +88,6 @@
 
 
     The 'key' string must be a simple string without escape sequences or whitespace.
     The 'key' string must be a simple string without escape sequences or whitespace.
 
 
-    Currently 'single keys' without values are not allowed, but may be
-    in the future.
-
     The 'value' string can be quoted, and quoted value strings can contain
     The 'value' string can be quoted, and quoted value strings can contain
     whitespace:
     whitespace:
 
 
@@ -123,7 +137,7 @@
                 ...
                 ...
             }
             }
 
 
-            // check if a key's value is "true", "yes" or "on"
+            // check if a key's value is "true", "yes" or "on" or if this is a standalone key
             if (sargs_boolean("joystick_enabled")) {
             if (sargs_boolean("joystick_enabled")) {
                 ...
                 ...
             }
             }
@@ -183,23 +197,23 @@
         Return true between sargs_setup() and sargs_shutdown()
         Return true between sargs_setup() and sargs_shutdown()
 
 
     bool sargs_exists(const char* key)
     bool sargs_exists(const char* key)
-        Test if a key arg exists.
+        Test if an argument exists by its key name.
 
 
     const char* sargs_value(const char* key)
     const char* sargs_value(const char* key)
-        Return value associated with key. Returns an empty
-        string ("") if the key doesn't exist.
+        Return value associated with key. Returns an empty string ("") if the
+        key doesn't exist, or if the key doesn't have a value.
 
 
     const char* sargs_value_def(const char* key, const char* default)
     const char* sargs_value_def(const char* key, const char* default)
-        Return value associated with key, or the provided default
-        value if the value doesn't exist.
+        Return value associated with key, or the provided default value if the
+        key doesn't exist, or this is a value-less key.
 
 
     bool sargs_equals(const char* key, const char* val);
     bool sargs_equals(const char* key, const char* val);
         Return true if the value associated with key matches
         Return true if the value associated with key matches
         the 'val' argument.
         the 'val' argument.
 
 
     bool sargs_boolean(const char* key)
     bool sargs_boolean(const char* key)
-        Return true if the value string of 'key' is one
-        of 'true', 'yes', 'on'.
+        Return true if the value string of 'key' is one of 'true', 'yes', 'on',
+        or this is a key without value.
 
 
     int sargs_find(const char* key)
     int sargs_find(const char* key)
         Find argument by key name and return its index, or -1 if not found.
         Find argument by key name and return its index, or -1 if not found.
@@ -213,7 +227,7 @@
 
 
     const char* sargs_value_at(int index)
     const char* sargs_value_at(int index)
         Return the value of argument at index. Returns empty string
         Return the value of argument at index. Returns empty string
-        if index is outside range.
+        if the key at index has no value, or the index is out-of-range.
 
 
 
 
     MEMORY ALLOCATION OVERRIDE
     MEMORY ALLOCATION OVERRIDE
@@ -233,8 +247,8 @@
             sargs_setup(&(sargs_desc){
             sargs_setup(&(sargs_desc){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...,
                     .user_data = ...,
                 }
                 }
             });
             });
@@ -302,12 +316,12 @@ extern "C" {
 
 
     Used in sargs_desc to provide custom memory-alloc and -free functions
     Used in sargs_desc to provide custom memory-alloc and -free functions
     to sokol_args.h. If memory management should be overridden, both the
     to sokol_args.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sargs_allocator {
 typedef struct sargs_allocator {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sargs_allocator;
 } sargs_allocator;
 
 
@@ -327,13 +341,13 @@ SOKOL_ARGS_API_DECL void sargs_shutdown(void);
 SOKOL_ARGS_API_DECL bool sargs_isvalid(void);
 SOKOL_ARGS_API_DECL bool sargs_isvalid(void);
 /* test if an argument exists by key name */
 /* test if an argument exists by key name */
 SOKOL_ARGS_API_DECL bool sargs_exists(const char* key);
 SOKOL_ARGS_API_DECL bool sargs_exists(const char* key);
-/* get value by key name, return empty string if key doesn't exist */
+/* get value by key name, return empty string if key doesn't exist or an existing key has no value */
 SOKOL_ARGS_API_DECL const char* sargs_value(const char* key);
 SOKOL_ARGS_API_DECL const char* sargs_value(const char* key);
-/* get value by key name, return provided default if key doesn't exist */
+/* get value by key name, return provided default if key doesn't exist or has no value */
 SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def);
 SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def);
 /* return true if val arg matches the value associated with key */
 /* return true if val arg matches the value associated with key */
 SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val);
 SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val);
-/* return true if key's value is "true", "yes" or "on" */
+/* return true if key's value is "true", "yes", "on" or an existing key has no value */
 SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key);
 SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key);
 /* get index of arg by key name, return -1 if not exists */
 /* get index of arg by key name, return -1 if not exists */
 SOKOL_ARGS_API_DECL int sargs_find(const char* key);
 SOKOL_ARGS_API_DECL int sargs_find(const char* key);
@@ -433,10 +447,9 @@ _SOKOL_PRIVATE void _sargs_clear(void* ptr, size_t size) {
 _SOKOL_PRIVATE void* _sargs_malloc(size_t size) {
 _SOKOL_PRIVATE void* _sargs_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_sargs.allocator.alloc) {
-        ptr = _sargs.allocator.alloc(size, _sargs.allocator.user_data);
-    }
-    else {
+    if (_sargs.allocator.alloc_fn) {
+        ptr = _sargs.allocator.alloc_fn(size, _sargs.allocator.user_data);
+    } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
     SOKOL_ASSERT(ptr);
     SOKOL_ASSERT(ptr);
@@ -450,10 +463,9 @@ _SOKOL_PRIVATE void* _sargs_malloc_clear(size_t size) {
 }
 }
 
 
 _SOKOL_PRIVATE void _sargs_free(void* ptr) {
 _SOKOL_PRIVATE void _sargs_free(void* ptr) {
-    if (_sargs.allocator.free) {
-        _sargs.allocator.free(ptr, _sargs.allocator.user_data);
-    }
-    else {
+    if (_sargs.allocator.free_fn) {
+        _sargs.allocator.free_fn(ptr, _sargs.allocator.user_data);
+    } else {
         free(ptr);
         free(ptr);
     }
     }
 }
 }
@@ -486,8 +498,8 @@ _SOKOL_PRIVATE bool _sargs_val_expected(void) {
     return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL);
     return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL);
 }
 }
 
 
-_SOKOL_PRIVATE void _sargs_expect_sep(void) {
-    _sargs.parse_state = _SARGS_EXPECT_SEP;
+_SOKOL_PRIVATE void _sargs_expect_sep_or_key(void) {
+    _sargs.parse_state = _SARGS_EXPECT_SEP | _SARGS_EXPECT_KEY;
 }
 }
 
 
 _SOKOL_PRIVATE bool _sargs_any_expected(void) {
 _SOKOL_PRIVATE bool _sargs_any_expected(void) {
@@ -524,14 +536,17 @@ _SOKOL_PRIVATE bool _sargs_is_whitespace(char c) {
 }
 }
 
 
 _SOKOL_PRIVATE void _sargs_start_key(void) {
 _SOKOL_PRIVATE void _sargs_start_key(void) {
-    SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
+    SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args));
     _sargs.parse_state = _SARGS_PARSING_KEY;
     _sargs.parse_state = _SARGS_PARSING_KEY;
     _sargs.args[_sargs.num_args].key = _sargs.buf_pos;
     _sargs.args[_sargs.num_args].key = _sargs.buf_pos;
 }
 }
 
 
 _SOKOL_PRIVATE void _sargs_end_key(void) {
 _SOKOL_PRIVATE void _sargs_end_key(void) {
-    SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
+    SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args));
     _sargs_putc(0);
     _sargs_putc(0);
+    // declare val as empty string in case this is a key-only arg
+    _sargs.args[_sargs.num_args].val = _sargs.buf_pos - 1;
+    _sargs.num_args++;
     _sargs.parse_state = 0;
     _sargs.parse_state = 0;
 }
 }
 
 
@@ -540,15 +555,13 @@ _SOKOL_PRIVATE bool _sargs_parsing_key(void) {
 }
 }
 
 
 _SOKOL_PRIVATE void _sargs_start_val(void) {
 _SOKOL_PRIVATE void _sargs_start_val(void) {
-    SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
+    SOKOL_ASSERT((_sargs.num_args > 0) && (_sargs.num_args <= _sargs.max_args));
     _sargs.parse_state = _SARGS_PARSING_VAL;
     _sargs.parse_state = _SARGS_PARSING_VAL;
-    _sargs.args[_sargs.num_args].val = _sargs.buf_pos;
+    _sargs.args[_sargs.num_args - 1].val = _sargs.buf_pos;
 }
 }
 
 
 _SOKOL_PRIVATE void _sargs_end_val(void) {
 _SOKOL_PRIVATE void _sargs_end_val(void) {
-    SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
     _sargs_putc(0);
     _sargs_putc(0);
-    _sargs.num_args++;
     _sargs.parse_state = 0;
     _sargs.parse_state = 0;
 }
 }
 
 
@@ -596,7 +609,12 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
         if (_sargs_any_expected()) {
         if (_sargs_any_expected()) {
             if (!_sargs_is_whitespace(c)) {
             if (!_sargs_is_whitespace(c)) {
                 /* start of key, value or separator */
                 /* start of key, value or separator */
-                if (_sargs_key_expected()) {
+                if (_sargs_is_separator(c)) {
+                    /* skip separator and expect value */
+                    _sargs_expect_val();
+                    continue;
+                }
+                else if (_sargs_key_expected()) {
                     /* start of new key */
                     /* start of new key */
                     _sargs_start_key();
                     _sargs_start_key();
                 }
                 }
@@ -608,13 +626,6 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
                     }
                     }
                     _sargs_start_val();
                     _sargs_start_val();
                 }
                 }
-                else {
-                    /* separator */
-                    if (_sargs_is_separator(c)) {
-                        _sargs_expect_val();
-                        continue;
-                    }
-                }
             }
             }
             else {
             else {
                 /* skip white space */
                 /* skip white space */
@@ -629,7 +640,7 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
                     _sargs_expect_val();
                     _sargs_expect_val();
                 }
                 }
                 else {
                 else {
-                    _sargs_expect_sep();
+                    _sargs_expect_sep_or_key();
                 }
                 }
                 continue;
                 continue;
             }
             }
@@ -657,7 +668,7 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
     }
     }
     if (_sargs_parsing_key()) {
     if (_sargs_parsing_key()) {
         _sargs_end_key();
         _sargs_end_key();
-        _sargs_expect_sep();
+        _sargs_expect_sep_or_key();
     }
     }
     else if (_sargs_parsing_val() && !_sargs_in_quotes()) {
     else if (_sargs_parsing_val() && !_sargs_in_quotes()) {
         _sargs_end_val();
         _sargs_end_val();
@@ -823,7 +834,13 @@ SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) {
     SOKOL_ASSERT(_sargs.valid && key && def);
     SOKOL_ASSERT(_sargs.valid && key && def);
     int arg_index = sargs_find(key);
     int arg_index = sargs_find(key);
     if (-1 != arg_index) {
     if (-1 != arg_index) {
-        return sargs_value_at(arg_index);
+        const char* res = sargs_value_at(arg_index);
+        SOKOL_ASSERT(res);
+        if (res[0] == 0) {
+            return def;
+        } else {
+            return res;
+        }
     }
     }
     else {
     else {
         return def;
         return def;
@@ -836,10 +853,15 @@ SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) {
 }
 }
 
 
 SOKOL_API_IMPL bool sargs_boolean(const char* key) {
 SOKOL_API_IMPL bool sargs_boolean(const char* key) {
-    const char* val = sargs_value(key);
-    return (0 == strcmp("true", val)) ||
-           (0 == strcmp("yes", val)) ||
-           (0 == strcmp("on", val));
+    if (sargs_exists(key)) {
+        const char* val = sargs_value(key);
+        return (0 == strcmp("true", val)) ||
+               (0 == strcmp("yes", val)) ||
+               (0 == strcmp("on", val)) ||
+               (0 == strcmp("", val));
+    } else {
+        return false;
+    }
 }
 }
 
 
 #endif /* SOKOL_ARGS_IMPL */
 #endif /* SOKOL_ARGS_IMPL */

+ 12 - 14
sokol_audio.h

@@ -397,8 +397,8 @@
             saudio_setup(&(saudio_desc){
             saudio_setup(&(saudio_desc){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...,
                     .user_data = ...,
                 }
                 }
             });
             });
@@ -575,12 +575,12 @@ typedef struct saudio_logger {
 
 
     Used in saudio_desc to provide custom memory-alloc and -free functions
     Used in saudio_desc to provide custom memory-alloc and -free functions
     to sokol_audio.h. If memory management should be overridden, both the
     to sokol_audio.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct saudio_allocator {
 typedef struct saudio_allocator {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } saudio_allocator;
 } saudio_allocator;
 
 
@@ -1146,10 +1146,9 @@ _SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) {
 _SOKOL_PRIVATE void* _saudio_malloc(size_t size) {
 _SOKOL_PRIVATE void* _saudio_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_saudio.desc.allocator.alloc) {
-        ptr = _saudio.desc.allocator.alloc(size, _saudio.desc.allocator.user_data);
-    }
-    else {
+    if (_saudio.desc.allocator.alloc_fn) {
+        ptr = _saudio.desc.allocator.alloc_fn(size, _saudio.desc.allocator.user_data);
+    } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
     if (0 == ptr) {
     if (0 == ptr) {
@@ -1165,10 +1164,9 @@ _SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) {
 }
 }
 
 
 _SOKOL_PRIVATE void _saudio_free(void* ptr) {
 _SOKOL_PRIVATE void _saudio_free(void* ptr) {
-    if (_saudio.desc.allocator.free) {
-        _saudio.desc.allocator.free(ptr, _saudio.desc.allocator.user_data);
-    }
-    else {
+    if (_saudio.desc.allocator.free_fn) {
+        _saudio.desc.allocator.free_fn(ptr, _saudio.desc.allocator.user_data);
+    } else {
         free(ptr);
         free(ptr);
     }
     }
 }
 }
@@ -2485,7 +2483,7 @@ void _saudio_backend_shutdown(void) {
 SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
 SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
     SOKOL_ASSERT(!_saudio.valid);
     SOKOL_ASSERT(!_saudio.valid);
     SOKOL_ASSERT(desc);
     SOKOL_ASSERT(desc);
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     _saudio_clear(&_saudio, sizeof(_saudio));
     _saudio_clear(&_saudio, sizeof(_saudio));
     _saudio.desc = *desc;
     _saudio.desc = *desc;
     _saudio.stream_cb = desc->stream_cb;
     _saudio.stream_cb = desc->stream_cb;

+ 28 - 18
sokol_fetch.h

@@ -820,8 +820,8 @@
             sfetch_setup(&(sfetch_desc_t){
             sfetch_setup(&(sfetch_desc_t){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...,
                     .user_data = ...,
                 }
                 }
             });
             });
@@ -1024,8 +1024,8 @@ typedef struct sfetch_range_t {
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sfetch_allocator_t {
 typedef struct sfetch_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sfetch_allocator_t;
 } sfetch_allocator_t;
 
 
@@ -1424,10 +1424,9 @@ _SOKOL_PRIVATE void _sfetch_clear(void* ptr, size_t size) {
 _SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) {
 _SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (allocator->alloc) {
-        ptr = allocator->alloc(size, allocator->user_data);
-    }
-    else {
+    if (allocator->alloc_fn) {
+        ptr = allocator->alloc_fn(size, allocator->user_data);
+    } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
     if (0 == ptr) {
     if (0 == ptr) {
@@ -1447,10 +1446,9 @@ _SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) {
 }
 }
 
 
 _SOKOL_PRIVATE void _sfetch_free(void* ptr) {
 _SOKOL_PRIVATE void _sfetch_free(void* ptr) {
-    if (_sfetch->desc.allocator.free) {
-        _sfetch->desc.allocator.free(ptr, _sfetch->desc.allocator.user_data);
-    }
-    else {
+    if (_sfetch->desc.allocator.free_fn) {
+        _sfetch->desc.allocator.free_fn(ptr, _sfetch->desc.allocator.user_data);
+    } else {
         free(ptr);
         free(ptr);
     }
     }
 }
 }
@@ -2457,6 +2455,12 @@ _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) {
     item->callback(&response);
     item->callback(&response);
 }
 }
 
 
+_SOKOL_PRIVATE void _sfetch_cancel_item(_sfetch_item_t* item) {
+    item->state = _SFETCH_STATE_FAILED;
+    item->user.finished = true;
+    item->user.error_code = SFETCH_ERROR_CANCELLED;
+}
+
 /* per-frame channel stuff: move requests in and out of the IO threads, call response callbacks */
 /* per-frame channel stuff: move requests in and out of the IO threads, call response callbacks */
 _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_t* pool) {
 _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_t* pool) {
 
 
@@ -2469,9 +2473,16 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
         _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id);
         _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id);
         SOKOL_ASSERT(item);
         SOKOL_ASSERT(item);
         SOKOL_ASSERT(item->state == _SFETCH_STATE_ALLOCATED);
         SOKOL_ASSERT(item->state == _SFETCH_STATE_ALLOCATED);
+        // if the item was cancelled early, kick it out immediately
+        if (item->user.cancel) {
+            _sfetch_cancel_item(item);
+            _sfetch_invoke_response_callback(item);
+            _sfetch_pool_item_free(pool, slot_id);
+            continue;
+        }
         item->state = _SFETCH_STATE_DISPATCHED;
         item->state = _SFETCH_STATE_DISPATCHED;
         item->lane = _sfetch_ring_dequeue(&chn->free_lanes);
         item->lane = _sfetch_ring_dequeue(&chn->free_lanes);
-        /* if no buffer provided yet, invoke response callback to do so */
+        // if no buffer provided yet, invoke response callback to do so
         if (0 == item->buffer.ptr) {
         if (0 == item->buffer.ptr) {
             _sfetch_invoke_response_callback(item);
             _sfetch_invoke_response_callback(item);
         }
         }
@@ -2498,8 +2509,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
             item->user.cont = false;
             item->user.cont = false;
         }
         }
         if (item->user.cancel) {
         if (item->user.cancel) {
-            item->state = _SFETCH_STATE_FAILED;
-            item->user.finished = true;
+            _sfetch_cancel_item(item);
         }
         }
         switch (item->state) {
         switch (item->state) {
             case _SFETCH_STATE_DISPATCHED:
             case _SFETCH_STATE_DISPATCHED:
@@ -2541,7 +2551,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
         item->user.fetched_offset = item->thread.fetched_offset;
         item->user.fetched_offset = item->thread.fetched_offset;
         item->user.fetched_size = item->thread.fetched_size;
         item->user.fetched_size = item->thread.fetched_size;
         if (item->user.cancel) {
         if (item->user.cancel) {
-            item->user.error_code = SFETCH_ERROR_CANCELLED;
+            _sfetch_cancel_item(item);
         }
         }
         else {
         else {
             item->user.error_code = item->thread.error_code;
             item->user.error_code = item->thread.error_code;
@@ -2558,7 +2568,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
         }
         }
         _sfetch_invoke_response_callback(item);
         _sfetch_invoke_response_callback(item);
 
 
-        /* when the request is finish, free the lane for another request,
+        /* when the request is finished, free the lane for another request,
            otherwise feed it back into the incoming queue
            otherwise feed it back into the incoming queue
         */
         */
         if (item->user.finished) {
         if (item->user.finished) {
@@ -2608,7 +2618,7 @@ _SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_reques
 }
 }
 
 
 _SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) {
 _SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) {
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     sfetch_desc_t res = *desc;
     sfetch_desc_t res = *desc;
     res.max_requests = _sfetch_def(desc->max_requests, 128);
     res.max_requests = _sfetch_def(desc->max_requests, 128);
     res.num_channels = _sfetch_def(desc->num_channels, 1);
     res.num_channels = _sfetch_def(desc->num_channels, 1);

+ 142 - 29
sokol_gfx.h

@@ -946,8 +946,8 @@
             sg_setup(&(sg_desc){
             sg_setup(&(sg_desc){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...,
                     .user_data = ...,
                 }
                 }
             });
             });
@@ -3059,7 +3059,10 @@ typedef struct sg_frame_stats {
     _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \
     _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \
     _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \
     _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \
     _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \
     _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \
+    _SG_LOGITEM_XMACRO(METAL_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \
     _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \
     _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \
+    _SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \
+    _SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \
     _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \
     _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \
     _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \
     _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \
     _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \
     _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \
@@ -3067,6 +3070,7 @@ typedef struct sg_frame_stats {
     _SG_LOGITEM_XMACRO(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND, "fragment shader entry not found (metal)") \
     _SG_LOGITEM_XMACRO(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND, "fragment shader entry not found (metal)") \
     _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \
     _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \
     _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \
     _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \
+    _SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \
     _SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \
     _SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \
     _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu_bindgroups_cache_size must be > 1 (wgpu)") \
     _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu_bindgroups_cache_size must be > 1 (wgpu)") \
     _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu_bindgroups_cache_size must be a power of 2 (wgpu)") \
     _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu_bindgroups_cache_size must be a power of 2 (wgpu)") \
@@ -3266,7 +3270,7 @@ typedef struct sg_frame_stats {
     _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to fragment stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \
     _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to fragment stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \
     _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \
     _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \
     _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \
     _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \
-    _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size exceeds declared uniform block size") \
+    _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \
     _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \
     _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \
     _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \
     _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \
     _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \
     _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \
@@ -3310,8 +3314,8 @@ typedef enum sg_log_item {
     .max_commit_listeners   1024
     .max_commit_listeners   1024
     .disable_validation     false
     .disable_validation     false
 
 
-    .allocator.alloc        0 (in this case, malloc() will be called)
-    .allocator.free         0 (in this case, free() will be called)
+    .allocator.alloc_fn     0 (in this case, malloc() will be called)
+    .allocator.free_fn      0 (in this case, free() will be called)
     .allocator.user_data    0
     .allocator.user_data    0
 
 
     .context.color_format: default value depends on selected backend:
     .context.color_format: default value depends on selected backend:
@@ -3424,6 +3428,12 @@ typedef struct sg_wgpu_context_desc {
     void* user_data;
     void* user_data;
 } sg_wgpu_context_desc;
 } sg_wgpu_context_desc;
 
 
+typedef struct sg_gl_context_desc {
+    uint32_t (*default_framebuffer_cb)(void);
+    uint32_t (*default_framebuffer_userdata_cb)(void*);
+    void* user_data;
+} sg_gl_context_desc;
+
 typedef struct sg_context_desc {
 typedef struct sg_context_desc {
     sg_pixel_format color_format;
     sg_pixel_format color_format;
     sg_pixel_format depth_format;
     sg_pixel_format depth_format;
@@ -3431,6 +3441,7 @@ typedef struct sg_context_desc {
     sg_metal_context_desc metal;
     sg_metal_context_desc metal;
     sg_d3d11_context_desc d3d11;
     sg_d3d11_context_desc d3d11;
     sg_wgpu_context_desc wgpu;
     sg_wgpu_context_desc wgpu;
+    sg_gl_context_desc gl;
 } sg_context_desc;
 } sg_context_desc;
 
 
 /*
 /*
@@ -3452,12 +3463,12 @@ typedef struct sg_commit_listener {
 
 
     Used in sg_desc to provide custom memory-alloc and -free functions
     Used in sg_desc to provide custom memory-alloc and -free functions
     to sokol_gfx.h. If memory management should be overridden, both the
     to sokol_gfx.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sg_allocator {
 typedef struct sg_allocator {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sg_allocator;
 } sg_allocator;
 
 
@@ -4978,8 +4989,7 @@ typedef struct {
 
 
 typedef struct {
 typedef struct {
     bool valid;
     bool valid;
-    bool has_unified_memory;
-    bool force_managed_storage_mode;
+    bool use_shared_storage_mode;
     const void*(*renderpass_descriptor_cb)(void);
     const void*(*renderpass_descriptor_cb)(void);
     const void*(*renderpass_descriptor_userdata_cb)(void*);
     const void*(*renderpass_descriptor_userdata_cb)(void*);
     const void*(*drawable_cb)(void);
     const void*(*drawable_cb)(void);
@@ -5317,8 +5327,8 @@ _SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) {
 _SOKOL_PRIVATE void* _sg_malloc(size_t size) {
 _SOKOL_PRIVATE void* _sg_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_sg.desc.allocator.alloc) {
-        ptr = _sg.desc.allocator.alloc(size, _sg.desc.allocator.user_data);
+    if (_sg.desc.allocator.alloc_fn) {
+        ptr = _sg.desc.allocator.alloc_fn(size, _sg.desc.allocator.user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -5335,8 +5345,8 @@ _SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) {
 }
 }
 
 
 _SOKOL_PRIVATE void _sg_free(void* ptr) {
 _SOKOL_PRIVATE void _sg_free(void* ptr) {
-    if (_sg.desc.allocator.free) {
-        _sg.desc.allocator.free(ptr, _sg.desc.allocator.user_data);
+    if (_sg.desc.allocator.free_fn) {
+        _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -7308,6 +7318,8 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) {
 
 
 _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) {
 _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) {
     _SOKOL_UNUSED(desc);
     _SOKOL_UNUSED(desc);
+    SOKOL_ASSERT(desc->context.gl.default_framebuffer_cb == 0 || desc->context.gl.default_framebuffer_userdata_cb == 0);
+
     // assumes that _sg.gl is already zero-initialized
     // assumes that _sg.gl is already zero-initialized
     _sg.gl.valid = true;
     _sg.gl.valid = true;
 
 
@@ -7798,11 +7810,13 @@ _SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_gl_attachment_t* gl_att,
     SOKOL_ASSERT(img);
     SOKOL_ASSERT(img);
     const GLuint gl_tex = img->gl.tex[0];
     const GLuint gl_tex = img->gl.tex[0];
     SOKOL_ASSERT(gl_tex);
     SOKOL_ASSERT(gl_tex);
+    const GLuint gl_target = img->gl.target;
+    SOKOL_ASSERT(gl_target);
     const int mip_level = cmn_att->mip_level;
     const int mip_level = cmn_att->mip_level;
     const int slice = cmn_att->slice;
     const int slice = cmn_att->slice;
     switch (img->cmn.type) {
     switch (img->cmn.type) {
         case SG_IMAGETYPE_2D:
         case SG_IMAGETYPE_2D:
-            glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, GL_TEXTURE_2D, gl_tex, mip_level);
+            glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level);
             break;
             break;
         case SG_IMAGETYPE_CUBE:
         case SG_IMAGETYPE_CUBE:
             glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level);
             glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level);
@@ -7994,6 +8008,12 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac
         #if defined(SOKOL_GLCORE33)
         #if defined(SOKOL_GLCORE33)
         glDisable(GL_FRAMEBUFFER_SRGB);
         glDisable(GL_FRAMEBUFFER_SRGB);
         #endif
         #endif
+        if (_sg.desc.context.gl.default_framebuffer_userdata_cb) {
+            _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_userdata_cb(_sg.desc.context.gl.user_data);
+        } else if (_sg.desc.context.gl.default_framebuffer_cb) {
+            _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_cb();
+        }
+
         glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer);
         glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer);
     }
     }
     glViewport(0, 0, w, h);
     glViewport(0, 0, w, h);
@@ -10576,12 +10596,13 @@ _SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resol
 
 
 _SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) {
 _SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) {
     #if defined(_SG_TARGET_MACOS)
     #if defined(_SG_TARGET_MACOS)
-    if (_sg.mtl.force_managed_storage_mode || !_sg.mtl.has_unified_memory) {
-        return MTLResourceStorageModeManaged;
-    } else {
+    if (_sg.mtl.use_shared_storage_mode) {
         return MTLResourceStorageModeShared;
         return MTLResourceStorageModeShared;
+    } else {
+        return MTLResourceStorageModeManaged;
     }
     }
     #else
     #else
+        // MTLResourceStorageModeManaged is not even defined on iOS SDK
         return MTLResourceStorageModeShared;
         return MTLResourceStorageModeShared;
     #endif
     #endif
 }
 }
@@ -11046,16 +11067,20 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) {
     _sg.features.mrt_independent_write_mask = true;
     _sg.features.mrt_independent_write_mask = true;
 
 
     _sg.features.image_clamp_to_border = false;
     _sg.features.image_clamp_to_border = false;
+    #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000)
     if (@available(macOS 12.0, iOS 14.0, *)) {
     if (@available(macOS 12.0, iOS 14.0, *)) {
         _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7]
         _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7]
                                              || [_sg.mtl.device supportsFamily:MTLGPUFamilyApple8]
                                              || [_sg.mtl.device supportsFamily:MTLGPUFamilyApple8]
                                              || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2];
                                              || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2];
+        #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160000)
         if (!_sg.features.image_clamp_to_border) {
         if (!_sg.features.image_clamp_to_border) {
             if (@available(macOS 13.0, iOS 16.0, *)) {
             if (@available(macOS 13.0, iOS 16.0, *)) {
                 _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3];
                 _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3];
             }
             }
         }
         }
+        #endif
     }
     }
+    #endif
 
 
     #if defined(_SG_TARGET_MACOS)
     #if defined(_SG_TARGET_MACOS)
         _sg.limits.max_image_size_2d = 16 * 1024;
         _sg.limits.max_image_size_2d = 16 * 1024;
@@ -11195,22 +11220,35 @@ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) {
     _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES);
     _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES);
     _sg.mtl.device = (__bridge id<MTLDevice>) desc->context.metal.device;
     _sg.mtl.device = (__bridge id<MTLDevice>) desc->context.metal.device;
     _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue];
     _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue];
+
     for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
     for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
         _sg.mtl.uniform_buffers[i] = [_sg.mtl.device
         _sg.mtl.uniform_buffers[i] = [_sg.mtl.device
             newBufferWithLength:(NSUInteger)_sg.mtl.ub_size
             newBufferWithLength:(NSUInteger)_sg.mtl.ub_size
             options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared
             options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared
         ];
         ];
+        #if defined(SOKOL_DEBUG)
+            _sg.mtl.uniform_buffers[i].label = [NSString stringWithFormat:@"sg-uniform-buffer.%d", i];
+        #endif
     }
     }
-    if (@available(macOS 10.15, iOS 13.0, *)) {
-        _sg.mtl.has_unified_memory = _sg.mtl.device.hasUnifiedMemory;
+
+    if (desc->mtl_force_managed_storage_mode) {
+        _sg.mtl.use_shared_storage_mode = false;
+    } else if (@available(macOS 10.15, iOS 13.0, *)) {
+        // on Intel Macs, always use managed resources even though the
+        // device says it supports unified memory (because of texture restrictions)
+        const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1];
+        if (!is_apple_gpu) {
+            _sg.mtl.use_shared_storage_mode = false;
+        } else {
+            _sg.mtl.use_shared_storage_mode = true;
+        }
     } else {
     } else {
         #if defined(_SG_TARGET_MACOS)
         #if defined(_SG_TARGET_MACOS)
-            _sg.mtl.has_unified_memory = false;
+            _sg.mtl.use_shared_storage_mode = false;
         #else
         #else
-            _sg.mtl.has_unified_memory = true;
+            _sg.mtl.use_shared_storage_mode = true;
         #endif
         #endif
     }
     }
-    _sg.mtl.force_managed_storage_mode = desc->mtl_force_managed_storage_mode;
     _sg_mtl_init_caps();
     _sg_mtl_init_caps();
 }
 }
 
 
@@ -11294,7 +11332,16 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const
             } else {
             } else {
                 mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options];
                 mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options];
             }
             }
+            if (nil == mtl_buf) {
+                _SG_ERROR(METAL_CREATE_BUFFER_FAILED);
+                return SG_RESOURCESTATE_FAILED;
+            }
         }
         }
+        #if defined(SOKOL_DEBUG)
+            if (desc->label) {
+                mtl_buf.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot];
+            }
+        #endif
         buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf);
         buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf);
         _SG_OBJC_RELEASE(mtl_buf);
         _SG_OBJC_RELEASE(mtl_buf);
     }
     }
@@ -11434,10 +11481,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg
             mtl_tex = (__bridge id<MTLTexture>) desc->mtl_textures[slot];
             mtl_tex = (__bridge id<MTLTexture>) desc->mtl_textures[slot];
         } else {
         } else {
             mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc];
             mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc];
+            if (nil == mtl_tex) {
+                _SG_OBJC_RELEASE(mtl_desc);
+                _SG_ERROR(METAL_CREATE_TEXTURE_FAILED);
+                return SG_RESOURCESTATE_FAILED;
+            }
             if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) {
             if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) {
                 _sg_mtl_copy_image_data(img, mtl_tex, &desc->data);
                 _sg_mtl_copy_image_data(img, mtl_tex, &desc->data);
             }
             }
         }
         }
+        #if defined(SOKOL_DEBUG)
+            if (desc->label) {
+                mtl_tex.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot];
+            }
+        #endif
         img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex);
         img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex);
         _SG_OBJC_RELEASE(mtl_tex);
         _SG_OBJC_RELEASE(mtl_tex);
     }
     }
@@ -11479,8 +11536,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, cons
         mtl_desc.maxAnisotropy = desc->max_anisotropy;
         mtl_desc.maxAnisotropy = desc->max_anisotropy;
         mtl_desc.normalizedCoordinates = YES;
         mtl_desc.normalizedCoordinates = YES;
         mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare);
         mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare);
+        #if defined(SOKOL_DEBUG)
+            if (desc->label) {
+                mtl_desc.label = [NSString stringWithUTF8String:desc->label];
+            }
+        #endif
         mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc];
         mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc];
         _SG_OBJC_RELEASE(mtl_desc);
         _SG_OBJC_RELEASE(mtl_desc);
+        if (nil == mtl_smp) {
+            _SG_ERROR(METAL_CREATE_SAMPLER_FAILED);
+            return SG_RESOURCESTATE_FAILED;
+        }
     }
     }
     smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp);
     smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp);
     _SG_OBJC_RELEASE(mtl_smp);
     _SG_OBJC_RELEASE(mtl_smp);
@@ -11558,6 +11624,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const
         _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND);
         _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND);
         goto failed;
         goto failed;
     }
     }
+    #if defined(SOKOL_DEBUG)
+        if (desc->label) {
+            vs_lib.label = [NSString stringWithFormat:@"%s.vs", desc->label];
+            fs_lib.label = [NSString stringWithFormat:@"%s.fs", desc->label];
+        }
+    #endif
     // it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index
     // it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index
     shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib  = _sg_mtl_add_resource(vs_lib);
     shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib  = _sg_mtl_add_resource(vs_lib);
     _SG_OBJC_RELEASE(vs_lib);
     _SG_OBJC_RELEASE(vs_lib);
@@ -11673,6 +11745,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s
         rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha);
         rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha);
         rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb);
         rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb);
     }
     }
+    #if defined(SOKOL_DEBUG)
+        if (desc->label) {
+            rp_desc.label = [NSString stringWithFormat:@"%s", desc->label];
+        }
+    #endif
     NSError* err = NULL;
     NSError* err = NULL;
     id<MTLRenderPipelineState> mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err];
     id<MTLRenderPipelineState> mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err];
     _SG_OBJC_RELEASE(rp_desc);
     _SG_OBJC_RELEASE(rp_desc);
@@ -11682,6 +11759,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s
         _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]);
         _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]);
         return SG_RESOURCESTATE_FAILED;
         return SG_RESOURCESTATE_FAILED;
     }
     }
+    pip->mtl.rps = _sg_mtl_add_resource(mtl_rps);
+    _SG_OBJC_RELEASE(mtl_rps);
 
 
     // depth-stencil-state
     // depth-stencil-state
     MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init];
     MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init];
@@ -11705,11 +11784,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s
         ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask;
         ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask;
         ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask;
         ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask;
     }
     }
-    // FIXME: can this actually fail?
+    #if defined(SOKOL_DEBUG)
+        if (desc->label) {
+            ds_desc.label = [NSString stringWithFormat:@"%s.dss", desc->label];
+        }
+    #endif
     id<MTLDepthStencilState> mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc];
     id<MTLDepthStencilState> mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc];
     _SG_OBJC_RELEASE(ds_desc);
     _SG_OBJC_RELEASE(ds_desc);
-    pip->mtl.rps = _sg_mtl_add_resource(mtl_rps);
-    _SG_OBJC_RELEASE(mtl_rps);
+    if (nil == mtl_dss) {
+        _SG_ERROR(METAL_CREATE_DSS_FAILED);
+        return SG_RESOURCESTATE_FAILED;
+    }
     pip->mtl.dss = _sg_mtl_add_resource(mtl_dss);
     pip->mtl.dss = _sg_mtl_add_resource(mtl_dss);
     _SG_OBJC_RELEASE(mtl_dss);
     _SG_OBJC_RELEASE(mtl_dss);
     return SG_RESOURCESTATE_VALID;
     return SG_RESOURCESTATE_VALID;
@@ -12249,6 +12334,19 @@ _SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data*
     _sg_mtl_copy_image_data(img, mtl_tex, data);
     _sg_mtl_copy_image_data(img, mtl_tex, data);
 }
 }
 
 
+_SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) {
+    SOKOL_ASSERT(name);
+    if (_sg.mtl.cmd_encoder) {
+        [_sg.mtl.cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]];
+    }
+}
+
+_SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) {
+    if (_sg.mtl.cmd_encoder) {
+        [_sg.mtl.cmd_encoder popDebugGroup];
+    }
+}
+
 // ██     ██ ███████ ██████   ██████  ██████  ██    ██     ██████   █████   ██████ ██   ██ ███████ ███    ██ ██████
 // ██     ██ ███████ ██████   ██████  ██████  ██    ██     ██████   █████   ██████ ██   ██ ███████ ███    ██ ██████
 // ██     ██ ██      ██   ██ ██       ██   ██ ██    ██     ██   ██ ██   ██ ██      ██  ██  ██      ████   ██ ██   ██
 // ██     ██ ██      ██   ██ ██       ██   ██ ██    ██     ██   ██ ██   ██ ██      ██  ██  ██      ████   ██ ██   ██
 // ██  █  ██ █████   ██████  ██   ███ ██████  ██    ██     ██████  ███████ ██      █████   █████   ██ ██  ██ ██   ██
 // ██  █  ██ █████   ██████  ██   ███ ██████  ██    ██     ██████  ███████ ██      █████   █████   ██ ██  ██ ██   ██
@@ -14592,6 +14690,20 @@ static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data)
     #endif
     #endif
 }
 }
 
 
+static inline void _sg_push_debug_group(const char* name) {
+    #if defined(SOKOL_METAL)
+    _sg_mtl_push_debug_group(name);
+    #else
+    _SOKOL_UNUSED(name);
+    #endif
+}
+
+static inline void _sg_pop_debug_group(void) {
+    #if defined(SOKOL_METAL)
+    _sg_mtl_pop_debug_group();
+    #endif
+}
+
 // ██████   ██████   ██████  ██
 // ██████   ██████   ██████  ██
 // ██   ██ ██    ██ ██    ██ ██
 // ██   ██ ██    ██ ██    ██ ██
 // ██████  ██    ██ ██    ██ ██
 // ██████  ██    ██ ██    ██ ██
@@ -15769,7 +15881,7 @@ _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int
         const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index];
         const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index];
         _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT);
         _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT);
 
 
-        // check that the provided data size doesn't exceed the uniform block size
+        // check that the provided data size matches the uniform block size
         _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE);
         _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE);
 
 
         return _sg_validate_end();
         return _sg_validate_end();
@@ -16431,7 +16543,7 @@ _SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) {
 SOKOL_API_IMPL void sg_setup(const sg_desc* desc) {
 SOKOL_API_IMPL void sg_setup(const sg_desc* desc) {
     SOKOL_ASSERT(desc);
     SOKOL_ASSERT(desc);
     SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0));
     SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0));
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg);
     _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg);
     _sg.desc = _sg_desc_defaults(desc);
     _sg.desc = _sg_desc_defaults(desc);
     _sg_setup_pools(&_sg.pools, &_sg.desc);
     _sg_setup_pools(&_sg.pools, &_sg.desc);
@@ -17530,12 +17642,13 @@ SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data)
 SOKOL_API_IMPL void sg_push_debug_group(const char* name) {
 SOKOL_API_IMPL void sg_push_debug_group(const char* name) {
     SOKOL_ASSERT(_sg.valid);
     SOKOL_ASSERT(_sg.valid);
     SOKOL_ASSERT(name);
     SOKOL_ASSERT(name);
-    _SOKOL_UNUSED(name);
+    _sg_push_debug_group(name);
     _SG_TRACE_ARGS(push_debug_group, name);
     _SG_TRACE_ARGS(push_debug_group, name);
 }
 }
 
 
 SOKOL_API_IMPL void sg_pop_debug_group(void) {
 SOKOL_API_IMPL void sg_pop_debug_group(void) {
     SOKOL_ASSERT(_sg.valid);
     SOKOL_ASSERT(_sg.valid);
+    _sg_pop_debug_group();
     _SG_TRACE_NOARGS(pop_debug_group);
     _SG_TRACE_NOARGS(pop_debug_group);
 }
 }
 
 

+ 48 - 0
tests/functional/sokol_args_test.c

@@ -252,3 +252,51 @@ UTEST(sokol_args, escape_sequence) {
     TSTR(sargs_value_at(2), "val2\tval3");
     TSTR(sargs_value_at(2), "val2\tval3");
     sargs_shutdown();
     sargs_shutdown();
 }
 }
+
+static char* argv_11[] = { "exe_name", "kvp0 kvp1", "kvp2 = val2", "kvp3", "kvp4=val4" };
+UTEST(sokol_args, key_only_args) {
+    sargs_setup(&(sargs_desc){
+        .argc = NUM_ARGS(argv_11),
+        .argv = argv_11,
+    });
+    T(sargs_isvalid());
+    T(sargs_num_args() == 5);
+    T(0 == sargs_find("kvp0"));
+    T(1 == sargs_find("kvp1"));
+    T(2 == sargs_find("kvp2"));
+    T(3 == sargs_find("kvp3"));
+    T(4 == sargs_find("kvp4"))
+    T(-1 == sargs_find("kvp5"));
+    T(-1 == sargs_find("val2"));
+    T(-1 == sargs_find("val4"));
+    T(sargs_exists("kvp0"));
+    T(sargs_exists("kvp1"));
+    T(sargs_exists("kvp2"));
+    T(sargs_exists("kvp3"));
+    T(sargs_exists("kvp4"));
+    T(!sargs_exists("kvp5"));
+    TSTR(sargs_value("kvp0"), "");
+    TSTR(sargs_value("kvp1"), "");
+    TSTR(sargs_value("kvp2"), "val2");
+    TSTR(sargs_value("kvp3"), "");
+    TSTR(sargs_value("kvp4"), "val4");
+    TSTR(sargs_value("kvp5"), "");
+    TSTR(sargs_value_def("kvp0", "bla0"), "bla0");
+    TSTR(sargs_value_def("kvp1", "bla1"), "bla1");
+    TSTR(sargs_value_def("kvp2", "bla2"), "val2");
+    TSTR(sargs_value_def("kvp3", "bla3"), "bla3");
+    TSTR(sargs_value_def("kvp4", "bla4"), "val4");
+    TSTR(sargs_value_def("kvp5", "bla5"), "bla5");
+    TSTR(sargs_key_at(0), "kvp0");
+    TSTR(sargs_key_at(1), "kvp1");
+    TSTR(sargs_key_at(2), "kvp2");
+    TSTR(sargs_key_at(3), "kvp3");
+    TSTR(sargs_key_at(4), "kvp4");
+    TSTR(sargs_key_at(5), "");
+    TSTR(sargs_value_at(0), "");
+    TSTR(sargs_value_at(1), "");
+    TSTR(sargs_value_at(2), "val2");
+    TSTR(sargs_value_at(3), "");
+    TSTR(sargs_value_at(4), "val4");
+    TSTR(sargs_value_at(5), "");
+}

+ 63 - 14
tests/functional/sokol_fetch_test.c

@@ -612,9 +612,9 @@ UTEST(sokol_fetch, load_file_chunked) {
 /* load N big files in small chunks interleaved on the same channel via lanes */
 /* load N big files in small chunks interleaved on the same channel via lanes */
 #define LOAD_FILE_LANES_NUM_LANES (4)
 #define LOAD_FILE_LANES_NUM_LANES (4)
 
 
-uint8_t load_file_lanes_chunk_buf[LOAD_FILE_LANES_NUM_LANES][8192];
-uint8_t load_file_lanes_content[LOAD_FILE_LANES_NUM_LANES][500000];
-int load_file_lanes_passed[LOAD_FILE_LANES_NUM_LANES];
+static uint8_t load_file_lanes_chunk_buf[LOAD_FILE_LANES_NUM_LANES][8192];
+static uint8_t load_file_lanes_content[LOAD_FILE_LANES_NUM_LANES][500000];
+static int load_file_lanes_passed[LOAD_FILE_LANES_NUM_LANES];
 static void load_file_lanes_callback(const sfetch_response_t* response) {
 static void load_file_lanes_callback(const sfetch_response_t* response) {
     assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES));
     assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES));
     if (response->fetched) {
     if (response->fetched) {
@@ -669,9 +669,9 @@ UTEST(sokol_fetch, load_file_lanes) {
 #define LOAD_FILE_THROTTLE_NUM_PASSES (3)
 #define LOAD_FILE_THROTTLE_NUM_PASSES (3)
 #define LOAD_FILE_THROTTLE_NUM_REQUESTS (12)    // lanes * passes
 #define LOAD_FILE_THROTTLE_NUM_REQUESTS (12)    // lanes * passes
 
 
-uint8_t load_file_throttle_chunk_buf[LOAD_FILE_THROTTLE_NUM_LANES][128000];
-uint8_t load_file_throttle_content[LOAD_FILE_THROTTLE_NUM_PASSES][LOAD_FILE_THROTTLE_NUM_LANES][500000];
-int load_file_throttle_passed[LOAD_FILE_THROTTLE_NUM_LANES];
+static uint8_t load_file_throttle_chunk_buf[LOAD_FILE_THROTTLE_NUM_LANES][128000];
+static uint8_t load_file_throttle_content[LOAD_FILE_THROTTLE_NUM_PASSES][LOAD_FILE_THROTTLE_NUM_LANES][500000];
+static int load_file_throttle_passed[LOAD_FILE_THROTTLE_NUM_LANES];
 
 
 static void load_file_throttle_callback(const sfetch_response_t* response) {
 static void load_file_throttle_callback(const sfetch_response_t* response) {
     assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES));
     assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES));
@@ -733,8 +733,8 @@ UTEST(sokol_fetch, load_file_throttle) {
 
 
 /* test parallel fetches on multiple channels */
 /* test parallel fetches on multiple channels */
 #define LOAD_CHANNEL_NUM_CHANNELS (16)
 #define LOAD_CHANNEL_NUM_CHANNELS (16)
-uint8_t load_channel_buf[LOAD_CHANNEL_NUM_CHANNELS][500000];
-bool load_channel_passed[LOAD_CHANNEL_NUM_CHANNELS];
+static uint8_t load_channel_buf[LOAD_CHANNEL_NUM_CHANNELS][500000];
+static bool load_channel_passed[LOAD_CHANNEL_NUM_CHANNELS];
 
 
 void load_channel_callback(const sfetch_response_t* response) {
 void load_channel_callback(const sfetch_response_t* response) {
     assert(response->channel < LOAD_CHANNEL_NUM_CHANNELS);
     assert(response->channel < LOAD_CHANNEL_NUM_CHANNELS);
@@ -781,15 +781,13 @@ UTEST(sokol_fetch, load_channel) {
     sfetch_shutdown();
     sfetch_shutdown();
 }
 }
 
 
-bool load_file_cancel_passed = false;
-void load_file_cancel_callback(const sfetch_response_t* response) {
+static bool load_file_cancel_passed = false;
+static void load_file_cancel_callback(const sfetch_response_t* response) {
     if (response->dispatched) {
     if (response->dispatched) {
         sfetch_cancel(response->handle);
         sfetch_cancel(response->handle);
     }
     }
-    if (response->failed) {
-        if (response->cancelled && response->finished && (response->error_code == SFETCH_ERROR_CANCELLED)) {
-            load_file_cancel_passed = true;
-        }
+    if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) {
+        load_file_cancel_passed = true;
     }
     }
 }
 }
 
 
@@ -811,3 +809,54 @@ UTEST(sokol_fetch, load_file_cancel) {
     T(load_file_cancel_passed);
     T(load_file_cancel_passed);
     sfetch_shutdown();
     sfetch_shutdown();
 }
 }
+
+static bool load_file_cancel_before_dispatch_passed = false;
+static void load_file_cancel_before_dispatch_callback(const sfetch_response_t* response) {
+    // cancelled, finished, failed and error code must all be set
+    if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) {
+        load_file_cancel_before_dispatch_passed = true;
+    }
+}
+
+UTEST(sokol_fetch, load_file_cancel_before_dispatch) {
+    sfetch_setup(&(sfetch_desc_t){
+        .num_channels = 1,
+    });
+    sfetch_handle_t h = sfetch_send(&(sfetch_request_t){
+        .path = "comsi.s3m",
+        .callback = load_file_cancel_before_dispatch_callback,
+    });
+    sfetch_cancel(h);
+    sfetch_dowork();
+    T(load_file_cancel_before_dispatch_passed);
+    sfetch_shutdown();
+}
+
+static bool load_file_cancel_after_dispatch_passed = false;
+static void load_file_cancel_after_dispatch_callback(const sfetch_response_t* response) {
+    // when cancelled, then finished, failed and error code must all be set
+    if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) {
+        load_file_cancel_after_dispatch_passed = true;
+    }
+}
+
+UTEST(sokol_fetch, load_file_cancel_after_dispatch) {
+    sfetch_setup(&(sfetch_desc_t){
+        .num_channels = 1,
+    });
+    sfetch_handle_t h = sfetch_send(&(sfetch_request_t){
+        .path = "comsi.s3m",
+        .callback = load_file_cancel_after_dispatch_callback,
+        .buffer = SFETCH_RANGE(load_file_buf),
+    });
+    int frame_count = 0;
+    const int max_frames = 10000;
+    while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) {
+        sfetch_dowork();
+        sfetch_cancel(h);
+        sleep_ms(1);
+    }
+    T(frame_count < max_frames);
+    T(load_file_cancel_after_dispatch_passed);
+    sfetch_shutdown();
+}

+ 10 - 10
util/sokol_debugtext.h

@@ -427,8 +427,8 @@
             sdtx_setup(&(sdtx_desc_t){
             sdtx_setup(&(sdtx_desc_t){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...;
                     .user_data = ...;
                 }
                 }
             });
             });
@@ -658,12 +658,12 @@ typedef struct sdtx_context_desc_t {
 
 
     Used in sdtx_desc_t to provide custom memory-alloc and -free functions
     Used in sdtx_desc_t to provide custom memory-alloc and -free functions
     to sokol_debugtext.h. If memory management should be overridden, both the
     to sokol_debugtext.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sdtx_allocator_t {
 typedef struct sdtx_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sdtx_allocator_t;
 } sdtx_allocator_t;
 
 
@@ -3598,8 +3598,8 @@ static void _sdtx_clear(void* ptr, size_t size) {
 static void* _sdtx_malloc(size_t size) {
 static void* _sdtx_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_sdtx.desc.allocator.alloc) {
-        ptr = _sdtx.desc.allocator.alloc(size, _sdtx.desc.allocator.user_data);
+    if (_sdtx.desc.allocator.alloc_fn) {
+        ptr = _sdtx.desc.allocator.alloc_fn(size, _sdtx.desc.allocator.user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -3616,8 +3616,8 @@ static void* _sdtx_malloc_clear(size_t size) {
 }
 }
 
 
 static void _sdtx_free(void* ptr) {
 static void _sdtx_free(void* ptr) {
-    if (_sdtx.desc.allocator.free) {
-        _sdtx.desc.allocator.free(ptr, _sdtx.desc.allocator.user_data);
+    if (_sdtx.desc.allocator.free_fn) {
+        _sdtx.desc.allocator.free_fn(ptr, _sdtx.desc.allocator.user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -4197,7 +4197,7 @@ SOKOL_API_IMPL void _sdtx_draw_layer(_sdtx_context_t* ctx, int layer_id) {
 
 
 
 
 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_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     sdtx_desc_t res = *desc;
     sdtx_desc_t res = *desc;
     res.context_pool_size = _sdtx_def(res.context_pool_size, _SDTX_DEFAULT_CONTEXT_POOL_SIZE);
     res.context_pool_size = _sdtx_def(res.context_pool_size, _SDTX_DEFAULT_CONTEXT_POOL_SIZE);
     res.printf_buf_size = _sdtx_def(res.printf_buf_size, _SDTX_DEFAULT_PRINTF_BUF_SIZE);
     res.printf_buf_size = _sdtx_def(res.printf_buf_size, _SDTX_DEFAULT_PRINTF_BUF_SIZE);

+ 10 - 10
util/sokol_fontstash.h

@@ -147,8 +147,8 @@
         FONScontext* fons_context = sfons_create(&(sfons_desc_t){
         FONScontext* fons_context = sfons_create(&(sfons_desc_t){
             ...
             ...
             .allocator = {
             .allocator = {
-                .alloc = my_alloc,
-                .free = my_free,
+                .alloc_fn = my_alloc,
+                .free_fn = my_free,
                 .user_data = ...,
                 .user_data = ...,
             }
             }
         });
         });
@@ -213,15 +213,15 @@ extern "C" {
 
 
     Used in sfons_desc_t to provide custom memory-alloc and -free functions
     Used in sfons_desc_t to provide custom memory-alloc and -free functions
     to sokol_fontstash.h. If memory management should be overridden, both the
     to sokol_fontstash.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 
 
     NOTE that this does not affect memory allocation calls inside
     NOTE that this does not affect memory allocation calls inside
     fontstash.h
     fontstash.h
 */
 */
 typedef struct sfons_allocator_t {
 typedef struct sfons_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sfons_allocator_t;
 } sfons_allocator_t;
 
 
@@ -1542,8 +1542,8 @@ static void _sfons_clear(void* ptr, size_t size) {
 static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) {
 static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) {
     SOKOL_ASSERT(allocator && (size > 0));
     SOKOL_ASSERT(allocator && (size > 0));
     void* ptr;
     void* ptr;
-    if (allocator->alloc) {
-        ptr = allocator->alloc(size, allocator->user_data);
+    if (allocator->alloc_fn) {
+        ptr = allocator->alloc_fn(size, allocator->user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -1559,8 +1559,8 @@ static void* _sfons_malloc_clear(const sfons_allocator_t* allocator, size_t size
 
 
 static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) {
 static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) {
     SOKOL_ASSERT(allocator);
     SOKOL_ASSERT(allocator);
-    if (allocator->free) {
-        allocator->free(ptr, allocator->user_data);
+    if (allocator->free_fn) {
+        allocator->free_fn(ptr, allocator->user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -1739,7 +1739,7 @@ static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) {
 
 
 SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) {
 SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) {
     SOKOL_ASSERT(desc);
     SOKOL_ASSERT(desc);
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t));
     _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t));
     sfons->desc = _sfons_desc_defaults(desc);
     sfons->desc = _sfons_desc_defaults(desc);
     FONSparams params;
     FONSparams params;

+ 11 - 11
util/sokol_gfx_imgui.h

@@ -73,8 +73,8 @@
 
 
             sg_imgui_init(&sg_imgui, &(sg_imgui_desc_t){
             sg_imgui_init(&sg_imgui, &(sg_imgui_desc_t){
                 .allocator = {
                 .allocator = {
-                    .alloc = my_malloc,
-                    .free = my_free,
+                    .alloc_fn = my_malloc,
+                    .free_fn = my_free,
                 }
                 }
             });
             });
 
 
@@ -179,8 +179,8 @@
             sg_imgui_init(&(&ctx, &(sg_imgui_desc_t){
             sg_imgui_init(&(&ctx, &(sg_imgui_desc_t){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...;
                     .user_data = ...;
                 }
                 }
             });
             });
@@ -736,8 +736,8 @@ typedef struct sg_imgui_frame_stats_t {
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sg_imgui_allocator_t {
 typedef struct sg_imgui_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sg_imgui_allocator_t;
 } sg_imgui_allocator_t;
 
 
@@ -972,8 +972,8 @@ _SOKOL_PRIVATE void _sg_imgui_clear(void* ptr, size_t size) {
 _SOKOL_PRIVATE void* _sg_imgui_malloc(const sg_imgui_allocator_t* allocator, size_t size) {
 _SOKOL_PRIVATE void* _sg_imgui_malloc(const sg_imgui_allocator_t* allocator, size_t size) {
     SOKOL_ASSERT(allocator && (size > 0));
     SOKOL_ASSERT(allocator && (size > 0));
     void* ptr;
     void* ptr;
-    if (allocator->alloc) {
-        ptr = allocator->alloc(size, allocator->user_data);
+    if (allocator->alloc_fn) {
+        ptr = allocator->alloc_fn(size, allocator->user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -989,8 +989,8 @@ _SOKOL_PRIVATE void* _sg_imgui_malloc_clear(const sg_imgui_allocator_t* allocato
 
 
 _SOKOL_PRIVATE void _sg_imgui_free(const sg_imgui_allocator_t* allocator, void* ptr) {
 _SOKOL_PRIVATE void _sg_imgui_free(const sg_imgui_allocator_t* allocator, void* ptr) {
     SOKOL_ASSERT(allocator);
     SOKOL_ASSERT(allocator);
-    if (allocator->free) {
-        allocator->free(ptr, allocator->user_data);
+    if (allocator->free_fn) {
+        allocator->free_fn(ptr, allocator->user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -4343,7 +4343,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_frame_stats_panel(sg_imgui_t* ctx) {
 #define _sg_imgui_def(val, def) (((val) == 0) ? (def) : (val))
 #define _sg_imgui_def(val, def) (((val) == 0) ? (def) : (val))
 
 
 _SOKOL_PRIVATE sg_imgui_desc_t _sg_imgui_desc_defaults(const sg_imgui_desc_t* desc) {
 _SOKOL_PRIVATE sg_imgui_desc_t _sg_imgui_desc_defaults(const sg_imgui_desc_t* desc) {
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     sg_imgui_desc_t res = *desc;
     sg_imgui_desc_t res = *desc;
     // FIXME: any additional default overrides would go here
     // FIXME: any additional default overrides would go here
     return res;
     return res;

+ 9 - 9
util/sokol_gl.h

@@ -606,8 +606,8 @@
             sgl_setup(&(sgl_desc_t){
             sgl_setup(&(sgl_desc_t){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...;
                     .user_data = ...;
                 }
                 }
             });
             });
@@ -796,8 +796,8 @@ typedef struct sgl_context_desc_t {
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct sgl_allocator_t {
 typedef struct sgl_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sgl_allocator_t;
 } sgl_allocator_t;
 
 
@@ -2437,8 +2437,8 @@ static void _sgl_clear(void* ptr, size_t size) {
 static void* _sgl_malloc(size_t size) {
 static void* _sgl_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_sgl.desc.allocator.alloc) {
-        ptr = _sgl.desc.allocator.alloc(size, _sgl.desc.allocator.user_data);
+    if (_sgl.desc.allocator.alloc_fn) {
+        ptr = _sgl.desc.allocator.alloc_fn(size, _sgl.desc.allocator.user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -2455,8 +2455,8 @@ static void* _sgl_malloc_clear(size_t size) {
 }
 }
 
 
 static void _sgl_free(void* ptr) {
 static void _sgl_free(void* ptr) {
-    if (_sgl.desc.allocator.free) {
-        _sgl.desc.allocator.free(ptr, _sgl.desc.allocator.user_data);
+    if (_sgl.desc.allocator.free_fn) {
+        _sgl.desc.allocator.free_fn(ptr, _sgl.desc.allocator.user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -3217,7 +3217,7 @@ static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) {
 
 
 // return sg_context_desc_t with patched defaults
 // return sg_context_desc_t with patched defaults
 static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) {
 static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) {
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     sgl_desc_t res = *desc;
     sgl_desc_t res = *desc;
     res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES);
     res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES);
     res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS);
     res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS);

+ 10 - 10
util/sokol_imgui.h

@@ -288,8 +288,8 @@
             simgui_setup(&(simgui_desc_t){
             simgui_setup(&(simgui_desc_t){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...;
                     .user_data = ...;
                 }
                 }
             });
             });
@@ -467,12 +467,12 @@ typedef enum simgui_log_item_t {
 
 
     Used in simgui_desc_t to provide custom memory-alloc and -free functions
     Used in simgui_desc_t to provide custom memory-alloc and -free functions
     to sokol_imgui.h. If memory management should be overridden, both the
     to sokol_imgui.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct simgui_allocator_t {
 typedef struct simgui_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } simgui_allocator_t;
 } simgui_allocator_t;
 
 
@@ -1903,8 +1903,8 @@ static void _simgui_clear(void* ptr, size_t size) {
 static void* _simgui_malloc(size_t size) {
 static void* _simgui_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_simgui.desc.allocator.alloc) {
-        ptr = _simgui.desc.allocator.alloc(size, _simgui.desc.allocator.user_data);
+    if (_simgui.desc.allocator.alloc_fn) {
+        ptr = _simgui.desc.allocator.alloc_fn(size, _simgui.desc.allocator.user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -1921,8 +1921,8 @@ static void* _simgui_malloc_clear(size_t size) {
 }
 }
 
 
 static void _simgui_free(void* ptr) {
 static void _simgui_free(void* ptr) {
-    if (_simgui.desc.allocator.free) {
-        _simgui.desc.allocator.free(ptr, _simgui.desc.allocator.user_data);
+    if (_simgui.desc.allocator.free_fn) {
+        _simgui.desc.allocator.free_fn(ptr, _simgui.desc.allocator.user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -2143,7 +2143,7 @@ static bool _simgui_is_osx(void) {
 }
 }
 
 
 static simgui_desc_t _simgui_desc_defaults(const simgui_desc_t* desc) {
 static simgui_desc_t _simgui_desc_defaults(const simgui_desc_t* desc) {
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     simgui_desc_t res = *desc;
     simgui_desc_t res = *desc;
     res.max_vertices = _simgui_def(res.max_vertices, 65536);
     res.max_vertices = _simgui_def(res.max_vertices, 65536);
     res.image_pool_size = _simgui_def(res.image_pool_size, 256);
     res.image_pool_size = _simgui_def(res.image_pool_size, 256);

+ 2 - 2
util/sokol_memtrack.h

@@ -27,8 +27,8 @@
         sg_setup(&(sg_desc){
         sg_setup(&(sg_desc){
             //...
             //...
             .allocator = {
             .allocator = {
-                .alloc = smemtrack_alloc,
-                .free = smemtrack_free,
+                .alloc_fn = smemtrack_alloc,
+                .free_fn = smemtrack_free,
             }
             }
         });
         });
 
 

+ 9 - 9
util/sokol_nuklear.h

@@ -223,8 +223,8 @@
             snk_setup(&(snk_desc_t){
             snk_setup(&(snk_desc_t){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...;
                     .user_data = ...;
                 }
                 }
             });
             });
@@ -383,12 +383,12 @@ typedef enum snk_log_item_t {
 
 
     Used in snk_desc_t to provide custom memory-alloc and -free functions
     Used in snk_desc_t to provide custom memory-alloc and -free functions
     to sokol_nuklear.h. If memory management should be overridden, both the
     to sokol_nuklear.h. If memory management should be overridden, both the
-    alloc and free function must be provided (e.g. it's not valid to
+    alloc_fn and free_fn function must be provided (e.g. it's not valid to
     override one function but not the other).
     override one function but not the other).
 */
 */
 typedef struct snk_allocator_t {
 typedef struct snk_allocator_t {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } snk_allocator_t;
 } snk_allocator_t;
 
 
@@ -1825,8 +1825,8 @@ static void _snk_clear(void* ptr, size_t size) {
 static void* _snk_malloc(size_t size) {
 static void* _snk_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_snuklear.desc.allocator.alloc) {
-        ptr = _snuklear.desc.allocator.alloc(size, _snuklear.desc.allocator.user_data);
+    if (_snuklear.desc.allocator.alloc_fn) {
+        ptr = _snuklear.desc.allocator.alloc_fn(size, _snuklear.desc.allocator.user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -1843,8 +1843,8 @@ static void* _snk_malloc_clear(size_t size) {
 }
 }
 
 
 static void _snk_free(void* ptr) {
 static void _snk_free(void* ptr) {
-    if (_snuklear.desc.allocator.free) {
-        _snuklear.desc.allocator.free(ptr, _snuklear.desc.allocator.user_data);
+    if (_snuklear.desc.allocator.free_fn) {
+        _snuklear.desc.allocator.free_fn(ptr, _snuklear.desc.allocator.user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }

+ 11 - 11
util/sokol_spine.h

@@ -202,8 +202,8 @@
 
 
         sspine_setup(&(sspine_desc){
         sspine_setup(&(sspine_desc){
             .allocator = {
             .allocator = {
-                .alloc = my_alloc,
-                .free = my_free,
+                .alloc_fn = my_alloc,
+                .free_fn = my_free,
                 .user_data = ...,
                 .user_data = ...,
             },
             },
             .logger = {
             .logger = {
@@ -908,8 +908,8 @@
             sspine_setup(&(sspine_desc){
             sspine_setup(&(sspine_desc){
                 // ...
                 // ...
                 .allocator = {
                 .allocator = {
-                    .alloc = my_alloc,
-                    .free = my_free,
+                    .alloc_fn = my_alloc,
+                    .free_fn = my_free,
                     .user_data = ...;
                     .user_data = ...;
                 }
                 }
             });
             });
@@ -1198,8 +1198,8 @@ typedef struct sspine_instance_desc {
 } sspine_instance_desc;
 } sspine_instance_desc;
 
 
 typedef struct sspine_allocator {
 typedef struct sspine_allocator {
-    void* (*alloc)(size_t size, void* user_data);
-    void (*free)(void* ptr, void* user_data);
+    void* (*alloc_fn)(size_t size, void* user_data);
+    void (*free_fn)(void* ptr, void* user_data);
     void* user_data;
     void* user_data;
 } sspine_allocator;
 } sspine_allocator;
 
 
@@ -3030,8 +3030,8 @@ static sspine_string _sspine_string(const char* cstr) {
 static void* _sspine_malloc(size_t size) {
 static void* _sspine_malloc(size_t size) {
     SOKOL_ASSERT(size > 0);
     SOKOL_ASSERT(size > 0);
     void* ptr;
     void* ptr;
-    if (_sspine.desc.allocator.alloc) {
-        ptr = _sspine.desc.allocator.alloc(size, _sspine.desc.allocator.user_data);
+    if (_sspine.desc.allocator.alloc_fn) {
+        ptr = _sspine.desc.allocator.alloc_fn(size, _sspine.desc.allocator.user_data);
     } else {
     } else {
         ptr = malloc(size);
         ptr = malloc(size);
     }
     }
@@ -3048,8 +3048,8 @@ static void* _sspine_malloc_clear(size_t size) {
 }
 }
 
 
 static void _sspine_free(void* ptr) {
 static void _sspine_free(void* ptr) {
-    if (_sspine.desc.allocator.free) {
-        _sspine.desc.allocator.free(ptr, _sspine.desc.allocator.user_data);
+    if (_sspine.desc.allocator.free_fn) {
+        _sspine.desc.allocator.free_fn(ptr, _sspine.desc.allocator.user_data);
     } else {
     } else {
         free(ptr);
         free(ptr);
     }
     }
@@ -4529,7 +4529,7 @@ static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_l
 
 
 // return sspine_desc with patched defaults
 // return sspine_desc with patched defaults
 static sspine_desc _sspine_desc_defaults(const sspine_desc* desc) {
 static sspine_desc _sspine_desc_defaults(const sspine_desc* desc) {
-    SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
+    SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
     sspine_desc res = *desc;
     sspine_desc res = *desc;
     res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES);
     res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES);
     res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS);
     res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS);