diff --git a/3rdparty/ozz-animation/samples/optimize/sample_optimize.cc b/3rdparty/ozz-animation/samples/optimize/sample_optimize.cc index 2bbd9b6..8e38224 100644 --- a/3rdparty/ozz-animation/samples/optimize/sample_optimize.cc +++ b/3rdparty/ozz-animation/samples/optimize/sample_optimize.cc @@ -57,35 +57,6 @@ OZZ_OPTIONS_DECLARE_STRING(skeleton, "Path to the runtime skeleton file.", OZZ_OPTIONS_DECLARE_STRING(animation, "Path to the raw animation file.", "media/animation_raw.ozz", false) -namespace { - -// Loads a raw animation from a file. -bool LoadAnimation(const char* _filename, - ozz::animation::offline::RawAnimation* _animation) { - assert(_filename && _animation); - ozz::log::Out() << "Loading raw animation archive: " << _filename << "." - << std::endl; - ozz::io::File file(_filename, "rb"); - if (!file.opened()) { - ozz::log::Err() << "Failed to open animation file " << _filename << "." - << std::endl; - return false; - } - ozz::io::IArchive archive(&file); - if (!archive.TestTag()) { - ozz::log::Err() << "Failed to load raw animation instance from file " - << _filename << "." << std::endl; - return false; - } - - // Once the tag is validated, reading cannot fail. - archive >> *_animation; - - // Ensure animation is valid. - return _animation->Validate(); -} -} // namespace - class OptimizeSampleApplication : public ozz::sample::Application { public: OptimizeSampleApplication() @@ -285,7 +256,7 @@ class OptimizeSampleApplication : public ozz::sample::Application { // Imports offline animation from a binary file. // Invalid animations are rejected by the load function. - if (!LoadAnimation(OPTIONS_animation, &raw_animation_)) { + if (!ozz::sample::LoadRawAnimation(OPTIONS_animation, &raw_animation_)) { return false; } diff --git a/3rdparty/sokol/README.md b/3rdparty/sokol/README.md index 5cef847..44dd893 100644 --- a/3rdparty/sokol/README.md +++ b/3rdparty/sokol/README.md @@ -1,12 +1,13 @@ # Sokol -[![Build Status](https://github.com/floooh/sokol/workflows/build_and_test/badge.svg)](https://github.com/floooh/sokol/actions) - Simple [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt) cross-platform libraries for C and C++, written in C. -[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**08-Oct-2021** revisited and cleaned up texture compression support in sokol_gfx.h) +[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**20-Feb-2023** a new set of functions in sokol_gfx.h +to get a pre-filled 'desc struct' for a resource) + +[![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) ## Examples and Related Projects @@ -14,10 +15,14 @@ cross-platform libraries for C and C++, written in C. - [Doom Shareware](https://floooh.github.io/doom-sokol/) ported to the Sokol headers ([source](https://github.com/floooh/doom-sokol)) +- [sokol_gp.h](https://github.com/edubart/sokol_gp) a 2D shape drawing library on top of sokol_gfx.h + - [LearnOpenGL examples ported to sokol-gfx](https://www.geertarien.com/learnopengl-examples-html5/) by @geertarien (cool stuff!) - [Dear ImGui starterkit](https://github.com/floooh/cimgui-sokol-starterkit) a self-contained starterkit for writing Dear ImGui apps in C. +- [qoiview](https://github.com/floooh/qoiview) a basic viewer for the new QOI image file format + - [Tiny 8-bit emulators](https://floooh.github.io/tiny8bit/) - A 'single-file' [Pacman clone in C99](https://github.com/floooh/pacman.c/), also available in [Zig](https://github.com/floooh/pacman.zig/) @@ -40,6 +45,7 @@ useful details for integrating the Sokol headers into your own project with your - [**sokol\_audio.h**](https://github.com/floooh/sokol/blob/master/sokol_audio.h): minimal buffer-streaming audio playback - [**sokol\_fetch.h**](https://github.com/floooh/sokol/blob/master/sokol_fetch.h): asynchronous data streaming from HTTP and local filesystem - [**sokol\_args.h**](https://github.com/floooh/sokol/blob/master/sokol_args.h): unified cmdline/URL arg parser for web and native apps +- [**sokol\_log.h**](https://github.com/floooh/sokol/blob/master/sokol_log.h): provides a standard logging callback for the other sokol headers ## Utility libraries @@ -52,6 +58,16 @@ useful details for integrating the Sokol headers into your own project with your - [**sokol\_memtrack.h**](https://github.com/floooh/sokol/blob/master/util/sokol_memtrack.h): easily track memory allocations in sokol headers - [**sokol\_shape.h**](https://github.com/floooh/sokol/blob/master/util/sokol_shape.h): generate simple shapes and plug them into sokol-gfx resource creation structs - [**sokol\_color.h**](https://github.com/floooh/sokol/blob/master/util/sokol_color.h): X11 style color constants and functions for creating sg_color objects +- [**sokol\_spine.h**](https://github.com/floooh/sokol/blob/master/util/sokol_spine.h): a sokol-style wrapper around the Spine C runtime (http://en.esotericsoftware.com/spine-in-depth) + +## 'Official' Language Bindings + +These are automatically updated on changes to the C headers: + +- [sokol-zig](https://github.com/floooh/sokol-zig) +- [sokol-odin](https://github.com/floooh/sokol-odin) +- [sokol-nim](https://github.com/floooh/sokol-nim) +- [sokol-rust](https://github.com/floooh/sokol-rust) ## Notes @@ -83,6 +99,7 @@ A triangle in C99 with GLFW: #define SOKOL_IMPL #define SOKOL_GLCORE33 #include "sokol_gfx.h" +#include "sokol_log.h" #define GLFW_INCLUDE_NONE #include "GLFW/glfw3.h" @@ -99,7 +116,9 @@ int main() { glfwSwapInterval(1); /* setup sokol_gfx */ - sg_setup(&(sg_desc){0}); + sg_setup(&(sg_desc){ + .logger.func = slog_func, + }); /* a vertex buffer */ const float vertices[] = { @@ -190,13 +209,15 @@ to split the Objective-C code from the C code of the sample): ```c #include "sokol_gfx.h" #include "sokol_app.h" +#include "sokol_log.h" #include "sokol_glue.h" sg_pass_action pass_action; void init(void) { sg_setup(&(sg_desc){ - .context = sapp_sgcontext() + .context = sapp_sgcontext(), + .logger.func = slog_func, }); pass_action = (sg_pass_action) { .colors[0] = { .action=SG_ACTION_CLEAR, .value={1.0f, 0.0f, 0.0f, 1.0f} } @@ -223,6 +244,7 @@ sapp_desc sokol_main(int argc, char* argv[]) { .width = 400, .height = 300, .window_title = "Clear Sample", + .logger.func = slog_func, }; } ``` @@ -257,7 +279,8 @@ static void stream_cb(float* buffer, int num_frames, int num_channels) { int main() { // init sokol-audio with default params saudio_setup(&(saudio_desc){ - .stream_cb = stream_cb + .stream_cb = stream_cb, + .logger.func = slog_func, }); // run main loop @@ -274,7 +297,9 @@ The same code using the push-model #define BUF_SIZE (32) int main() { // init sokol-audio with default params, no callback - saudio_setup(&(saudio_desc){0}); + saudio_setup(&(saudio_desc){ + .logger.func = slog_func, + }); assert(saudio_channels() == 1); // a small intermediate buffer so we don't need to push @@ -315,6 +340,7 @@ Simple C99 example loading a file into a static buffer: ```c #include "sokol_fetch.h" +#include "sokol_log.h" static void response_callback(const sfetch_response*); @@ -325,7 +351,7 @@ static uint8_t buffer[MAX_FILE_SIZE]; static void init(void) { ... // setup sokol-fetch with default config: - sfetch_setup(&(sfetch_desc_t){0}); + sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func }); // start loading a file into a statically allocated buffer: sfetch_send(&(sfetch_request_t){ diff --git a/3rdparty/sokol/bindgen/README.md b/3rdparty/sokol/bindgen/README.md index a2b6ba4..f90e499 100644 --- a/3rdparty/sokol/bindgen/README.md +++ b/3rdparty/sokol/bindgen/README.md @@ -22,6 +22,7 @@ To update the Zig bindings: > cd sokol/bindgen > git clone https://github.com/floooh/sokol-zig > git clone https://github.com/floooh/sokol-nim +> git clone https://github.com/floooh/sokol-odin > python3 gen_all.py ``` diff --git a/3rdparty/sokol/sokol_app.h b/3rdparty/sokol/sokol_app.h index 5455015..4b3d62c 100644 --- a/3rdparty/sokol/sokol_app.h +++ b/3rdparty/sokol/sokol_app.h @@ -28,16 +28,12 @@ Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) - SOKOL_ABORT() - called after an unrecoverable error (default: abort()) SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function SOKOL_APP_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_APP_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_CALLOC - your own calloc function (default: calloc(n, s)) - SOKOL_FREE - your own free function (default: free(p)) Optionally define the following to force debug checks and validations even in release mode: @@ -52,6 +48,9 @@ On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. + On Linux, SOKOL_GLCORE33 can use either GLX or EGL. + GLX is default, set SOKOL_FORCE_EGL to override. + For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp Portions of the Windows and Linux GL initialization, event-, icon- etc... code @@ -65,7 +64,8 @@ - on macOS with GL: Cocoa, QuartzCore, OpenGL - on iOS with Metal: Foundation, UIKit, Metal, MetalKit - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit - - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?) + - on Linux with EGL: X11, Xi, Xcursor, EGL, GL (or GLESv2), dl, pthread, m(?) + - on Linux with GLX: X11, Xi, Xcursor, GL, dl, pthread, m(?) - on Android: GLESv3, EGL, log, android - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined @@ -76,10 +76,6 @@ On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 - Building for UWP requires a recent Visual Studio toolchain and Windows SDK - (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is - selected, the sokol_app.h implementation must be compiled as C++17. - On macOS and iOS, the implementation must be compiled as Objective-C. FEATURE OVERVIEW @@ -91,55 +87,56 @@ - creates a window and 3D-API context/device with a 'default framebuffer' - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi) + - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 FEATURE/PLATFORM MATRIX ======================= - | Windows | macOS | Linux | iOS | Android | UWP | Raspi | HTML5 - --------------------+---------+-------+-------+-------+---------+------+-------+------- - gl 3.x | YES | YES | YES | --- | --- | --- | --- | --- - gles2/webgl | --- | --- | --- | YES | YES | --- | TODO | YES - gles3/webgl2 | --- | --- | --- | YES | YES | --- | --- | YES - metal | --- | YES | --- | YES | --- | --- | --- | --- - d3d11 | YES | --- | --- | --- | --- | YES | --- | --- - KEY_DOWN | YES | YES | YES | SOME | TODO | YES | TODO | YES - KEY_UP | YES | YES | YES | SOME | TODO | YES | TODO | YES - CHAR | YES | YES | YES | YES | TODO | YES | TODO | YES - MOUSE_DOWN | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_UP | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_MOVE | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_ENTER | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | TODO | YES - TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | --- | YES - TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | --- | YES - TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | --- | YES - TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | --- | YES - RESIZED | YES | YES | YES | YES | YES | YES | --- | YES - ICONIFIED | YES | YES | YES | --- | --- | YES | --- | --- - RESTORED | YES | YES | YES | --- | --- | YES | --- | --- - FOCUSED | YES | YES | YES | --- | --- | --- | --- | YES - UNFOCUSED | YES | YES | YES | --- | --- | --- | --- | YES - SUSPENDED | --- | --- | --- | YES | YES | YES | --- | TODO - RESUMED | --- | --- | --- | YES | YES | YES | --- | TODO - QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | TODO | YES - UPDATE_CURSOR | YES | YES | TODO | --- | --- | TODO | --- | TODO - IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? | ??? - key repeat flag | YES | YES | YES | --- | --- | YES | TODO | YES - windowed | YES | YES | YES | --- | --- | YES | TODO | YES - fullscreen | YES | YES | YES | YES | YES | YES | TODO | --- - mouse hide | YES | YES | YES | --- | --- | YES | TODO | TODO - mouse lock | YES | YES | YES | --- | --- | TODO | TODO | YES - screen keyboard | --- | --- | --- | YES | TODO | TODO | --- | YES - swap interval | YES | YES | YES | YES | TODO | --- | TODO | YES - high-dpi | YES | YES | TODO | YES | YES | YES | TODO | YES - clipboard | YES | YES | TODO | --- | --- | TODO | --- | YES - MSAA | YES | YES | YES | YES | YES | TODO | TODO | YES - drag'n'drop | YES | YES | YES | --- | --- | TODO | TODO | YES - window icon | YES | YES(1)| YES | --- | --- | TODO | TODO | YES + | Windows | macOS | Linux | iOS | Android | HTML5 + --------------------+---------+-------+-------+-------+---------+-------- + gl 3.x | YES | YES | YES | --- | --- | --- + gles2/webgl | --- | --- | YES(2)| YES | YES | YES + gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES + metal | --- | YES | --- | YES | --- | --- + d3d11 | YES | --- | --- | --- | --- | --- + KEY_DOWN | YES | YES | YES | SOME | TODO | YES + KEY_UP | YES | YES | YES | SOME | TODO | YES + CHAR | YES | YES | YES | YES | TODO | YES + MOUSE_DOWN | YES | YES | YES | --- | --- | YES + MOUSE_UP | YES | YES | YES | --- | --- | YES + MOUSE_SCROLL | YES | YES | YES | --- | --- | YES + MOUSE_MOVE | YES | YES | YES | --- | --- | YES + MOUSE_ENTER | YES | YES | YES | --- | --- | YES + MOUSE_LEAVE | YES | YES | YES | --- | --- | YES + TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES + TOUCHES_MOVED | --- | --- | --- | YES | YES | YES + TOUCHES_ENDED | --- | --- | --- | YES | YES | YES + TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES + RESIZED | YES | YES | YES | YES | YES | YES + ICONIFIED | YES | YES | YES | --- | --- | --- + RESTORED | YES | YES | YES | --- | --- | --- + FOCUSED | YES | YES | YES | --- | --- | YES + UNFOCUSED | YES | YES | YES | --- | --- | YES + SUSPENDED | --- | --- | --- | YES | YES | TODO + RESUMED | --- | --- | --- | YES | YES | TODO + QUIT_REQUESTED | YES | YES | YES | --- | --- | YES + IME | TODO | TODO? | TODO | ??? | TODO | ??? + key repeat flag | YES | YES | YES | --- | --- | YES + windowed | YES | YES | YES | --- | --- | YES + fullscreen | YES | YES | YES | YES | YES | --- + mouse hide | YES | YES | YES | --- | --- | YES + mouse lock | YES | YES | YES | --- | --- | YES + set cursor type | YES | YES | YES | --- | --- | YES + screen keyboard | --- | --- | --- | YES | TODO | YES + swap interval | YES | YES | YES | YES | TODO | YES + high-dpi | YES | YES | TODO | YES | YES | YES + clipboard | YES | YES | TODO | --- | --- | YES + MSAA | YES | YES | YES | YES | YES | YES + drag'n'drop | YES | YES | YES | --- | --- | YES + window icon | YES | YES(1)| YES | --- | --- | YES (1) macOS has no regular window icons, instead the dock icon is changed + (2) supported with EGL only (not GLX) STEP BY STEP ============ @@ -162,6 +159,18 @@ }; } + To get any logging output in case of errors you need to provide a log + callback. The easiest way is via sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } + There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. @@ -173,7 +182,10 @@ rendering canvas. The actual size may differ from this depending on platform and other circumstances. Also the canvas size may change at any time (for instance when the user resizes the application window, - or rotates the mobile device). + or rotates the mobile device). You can just keep .width and .height + zero-initialized to open a default-sized window (what "default-size" + exactly means is platform-specific, but usually it's a size that covers + most of, but not all, of the display). All provided function callbacks will be called from the same thread, but this may be different from the thread where sokol_main() was called. @@ -194,11 +206,6 @@ used to communicate other types of events to the application. Keep the event_cb struct member zero-initialized if your application doesn't require event handling. - .fail_cb (void (*)(const char* msg)) - The fail callback is called when a fatal error is encountered - during start which doesn't allow the program to continue. - Providing a callback here gives you a chance to show an error message - to the user. The default behaviour is SOKOL_LOG(msg) As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks @@ -212,10 +219,6 @@ .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) - .fail_userdata_cb (void(*)(const char* msg, void* user_data)) - These are the user-data versions of the callback functions. You - can mix those with the standard callbacks that don't have the - user_data argument. The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. @@ -245,6 +248,10 @@ may help to prevent casting back and forth between int and float in more strongly typed languages than C and C++. + double sapp_frame_duration(void) + Returns the frame duration in seconds averaged over a number of + frames to smooth out any jittering spikes. + int sapp_color_format(void) int sapp_depth_format(void) The color and depth-stencil pixelformats of the default framebuffer, @@ -253,9 +260,9 @@ where sg_pixel_format is expected). Possible values are: 23 == SG_PIXELFORMAT_RGBA8 - 27 == SG_PIXELFORMAT_BGRA8 - 41 == SG_PIXELFORMAT_DEPTH - 42 == SG_PIXELFORMAT_DEPTH_STENCIL + 28 == SG_PIXELFORMAT_BGRA8 + 42 == SG_PIXELFORMAT_DEPTH + 43 == SG_PIXELFORMAT_DEPTH_STENCIL int sapp_sample_count(void) Return the MSAA sample count of the default framebuffer. @@ -358,6 +365,32 @@ "Really Quit?" dialog box). Note that the cleanup-callback isn't guaranteed to be called on the web and mobile platforms. + MOUSE CURSOR TYPE AND VISIBILITY + ================================ + You can show and hide the mouse cursor with + + void sapp_show_mouse(bool show) + + And to get the current shown status: + + bool sapp_mouse_shown(void) + + NOTE that hiding the mouse cursor is different and independent from + the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when + active (MOUSE LOCK is described below). + + To change the mouse cursor to one of several predefined types, call + the function: + + void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) + + Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore + the standard look. + + To get the currently active mouse cursor type, call: + + sapp_mouse_cursor sapp_get_mouse_cursor(void) + MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) ================================================ In normal mouse mode, no mouse movement events are reported when the @@ -404,7 +437,7 @@ - SAPP_EVENTTYPE_MOUSE_DOWN - SAPP_EVENTTYPE_MOUSE_UP - SAPP_EVENTTYPE_MOUSE_SCROLL - - SAPP_EVENTYTPE_KEY_UP + - SAPP_EVENTTYPE_KEY_UP - SAPP_EVENTTYPE_KEY_DOWN - The mouse lock/unlock action on the web platform is asynchronous, this means that sapp_mouse_locked() won't immediately return @@ -598,8 +631,10 @@ sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){ .dropped_file_index = 0, .callback = fetch_cb - .buffer_ptr = buf, - .buffer_size = buf_size, + .buffer = { + .ptr = buf, + .size = sizeof(buf) + }, .user_data = ... }); @@ -613,9 +648,9 @@ // IMPORTANT: check if the loading operation actually succeeded: if (response->succeeded) { // the size of the loaded file: - const uint32_t num_bytes = response->fetched_size; + const size_t num_bytes = response->data.size; // and the pointer to the data (same as 'buf' in the fetch-call): - const void* ptr = response->buffer_ptr; + const void* ptr = response->data.ptr; } else { // on error check the error code: @@ -646,10 +681,18 @@ In a HighDPI scenario, you still request the same window size during sokol_main(), but the framebuffer sizes returned by sapp_width() and sapp_height() will be scaled up according to the DPI scaling - ratio. You can also get a DPI scaling factor with the function - sapp_dpi_scale(). + ratio. - Here's an example on a Mac with Retina display: + Note that on some platforms the DPI scaling factor may change at any + time (for instance when a window is moved from a high-dpi display + to a low-dpi display). + + To query the current DPI scaling factor, call the function: + + float sapp_dpi_scale(void); + + For instance on a Retina Mac, returning the following sapp_desc + struct from sokol_main(): sapp_desc sokol_main() { return (sapp_desc) { @@ -660,19 +703,32 @@ }; } - The functions sapp_width(), sapp_height() and sapp_dpi_scale() will - return the following values: + ...the functions the functions sapp_width(), sapp_height() + and sapp_dpi_scale() will return the following values: - sapp_width -> 1280 - sapp_height -> 960 - sapp_dpi_scale -> 2.0 + sapp_width: 1280 + sapp_height: 960 + sapp_dpi_scale: 2.0 If the high_dpi flag is false, or you're not running on a Retina display, the values would be: - sapp_width -> 640 - sapp_height -> 480 - sapp_dpi_scale -> 1.0 + sapp_width: 640 + sapp_height: 480 + sapp_dpi_scale: 1.0 + + If the window is moved from the Retina display to a low-dpi external display, + the values would change as follows: + + sapp_width: 1280 => 640 + sapp_height: 960 => 480 + sapp_dpi_scale: 2.0 => 1.0 + + Currently there is no event associated with a DPI change, but an + SAPP_EVENTTYPE_RESIZED will be sent as a side effect of the + framebuffer size changing. + + Per-monitor DPI is currently supported on macOS and Windows. APPLICATION QUIT ================ @@ -724,7 +780,7 @@ programmatically close the browser tab). On the web it's also not possible to run custom code when the user - closes a brower tab, so it's not possible to prevent this with a + closes a browser tab, so it's not possible to prevent this with a fancy custom dialog box. Instead the standard "Leave Site?" dialog box can be activated (or @@ -953,15 +1009,89 @@ doesn't matter if the application is started from the command line or via double-click. + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ..., + } + }; + } + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_app.h + itself though, not any allocations in OS libraries. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sapp' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-app like this: + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }; + } + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + TEMP NOTE DUMP ============== - onscreen keyboard support on Android requires Java :(, should we even bother? - sapp_desc needs a bool whether to initialize depth-stencil surface - GL context initialization needs more control (at least what GL version to initialize) - application icon - - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX - (Win32 sends the event each frame when the mouse moves and is inside the window - client area, OSX sends it only once when the mouse enters the client area) - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy at the latest but should do it earlier, in onStop, as an app is "killable" after onStop on Android Honeycomb and later (it can't be done at the moment as the app may be started @@ -1053,7 +1183,6 @@ typedef enum sapp_event_type { SAPP_EVENTTYPE_UNFOCUSED, SAPP_EVENTTYPE_SUSPENDED, SAPP_EVENTTYPE_RESUMED, - SAPP_EVENTTYPE_UPDATE_CURSOR, SAPP_EVENTTYPE_QUIT_REQUESTED, SAPP_EVENTTYPE_CLIPBOARD_PASTED, SAPP_EVENTTYPE_FILES_DROPPED, @@ -1193,6 +1322,23 @@ typedef enum sapp_keycode { SAPP_KEYCODE_MENU = 348, } sapp_keycode; +/* + Android specific 'tool type' enum for touch events. This lets the + application check what type of input device was used for + touch events. + + NOTE: the values must remain in sync with the corresponding + Android SDK type, so don't change those. + + See https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN +*/ +typedef enum sapp_android_tooltype { + SAPP_ANDROIDTOOLTYPE_UNKNOWN = 0, // TOOL_TYPE_UNKNOWN + SAPP_ANDROIDTOOLTYPE_FINGER = 1, // TOOL_TYPE_FINGER + SAPP_ANDROIDTOOLTYPE_STYLUS = 2, // TOOL_TYPE_STYLUS + SAPP_ANDROIDTOOLTYPE_MOUSE = 3, // TOOL_TYPE_MOUSE +} sapp_android_tooltype; + /* sapp_touchpoint @@ -1206,6 +1352,7 @@ typedef struct sapp_touchpoint { uintptr_t identifier; float pos_x; float pos_y; + sapp_android_tooltype android_tooltype; // only valid on Android bool changed; } sapp_touchpoint; @@ -1328,20 +1475,153 @@ typedef struct sapp_icon_desc { sapp_image_desc images[SAPP_MAX_ICONIMAGES]; } sapp_icon_desc; +/* + sapp_allocator + + Used in sapp_desc to provide custom memory-alloc and -free functions + 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 + override one function but not the other). +*/ +typedef struct sapp_allocator { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sapp_allocator; + +/* + sapp_log_item + + Log items are defined via X-Macros and expanded to an enum + 'sapp_log_item', and in debug mode to corresponding + human readable error messages. +*/ +#define _SAPP_LOG_ITEMS \ + _SAPP_LOGITEM_XMACRO(OK, "Ok") \ + _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0, 3.2 and 4.1)") \ + _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, "failed loading opengl32.dll") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, "failed to create helper window") \ + _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, "failed to get helper window DC") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED, "failed to set pixel format for dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_DUMMY_CONTEXT_FAILED, "failed to create dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED, "failed to make dummy GL context current") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED, "failed to get WGL pixel format attribute") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_FIND_PIXELFORMAT_FAILED, "failed to find matching WGL pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED, "failed to get pixel format descriptor") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, "failed to set selected pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, "ARB_create_context required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, "ARB_create_context_profile required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED, "OpenGL 3.2 not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, "requested OpenGL profile not support by GL driver (ERROR_INVALID_PROFILE_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, "CreateContextAttribsARB failed with ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, "CreateContextAttribsARB failed for other reason") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED, "D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIFACTORY_FAILED, "could not obtain IDXGIFactory object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIADAPTER_FAILED, "could not obtain IDXGIAdapter object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED, "could not obtain IDXGIDevice1 interface") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK, "RegisterRawInputDevices() failed (on mouse lock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK, "RegisterRawInputDevices() failed (on mouse unlock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_RAW_INPUT_DATA_FAILED, "GetRawInputData() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_LIBGL_FAILED, "failed to load libGL") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED, "failed to load GLX entry points") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_EXTENSION_NOT_FOUND, "GLX extension not found") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_QUERY_VERSION_FAILED, "failed to query GLX version") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_VERSION_TOO_LOW, "GLX version too low (need at least 1.3)") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_GLXFBCONFIGS, "glXGetFBConfigs() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, "failed to find a suitable GLXFBConfig") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, "glXGetVisualFromFBConfig failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, "GLX extensions ARB_create_context and ARB_create_context_profile missing") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_CONTEXT_FAILED, "Failed to create GL context via glXCreateContextAttribsARB") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, "glXCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, "XCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_API_FAILED, "eglBindAPI(EGL_OPENGL_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_ES_API_FAILED, "eglBindAPI(EGL_OPENGL_ES_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_DISPLAY_FAILED, "eglGetDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_INITIALIZE_FAILED, "eglInitialize() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_CONFIGS, "eglChooseConfig() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_NATIVE_VISUAL, "eglGetConfigAttrib() for EGL_NATIVE_VISUAL_ID failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_VISUAL_INFO_FAILED, "XGetVisualInfo() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED, "eglCreateWindowSurface() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_CONTEXT_FAILED, "eglCreateContext() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_MAKE_CURRENT_FAILED, "eglMakeCurrent() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_QUERY_SYSTEM_DPI_FAILED, "failed to query system dpi value, assuming default 96.0") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, "dropped file URL doesn't start with 'file://'") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, "unsupported input event encountered in _sapp_android_input_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, "unsupported input event encountered in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, "failed to read message in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_WRITE_MSG_FAILED, "failed to write message in _sapp_android_msg") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_CREATE, "MSG_CREATE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_RESUME, "MSG_RESUME") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_PAUSE, "MSG_PAUSE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_FOCUS, "MSG_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_NO_FOCUS, "MSG_NO_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_NATIVE_WINDOW, "MSG_SET_NATIVE_WINDOW") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_INPUT_QUEUE, "MSG_SET_INPUT_QUEUE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_DESTROY, "MSG_DESTROY") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNKNOWN_MSG, "unknown msg type received") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_STARTED, "loop thread started") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_DONE, "loop thread done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTART, "NativeActivity onStart()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONRESUME, "NativeActivity onResume") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE, "NativeActivity onSaveInstanceState") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED, "NativeActivity onWindowFocusChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONPAUSE, "NativeActivity onPause") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTOP, "NativeActivity onStop()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED, "NativeActivity onNativeWindowCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED, "NativeActivity onNativeWindowDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED, "NativeActivity onInputQueueCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED, "NativeActivity onInputQueueDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED, "NativeActivity onConfigurationChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY, "NativeActivity onLowMemory") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONDESTROY, "NativeActivity onDestroy") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ + _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity sucessfully created") \ + _SAPP_LOGITEM_XMACRO(IMAGE_DATA_SIZE_MISMATCH, "image data size mismatch (must be width*height*4 bytes)") \ + _SAPP_LOGITEM_XMACRO(DROPPED_FILE_PATH_TOO_LONG, "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \ + _SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, "clipboard string didn't fit into clipboard buffer") \ + +#define _SAPP_LOGITEM_XMACRO(item,msg) SAPP_LOGITEM_##item, +typedef enum sapp_log_item { + _SAPP_LOG_ITEMS +} sapp_log_item; +#undef _SAPP_LOGITEM_XMACRO + +/* + sapp_logger + + Used in sapp_desc to provide a logging function. Please be aware that + without logging function, sokol-app will be completely silent, e.g. it will + not report errors or warnings. For maximum error verbosity, compile in + debug mode (e.g. NDEBUG *not* defined) and install a logger (for instance + the standard logging function from sokol_log.h). +*/ +typedef struct sapp_logger { + void (*func)( + const char* tag, // always "sapp" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sapp_logger; typedef struct sapp_desc { void (*init_cb)(void); // these are the user-provided callbacks without user data void (*frame_cb)(void); void (*cleanup_cb)(void); void (*event_cb)(const sapp_event*); - void (*fail_cb)(const char*); void* user_data; // these are the user-provided callbacks with user data void (*init_userdata_cb)(void*); void (*frame_userdata_cb)(void*); void (*cleanup_userdata_cb)(void*); void (*event_userdata_cb)(const sapp_event*, void*); - void (*fail_userdata_cb)(const char*, void*); int width; // the preferred width of the window / canvas int height; // the preferred height of the window / canvas @@ -1351,16 +1631,19 @@ typedef struct sapp_desc { bool fullscreen; // whether the window should be created in fullscreen mode bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms) const char* window_title; // the window title as UTF-8 encoded string - bool user_cursor; // if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR bool enable_clipboard; // enable clipboard access, default is false int clipboard_size; // max size of clipboard content in bytes bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false int max_dropped_files; // max number of dropped files to process (default: 1) int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048) sapp_icon_desc icon; // the initial window icon to set + sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free) + sapp_logger logger; // logging callback override (default: NO LOGGING!) /* backend-specific options */ bool gl_force_gles2; // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available + int gl_major_version; // override GL major and minor version (the default GL version is 3.2) + int gl_minor_version; bool win32_console_utf8; // if true, set the output console codepage to UTF-8 bool win32_console_create; // if true, attach stdout/stderr to a new console window bool win32_console_attach; // if true, attach stdout/stderr to parent process @@ -1382,23 +1665,41 @@ typedef enum sapp_html5_fetch_error { } sapp_html5_fetch_error; typedef struct sapp_html5_fetch_response { - bool succeeded; /* true if the loading operation has succeeded */ + bool succeeded; // true if the loading operation has succeeded sapp_html5_fetch_error error_code; - int file_index; /* index of the dropped file (0..sapp_get_num_dropped_filed()-1) */ - uint32_t fetched_size; /* size in bytes of loaded data */ - void* buffer_ptr; /* pointer to user-provided buffer which contains the loaded data */ - uint32_t buffer_size; /* size of user-provided buffer (buffer_size >= fetched_size) */ - void* user_data; /* user-provided user data pointer */ + int file_index; // index of the dropped file (0..sapp_get_num_dropped_filed()-1) + sapp_range data; // pointer and size of the fetched data (data.ptr == buffer.ptr, data.size <= buffer.size) + sapp_range buffer; // the user-provided buffer ptr/size pair (buffer.ptr == data.ptr, buffer.size >= data.size) + void* user_data; // user-provided user data pointer } sapp_html5_fetch_response; typedef struct sapp_html5_fetch_request { - int dropped_file_index; /* 0..sapp_get_num_dropped_files()-1 */ - void (*callback)(const sapp_html5_fetch_response*); /* response callback function pointer (required) */ - void* buffer_ptr; /* pointer to buffer to load data into */ - uint32_t buffer_size; /* size in bytes of buffer */ - void* user_data; /* optional userdata pointer */ + int dropped_file_index; // 0..sapp_get_num_dropped_files()-1 + void (*callback)(const sapp_html5_fetch_response*); // response callback function pointer (required) + sapp_range buffer; // ptr/size of a memory buffer to load the data into + void* user_data; // optional userdata pointer } sapp_html5_fetch_request; +/* + sapp_mouse_cursor + + Predefined cursor image definitions, set with sapp_set_mouse_cursor(sapp_mouse_cursor cursor) +*/ +typedef enum sapp_mouse_cursor { + SAPP_MOUSECURSOR_DEFAULT = 0, // equivalent with system default cursor + SAPP_MOUSECURSOR_ARROW, + SAPP_MOUSECURSOR_IBEAM, + SAPP_MOUSECURSOR_CROSSHAIR, + SAPP_MOUSECURSOR_POINTING_HAND, + SAPP_MOUSECURSOR_RESIZE_EW, + SAPP_MOUSECURSOR_RESIZE_NS, + SAPP_MOUSECURSOR_RESIZE_NWSE, + SAPP_MOUSECURSOR_RESIZE_NESW, + SAPP_MOUSECURSOR_RESIZE_ALL, + SAPP_MOUSECURSOR_NOT_ALLOWED, + _SAPP_MOUSECURSOR_NUM, +} sapp_mouse_cursor; + /* user-provided functions */ extern sapp_desc sokol_main(int argc, char* argv[]); @@ -1438,6 +1739,10 @@ SOKOL_APP_API_DECL bool sapp_mouse_shown(void); SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock); /* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ SOKOL_APP_API_DECL bool sapp_mouse_locked(void); +/* set mouse cursor type */ +SOKOL_APP_API_DECL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor); +/* get current mouse cursor type */ +SOKOL_APP_API_DECL sapp_mouse_cursor sapp_get_mouse_cursor(void); /* return the userdata pointer optionally provided in sapp_desc */ SOKOL_APP_API_DECL void* sapp_userdata(void); /* return a copy of the sapp_desc structure */ @@ -1452,6 +1757,8 @@ SOKOL_APP_API_DECL void sapp_quit(void); SOKOL_APP_API_DECL void sapp_consume_event(void); /* get the current frame counter (for comparison with sapp_event.frame_count) */ SOKOL_APP_API_DECL uint64_t sapp_frame_count(void); +/* get an averaged/smoothed frame duration in seconds */ +SOKOL_APP_API_DECL double sapp_frame_duration(void); /* write string into clipboard */ SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str); /* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */ @@ -1468,6 +1775,11 @@ SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index); /* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc); +/* EGL: get EGLDisplay object */ +SOKOL_APP_API_DECL const void* sapp_egl_get_display(void); +/* EGL: get EGLContext object */ +SOKOL_APP_API_DECL const void* sapp_egl_get_context(void); + /* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ SOKOL_APP_API_DECL bool sapp_gles2(void); @@ -1532,12 +1844,24 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #endif // SOKOL_APP_INCLUDED -/*-- IMPLEMENTATION ----------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_APP_IMPL #define SOKOL_APP_IMPL_INCLUDED (1) +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sapp_desc.allocator to override memory allocation functions" +#endif + +#include // malloc, free #include // memset #include // size_t +#include // roundf /* check if the config defines are alright */ #if defined(__APPLE__) @@ -1570,20 +1894,9 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #endif #elif defined(_WIN32) /* Windows (D3D11 or GL) */ - #include - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #define _SAPP_UWP (1) - #if !defined(SOKOL_D3D11) - #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11") - #endif - #if !defined(__cplusplus) - #error("sokol_app.h: UWP bindings require C++/17") - #endif - #else - #define _SAPP_WIN32 (1) - #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") - #endif + #define _SAPP_WIN32 (1) + #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) + #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") #endif #elif defined(__ANDROID__) /* Android */ @@ -1597,8 +1910,12 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #elif defined(__linux__) || defined(__unix__) /* Linux */ #define _SAPP_LINUX (1) - #if !defined(SOKOL_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33") + #if defined(SOKOL_GLCORE33) + #if !defined(SOKOL_FORCE_EGL) + #define _SAPP_GLX (1) + #endif + #elif !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3 or SOKOL_GLES2") #endif #else #error "sokol_app.h: Unknown platform" @@ -1609,7 +1926,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT @@ -1619,32 +1936,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE) - #include -#endif -#if !defined(SOKOL_CALLOC) - #define SOKOL_CALLOC(n,s) calloc(n,s) -#endif -#if !defined(SOKOL_FREE) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); } - #else - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #endif - #else - #define SOKOL_LOG(s) - #endif -#endif -#ifndef SOKOL_ABORT - #include - #define SOKOL_ABORT() abort() -#endif + #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -1656,7 +1948,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #define _SOKOL_UNUSED(x) (void)(x) #endif -/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ #if defined(_SAPP_APPLE) #if defined(SOKOL_METAL) #import @@ -1675,6 +1966,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #import #endif #endif + #include + #include #elif defined(_SAPP_EMSCRIPTEN) #if defined(SOKOL_WGPU) #include @@ -1731,39 +2024,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ #define WM_MOUSEHWHEEL (0x020E) #endif -#elif defined(_SAPP_UWP) - #ifndef NOMINMAX - #define NOMINMAX + #ifndef WM_DPICHANGED + #define WM_DPICHANGED (0x02E0) #endif - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4054) /* 'type cast': from function pointer */ - #pragma warning(disable:4055) /* 'type cast': from data pointer */ - #pragma warning(disable:4505) /* unreferenced local function has been removed */ - #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ - #endif - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - - #pragma comment (lib, "WindowsApp") - #pragma comment (lib, "dxguid") #elif defined(_SAPP_ANDROID) #include #include + #include #include #include #include @@ -1777,13 +2044,241 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include #include #include + #include /* XC_* font cursors */ #include /* CARD32 */ + #if !defined(_SAPP_GLX) + #include + #endif #include /* dlopen, dlsym, dlclose */ #include /* LONG_MAX */ #include /* only used a linker-guard, search for _sapp_linux_run() and see first comment */ + #include #endif -/*== MACOS DECLARATIONS ======================================================*/ +// ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ +// █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ██████ +// +// >>frame timing +#define _SAPP_RING_NUM_SLOTS (256) +typedef struct { + int head; + int tail; + double buf[_SAPP_RING_NUM_SLOTS]; +} _sapp_ring_t; + +_SOKOL_PRIVATE int _sapp_ring_idx(int i) { + return i % _SAPP_RING_NUM_SLOTS; +} + +_SOKOL_PRIVATE void _sapp_ring_init(_sapp_ring_t* ring) { + ring->head = 0; + ring->tail = 0; +} + +_SOKOL_PRIVATE bool _sapp_ring_full(_sapp_ring_t* ring) { + return _sapp_ring_idx(ring->head + 1) == ring->tail; +} + +_SOKOL_PRIVATE bool _sapp_ring_empty(_sapp_ring_t* ring) { + return ring->head == ring->tail; +} + +_SOKOL_PRIVATE int _sapp_ring_count(_sapp_ring_t* ring) { + int count; + if (ring->head >= ring->tail) { + count = ring->head - ring->tail; + } + else { + count = (ring->head + _SAPP_RING_NUM_SLOTS) - ring->tail; + } + SOKOL_ASSERT((count >= 0) && (count < _SAPP_RING_NUM_SLOTS)); + return count; +} + +_SOKOL_PRIVATE void _sapp_ring_enqueue(_sapp_ring_t* ring, double val) { + SOKOL_ASSERT(!_sapp_ring_full(ring)); + ring->buf[ring->head] = val; + ring->head = _sapp_ring_idx(ring->head + 1); +} + +_SOKOL_PRIVATE double _sapp_ring_dequeue(_sapp_ring_t* ring) { + SOKOL_ASSERT(!_sapp_ring_empty(ring)); + double val = ring->buf[ring->tail]; + ring->tail = _sapp_ring_idx(ring->tail + 1); + return val; +} + +/* + NOTE: + + Q: Why not use CAMetalDrawable.presentedTime on macOS and iOS? + A: The value appears to be highly unstable during the first few + seconds, sometimes several frames are dropped in sequence, or + switch between 120 and 60 Hz for a few frames. Simply measuring + and averaging the frame time yielded a more stable frame duration. + Maybe switching to CVDisplayLink would yield better results. + Until then just measure the time. +*/ +typedef struct { + #if defined(_SAPP_APPLE) + struct { + mach_timebase_info_data_t timebase; + uint64_t start; + } mach; + #elif defined(_SAPP_EMSCRIPTEN) + // empty + #elif defined(_SAPP_WIN32) + struct { + LARGE_INTEGER freq; + LARGE_INTEGER start; + } win; + #else // Linux, Android, ... + #ifdef CLOCK_MONOTONIC + #define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC + #else + // on some embedded platforms, CLOCK_MONOTONIC isn't defined + #define _SAPP_CLOCK_MONOTONIC (1) + #endif + struct { + uint64_t start; + } posix; + #endif +} _sapp_timestamp_t; + +_SOKOL_PRIVATE int64_t _sapp_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { + int64_t q = value / denom; + int64_t r = value % denom; + return q * numer + r * numer / denom; +} + +_SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) { + #if defined(_SAPP_APPLE) + mach_timebase_info(&ts->mach.timebase); + ts->mach.start = mach_absolute_time(); + #elif defined(_SAPP_EMSCRIPTEN) + (void)ts; + #elif defined(_SAPP_WIN32) + QueryPerformanceFrequency(&ts->win.freq); + QueryPerformanceCounter(&ts->win.start); + #else + struct timespec tspec; + clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec); + ts->posix.start = (uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec; + #endif +} + +_SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) { + #if defined(_SAPP_APPLE) + const uint64_t traw = mach_absolute_time() - ts->mach.start; + const uint64_t now = (uint64_t) _sapp_int64_muldiv((int64_t)traw, (int64_t)ts->mach.timebase.numer, (int64_t)ts->mach.timebase.denom); + return (double)now / 1000000000.0; + #elif defined(_SAPP_EMSCRIPTEN) + (void)ts; + SOKOL_ASSERT(false); + return 0.0; + #elif defined(_SAPP_WIN32) + LARGE_INTEGER qpc; + QueryPerformanceCounter(&qpc); + const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart); + return (double)now / 1000000000.0; + #else + struct timespec tspec; + clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec); + const uint64_t now = ((uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec) - ts->posix.start; + return (double)now / 1000000000.0; + #endif +} + +typedef struct { + double last; + double accum; + double avg; + int spike_count; + int num; + _sapp_timestamp_t timestamp; + _sapp_ring_t ring; +} _sapp_timing_t; + +_SOKOL_PRIVATE void _sapp_timing_reset(_sapp_timing_t* t) { + t->last = 0.0; + t->accum = 0.0; + t->spike_count = 0; + t->num = 0; + _sapp_ring_init(&t->ring); +} + +_SOKOL_PRIVATE void _sapp_timing_init(_sapp_timing_t* t) { + t->avg = 1.0 / 60.0; // dummy value until first actual value is available + _sapp_timing_reset(t); + _sapp_timestamp_init(&t->timestamp); +} + +_SOKOL_PRIVATE void _sapp_timing_put(_sapp_timing_t* t, double dur) { + // arbitrary upper limit to ignore outliers (e.g. during window resizing, or debugging) + double min_dur = 0.0; + double max_dur = 0.1; + // if we have enough samples for a useful average, use a much tighter 'valid window' + if (_sapp_ring_full(&t->ring)) { + min_dur = t->avg * 0.8; + max_dur = t->avg * 1.2; + } + if ((dur < min_dur) || (dur > max_dur)) { + t->spike_count++; + // if there have been many spikes in a row, the display refresh rate + // might have changed, so a timing reset is needed + if (t->spike_count > 20) { + _sapp_timing_reset(t); + } + return; + } + if (_sapp_ring_full(&t->ring)) { + double old_val = _sapp_ring_dequeue(&t->ring); + t->accum -= old_val; + t->num -= 1; + } + _sapp_ring_enqueue(&t->ring, dur); + t->accum += dur; + t->num += 1; + SOKOL_ASSERT(t->num > 0); + t->avg = t->accum / t->num; + t->spike_count = 0; +} + +_SOKOL_PRIVATE void _sapp_timing_discontinuity(_sapp_timing_t* t) { + t->last = 0.0; +} + +_SOKOL_PRIVATE void _sapp_timing_measure(_sapp_timing_t* t) { + const double now = _sapp_timestamp_now(&t->timestamp); + if (t->last > 0.0) { + double dur = now - t->last; + _sapp_timing_put(t, dur); + } + t->last = now; +} + +_SOKOL_PRIVATE void _sapp_timing_external(_sapp_timing_t* t, double now) { + if (t->last > 0.0) { + double dur = now - t->last; + _sapp_timing_put(t, dur); + } + t->last = now; +} + +_SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) { + return t->avg; +} + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >> structs #if defined(_SAPP_MACOS) @interface _sapp_macos_app_delegate : NSObject @end @@ -1805,9 +2300,11 @@ typedef struct { uint8_t mouse_buttons; NSWindow* window; NSTrackingArea* tracking_area; + id keyup_monitor; _sapp_macos_app_delegate* app_dlg; _sapp_macos_window_delegate* win_dlg; _sapp_macos_view* view; + NSCursor* cursors[_SAPP_MOUSECURSOR_NUM]; #if defined(SOKOL_METAL) id mtl_device; #endif @@ -1815,7 +2312,6 @@ typedef struct { #endif // _SAPP_MACOS -/*== IOS DECLARATIONS ========================================================*/ #if defined(_SAPP_IOS) @interface _sapp_app_delegate : NSObject @@ -1850,7 +2346,6 @@ typedef struct { #endif // _SAPP_IOS -/*== EMSCRIPTEN DECLARATIONS =================================================*/ #if defined(_SAPP_EMSCRIPTEN) #if defined(SOKOL_WGPU) @@ -1879,8 +2374,7 @@ typedef struct { } _sapp_emsc_t; #endif // _SAPP_EMSCRIPTEN -/*== WIN32 DECLARATIONS ======================================================*/ -#if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP)) +#if defined(SOKOL_D3D11) && defined(_SAPP_WIN32) typedef struct { ID3D11Device* device; ID3D11DeviceContext* device_context; @@ -1892,10 +2386,12 @@ typedef struct { ID3D11DepthStencilView* dsv; DXGI_SWAP_CHAIN_DESC swap_chain_desc; IDXGISwapChain* swap_chain; + IDXGIDevice1* dxgi_device; + bool use_dxgi_frame_stats; + UINT sync_refresh_count; } _sapp_d3d11_t; #endif -/*== WIN32 DECLARATIONS ======================================================*/ #if defined(_SAPP_WIN32) #ifndef DPI_ENUMS_DECLARED @@ -1922,11 +2418,14 @@ typedef struct { typedef struct { HWND hwnd; + HMONITOR hmonitor; HDC dc; HICON big_icon; HICON small_icon; + HCURSOR cursors[_SAPP_MOUSECURSOR_NUM]; UINT orig_codepage; LONG mouse_locked_x, mouse_locked_y; + RECT stored_window_rect; // used to restore window pos/size when toggling fullscreen => windowed bool is_win10_or_greater; bool in_create_window; bool iconified; @@ -2000,25 +2499,6 @@ typedef struct { #endif // _SAPP_WIN32 -/*== UWP DECLARATIONS ======================================================*/ -#if defined(_SAPP_UWP) - -typedef struct { - float content_scale; - float window_scale; - float mouse_scale; -} _sapp_uwp_dpi_t; - -typedef struct { - bool mouse_tracked; - uint8_t mouse_buttons; - _sapp_uwp_dpi_t dpi; -} _sapp_uwp_t; - -#endif // _SAPP_UWP - -/*== ANDROID DECLARATIONS ====================================================*/ - #if defined(_SAPP_ANDROID) typedef enum { _SOKOL_ANDROID_MSG_CREATE, @@ -2064,7 +2544,6 @@ typedef struct { #endif // _SAPP_ANDROID -/*== LINUX DECLARATIONS ======================================================*/ #if defined(_SAPP_LINUX) #define _SAPP_X11_XDND_VERSION (5) @@ -2146,6 +2625,7 @@ typedef struct { Colormap colormap; Window window; Cursor hidden_cursor; + Cursor cursors[_SAPP_MOUSECURSOR_NUM]; int window_state; float dpi; unsigned char error_code; @@ -2162,6 +2642,8 @@ typedef struct { _sapp_xdnd_t xdnd; } _sapp_x11_t; +#if defined(_SAPP_GLX) + typedef struct { void* libgl; int major; @@ -2200,30 +2682,40 @@ typedef struct { bool ARB_create_context_profile; } _sapp_glx_t; -#endif // _SAPP_LINUX +#else -/*== COMMON DECLARATIONS =====================================================*/ +typedef struct { + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_egl_t; + +#endif // _SAPP_GLX + +#endif // _SAPP_LINUX /* helper macros */ #define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) #define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) #define _SAPP_MAX_TITLE_LENGTH (128) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) /* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ #define _SAPP_PIXELFORMAT_RGBA8 (23) -#define _SAPP_PIXELFORMAT_BGRA8 (27) -#define _SAPP_PIXELFORMAT_DEPTH (41) -#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42) +#define _SAPP_PIXELFORMAT_BGRA8 (28) +#define _SAPP_PIXELFORMAT_DEPTH (42) +#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (43) #if defined(_SAPP_MACOS) || defined(_SAPP_IOS) // this is ARC compatible #if defined(__cplusplus) - #define _SAPP_CLEAR(type, item) { item = (type) { }; } + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); } #else - #define _SAPP_CLEAR(type, item) { item = (type) { 0 }; } + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } #endif #else - #define _SAPP_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); } #endif typedef struct { @@ -2247,6 +2739,7 @@ typedef struct { bool shown; bool locked; bool pos_valid; + sapp_mouse_cursor current_cursor; } _sapp_mouse_t; typedef struct { @@ -2270,6 +2763,7 @@ typedef struct { int swap_interval; float dpi_scale; uint64_t frame_count; + _sapp_timing_t timing; sapp_event event; _sapp_mouse_t mouse; _sapp_clipboard_t clipboard; @@ -2289,16 +2783,15 @@ typedef struct { #elif defined(SOKOL_GLCORE33) _sapp_wgl_t wgl; #endif - #elif defined(_SAPP_UWP) - _sapp_uwp_t uwp; - #if defined(SOKOL_D3D11) - _sapp_d3d11_t d3d11; - #endif #elif defined(_SAPP_ANDROID) _sapp_android_t android; #elif defined(_SAPP_LINUX) _sapp_x11_t x11; - _sapp_glx_t glx; + #if defined(_SAPP_GLX) + _sapp_glx_t glx; + #else + _sapp_egl_t egl; + #endif #endif char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ @@ -2307,20 +2800,94 @@ typedef struct { } _sapp_t; static _sapp_t _sapp; -/*=== PRIVATE HELPER FUNCTIONS ===============================================*/ -_SOKOL_PRIVATE void _sapp_fail(const char* msg) { - if (_sapp.desc.fail_cb) { - _sapp.desc.fail_cb(msg); - } - else if (_sapp.desc.fail_userdata_cb) { - _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAPP_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sapp_log_messages[] = { + _SAPP_LOG_ITEMS +}; +#undef _SAPP_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAPP_PANIC(code) _sapp_log(SAPP_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_ ##code, 3, 0, __LINE__) + +static void _sapp_log(sapp_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sapp.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sapp_log_messages[log_item]; + } + #endif + _sapp.desc.logger.func("sapp", log_level, log_item, msg, line_nr, filename, _sapp.desc.logger.user_data); } else { - SOKOL_LOG(msg); + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } } - SOKOL_ABORT(); } +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +_SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sapp_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sapp.desc.allocator.alloc) { + ptr = _sapp.desc.allocator.alloc(size, _sapp.desc.allocator.user_data); + } + else { + ptr = malloc(size); + } + if (0 == ptr) { + _SAPP_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) { + void* ptr = _sapp_malloc(size); + _sapp_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sapp_free(void* ptr) { + if (_sapp.desc.allocator.free) { + _sapp.desc.allocator.free(ptr, _sapp.desc.allocator.user_data); + } + else { + free(ptr); + } +} + +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers _SOKOL_PRIVATE void _sapp_call_init(void) { if (_sapp.desc.init_cb) { _sapp.desc.init_cb(); @@ -2410,24 +2977,40 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { } } -_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) { - sapp_desc desc = *in_desc; - desc.width = _sapp_def(desc.width, 640); - desc.height = _sapp_def(desc.height, 480); - desc.sample_count = _sapp_def(desc.sample_count, 1); - desc.swap_interval = _sapp_def(desc.swap_interval, 1); - desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas"); - desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192); - desc.max_dropped_files = _sapp_def(desc.max_dropped_files, 1); - desc.max_dropped_file_path_length = _sapp_def(desc.max_dropped_file_path_length, 2048); - desc.window_title = _sapp_def(desc.window_title, "sokol_app"); - return 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)); + sapp_desc res = *desc; + res.sample_count = _sapp_def(res.sample_count, 1); + res.swap_interval = _sapp_def(res.swap_interval, 1); + // NOTE: can't patch the default for gl_major_version and gl_minor_version + // independently, because a desired version 4.0 would be patched to 4.2 + // (or expressed differently: zero is a valid value for gl_minor_version + // and can't be used to indicate 'default') + if (0 == res.gl_major_version) { + res.gl_major_version = 3; + res.gl_minor_version = 2; + } + res.html5_canvas_name = _sapp_def(res.html5_canvas_name, "canvas"); + res.clipboard_size = _sapp_def(res.clipboard_size, 8192); + res.max_dropped_files = _sapp_def(res.max_dropped_files, 1); + res.max_dropped_file_path_length = _sapp_def(res.max_dropped_file_path_length, 2048); + res.window_title = _sapp_def(res.window_title, "sokol_app"); + return res; } _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { - _SAPP_CLEAR(_sapp_t, _sapp); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->width >= 0); + SOKOL_ASSERT(desc->height >= 0); + SOKOL_ASSERT(desc->sample_count >= 0); + SOKOL_ASSERT(desc->swap_interval >= 0); + SOKOL_ASSERT(desc->clipboard_size >= 0); + SOKOL_ASSERT(desc->max_dropped_files >= 0); + SOKOL_ASSERT(desc->max_dropped_file_path_length >= 0); + _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); _sapp.desc = _sapp_desc_defaults(desc); _sapp.first_frame = true; + // NOTE: _sapp.desc.width/height may be 0! Platform backends need to deal with this _sapp.window_width = _sapp.desc.width; _sapp.window_height = _sapp.desc.height; _sapp.framebuffer_width = _sapp.window_width; @@ -2441,39 +3024,40 @@ _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; if (_sapp.clipboard.enabled) { _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; - _sapp.clipboard.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.clipboard.buf_size); + _sapp.clipboard.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.clipboard.buf_size); } _sapp.drop.enabled = _sapp.desc.enable_dragndrop; if (_sapp.drop.enabled) { _sapp.drop.max_files = _sapp.desc.max_dropped_files; _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length; _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length; - _sapp.drop.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.drop.buf_size); + _sapp.drop.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.drop.buf_size); } _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); _sapp.desc.window_title = _sapp.window_title; _sapp.dpi_scale = 1.0f; _sapp.fullscreen = _sapp.desc.fullscreen; _sapp.mouse.shown = true; + _sapp_timing_init(&_sapp.timing); } _SOKOL_PRIVATE void _sapp_discard_state(void) { if (_sapp.clipboard.enabled) { SOKOL_ASSERT(_sapp.clipboard.buffer); - SOKOL_FREE((void*)_sapp.clipboard.buffer); + _sapp_free((void*)_sapp.clipboard.buffer); } if (_sapp.drop.enabled) { SOKOL_ASSERT(_sapp.drop.buffer); - SOKOL_FREE((void*)_sapp.drop.buffer); + _sapp_free((void*)_sapp.drop.buffer); } if (_sapp.default_icon_pixels) { - SOKOL_FREE((void*)_sapp.default_icon_pixels); + _sapp_free((void*)_sapp.default_icon_pixels); } - _SAPP_CLEAR(_sapp_t, _sapp); + _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); } _SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { - memset(&_sapp.event, 0, sizeof(_sapp.event)); + _sapp_clear(&_sapp.event, sizeof(_sapp.event)); _sapp.event.type = type; _sapp.event.frame_count = _sapp.frame_count; _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; @@ -2504,7 +3088,7 @@ _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { _SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { if (_sapp.drop.enabled) { SOKOL_ASSERT(_sapp.drop.buffer); - memset(_sapp.drop.buffer, 0, (size_t)_sapp.drop.buf_size); + _sapp_clear(_sapp.drop.buffer, (size_t)_sapp.drop.buf_size); } } @@ -2524,7 +3108,7 @@ _SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { SOKOL_ASSERT(desc->pixels.size > 0); const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t); if (wh_size != desc->pixels.size) { - SOKOL_LOG("Image data size mismatch (must be width*height*4 bytes)\n"); + _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH); return false; } return true; @@ -2578,7 +3162,7 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { for (int i = 0; i < num_icons; i++) { all_num_pixels += icon_sizes[i] * icon_sizes[i]; } - _sapp.default_icon_pixels = (uint32_t*) SOKOL_CALLOC((size_t)all_num_pixels, sizeof(uint32_t)); + _sapp.default_icon_pixels = (uint32_t*) _sapp_malloc_clear((size_t)all_num_pixels * sizeof(uint32_t)); // initialize default_icon_desc struct uint32_t* dst = _sapp.default_icon_pixels; @@ -2680,7 +3264,13 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { SOKOL_ASSERT(dst == dst_end); } -/*== MacOS/iOS ===============================================================*/ +// █████ ██████ ██████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██ █████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ +// +// >>apple #if defined(_SAPP_APPLE) #if __has_feature(objc_arc) @@ -2689,7 +3279,13 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { #define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } #endif -/*== MacOS ===================================================================*/ +// ███ ███ █████ ██████ ██████ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ███████ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>macos #if defined(_SAPP_MACOS) _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { @@ -2808,6 +3404,11 @@ _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { // NOTE: it's safe to call [release] on a nil object + if (_sapp.macos.keyup_monitor != nil) { + [NSEvent removeMonitor:_sapp.macos.keyup_monitor]; + // NOTE: removeMonitor also releases the object + _sapp.macos.keyup_monitor = nil; + } _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); @@ -2818,15 +3419,48 @@ _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { _SAPP_OBJC_RELEASE(_sapp.macos.window); } +// undocumented methods for creating cursors (see GLFW 3.4 and imgui_impl_osx.mm) +@interface NSCursor() ++ (id)_windowResizeNorthWestSouthEastCursor; ++ (id)_windowResizeNorthEastSouthWestCursor; ++ (id)_windowResizeNorthSouthCursor; ++ (id)_windowResizeEastWestCursor; +@end + +_SOKOL_PRIVATE void _sapp_macos_init_cursors(void) { + _sapp.macos.cursors[SAPP_MOUSECURSOR_DEFAULT] = nil; // not a bug + _sapp.macos.cursors[SAPP_MOUSECURSOR_ARROW] = [NSCursor arrowCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_IBEAM] = [NSCursor IBeamCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_CROSSHAIR] = [NSCursor crosshairCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_POINTING_HAND] = [NSCursor pointingHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_EW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_ALL] = [NSCursor closedHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_NOT_ALLOWED] = [NSCursor operationNotAllowedCursor]; +} + _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_macos_init_keytable(); [NSApplication sharedApplication]; + // set the application dock icon as early as possible, otherwise // the dummy icon will be visible for a short time sapp_set_icon(&_sapp.desc.icon); _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; NSApp.delegate = _sapp.macos.app_dlg; + + // workaround for "no key-up sent while Cmd is pressed" taken from GLFW: + NSEvent* (^keyup_monitor)(NSEvent*) = ^NSEvent* (NSEvent* event) { + if ([event modifierFlags] & NSEventModifierFlagCommand) { + [[NSApp keyWindow] sendEvent:event]; + } + return event; + }; + _sapp.macos.keyup_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:keyup_monitor]; + [NSApp run]; // NOTE: [NSApp run] never returns, instead cleanup code // must be put into applicationWillTerminate @@ -2900,18 +3534,31 @@ _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { between HighDPI / LowDPI screens. */ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { - #if defined(SOKOL_METAL) - const NSRect fb_rect = [_sapp.macos.view bounds]; - _sapp.framebuffer_width = fb_rect.size.width * _sapp.dpi_scale; - _sapp.framebuffer_height = fb_rect.size.height * _sapp.dpi_scale; - #elif defined(SOKOL_GLCORE33) - const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]]; - _sapp.framebuffer_width = fb_rect.size.width; - _sapp.framebuffer_height = fb_rect.size.height; - #endif + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = [_sapp.macos.window screen].backingScaleFactor; + } + else { + _sapp.dpi_scale = 1.0f; + } const NSRect bounds = [_sapp.macos.view bounds]; - _sapp.window_width = bounds.size.width; - _sapp.window_height = bounds.size.height; + _sapp.window_width = (int)roundf(bounds.size.width); + _sapp.window_height = (int)roundf(bounds.size.height); + #if defined(SOKOL_METAL) + _sapp.framebuffer_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); + const CGSize fb_size = _sapp.macos.view.drawableSize; + const int cur_fb_width = (int)roundf(fb_size.width); + const int cur_fb_height = (int)roundf(fb_size.height); + const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + #elif defined(SOKOL_GLCORE33) + const int cur_fb_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); + const int cur_fb_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); + const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + _sapp.framebuffer_width = cur_fb_width; + _sapp.framebuffer_height = cur_fb_height; + #endif if (_sapp.framebuffer_width == 0) { _sapp.framebuffer_width = 1; } @@ -2924,17 +3571,17 @@ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { if (_sapp.window_height == 0) { _sapp.window_height = 1; } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; - - /* NOTE: _sapp_macos_update_dimensions() isn't called each frame, but only - when the window size actually changes, so resizing the MTKView's - in each call is fine even when MTKView doesn't ignore setting an - identical drawableSize. - */ - #if defined(SOKOL_METAL) - CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; - _sapp.macos.view.drawableSize = drawable_size; - #endif + if (dim_changed) { + #if defined(SOKOL_METAL) + CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.view.drawableSize = drawable_size; + #else + // nothing to do for GL? + #endif + if (!_sapp.first_frame) { + _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); + } + } } _SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { @@ -2975,13 +3622,13 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; } -_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { +_SOKOL_PRIVATE void _sapp_macos_mouse_update(NSEvent* event) { if (!_sapp.mouse.locked) { const NSPoint mouse_pos = event.locationInWindow; float new_x = mouse_pos.x * _sapp.dpi_scale; float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; - /* don't update dx/dy in the very first update */ if (_sapp.mouse.pos_valid) { + // don't update dx/dy in the very first update _sapp.mouse.dx = new_x - _sapp.mouse.x; _sapp.mouse.dy = new_y - _sapp.mouse.y; } @@ -3020,14 +3667,34 @@ _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { */ if (_sapp.mouse.locked) { CGAssociateMouseAndMouseCursorPosition(NO); - CGDisplayHideCursor(kCGDirectMainDisplay); + [NSCursor hide]; } else { - CGDisplayShowCursor(kCGDirectMainDisplay); + [NSCursor unhide]; CGAssociateMouseAndMouseCursorPosition(YES); } } +_SOKOL_PRIVATE void _sapp_macos_update_cursor(sapp_mouse_cursor cursor, bool shown) { + // show/hide cursor only if visibility status has changed (required because show/hide stacks) + if (shown != _sapp.mouse.shown) { + if (shown) { + [NSCursor unhide]; + } + else { + [NSCursor hide]; + } + } + // update cursor type + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (_sapp.macos.cursors[cursor]) { + [_sapp.macos.cursors[cursor] set]; + } + else { + [[NSCursor arrowCursor] set]; + } +} + _SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int num_images) { NSDockTile* dock_tile = NSApp.dockTile; const int wanted_width = (int) dock_tile.size.width; @@ -3071,20 +3738,17 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { @implementation _sapp_macos_app_delegate - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { _SOKOL_UNUSED(aNotification); - if (_sapp.fullscreen) { + _sapp_macos_init_cursors(); + if ((_sapp.window_width == 0) || (_sapp.window_height == 0)) { + // use 4/5 of screen size as default size NSRect screen_rect = NSScreen.mainScreen.frame; - _sapp.window_width = screen_rect.size.width; - _sapp.window_height = screen_rect.size.height; + if (_sapp.window_width == 0) { + _sapp.window_width = (int)roundf((screen_rect.size.width * 4.0f) / 5.0f); + } + if (_sapp.window_height == 0) { + _sapp.window_height = (int)roundf((screen_rect.size.height * 4.0f) / 5.0f); + } } - if (_sapp.desc.high_dpi) { - _sapp.framebuffer_width = 2 * _sapp.window_width; - _sapp.framebuffer_height = 2 * _sapp.window_height; - } - else { - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; const NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | @@ -3104,10 +3768,16 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; _sapp.macos.window.delegate = _sapp.macos.win_dlg; #if defined(SOKOL_METAL) + NSInteger max_fps = 60; + #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) + if (@available(macOS 12.0, *)) { + max_fps = [NSScreen.mainScreen maximumFramesPerSecond]; + } + #endif _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); _sapp.macos.view = [[_sapp_macos_view alloc] init]; [_sapp.macos.view updateTrackingAreas]; - _sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.macos.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval; _sapp.macos.view.device = _sapp.macos.mtl_device; _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; @@ -3121,7 +3791,15 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { int i = 0; attrs[i++] = NSOpenGLPFAAccelerated; attrs[i++] = NSOpenGLPFADoubleBuffer; - attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core; + attrs[i++] = NSOpenGLPFAOpenGLProfile; + const int glVersion = _sapp.desc.gl_major_version * 10 + _sapp.desc.gl_minor_version; + switch(glVersion) { + case 10: attrs[i++] = NSOpenGLProfileVersionLegacy; break; + case 32: attrs[i++] = NSOpenGLProfileVersion3_2Core; break; + case 41: attrs[i++] = NSOpenGLProfileVersion4_1Core; break; + default: + _SAPP_PANIC(MACOS_INVALID_NSOPENGL_PROFILE); + } attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; @@ -3161,14 +3839,12 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; timer_obj = nil; #endif + [_sapp.macos.window center]; _sapp.valid = true; if (_sapp.fullscreen) { - /* on GL, this already toggles a rendered frame, so set the valid flag before */ + /* ^^^ on GL, this already toggles a rendered frame, so set the valid flag before */ [_sapp.macos.window toggleFullScreen:self]; } - else { - [_sapp.macos.window center]; - } NSApp.activationPolicy = NSApplicationActivationPolicyRegular; [NSApp activateIgnoringOtherApps:YES]; [_sapp.macos.window makeKeyAndOrderFront:nil]; @@ -3215,9 +3891,12 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { - (void)windowDidResize:(NSNotification*)notification { _SOKOL_UNUSED(notification); _sapp_macos_update_dimensions(); - if (!_sapp.first_frame) { - _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); - } +} + +- (void)windowDidChangeScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_timing_reset(&_sapp.timing); + _sapp_macos_update_dimensions(); } - (void)windowDidMiniaturize:(NSNotification*)notification { @@ -3277,12 +3956,12 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { NSPasteboard *pboard = [sender draggingPasteboard]; if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { _sapp_clear_drop_buffer(); - _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : pboard.pasteboardItems.count; + _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : (int)pboard.pasteboardItems.count; bool drop_failed = false; for (int i = 0; i < _sapp.drop.num_files; i++) { NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; break; } @@ -3336,7 +4015,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { /* NOTE: late event polling temporarily out-commented to check if this - causes infrequent and almost impossible to reproduce probelms with the + causes infrequent and almost impossible to reproduce problems with the window close events, see: https://github.com/floooh/sokol/pull/483#issuecomment-805148815 @@ -3377,6 +4056,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { - (void)drawRect:(NSRect)rect { _SOKOL_UNUSED(rect); + _sapp_timing_measure(&_sapp.timing); /* Catch any last-moment input events */ _sapp_macos_poll_input_events(); @autoreleasepool { @@ -3412,7 +4092,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { [super updateTrackingAreas]; } - (void)mouseEntered:(NSEvent*)event { - _sapp_macos_update_mouse(event); + _sapp_macos_mouse_update(event); /* don't send mouse enter/leave while dragging (so that it behaves the same as on Windows while SetCapture is active */ @@ -3421,47 +4101,47 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } } - (void)mouseExited:(NSEvent*)event { - _sapp_macos_update_mouse(event); + _sapp_macos_mouse_update(event); if (0 == _sapp.macos.mouse_buttons) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); } } - (void)mouseDown:(NSEvent*)event { - _sapp_macos_update_mouse(event); + _sapp_macos_mouse_update(event); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); _sapp.macos.mouse_buttons |= (1< 0) { @@ -3554,7 +4227,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } - (void)flagsChanged:(NSEvent*)event { const uint32_t old_f = _sapp.macos.flags_changed_store; - const uint32_t new_f = event.modifierFlags; + const uint32_t new_f = (uint32_t)event.modifierFlags; _sapp.macos.flags_changed_store = new_f; sapp_keycode key_code = SAPP_KEYCODE_INVALID; bool down = false; @@ -3581,17 +4254,17 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { _sapp_macos_mods(event)); } } -- (void)cursorUpdate:(NSEvent*)event { - _SOKOL_UNUSED(event); - if (_sapp.desc.user_cursor) { - _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); - } -} @end -#endif /* MacOS */ +#endif // macOS -/*== iOS =====================================================================*/ +// ██ ██████ ███████ +// ██ ██ ██ ██ +// ██ ██ ██ ███████ +// ██ ██ ██ ██ +// ██ ██████ ███████ +// +// >>ios #if defined(_SAPP_IOS) _SOKOL_PRIVATE void _sapp_ios_discard_state(void) { @@ -3655,18 +4328,18 @@ _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet _SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { CGRect screen_rect = UIScreen.mainScreen.bounds; - _sapp.framebuffer_width = (int)(screen_rect.size.width * _sapp.dpi_scale); - _sapp.framebuffer_height = (int)(screen_rect.size.height * _sapp.dpi_scale); - _sapp.window_width = (int)screen_rect.size.width; - _sapp.window_height = (int)screen_rect.size.height; + _sapp.framebuffer_width = (int)roundf(screen_rect.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(screen_rect.size.height * _sapp.dpi_scale); + _sapp.window_width = (int)roundf(screen_rect.size.width); + _sapp.window_height = (int)roundf(screen_rect.size.height); int cur_fb_width, cur_fb_height; #if defined(SOKOL_METAL) const CGSize fb_size = _sapp.ios.view.drawableSize; - cur_fb_width = (int) fb_size.width; - cur_fb_height = (int) fb_size.height; + cur_fb_width = (int)roundf(fb_size.width); + cur_fb_height = (int)roundf(fb_size.height); #else - cur_fb_width = (int) _sapp.ios.view.drawableWidth; - cur_fb_height = (int) _sapp.ios.view.drawableHeight; + cur_fb_width = (int)roundf(_sapp.ios.view.drawableWidth); + cur_fb_height = (int)roundf(_sapp.ios.view.drawableHeight); #endif const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); @@ -3726,20 +4399,21 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { CGRect screen_rect = UIScreen.mainScreen.bounds; _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; - _sapp.window_width = screen_rect.size.width; - _sapp.window_height = screen_rect.size.height; + _sapp.window_width = (int)roundf(screen_rect.size.width); + _sapp.window_height = (int)roundf(screen_rect.size.height); if (_sapp.desc.high_dpi) { _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale; } else { _sapp.dpi_scale = 1.0f; } - _sapp.framebuffer_width = _sapp.window_width * _sapp.dpi_scale; - _sapp.framebuffer_height = _sapp.window_height * _sapp.dpi_scale; + _sapp.framebuffer_width = (int)roundf(_sapp.window_width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(_sapp.window_height * _sapp.dpi_scale); + NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond; #if defined(SOKOL_METAL) _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); _sapp.ios.view = [[_sapp_ios_view alloc] init]; - _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval; _sapp.ios.view.device = _sapp.ios.mtl_device; _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; @@ -3786,7 +4460,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { } _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; _sapp.ios.view_ctrl.view = _sapp.ios.view; - _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.view_ctrl.preferredFramesPerSecond = max_fps / _sapp.swap_interval; _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; #endif [_sapp.ios.window makeKeyAndVisible]; @@ -3898,6 +4572,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { @implementation _sapp_ios_view - (void)drawRect:(CGRect)rect { _SOKOL_UNUSED(rect); + _sapp_timing_measure(&_sapp.timing); @autoreleasepool { _sapp_ios_frame(); } @@ -3922,9 +4597,19 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { #endif /* _SAPP_APPLE */ -/*== EMSCRIPTEN ==============================================================*/ +// ███████ ███ ███ ███████ ██████ ██████ ██ ██████ ████████ ███████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// █████ ██ ████ ██ ███████ ██ ██████ ██ ██████ ██ █████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ███████ ██ ████ +// +// >>emscripten #if defined(_SAPP_EMSCRIPTEN) +#if defined(EM_JS_DEPS) +EM_JS_DEPS(sokol_app, "$withStackSave,$allocateUTF8OnStack"); +#endif + #ifdef __cplusplus extern "C" { #endif @@ -3980,7 +4665,7 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { return; } if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SOKOL_LOG("sokol_app.h: dropped file path too long!\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); _sapp.drop.num_files = 0; } } @@ -4007,13 +4692,14 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int error_code, _sapp_html5_fetch_callback callback, uint32_t fetched_size, void* buf_ptr, uint32_t buf_size, void* user_data) { sapp_html5_fetch_response response; - memset(&response, 0, sizeof(response)); + _sapp_clear(&response, sizeof(response)); response.succeeded = (0 != success); response.error_code = (sapp_html5_fetch_error) error_code; response.file_index = index; - response.fetched_size = fetched_size; - response.buffer_ptr = buf_ptr; - response.buffer_size = buf_size; + response.data.ptr = buf_ptr; + response.data.size = fetched_size; + response.buffer.ptr = buf_ptr; + response.buffer.size = buf_size; response.user_data = user_data; callback(&response); } @@ -4024,7 +4710,7 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int /* Javascript helper functions for mobile virtual keyboard input */ EM_JS(void, sapp_js_create_textfield, (void), { - var _sapp_inp = document.createElement("input"); + const _sapp_inp = document.createElement("input"); _sapp_inp.type = "text"; _sapp_inp.id = "_sokol_app_input_element"; _sapp_inp.autocapitalize = "none"; @@ -4044,7 +4730,7 @@ EM_JS(void, sapp_js_unfocus_textfield, (void), { }); EM_JS(void, sapp_js_add_beforeunload_listener, (void), { - Module.sokol_beforeunload = function(event) { + Module.sokol_beforeunload = (event) => { if (__sapp_html5_get_ask_leave_site() != 0) { event.preventDefault(); event.returnValue = ' '; @@ -4058,9 +4744,12 @@ EM_JS(void, sapp_js_remove_beforeunload_listener, (void), { }); EM_JS(void, sapp_js_add_clipboard_listener, (void), { - Module.sokol_paste = function(event) { - var pasted_str = event.clipboardData.getData('text'); - ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]); + Module.sokol_paste = (event) => { + const pasted_str = event.clipboardData.getData('text'); + withStackSave(() => { + const cstr = allocateUTF8OnStack(pasted_str); + __sapp_emsc_onpaste(cstr); + }); }; window.addEventListener('paste', Module.sokol_paste); }); @@ -4070,8 +4759,8 @@ EM_JS(void, sapp_js_remove_clipboard_listener, (void), { }); EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { - var str = UTF8ToString(c_str); - var ta = document.createElement('textarea'); + const str = UTF8ToString(c_str); + const ta = document.createElement('textarea'); ta.setAttribute('autocomplete', 'off'); ta.setAttribute('autocorrect', 'off'); ta.setAttribute('autocapitalize', 'off'); @@ -4093,29 +4782,31 @@ _SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { Module.sokol_drop_files = []; - var canvas_name = UTF8ToString(canvas_name_cstr); - var canvas = document.getElementById(canvas_name); - Module.sokol_dragenter = function(event) { + const canvas_name = UTF8ToString(canvas_name_cstr); + const canvas = document.getElementById(canvas_name); + Module.sokol_dragenter = (event) => { event.stopPropagation(); event.preventDefault(); }; - Module.sokol_dragleave = function(event) { + Module.sokol_dragleave = (event) => { event.stopPropagation(); event.preventDefault(); }; - Module.sokol_dragover = function(event) { + Module.sokol_dragover = (event) => { event.stopPropagation(); event.preventDefault(); }; - Module.sokol_drop = function(event) { + Module.sokol_drop = (event) => { event.stopPropagation(); event.preventDefault(); - var files = event.dataTransfer.files; + const files = event.dataTransfer.files; Module.sokol_dropped_files = files; __sapp_emsc_begin_drop(files.length); - var i; - for (i = 0; i < files.length; i++) { - ccall('_sapp_emsc_drop', 'void', ['number', 'string'], [i, files[i].name]); + for (let i = 0; i < files.length; i++) { + withStackSave(() => { + const cstr = allocateUTF8OnStack(files[i].name); + __sapp_emsc_drop(i, cstr); + }); } // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect __sapp_emsc_end_drop(event.clientX, event.clientY); @@ -4127,18 +4818,20 @@ EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { }); EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), { - if ((index < 0) || (index >= Module.sokol_dropped_files.length)) { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const files = Module.sokol_dropped_files; + if ((index < 0) || (index >= files.length)) { return 0; } else { - return Module.sokol_dropped_files[index].size; + return files[index].size; } }); EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), { - var reader = new FileReader(); - reader.onload = function(loadEvent) { - var content = loadEvent.target.result; + const reader = new FileReader(); + reader.onload = (loadEvent) => { + const content = loadEvent.target.result; if (content.byteLength > buf_size) { // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data); @@ -4148,16 +4841,18 @@ EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback c __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data); } }; - reader.onerror = function() { + reader.onerror = () => { // SAPP_HTML5_FETCH_ERROR_OTHER __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data); }; - reader.readAsArrayBuffer(Module.sokol_dropped_files[index]); + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const files = Module.sokol_dropped_files; + reader.readAsArrayBuffer(files[index]); }); EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), { - var canvas_name = UTF8ToString(canvas_name_cstr); - var canvas = document.getElementById(canvas_name); + const canvas_name = UTF8ToString(canvas_name_cstr); + const canvas = document.getElementById(canvas_name); canvas.removeEventListener('dragenter', Module.sokol_dragenter); canvas.removeEventListener('dragleave', Module.sokol_dragleave); canvas.removeEventListener('dragover', Module.sokol_dragover); @@ -4203,9 +4898,9 @@ _SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { } } -EM_JS(void, sapp_js_pointer_init, (const char* c_str_target), { +EM_JS(void, sapp_js_init, (const char* c_str_target), { // lookup and store canvas object by name - var target_str = UTF8ToString(c_str_target); + const target_str = UTF8ToString(c_str_target); Module.sapp_emsc_target = document.getElementById(target_str); if (!Module.sapp_emsc_target) { console.log("sokol_app.h: invalid target:" + target_str); @@ -4267,23 +4962,53 @@ _SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { } } +// set mouse cursor type +EM_JS(void, sapp_js_set_cursor, (int cursor_type, int shown), { + if (Module.sapp_emsc_target) { + let cursor; + if (shown === 0) { + cursor = "none"; + } + else switch (cursor_type) { + case 0: cursor = "auto"; break; // SAPP_MOUSECURSOR_DEFAULT + case 1: cursor = "default"; break; // SAPP_MOUSECURSOR_ARROW + case 2: cursor = "text"; break; // SAPP_MOUSECURSOR_IBEAM + case 3: cursor = "crosshair"; break; // SAPP_MOUSECURSOR_CROSSHAIR + case 4: cursor = "pointer"; break; // SAPP_MOUSECURSOR_POINTING_HAND + case 5: cursor = "ew-resize"; break; // SAPP_MOUSECURSOR_RESIZE_EW + case 6: cursor = "ns-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NS + case 7: cursor = "nwse-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NWSE + case 8: cursor = "nesw-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NESW + case 9: cursor = "all-scroll"; break; // SAPP_MOUSECURSOR_RESIZE_ALL + case 10: cursor = "not-allowed"; break; // SAPP_MOUSECURSOR_NOT_ALLOWED + default: cursor = "auto"; break; + } + Module.sapp_emsc_target.style.cursor = cursor; + } +}); + +_SOKOL_PRIVATE void _sapp_emsc_update_cursor(sapp_mouse_cursor cursor, bool shown) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + sapp_js_set_cursor((int)cursor, shown ? 1 : 0); +} + /* JS helper functions to update browser tab favicon */ EM_JS(void, sapp_js_clear_favicon, (void), { - var link = document.getElementById('sokol-app-favicon'); + const link = document.getElementById('sokol-app-favicon'); if (link) { document.head.removeChild(link); } }); EM_JS(void, sapp_js_set_favicon, (int w, int h, const uint8_t* pixels), { - var canvas = document.createElement('canvas'); + const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; - var ctx = canvas.getContext('2d'); - var img_data = ctx.createImageData(w, h); + const ctx = canvas.getContext('2d'); + const img_data = ctx.createImageData(w, h); img_data.data.set(HEAPU8.subarray(pixels, pixels + w*h*4)); ctx.putImageData(img_data, 0, 0); - var new_link = document.createElement('link'); + const new_link = document.createElement('link'); new_link.id = 'sokol-app-favicon'; new_link.rel = 'shortcut icon'; new_link.href = canvas.toDataURL(); @@ -4371,19 +5096,19 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU w = ui_event->windowInnerWidth; } else { - _sapp.window_width = (int) w; + _sapp.window_width = (int)roundf(w); } if (h < 1.0) { h = ui_event->windowInnerHeight; } else { - _sapp.window_height = (int) h; + _sapp.window_height = (int)roundf(h); } if (_sapp.desc.high_dpi) { _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); } - _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); - _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); + _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); #if defined(SOKOL_WGPU) @@ -4636,6 +5361,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard _sapp.event.key_repeat = emsc_event->repeat; _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); if (type == SAPP_EVENTTYPE_CHAR) { + // FIXME: this doesn't appear to work on Android Chrome _sapp.event.char_code = emsc_event->charCode; /* workaround to make Cmd+V work on Safari */ if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { @@ -4643,7 +5369,18 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } } else { - _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + if (0 != emsc_event->code[0]) { + // This code path is for desktop browsers which send untranslated 'physical' key code strings + // (which is what we actually want for key events) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + } else { + // This code path is for mobile browsers which only send localized key code + // strings. Note that the translation will only work for a small subset + // of localization-agnostic keys (like Enter, arrow keys, etc...), but + // regular alpha-numeric keys will all result in an SAPP_KEYCODE_INVALID) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key); + } + /* Special hack for macOS: if the Super key is pressed, macOS doesn't send keyUp events. As a workaround, to prevent keys from "sticking", we'll send a keyup event following a keydown @@ -4656,7 +5393,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard { send_keyup_followup = true; } - /* only forward a certain key ranges to the browser */ + // only forward keys to the browser (can further be suppressed by sapp_consume_event()) switch (_sapp.event.key_code) { case SAPP_KEYCODE_WORLD_1: case SAPP_KEYCODE_WORLD_2: @@ -4722,7 +5459,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } } if (_sapp_call_event(&_sapp.event)) { - /* consume event via sapp_consume_event() */ + // event was consumed via sapp_consume_event() retval = true; } if (send_keyup_followup) { @@ -4878,17 +5615,17 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, EM_JS(void, sapp_js_wgpu_init, (), { WebGPU.initManagers(); // FIXME: the extension activation must be more clever here - navigator.gpu.requestAdapter().then(function(adapter) { + navigator.gpu.requestAdapter().then((adapter) => { console.log("wgpu adapter extensions: " + adapter.extensions); - adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then(function(device) { + adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then((device) => { var gpuContext = document.getElementById("canvas").getContext("gpupresent"); console.log("wgpu device extensions: " + adapter.extensions); - gpuContext.getSwapChainPreferredFormat(device).then(function(fmt) { - var swapChainDescriptor = { device: device, format: fmt }; - var swapChain = gpuContext.configureSwapChain(swapChainDescriptor); - var deviceId = WebGPU.mgrDevice.create(device); - var swapChainId = WebGPU.mgrSwapChain.create(swapChain); - var fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; }); + gpuContext.getSwapChainPreferredFormat(device).then((fmt) => { + const swapChainDescriptor = { device: device, format: fmt }; + const swapChain = gpuContext.configureSwapChain(swapChainDescriptor); + const deviceId = WebGPU.mgrDevice.create(device); + const swapChainId = WebGPU.mgrSwapChain.create(swapChain); + const fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; }); console.log("wgpu device: " + device); console.log("wgpu swap chain: " + swapChain); console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")"); @@ -4907,7 +5644,7 @@ _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view); WGPUTextureDescriptor ds_desc; - memset(&ds_desc, 0, sizeof(ds_desc)); + _sapp_clear(&ds_desc, sizeof(ds_desc)); ds_desc.usage = WGPUTextureUsage_OutputAttachment; ds_desc.dimension = WGPUTextureDimension_2D; ds_desc.size.width = (uint32_t) _sapp.framebuffer_width; @@ -4922,7 +5659,7 @@ _SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { if (_sapp.sample_count > 1) { WGPUTextureDescriptor msaa_desc; - memset(&msaa_desc, 0, sizeof(msaa_desc)); + _sapp_clear(&msaa_desc, sizeof(msaa_desc)); msaa_desc.usage = WGPUTextureUsage_OutputAttachment; msaa_desc.dimension = WGPUTextureDimension_2D; msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width; @@ -5027,8 +5764,8 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { - _SOKOL_UNUSED(time); _SOKOL_UNUSED(userData); + _sapp_timing_external(&_sapp.timing, time / 1000.0); #if defined(SOKOL_WGPU) /* @@ -5075,11 +5812,11 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { _sapp_init_state(desc); - sapp_js_pointer_init(&_sapp.html5_canvas_selector[1]); + sapp_js_init(&_sapp.html5_canvas_selector[1]); double w, h; if (_sapp.desc.html5_canvas_resize) { - w = (double) _sapp.desc.width; - h = (double) _sapp.desc.height; + w = (double) _sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); + h = (double) _sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT); } else { emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); @@ -5088,10 +5825,10 @@ _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { if (_sapp.desc.high_dpi) { _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); } - _sapp.window_width = (int) w; - _sapp.window_height = (int) h; - _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); - _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); + _sapp.window_width = (int)roundf(w); + _sapp.window_height = (int)roundf(h); + _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) _sapp_emsc_webgl_init(); @@ -5119,7 +5856,13 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ #endif /* _SAPP_EMSCRIPTEN */ -/*== MISC GL SUPPORT FUNCTIONS ================================================*/ +// ██████ ██ ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>gl helpers #if defined(SOKOL_GLCORE33) typedef struct { int red_bits; @@ -5134,7 +5877,7 @@ typedef struct { } _sapp_gl_fbconfig; _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { - memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig)); + _sapp_clear(fbconfig, sizeof(_sapp_gl_fbconfig)); /* -1 means "don't care" */ fbconfig->red_bits = -1; fbconfig->green_bits = -1; @@ -5228,11 +5971,17 @@ _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_ } #endif -/*== WINDOWS DESKTOP and UWP====================================================*/ -#if defined(_SAPP_WIN32) || defined(_SAPP_UWP) -_SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows +#if defined(_SAPP_WIN32) +_SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); - memset(dst, 0, (size_t)dst_num_bytes); + _sapp_clear(dst, (size_t)dst_num_bytes); const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); if ((dst_needed > 0) && (dst_needed < dst_chars)) { @@ -5245,14 +5994,14 @@ _SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, } } -_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { +_SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { /* same as GLFW */ _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; _sapp.keycodes[0x002] = SAPP_KEYCODE_1; @@ -5373,22 +6122,26 @@ _SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; } -#endif // _SAPP_WIN32 || _SAPP_UWP +#endif // _SAPP_WIN32 -/*== WINDOWS DESKTOP===========================================================*/ #if defined(_SAPP_WIN32) #if defined(SOKOL_D3D11) #if defined(__cplusplus) #define _sapp_d3d11_Release(self) (self)->Release() +#define _sapp_win32_refiid(iid) iid #else #define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self) +#define _sapp_win32_refiid(iid) &iid #endif #define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; } -static const IID _sapp_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89,0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c }; + +static const IID _sapp_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89, {0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} }; +static const IID _sapp_IID_IDXGIDevice1 = { 0x77db970f,0x6276,0x48ba, {0xba,0x28,0x07,0x01,0x43,0xb4,0x39,0x2c} }; +static const IID _sapp_IID_IDXGIFactory = { 0x7b7166ec,0x21c7,0x44ae, {0xb2,0x1a,0xc9,0xae,0x32,0x1a,0xe3,0x69} }; static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) { #if defined(__cplusplus) @@ -5398,6 +6151,14 @@ static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, RE #endif } +static inline HRESULT _sapp_d3d11_QueryInterface(ID3D11Device* self, REFIID riid, void** ppvObject) { + #if defined(__cplusplus) + return self->QueryInterface(riid, ppvObject); + #else + return self->lpVtbl->QueryInterface(self, riid, ppvObject); + #endif +} + static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { #if defined(__cplusplus) return self->CreateRenderTargetView(pResource, pDesc, ppRTView); @@ -5446,6 +6207,46 @@ static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval #endif } +static inline HRESULT _sapp_dxgi_GetFrameStatistics(IDXGISwapChain* self, DXGI_FRAME_STATISTICS* pStats) { + #if defined(__cplusplus) + return self->GetFrameStatistics(pStats); + #else + return self->lpVtbl->GetFrameStatistics(self, pStats); + #endif +} + +static inline HRESULT _sapp_dxgi_SetMaximumFrameLatency(IDXGIDevice1* self, UINT MaxLatency) { + #if defined(__cplusplus) + return self->SetMaximumFrameLatency(MaxLatency); + #else + return self->lpVtbl->SetMaximumFrameLatency(self, MaxLatency); + #endif +} + +static inline HRESULT _sapp_dxgi_GetAdapter(IDXGIDevice1* self, IDXGIAdapter** pAdapter) { + #if defined(__cplusplus) + return self->GetAdapter(pAdapter); + #else + return self->lpVtbl->GetAdapter(self, pAdapter); + #endif +} + +static inline HRESULT _sapp_dxgi_GetParent(IDXGIObject* self, REFIID riid, void** ppParent) { + #if defined(__cplusplus) + return self->GetParent(riid, ppParent); + #else + return self->lpVtbl->GetParent(self, riid, ppParent); + #endif +} + +static inline HRESULT _sapp_dxgi_MakeWindowAssociation(IDXGIFactory* self, HWND WindowHandle, UINT Flags) { + #if defined(__cplusplus) + return self->MakeWindowAssociation(WindowHandle, Flags); + #else + return self->lpVtbl->MakeWindowAssociation(self, WindowHandle, Flags); + #endif +} + _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width; @@ -5458,10 +6259,12 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { if (_sapp.win32.is_win10_or_greater) { sc_desc->BufferCount = 2; sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD; + _sapp.d3d11.use_dxgi_frame_stats = true; } else { sc_desc->BufferCount = 1; sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + _sapp.d3d11.use_dxgi_frame_stats = false; } sc_desc->SampleDesc.Count = 1; sc_desc->SampleDesc.Quality = 0; @@ -5485,11 +6288,66 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { &feature_level, /* pFeatureLevel */ &_sapp.d3d11.device_context); /* ppImmediateContext */ _SOKOL_UNUSED(hr); + #if defined(SOKOL_DEBUG) + if (!SUCCEEDED(hr)) { + // if initialization with D3D11_CREATE_DEVICE_DEBUG fails, this could be because the + // 'D3D11 debug layer' stopped working, indicated by the error message: + // === + // D3D11CreateDevice: Flags (0x2) were specified which require the D3D11 SDK Layers for Windows 10, but they are not present on the system. + // These flags must be removed, or the Windows 10 SDK must be installed. + // Flags include: D3D11_CREATE_DEVICE_DEBUG + // === + // + // ...just retry with the DEBUG flag switched off + _SAPP_ERROR(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED); + create_flags &= ~D3D11_CREATE_DEVICE_DEBUG; + hr = D3D11CreateDeviceAndSwapChain( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + NULL, /* pFeatureLevels */ + 0, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + sc_desc, /* pSwapChainDesc */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ + &feature_level, /* pFeatureLevel */ + &_sapp.d3d11.device_context); /* ppImmediateContext */ + } + #endif SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); + + // minimize frame latency, disable Alt-Enter + hr = _sapp_d3d11_QueryInterface(_sapp.d3d11.device, _sapp_win32_refiid(_sapp_IID_IDXGIDevice1), (void**)&_sapp.d3d11.dxgi_device); + if (SUCCEEDED(hr) && _sapp.d3d11.dxgi_device) { + _sapp_dxgi_SetMaximumFrameLatency(_sapp.d3d11.dxgi_device, 1); + IDXGIAdapter* dxgi_adapter = 0; + hr = _sapp_dxgi_GetAdapter(_sapp.d3d11.dxgi_device, &dxgi_adapter); + if (SUCCEEDED(hr) && dxgi_adapter) { + IDXGIFactory* dxgi_factory = 0; + hr = _sapp_dxgi_GetParent((IDXGIObject*)dxgi_adapter, _sapp_win32_refiid(_sapp_IID_IDXGIFactory), (void**)&dxgi_factory); + if (SUCCEEDED(hr)) { + _sapp_dxgi_MakeWindowAssociation(dxgi_factory, _sapp.win32.hwnd, DXGI_MWA_NO_ALT_ENTER|DXGI_MWA_NO_PRINT_SCREEN); + _SAPP_SAFE_RELEASE(dxgi_factory); + } + else { + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIFACTORY_FAILED); + } + _SAPP_SAFE_RELEASE(dxgi_adapter); + } + else { + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIADAPTER_FAILED); + } + } + else { + _SAPP_PANIC(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED); + } } _SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain); + _SAPP_SAFE_RELEASE(_sapp.d3d11.dxgi_device); _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context); _SAPP_SAFE_RELEASE(_sapp.d3d11.device); } @@ -5505,18 +6363,14 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { HRESULT hr; /* view for the swapchain-created framebuffer */ - #ifdef __cplusplus - hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, _sapp_IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); - #else - hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, &_sapp_IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); - #endif + hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, _sapp_win32_refiid(_sapp_IID_ID3D11Texture2D), (void**)&_sapp.d3d11.rt); SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); /* common desc for MSAA and depth-stencil texture */ D3D11_TEXTURE2D_DESC tex_desc; - memset(&tex_desc, 0, sizeof(tex_desc)); + _sapp_clear(&tex_desc, sizeof(tex_desc)); tex_desc.Width = (UINT)_sapp.framebuffer_width; tex_desc.Height = (UINT)_sapp.framebuffer_height; tex_desc.MipLevels = 1; @@ -5561,14 +6415,22 @@ _SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { } } -_SOKOL_PRIVATE void _sapp_d3d11_present(void) { +_SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { /* do MSAA resolve if needed */ if (_sapp.sample_count > 1) { SOKOL_ASSERT(_sapp.d3d11.rt); SOKOL_ASSERT(_sapp.d3d11.msaa_rt); _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM); } - _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, 0); + UINT flags = 0; + if (_sapp.win32.is_win10_or_greater && do_not_wait) { + /* this hack/workaround somewhat improves window-movement and -sizing + responsiveness when rendering is controlled via WM_TIMER during window + move and resize on NVIDIA cards on Win10 with recent drivers. + */ + flags = DXGI_PRESENT_DO_NOT_WAIT; + } + _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, flags); } #endif /* SOKOL_D3D11 */ @@ -5577,7 +6439,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_present(void) { _SOKOL_PRIVATE void _sapp_wgl_init(void) { _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); if (!_sapp.wgl.opengl32) { - _sapp_fail("Failed to load opengl32.dll\n"); + _SAPP_PANIC(WIN32_LOAD_OPENGL32_DLL_FAILED); } SOKOL_ASSERT(_sapp.wgl.opengl32); _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); @@ -5600,7 +6462,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { GetModuleHandleW(NULL), NULL); if (!_sapp.wgl.msg_hwnd) { - _sapp_fail("Win32: failed to create helper window!\n"); + _SAPP_PANIC(WIN32_CREATE_HELPER_WINDOW_FAILED); } SOKOL_ASSERT(_sapp.wgl.msg_hwnd); ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); @@ -5611,7 +6473,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { } _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); if (!_sapp.wgl.msg_dc) { - _sapp_fail("Win32: failed to obtain helper window DC!\n"); + _SAPP_PANIC(WIN32_HELPER_WINDOW_GETDC_FAILED); } } @@ -5664,21 +6526,21 @@ _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { SOKOL_ASSERT(_sapp.wgl.msg_dc); PIXELFORMATDESCRIPTOR pfd; - memset(&pfd, 0, sizeof(pfd)); + _sapp_clear(&pfd, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { - _sapp_fail("WGL: failed to set pixel format for dummy context\n"); + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED); } HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); if (!rc) { - _sapp_fail("WGL: Failed to create dummy context\n"); + _SAPP_PANIC(WIN32_CREATE_DUMMY_CONTEXT_FAILED); } if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { - _sapp_fail("WGL: Failed to make context current\n"); + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED); } _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); @@ -5698,7 +6560,7 @@ _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); int value = 0; if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { - _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); } return value; } @@ -5709,7 +6571,7 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { const _sapp_gl_fbconfig* closest; int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); - _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((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); int usable_count = 0; for (int i = 0; i < native_count; i++) { @@ -5756,31 +6618,31 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { if (closest) { pixel_format = (int) closest->handle; } - SOKOL_FREE(usable_configs); + _sapp_free(usable_configs); return pixel_format; } _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { int pixel_format = _sapp_wgl_find_pixel_format(); if (0 == pixel_format) { - _sapp_fail("WGL: Didn't find matching pixel format.\n"); + _SAPP_PANIC(WIN32_WGL_FIND_PIXELFORMAT_FAILED); } PIXELFORMATDESCRIPTOR pfd; if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { - _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); + _SAPP_PANIC(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED); } if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { - _sapp_fail("WGL: Failed to set selected pixel format!\n"); + _SAPP_PANIC(WIN32_WGL_SET_PIXELFORMAT_FAILED); } if (!_sapp.wgl.arb_create_context) { - _sapp_fail("WGL: ARB_create_context required!\n"); + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED); } if (!_sapp.wgl.arb_create_context_profile) { - _sapp_fail("WGL: ARB_create_context_profile required!\n"); + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED); } const int attrs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, 3, - WGL_CONTEXT_MINOR_VERSION_ARB, 3, + WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, + WGL_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0, 0 @@ -5789,16 +6651,16 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { if (!_sapp.wgl.gl_ctx) { const DWORD err = GetLastError(); if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { - _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); + _SAPP_PANIC(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { - _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); + _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { - _sapp_fail("WGL: The share context is not compatible with the requested context"); + _SAPP_PANIC(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT); } else { - _sapp_fail("WGL: Failed to create OpenGL context"); + _SAPP_PANIC(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER); } } _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); @@ -5823,7 +6685,7 @@ _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); - memset(dst, 0, (size_t)dst_num_bytes); + _sapp_clear(dst, (size_t)dst_num_bytes); const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); if (bytes_needed <= dst_num_bytes) { WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL); @@ -5834,10 +6696,40 @@ _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int } } -_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { +/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ +_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { + RECT rect; + if (GetClientRect(_sapp.win32.hwnd, &rect)) { + float window_width = (float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale; + float window_height = (float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale; + _sapp.window_width = (int)roundf(window_width); + _sapp.window_height = (int)roundf(window_height); + int fb_width = (int)roundf(window_width * _sapp.win32.dpi.content_scale); + int fb_height = (int)roundf(window_height * _sapp.win32.dpi.content_scale); + /* prevent a framebuffer size of 0 when window is minimized */ + if (0 == fb_width) { + fb_width = 1; + } + if (0 == fb_height) { + fb_height = 1; + } + if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { + _sapp.framebuffer_width = fb_width; + _sapp.framebuffer_height = fb_height; + return true; + } + } + else { + _sapp.window_width = _sapp.window_height = 1; + _sapp.framebuffer_width = _sapp.framebuffer_height = 1; + } + return false; +} + +_SOKOL_PRIVATE void _sapp_win32_set_fullscreen(bool fullscreen, UINT swp_flags) { HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); MONITORINFO minfo; - memset(&minfo, 0, sizeof(minfo)); + _sapp_clear(&minfo, sizeof(minfo)); minfo.cbSize = sizeof(MONITORINFO); GetMonitorInfo(monitor, &minfo); const RECT mr = minfo.rcMonitor; @@ -5848,32 +6740,99 @@ _SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { DWORD win_style; RECT rect = { 0, 0, 0, 0 }; - _sapp.fullscreen = !_sapp.fullscreen; + _sapp.fullscreen = fullscreen; if (!_sapp.fullscreen) { win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; - rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale); - rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale); + rect = _sapp.win32.stored_window_rect; } else { + GetWindowRect(_sapp.win32.hwnd, &_sapp.win32.stored_window_rect); win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; - rect.right = monitor_w; - rect.bottom = monitor_h; + rect.left = mr.left; + rect.top = mr.top; + rect.right = rect.left + monitor_w; + rect.bottom = rect.top + monitor_h; + AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); } - AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); - int win_width = rect.right - rect.left; - int win_height = rect.bottom - rect.top; - if (!_sapp.fullscreen) { - rect.left = (monitor_w - win_width) / 2; - rect.top = (monitor_h - win_height) / 2; - } - + const int win_w = rect.right - rect.left; + const int win_h = rect.bottom - rect.top; + const int win_x = rect.left; + const int win_y = rect.top; SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); - SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); + SetWindowPos(_sapp.win32.hwnd, HWND_TOP, win_x, win_y, win_w, win_h, swp_flags | SWP_FRAMECHANGED); } -_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) { - /* NOTE: this function is only called when the mouse visibility actually changes */ - ShowCursor((BOOL)visible); +_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { + _sapp_win32_set_fullscreen(!_sapp.fullscreen, SWP_SHOWWINDOW); +} + +_SOKOL_PRIVATE void _sapp_win32_init_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + // NOTE: the OCR_* constants are only defined if OEMRESOURCE is defined + // before windows.h is included, but we can't guarantee that because + // the sokol_app.h implementation may be included with other implementations + // in the same compilation unit + int id = 0; + switch (cursor) { + case SAPP_MOUSECURSOR_ARROW: id = 32512; break; // OCR_NORMAL + case SAPP_MOUSECURSOR_IBEAM: id = 32513; break; // OCR_IBEAM + case SAPP_MOUSECURSOR_CROSSHAIR: id = 32515; break; // OCR_CROSS + case SAPP_MOUSECURSOR_POINTING_HAND: id = 32649; break; // OCR_HAND + case SAPP_MOUSECURSOR_RESIZE_EW: id = 32644; break; // OCR_SIZEWE + case SAPP_MOUSECURSOR_RESIZE_NS: id = 32645; break; // OCR_SIZENS + case SAPP_MOUSECURSOR_RESIZE_NWSE: id = 32642; break; // OCR_SIZENWSE + case SAPP_MOUSECURSOR_RESIZE_NESW: id = 32643; break; // OCR_SIZENESW + case SAPP_MOUSECURSOR_RESIZE_ALL: id = 32646; break; // OCR_SIZEALL + case SAPP_MOUSECURSOR_NOT_ALLOWED: id = 32648; break; // OCR_NO + default: break; + } + if (id != 0) { + _sapp.win32.cursors[cursor] = (HCURSOR)LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED); + } + // fallback: default cursor + if (0 == _sapp.win32.cursors[cursor]) { + // 32512 => IDC_ARROW + _sapp.win32.cursors[cursor] = LoadCursorW(NULL, MAKEINTRESOURCEW(32512)); + } + SOKOL_ASSERT(0 != _sapp.win32.cursors[cursor]); +} + +_SOKOL_PRIVATE void _sapp_win32_init_cursors(void) { + for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { + _sapp_win32_init_cursor((sapp_mouse_cursor)i); + } +} + +_SOKOL_PRIVATE bool _sapp_win32_cursor_in_content_area(void) { + POINT pos; + if (!GetCursorPos(&pos)) { + return false; + } + if (WindowFromPoint(pos) != _sapp.win32.hwnd) { + return false; + } + RECT area; + GetClientRect(_sapp.win32.hwnd, &area); + ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.left); + ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.right); + return PtInRect(&area, pos) == TRUE; +} + +_SOKOL_PRIVATE void _sapp_win32_update_cursor(sapp_mouse_cursor cursor, bool shown, bool skip_area_test) { + // NOTE: when called from WM_SETCURSOR, the area test would be redundant + if (!skip_area_test) { + if (!_sapp_win32_cursor_in_content_area()) { + return; + } + } + if (!shown) { + SetCursor(NULL); + } + else { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(0 != _sapp.win32.cursors[cursor]); + SetCursor(_sapp.win32.cursors[cursor]); + } } _SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { @@ -5910,7 +6869,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* while the mouse is locked, make the mouse cursor invisible and confine the mouse movement to a small rectangle inside our window - (so that we dont miss any mouse up events) + (so that we don't miss any mouse up events) */ RECT client_rect = { _sapp.win32.mouse_locked_x, @@ -5931,7 +6890,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { _sapp.win32.hwnd // hwndTarget }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK); } /* in case the raw mouse device only supports absolute position reporting, we need to skip the dx/dy compution for the first WM_INPUT event @@ -5942,7 +6901,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* disable raw input for mouse */ const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SOKOL_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK); } /* let the mouse roam freely again */ @@ -5955,32 +6914,15 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { } } -/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ -_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { - RECT rect; - if (GetClientRect(_sapp.win32.hwnd, &rect)) { - _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale); - _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale); - int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale); - int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale); - /* prevent a framebuffer size of 0 when window is minimized */ - if (0 == fb_width) { - fb_width = 1; - } - if (0 == fb_height) { - fb_height = 1; - } - if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { - _sapp.framebuffer_width = fb_width; - _sapp.framebuffer_height = fb_height; - return true; - } +_SOKOL_PRIVATE bool _sapp_win32_update_monitor(void) { + const HMONITOR cur_monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL); + if (cur_monitor != _sapp.win32.hmonitor) { + _sapp.win32.hmonitor = cur_monitor; + return true; } else { - _sapp.window_width = _sapp.window_height = 1; - _sapp.framebuffer_width = _sapp.framebuffer_height = 1; + return false; } - return false; } _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { @@ -6010,6 +6952,21 @@ _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { return mods; } +_SOKOL_PRIVATE void _sapp_win32_mouse_update(LPARAM lParam) { + if (!_sapp.mouse.locked) { + const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; + const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; + if (_sapp.mouse.pos_valid) { + // don't update dx/dy in the very first event + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { if (_sapp_events_enabled()) { _sapp_init_event(type); @@ -6058,6 +7015,34 @@ _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { } } +_SOKOL_PRIVATE void _sapp_win32_dpi_changed(HWND hWnd, LPRECT proposed_win_rect) { + /* called on WM_DPICHANGED, which will only be sent to the application + if sapp_desc.high_dpi is true and the Windows version is recent enough + to support DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 + */ + SOKOL_ASSERT(_sapp.desc.high_dpi); + HINSTANCE user32 = LoadLibraryA("user32.dll"); + if (!user32) { + return; + } + typedef UINT(WINAPI * GETDPIFORWINDOW_T)(HWND hwnd); + GETDPIFORWINDOW_T fn_getdpiforwindow = (GETDPIFORWINDOW_T)(void*)GetProcAddress(user32, "GetDpiForWindow"); + if (fn_getdpiforwindow) { + UINT dpix = fn_getdpiforwindow(_sapp.win32.hwnd); + // NOTE: for high-dpi apps, mouse_scale remains one + _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; + _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; + _sapp.dpi_scale = _sapp.win32.dpi.window_scale; + SetWindowPos(hWnd, 0, + proposed_win_rect->left, + proposed_win_rect->top, + proposed_win_rect->right - proposed_win_rect->left, + proposed_win_rect->bottom - proposed_win_rect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + FreeLibrary(user32); +} + _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { if (!_sapp.drop.enabled) { return; @@ -6068,13 +7053,13 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count; for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) { const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1; - WCHAR* buffer = (WCHAR*) SOKOL_CALLOC(num_chars, sizeof(WCHAR)); + WCHAR* buffer = (WCHAR*) _sapp_malloc_clear(num_chars * sizeof(WCHAR)); DragQueryFileW(hdrop, i, buffer, num_chars); if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { - SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; } - SOKOL_FREE(buffer); + _sapp_free(buffer); } DragFinish(hdrop); if (!drop_failed) { @@ -6089,6 +7074,34 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { } } +_SOKOL_PRIVATE void _sapp_win32_timing_measure(void) { + #if defined(SOKOL_D3D11) + // on D3D11, use the more precise DXGI timestamp + if (_sapp.d3d11.use_dxgi_frame_stats) { + DXGI_FRAME_STATISTICS dxgi_stats; + _sapp_clear(&dxgi_stats, sizeof(dxgi_stats)); + HRESULT hr = _sapp_dxgi_GetFrameStatistics(_sapp.d3d11.swap_chain, &dxgi_stats); + if (SUCCEEDED(hr)) { + if (dxgi_stats.SyncRefreshCount != _sapp.d3d11.sync_refresh_count) { + if ((_sapp.d3d11.sync_refresh_count + 1) != dxgi_stats.SyncRefreshCount) { + _sapp_timing_discontinuity(&_sapp.timing); + } + _sapp.d3d11.sync_refresh_count = dxgi_stats.SyncRefreshCount; + LARGE_INTEGER qpc = dxgi_stats.SyncQPCTime; + const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - _sapp.timing.timestamp.win.start.QuadPart, 1000000000, _sapp.timing.timestamp.win.freq.QuadPart); + _sapp_timing_external(&_sapp.timing, (double)now / 1000000000.0); + } + return; + } + } + // fallback if swap model isn't "flip-discard" or GetFrameStatistics failed for another reason + _sapp_timing_measure(&_sapp.timing); + #endif + #if defined(SOKOL_GLCORE33) + _sapp_timing_measure(&_sapp.timing); + #endif +} + _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!_sapp.win32.in_create_window) { switch (uMsg) { @@ -6099,7 +7112,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM a change to intervene via sapp_cancel_quit() */ _sapp.quit_requested = true; - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_win32_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); /* if user code hasn't intervened, quit the app */ if (_sapp.quit_requested) { _sapp.quit_ordered = true; @@ -6131,72 +7144,75 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM if (iconified != _sapp.win32.iconified) { _sapp.win32.iconified = iconified; if (iconified) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); + _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); } else { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED); } } } break; case WM_SETFOCUS: - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_FOCUSED); + _sapp_win32_app_event(SAPP_EVENTTYPE_FOCUSED); break; case WM_KILLFOCUS: /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ if (_sapp.mouse.locked) { _sapp_win32_lock_mouse(false); } - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UNFOCUSED); + _sapp_win32_app_event(SAPP_EVENTTYPE_UNFOCUSED); break; case WM_SETCURSOR: - if (_sapp.desc.user_cursor) { - if (LOWORD(lParam) == HTCLIENT) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); - return 1; - } + if (LOWORD(lParam) == HTCLIENT) { + _sapp_win32_update_cursor(_sapp.mouse.current_cursor, _sapp.mouse.shown, true); + return TRUE; } break; + case WM_DPICHANGED: + { + /* Update window's DPI and size if its moved to another monitor with a different DPI + Only sent if DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 is used. + */ + _sapp_win32_dpi_changed(hWnd, (LPRECT)lParam); + break; + } case WM_LBUTTONDOWN: + _sapp_win32_mouse_update(lParam); _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); _sapp_win32_capture_mouse(1<data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { /* mouse only reports absolute position - NOTE: THIS IS UNTESTED, it's unclear from reading the - Win32 RawInput docs under which circumstances absolute - positions are sent. + NOTE: This code is untested and will most likely behave wrong in Remote Desktop sessions. + (such remote desktop sessions are setting the MOUSE_MOVE_ABSOLUTE flag). + See: https://github.com/floooh/sokol/issues/806 and + https://github.com/microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555) */ + LONG new_x = raw_mouse_data->data.mouse.lLastX; + LONG new_y = raw_mouse_data->data.mouse.lLastY; if (_sapp.win32.raw_input_mousepos_valid) { - LONG new_x = raw_mouse_data->data.mouse.lLastX; - LONG new_y = raw_mouse_data->data.mouse.lLastY; _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); - _sapp.win32.raw_input_mousepos_x = new_x; - _sapp.win32.raw_input_mousepos_y = new_y; - _sapp.win32.raw_input_mousepos_valid = true; } + _sapp.win32.raw_input_mousepos_x = new_x; + _sapp.win32.raw_input_mousepos_y = new_y; + _sapp.win32.raw_input_mousepos_valid = true; } else { /* mouse reports movement delta (this seems to be the common case) */ @@ -6249,9 +7266,11 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM } break; case WM_MOUSEWHEEL: + _sapp_win32_mouse_update(lParam); _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); break; case WM_MOUSEHWHEEL: + _sapp_win32_mouse_update(lParam); _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); break; case WM_CHAR: @@ -6272,9 +7291,11 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM KillTimer(_sapp.win32.hwnd, 1); break; case WM_TIMER: + _sapp_win32_timing_measure(); _sapp_frame(); #if defined(SOKOL_D3D11) - _sapp_d3d11_present(); + // present with DXGI_PRESENT_DO_NOT_WAIT + _sapp_d3d11_present(true); #endif #if defined(SOKOL_GLCORE33) _sapp_wgl_swap_buffers(); @@ -6286,13 +7307,29 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } */ break; + case WM_NCLBUTTONDOWN: + /* workaround for half-second pause when starting to move window + see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/ + */ + if (SendMessage(_sapp.win32.hwnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION) { + POINT point; + GetCursorPos(&point); + ScreenToClient(_sapp.win32.hwnd, &point); + PostMessage(_sapp.win32.hwnd, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16)); + } + break; case WM_DROPFILES: _sapp_win32_files_dropped((HDROP)wParam); break; + case WM_DISPLAYCHANGE: + // refresh rate might have changed + _sapp_timing_reset(&_sapp.timing); + break; + default: break; } @@ -6302,7 +7339,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM _SOKOL_PRIVATE void _sapp_win32_create_window(void) { WNDCLASSW wndclassw; - memset(&wndclassw, 0, sizeof(wndclassw)); + _sapp_clear(&wndclassw, sizeof(wndclassw)); wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; wndclassw.hInstance = GetModuleHandleW(NULL); @@ -6311,42 +7348,50 @@ _SOKOL_PRIVATE void _sapp_win32_create_window(void) { wndclassw.lpszClassName = L"SOKOLAPP"; RegisterClassW(&wndclassw); - DWORD win_style; + /* NOTE: regardless whether fullscreen is requested or not, a regular + windowed-mode window will always be created first (however in hidden + mode, so that no windowed-mode window pops up before the fullscreen window) + */ const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; RECT rect = { 0, 0, 0, 0 }; - if (_sapp.fullscreen) { - win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; - rect.right = GetSystemMetrics(SM_CXSCREEN); - rect.bottom = GetSystemMetrics(SM_CYSCREEN); - } - else { - win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; - rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); - rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); - } + DWORD win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); + const bool use_default_width = 0 == _sapp.window_width; + const bool use_default_height = 0 == _sapp.window_height; AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); const int win_width = rect.right - rect.left; const int win_height = rect.bottom - rect.top; _sapp.win32.in_create_window = true; _sapp.win32.hwnd = CreateWindowExW( - win_ex_style, /* dwExStyle */ - L"SOKOLAPP", /* lpClassName */ - _sapp.window_title_wide, /* lpWindowName */ - win_style, /* dwStyle */ - CW_USEDEFAULT, /* X */ - CW_USEDEFAULT, /* Y */ - win_width, /* nWidth */ - win_height, /* nHeight */ - NULL, /* hWndParent */ - NULL, /* hMenu */ - GetModuleHandle(NULL), /* hInstance */ - NULL); /* lParam */ - ShowWindow(_sapp.win32.hwnd, SW_SHOW); + win_ex_style, // dwExStyle + L"SOKOLAPP", // lpClassName + _sapp.window_title_wide, // lpWindowName + win_style, // dwStyle + CW_USEDEFAULT, // X + SW_HIDE, // Y (NOTE: CW_USEDEFAULT is not used for position here, but internally calls ShowWindow! + use_default_width ? CW_USEDEFAULT : win_width, // nWidth + use_default_height ? CW_USEDEFAULT : win_height, // nHeight (NOTE: if width is CW_USEDEFAULT, height is actually ignored) + NULL, // hWndParent + NULL, // hMenu + GetModuleHandle(NULL), // hInstance + NULL); // lParam _sapp.win32.in_create_window = false; _sapp.win32.dc = GetDC(_sapp.win32.hwnd); + _sapp.win32.hmonitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL); SOKOL_ASSERT(_sapp.win32.dc); - _sapp_win32_update_dimensions(); + /* this will get the actual windowed-mode window size, if fullscreen + is requested, the set_fullscreen function will then capture the + current window rectangle, which then might be used later to + restore the window position when switching back to windowed + */ + _sapp_win32_update_dimensions(); + if (_sapp.fullscreen) { + _sapp_win32_set_fullscreen(_sapp.fullscreen, SWP_HIDEWINDOW); + _sapp_win32_update_dimensions(); + } + ShowWindow(_sapp.win32.hwnd, SW_SHOW); DragAcceptFiles(_sapp.win32.hwnd, 1); } @@ -6379,6 +7424,7 @@ _SOKOL_PRIVATE void _sapp_win32_init_console(void) { FILE* res_fp = 0; errno_t err; err = freopen_s(&res_fp, "CON", "w", stdout); + (void)err; err = freopen_s(&res_fp, "CON", "w", stderr); (void)err; } @@ -6397,35 +7443,56 @@ _SOKOL_PRIVATE void _sapp_win32_restore_console(void) { _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { + DECLARE_HANDLE(DPI_AWARENESS_CONTEXT_T); typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); + typedef bool (WINAPI * SETPROCESSDPIAWARENESSCONTEXT_T)(DPI_AWARENESS_CONTEXT_T); // since Windows 10, version 1703 typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0; SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0; GETDPIFORMONITOR_T fn_getdpiformonitor = 0; + SETPROCESSDPIAWARENESSCONTEXT_T fn_setprocessdpiawarenesscontext =0; + HINSTANCE user32 = LoadLibraryA("user32.dll"); if (user32) { fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware"); + fn_setprocessdpiawarenesscontext = (SETPROCESSDPIAWARENESSCONTEXT_T)(void*) GetProcAddress(user32, "SetProcessDpiAwarenessContext"); } HINSTANCE shcore = LoadLibraryA("shcore.dll"); if (shcore) { fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness"); fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor"); } + /* + NOTE on SetProcessDpiAware() vs SetProcessDpiAwareness() vs SetProcessDpiAwarenessContext(): + + These are different attempts to get DPI handling on Windows right, from oldest + to newest. SetProcessDpiAwarenessContext() is required for the new + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 method. + */ if (fn_setprocessdpiawareness) { - /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ - PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE; - _sapp.win32.dpi.aware = true; - if (!_sapp.desc.high_dpi) { - process_dpi_awareness = PROCESS_DPI_UNAWARE; - _sapp.win32.dpi.aware = false; + if (_sapp.desc.high_dpi) { + /* app requests HighDPI rendering, first try the Win10 Creator Update per-monitor-dpi awareness, + if that fails, fall back to system-dpi-awareness + */ + _sapp.win32.dpi.aware = true; + DPI_AWARENESS_CONTEXT_T per_monitor_aware_v2 = (DPI_AWARENESS_CONTEXT_T)-4; + if (!(fn_setprocessdpiawarenesscontext && fn_setprocessdpiawarenesscontext(per_monitor_aware_v2))) { + // fallback to system-dpi-aware + fn_setprocessdpiawareness(PROCESS_SYSTEM_DPI_AWARE); + } + } + else { + /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ + _sapp.win32.dpi.aware = false; + fn_setprocessdpiawareness(PROCESS_DPI_UNAWARE); } - fn_setprocessdpiawareness(process_dpi_awareness); } else if (fn_setprocessdpiaware) { - fn_setprocessdpiaware(); + // fallback for Windows 7 _sapp.win32.dpi.aware = true; + fn_setprocessdpiaware(); } /* get dpi scale factor for main monitor */ if (fn_getdpiformonitor && _sapp.win32.dpi.aware) { @@ -6463,26 +7530,32 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { SOKOL_ASSERT(_sapp.win32.hwnd); SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); + if (!OpenClipboard(_sapp.win32.hwnd)) { + return false; + } + + HANDLE object = 0; wchar_t* wchar_buf = 0; + const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); - HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); - if (!object) { + object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); + if (NULL == object) { goto error; } wchar_buf = (wchar_t*) GlobalLock(object); - if (!wchar_buf) { + if (NULL == wchar_buf) { goto error; } - if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { + if (!_sapp_win32_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { goto error; } - GlobalUnlock(wchar_buf); + GlobalUnlock(object); wchar_buf = 0; - if (!OpenClipboard(_sapp.win32.hwnd)) { + EmptyClipboard(); + // NOTE: when successful, SetClipboardData() takes ownership of memory object! + if (NULL == SetClipboardData(CF_UNICODETEXT, object)) { goto error; } - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); CloseClipboard(); return true; @@ -6493,6 +7566,7 @@ error: if (object) { GlobalFree(object); } + CloseClipboard(); return false; } @@ -6516,7 +7590,7 @@ _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { return _sapp.clipboard.buffer; } if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { - SOKOL_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); } GlobalUnlock(object); CloseClipboard(); @@ -6524,13 +7598,13 @@ _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { } _SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); } _SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* desc) { BITMAPV5HEADER bi; - memset(&bi, 0, sizeof(bi)); + _sapp_clear(&bi, sizeof(bi)); bi.bV5Size = sizeof(bi); bi.bV5Width = desc->width; bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left @@ -6569,7 +7643,7 @@ _SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* d } ICONINFO icon_info; - memset(&icon_info, 0, sizeof(icon_info)); + _sapp_clear(&icon_info, sizeof(icon_info)); icon_info.fIcon = true; icon_info.xHotspot = 0; icon_info.yHotspot = 0; @@ -6626,9 +7700,10 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_win32_init_console(); _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_init_keytable(); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); _sapp_win32_init_dpi(); + _sapp_win32_init_cursors(); _sapp_win32_create_window(); sapp_set_icon(&desc->icon); #if defined(SOKOL_D3D11) @@ -6644,6 +7719,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { bool done = false; while (!(done || _sapp.quit_ordered)) { + _sapp_win32_timing_measure(); MSG msg; while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (WM_QUIT == msg.message) { @@ -6652,12 +7728,12 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { } else { TranslateMessage(&msg); - DispatchMessage(&msg); + DispatchMessageW(&msg); } } _sapp_frame(); #if defined(SOKOL_D3D11) - _sapp_d3d11_present(); + _sapp_d3d11_present(false); if (IsIconic(_sapp.win32.hwnd)) { Sleep((DWORD)(16 * _sapp.swap_interval)); } @@ -6670,7 +7746,13 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); + } + /* check if the window monitor has changed, need to reset timing because + the new monitor might have a different refresh rate + */ + if (_sapp_win32_update_monitor()) { + _sapp_timing_reset(&_sapp.timing); } if (_sapp.quit_requested) { PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); @@ -6698,17 +7780,17 @@ _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_lin LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); if (w_argv == NULL) { - _sapp_fail("Win32: failed to parse command line"); + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! } else { size_t size = wcslen(w_command_line) * 4; - argv = (char**) SOKOL_CALLOC(1, ((size_t)argc + 1) * sizeof(char*) + size); + argv = (char**) _sapp_malloc_clear(((size_t)argc + 1) * sizeof(char*) + size); SOKOL_ASSERT(argv); args = (char*) &argv[argc + 1]; int n; for (int i = 0; i < argc; ++i) { n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); if (n == 0) { - _sapp_fail("Win32: failed to convert all arguments to utf8"); + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! break; } argv[i] = args; @@ -6738,7 +7820,7 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8); sapp_desc desc = sokol_main(argc_utf8, argv_utf8); _sapp_win32_run(&desc); - SOKOL_FREE(argv_utf8); + _sapp_free(argv_utf8); return 0; } #endif /* SOKOL_WIN32_FORCE_MAIN */ @@ -6750,1017 +7832,13 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ #endif /* _SAPP_WIN32 */ -/*== UWP ================================================================*/ -#if defined(_SAPP_UWP) - -// Helper functions -_SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { - _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; - if (_sapp.desc.high_dpi) { - _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; - _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; - } - else { - _sapp.uwp.dpi.content_scale = 1.0f; - _sapp.uwp.dpi.mouse_scale = 1.0f; - } - _sapp.dpi_scale = _sapp.uwp.dpi.content_scale; -} - -_SOKOL_PRIVATE void _sapp_uwp_show_mouse(bool visible) { - using namespace winrt::Windows::UI::Core; - - /* NOTE: this function is only called when the mouse visibility actually changes */ - CoreWindow::GetForCurrentThread().PointerCursor(visible ? - CoreCursor(CoreCursorType::Arrow, 0) : - CoreCursor(nullptr)); -} - -_SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) { - using namespace winrt::Windows::System; - using namespace winrt::Windows::UI::Core; - - uint32_t mods = 0; - if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_SHIFT; - } - if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_CTRL; - } - if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_ALT; - } - if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) || - ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) - { - mods |= SAPP_MODIFIER_SUPER; - } - if (0 != (_sapp.uwp.mouse_buttons & (1<= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = _sapp_uwp_mods(sender_window); - _sapp.event.char_code = c; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - _sapp.fullscreen = appView.IsFullScreenMode(); - if (!_sapp.fullscreen) { - appView.TryEnterFullScreenMode(); - } - else { - appView.ExitFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ - -// Controls all the DirectX device resources. -class DeviceResources { -public: - // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. - interface IDeviceNotify { - virtual void OnDeviceLost() = 0; - virtual void OnDeviceRestored() = 0; - }; - - DeviceResources(); - ~DeviceResources(); - void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize); - void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation); - void SetDpi(float dpi); - void ValidateDevice(); - void HandleDeviceLost(); - void RegisterDeviceNotify(IDeviceNotify* deviceNotify); - void Trim(); - void Present(); - -private: - - // Swapchain Rotation Matrices (Z-rotation) - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = { - 0.0f, 1.0f, 0.0f, 0.0f, - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = { - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = { - 0.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - void CreateDeviceResources(); - void CreateWindowSizeDependentResources(); - void UpdateRenderTargetSize(); - DXGI_MODE_ROTATION ComputeDisplayRotation(); - bool SdkLayersAvailable(); - - // Direct3D objects. - winrt::com_ptr m_d3dDevice; - winrt::com_ptr m_d3dContext; - winrt::com_ptr m_swapChain; - - // Direct3D rendering objects. Required for 3D. - winrt::com_ptr m_d3dRenderTarget; - winrt::com_ptr m_d3dRenderTargetView; - winrt::com_ptr m_d3dMSAARenderTarget; - winrt::com_ptr m_d3dMSAARenderTargetView; - winrt::com_ptr m_d3dDepthStencil; - winrt::com_ptr m_d3dDepthStencilView; - D3D11_VIEWPORT m_screenViewport = { }; - - // Cached reference to the Window. - winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window; - - // Cached device properties. - D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1; - winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { }; - winrt::Windows::Foundation::Size m_outputSize = { }; - winrt::Windows::Foundation::Size m_logicalSize = { }; - winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - float m_dpi = -1.0f; - - // Transforms used for display orientation. - DirectX::XMFLOAT4X4 m_orientationTransform3D; - - // The IDeviceNotify can be held directly as it owns the DeviceResources. - IDeviceNotify* m_deviceNotify = nullptr; -}; - -// Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. -struct App : winrt::implements { -public: - // IFrameworkViewSource Methods - winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; } - - // IFrameworkView Methods. - virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView); - virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - virtual void Load(winrt::hstring const& entryPoint); - virtual void Run(); - virtual void Uninitialize(); - -protected: - // Application lifecycle event handlers - void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args); - void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args); - void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args); - - // Window event handlers - void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args); - void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args); - - // Navigation event handlers - void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args); - - // Input event handlers - void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args); - - // Pointer event handlers - void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - - // DisplayInformation event handlers. - void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - -private: - std::unique_ptr m_deviceResources; - bool m_windowVisible = true; -}; - -DeviceResources::DeviceResources() { - CreateDeviceResources(); -} - -DeviceResources::~DeviceResources() { - // Cleanup Sokol Context - _sapp.d3d11.device = nullptr; - _sapp.d3d11.device_context = nullptr; -} - -void DeviceResources::CreateDeviceResources() { - // This flag adds support for surfaces with a different color channel ordering - // than the API default. It is required for compatibility with Direct2D. - UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - - #if defined(_DEBUG) - if (SdkLayersAvailable()) { - // If the project is in a debug build, enable debugging via SDK Layers with this flag. - creationFlags |= D3D11_CREATE_DEVICE_DEBUG; - } - #endif - - // This array defines the set of DirectX hardware feature levels this app will support. - // Note the ordering should be preserved. - // Don't forget to declare your application's minimum required feature level in its - // description. All applications are assumed to support 9.1 unless otherwise stated. - D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - // Create the Direct3D 11 API device object and a corresponding context. - winrt::com_ptr device; - winrt::com_ptr context; - - HRESULT hr = D3D11CreateDevice( - nullptr, // Specify nullptr to use the default adapter. - D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. - 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. - creationFlags, // Set debug and Direct2D compatibility flags. - featureLevels, // List of feature levels this app can support. - ARRAYSIZE(featureLevels), // Size of the list above. - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - device.put(), // Returns the Direct3D device created. - &m_d3dFeatureLevel, // Returns feature level of device created. - context.put() // Returns the device immediate context. - ); - - if (FAILED(hr)) { - // If the initialization fails, fall back to the WARP device. - // For more information on WARP, see: - // https://go.microsoft.com/fwlink/?LinkId=286690 - winrt::check_hresult( - D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. - 0, - creationFlags, - featureLevels, - ARRAYSIZE(featureLevels), - D3D11_SDK_VERSION, - device.put(), - &m_d3dFeatureLevel, - context.put() - ) - ); - } - - // Store pointers to the Direct3D 11.3 API device and immediate context. - m_d3dDevice = device.as(); - m_d3dContext = context.as(); - - // Setup Sokol Context - _sapp.d3d11.device = m_d3dDevice.get(); - _sapp.d3d11.device_context = m_d3dContext.get(); -} - -void DeviceResources::CreateWindowSizeDependentResources() { - // Cleanup Sokol Context (these are non-owning raw pointers) - _sapp.d3d11.rt = nullptr; - _sapp.d3d11.rtv = nullptr; - _sapp.d3d11.msaa_rt = nullptr; - _sapp.d3d11.msaa_rtv = nullptr; - _sapp.d3d11.ds = nullptr; - _sapp.d3d11.dsv = nullptr; - - // Clear the previous window size specific context. - ID3D11RenderTargetView* nullViews[] = { nullptr }; - m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); - // these are smart pointers, setting to nullptr will delete the objects - m_d3dRenderTarget = nullptr; - m_d3dRenderTargetView = nullptr; - m_d3dMSAARenderTarget = nullptr; - m_d3dMSAARenderTargetView = nullptr; - m_d3dDepthStencilView = nullptr; - m_d3dDepthStencil = nullptr; - m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); - - UpdateRenderTargetSize(); - - // The width and height of the swap chain must be based on the window's - // natively-oriented width and height. If the window is not in the native - // orientation, the dimensions must be reversed. - DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); - - bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; - m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; - m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; - - if (m_swapChain != nullptr) { - // If the swap chain already exists, resize it. - HRESULT hr = m_swapChain->ResizeBuffers( - 2, // Double-buffered swap chain. - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - DXGI_FORMAT_B8G8R8A8_UNORM, - 0 - ); - - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - // If the device was removed for any reason, a new device and swap chain will need to be created. - HandleDeviceLost(); - - // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method - // and correctly set up the new device. - return; - } - else { - winrt::check_hresult(hr); - } - } - else { - // Otherwise, create a new one using the same adapter as the existing Direct3D device. - DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; - - swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. - swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); - swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. - swapChainDesc.Stereo = false; - swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect. - swapChainDesc.Flags = 0; - swapChainDesc.Scaling = scaling; - swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - - // This sequence obtains the DXGI factory that was used to create the Direct3D device above. - winrt::com_ptr dxgiDevice = m_d3dDevice.as(); - winrt::com_ptr dxgiAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put())); - winrt::com_ptr dxgiFactory; - winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void())); - winrt::com_ptr swapChain; - winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put())); - m_swapChain = swapChain.as(); - - // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and - // ensures that the application will only render after each VSync, minimizing power consumption. - winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); - - // Setup Sokol Context - winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); - _sapp.d3d11.swap_chain = m_swapChain.as().detach(); - } - - // Set the proper orientation for the swap chain, and generate 2D and - // 3D matrix transformations for rendering to the rotated swap chain. - // Note the rotation angle for the 2D and 3D transforms are different. - // This is due to the difference in coordinate spaces. Additionally, - // the 3D matrix is specified explicitly to avoid rounding errors. - switch (displayRotation) { - case DXGI_MODE_ROTATION_IDENTITY: - m_orientationTransform3D = m_rotation0; - break; - - case DXGI_MODE_ROTATION_ROTATE90: - m_orientationTransform3D = m_rotation270; - break; - - case DXGI_MODE_ROTATION_ROTATE180: - m_orientationTransform3D = m_rotation180; - break; - - case DXGI_MODE_ROTATION_ROTATE270: - m_orientationTransform3D = m_rotation90; - break; - } - winrt::check_hresult(m_swapChain->SetRotation(displayRotation)); - - // Create a render target view of the swap chain back buffer. - winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget))); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); - - // Create MSAA texture and view if needed - if (_sapp.sample_count > 1) { - CD3D11_TEXTURE2D_DESC1 msaaTexDesc( - DXGI_FORMAT_B8G8R8A8_UNORM, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // arraySize - 1, // mipLevels - D3D11_BIND_RENDER_TARGET, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlags - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); - } - - // Create a depth stencil view for use with 3D rendering if needed. - CD3D11_TEXTURE2D_DESC1 depthStencilDesc( - DXGI_FORMAT_D24_UNORM_S8_UINT, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // This depth stencil view has only one texture. - 1, // Use a single mipmap level. - D3D11_BIND_DEPTH_STENCIL, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlag - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); - - CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); - winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); - - // Set sokol window and framebuffer sizes - _sapp.window_width = (int) m_logicalSize.Width; - _sapp.window_height = (int) m_logicalSize.Height; - _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); - _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); - - // Setup Sokol Context - _sapp.d3d11.rt = m_d3dRenderTarget.as().get(); - _sapp.d3d11.rtv = m_d3dRenderTargetView.as().get(); - _sapp.d3d11.ds = m_d3dDepthStencil.as().get(); - _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); - if (_sapp.sample_count > 1) { - _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); - _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); - } - - // Sokol app is now valid - _sapp.valid = true; -} - -// Determine the dimensions of the render target and whether it will be scaled down. -void DeviceResources::UpdateRenderTargetSize() { - // Calculate the necessary render target size in pixels. - m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale; - m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale; - - // Prevent zero size DirectX content from being created. - m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); - m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); -} - -// This method is called when the CoreWindow is created (or re-created). -void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - m_window = window; - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - m_nativeOrientation = currentDisplayInformation.NativeOrientation(); - m_currentOrientation = currentDisplayInformation.CurrentOrientation(); - m_dpi = currentDisplayInformation.LogicalDpi(); - _sapp_uwp_configure_dpi(m_dpi); - CreateWindowSizeDependentResources(); -} - -// This method is called in the event handler for the SizeChanged event. -void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) { - if (m_logicalSize != logicalSize) { - m_logicalSize = logicalSize; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DpiChanged event. -void DeviceResources::SetDpi(float dpi) { - if (dpi != m_dpi) { - m_dpi = dpi; - _sapp_uwp_configure_dpi(m_dpi); - // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. - auto window = m_window.get(); - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the OrientationChanged event. -void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) { - if (m_currentOrientation != currentOrientation) { - m_currentOrientation = currentOrientation; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DisplayContentsInvalidated event. -void DeviceResources::ValidateDevice() { - // The D3D Device is no longer valid if the default adapter changed since the device - // was created or if the device has been removed. - - // First, get the information for the default adapter from when the device was created. - winrt::com_ptr dxgiDevice = m_d3dDevice.as< IDXGIDevice3>(); - winrt::com_ptr deviceAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put())); - winrt::com_ptr deviceFactory; - winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); - winrt::com_ptr previousDefaultAdapter; - winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 previousDesc; - winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc)); - - // Next, get the information for the current default adapter. - winrt::com_ptr currentFactory; - winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); - winrt::com_ptr currentDefaultAdapter; - winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 currentDesc; - winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc)); - - // If the adapter LUIDs don't match, or if the device reports that it has been removed, - // a new D3D device must be created. - if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || - previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || - FAILED(m_d3dDevice->GetDeviceRemovedReason())) - { - // Release references to resources related to the old device. - dxgiDevice = nullptr; - deviceAdapter = nullptr; - deviceFactory = nullptr; - previousDefaultAdapter = nullptr; - - // Create a new device and swap chain. - HandleDeviceLost(); - } -} - -// Recreate all device resources and set them back to the current state. -void DeviceResources::HandleDeviceLost() { - m_swapChain = nullptr; - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceLost(); - } - CreateDeviceResources(); - CreateWindowSizeDependentResources(); - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceRestored(); - } -} - -// Register our DeviceNotify to be informed on device lost and creation. -void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) { - m_deviceNotify = deviceNotify; -} - -// Call this method when the app suspends. It provides a hint to the driver that the app -// is entering an idle state and that temporary buffers can be reclaimed for use by other apps. -void DeviceResources::Trim() { - m_d3dDevice.as()->Trim(); -} - -// Present the contents of the swap chain to the screen. -void DeviceResources::Present() { - - // MSAA resolve if needed - if (_sapp.sample_count > 1) { - m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); - m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); - } - - // The first argument instructs DXGI to block until VSync, putting the application - // to sleep until the next VSync. This ensures we don't waste any cycles rendering - // frames that will never be displayed to the screen. - DXGI_PRESENT_PARAMETERS parameters = { 0 }; - HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); - - // Discard the contents of the render target. - // This is a valid operation only when the existing contents will be entirely - // overwritten. If dirty or scroll rects are used, this call should be removed. - m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0); - - // Discard the contents of the depth stencil. - m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0); - - // If the device was removed either by a disconnection or a driver upgrade, we - // must recreate all device resources. - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - HandleDeviceLost(); - } - else { - winrt::check_hresult(hr); - } -} - -// This method determines the rotation between the display device's native orientation and the -// current display orientation. -DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() { - DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; - - // Note: NativeOrientation can only be Landscape or Portrait even though - // the DisplayOrientations enum has other values. - switch (m_nativeOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - } - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - } - break; - } - return rotation; -} - -// Check for SDK Layer support. -bool DeviceResources::SdkLayersAvailable() { - #if defined(_DEBUG) - HRESULT hr = D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. - 0, - D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. - nullptr, // Any feature level will do. - 0, - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - nullptr, // No need to keep the D3D device reference. - nullptr, // No need to know the feature level. - nullptr // No need to keep the D3D device context reference. - ); - return SUCCEEDED(hr); - #else - return false; - #endif -} - -// The first method called when the IFrameworkView is being created. -void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { - // Register event handlers for app lifecycle. This example includes Activated, so that we - // can make the CoreWindow active and start rendering on the window. - applicationView.Activated({ this, &App::OnActivated }); - - winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending }); - winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming }); - - // At this point we have access to the device. - // We can create the device-dependent resources. - m_deviceResources = std::make_unique(); -} - -// Called when the CoreWindow object is created (or re-created). -void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - window.SizeChanged({ this, &App::OnWindowSizeChanged }); - window.VisibilityChanged({ this, &App::OnVisibilityChanged }); - - window.KeyDown({ this, &App::OnKeyDown }); - window.KeyUp({ this, &App::OnKeyUp }); - window.CharacterReceived({ this, &App::OnCharacterReceived }); - - window.PointerEntered({ this, &App::OnPointerEntered }); - window.PointerExited({ this, &App::OnPointerExited }); - window.PointerPressed({ this, &App::OnPointerPressed }); - window.PointerReleased({ this, &App::OnPointerReleased }); - window.PointerMoved({ this, &App::OnPointerMoved }); - window.PointerWheelChanged({ this, &App::OnPointerWheelChanged }); - - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - - currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged }); - currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged }); - winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated }); - - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested }); - - m_deviceResources->SetWindow(window); -} - -// Initializes scene resources, or loads a previously saved app state. -void App::Load(winrt::hstring const& entryPoint) { - _SOKOL_UNUSED(entryPoint); -} - -// This method is called after the window becomes active. -void App::Run() { - // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed - while (true) { - if (m_windowVisible) { - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); - _sapp_frame(); - m_deviceResources->Present(); - } - else { - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending); - } - } -} - -// Required for IFrameworkView. -// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView -// class is torn down while the app is in the foreground. -void App::Uninitialize() { - // empty -} - -// Application lifecycle event handlers. -void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(applicationView); - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.width, (float)_sapp.desc.height); - appView.SetPreferredMinSize(targetSize); - appView.TryResizeView(targetSize); - - // Disabling this since it can only append the title to the app name (Title - Appname). - // There's no way of just setting a string to be the window title. - //appView.Title(_sapp.window_title_wide); - - // Run() won't start until the CoreWindow is activated. - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); - if (_sapp.desc.fullscreen) { - appView.TryEnterFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { - _SOKOL_UNUSED(sender); - _SOKOL_UNUSED(args); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); -} - -void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); -} - -void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { - _SOKOL_UNUSED(args); - m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { - _SOKOL_UNUSED(sender); - m_windowVisible = args.Visible(); - _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); -} - -void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { - _SOKOL_UNUSED(sender); - args.Handled(true); -} - -void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args); -} - -void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args); -} - -void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) { - _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender); -} - -void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = false; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -// NOTE: for some reason this event handler is never called?? -void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto position = args.CurrentPoint().Position(); - const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); - const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); - // don't update dx/dy in the very first event - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - if (!_sapp.uwp.mouse_tracked) { - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); - } - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender); - - // HACK for detecting multiple mouse button presses - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto properties = args.CurrentPoint().Properties(); - _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender); -} - -void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetDpi(sender.LogicalDpi()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - m_deviceResources->ValidateDevice(); -} - -} /* End empty namespace */ - -_SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); -} - -#if !defined(SOKOL_NO_ENTRY) -#if defined(UNICODE) -int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { -#else -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { -#endif - _SOKOL_UNUSED(hInstance); - _SOKOL_UNUSED(hPrevInstance); - _SOKOL_UNUSED(lpCmdLine); - _SOKOL_UNUSED(nCmdShow); - sapp_desc desc = sokol_main(0, nullptr); - _sapp_uwp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* _SAPP_UWP */ - -/*== Android ================================================================*/ +// █████ ███ ██ ██████ ██████ ██████ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██████ +// +// >>android #if defined(_SAPP_ANDROID) /* android loop thread */ @@ -7775,10 +7853,16 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { return false; } + _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; const EGLint cfg_attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + #if defined(SOKOL_GLES3) + EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2?EGL_OPENGL_ES2_BIT:EGL_OPENGL_ES3_BIT, + #else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + #endif EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -7837,16 +7921,13 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { if (_sapp.android.display != EGL_NO_DISPLAY) { eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (_sapp.android.surface != EGL_NO_SURFACE) { - SOKOL_LOG("Destroying egl surface"); eglDestroySurface(_sapp.android.display, _sapp.android.surface); _sapp.android.surface = EGL_NO_SURFACE; } if (_sapp.android.context != EGL_NO_CONTEXT) { - SOKOL_LOG("Destroying egl context"); eglDestroyContext(_sapp.android.display, _sapp.android.context); _sapp.android.context = EGL_NO_CONTEXT; } - SOKOL_LOG("Terminating egl display"); eglTerminate(_sapp.android.display); _sapp.android.display = EGL_NO_DISPLAY; } @@ -7887,7 +7968,6 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); - SOKOL_LOG("event_cb()"); _sapp_call_event(&_sapp.event); } } @@ -7910,14 +7990,14 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool const int32_t buf_h = win_h / 2; EGLint format; EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format); - SOKOL_ASSERT(egl_result == EGL_TRUE); + SOKOL_ASSERT(egl_result == EGL_TRUE); _SOKOL_UNUSED(egl_result); /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions as the ANativeWindow size results in weird display artefacts, that's why it's only called when the buffer geometry is different from the window size */ int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format); - SOKOL_ASSERT(result == 0); + SOKOL_ASSERT(result == 0); _SOKOL_UNUSED(result); } } @@ -7925,26 +8005,23 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool EGLint fb_w, fb_h; EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w); EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h); - SOKOL_ASSERT(egl_result_w == EGL_TRUE); - SOKOL_ASSERT(egl_result_h == EGL_TRUE); + SOKOL_ASSERT(egl_result_w == EGL_TRUE); _SOKOL_UNUSED(egl_result_w); + SOKOL_ASSERT(egl_result_h == EGL_TRUE); _SOKOL_UNUSED(egl_result_h); const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); _sapp.framebuffer_width = fb_w; _sapp.framebuffer_height = fb_h; _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; if (win_changed || fb_changed || force_update) { if (!_sapp.first_frame) { - SOKOL_LOG("SAPP_EVENTTYPE_RESIZED"); _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); } } } _SOKOL_PRIVATE void _sapp_android_cleanup(void) { - SOKOL_LOG("Cleaning up"); if (_sapp.android.surface != EGL_NO_SURFACE) { /* egl context is bound, cleanup gracefully */ if (_sapp.init_called && !_sapp.cleanup_called) { - SOKOL_LOG("cleanup_cb()"); _sapp_call_cleanup(); } } @@ -7963,6 +8040,7 @@ _SOKOL_PRIVATE void _sapp_android_frame(void) { SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + _sapp_timing_measure(&_sapp.timing); _sapp_android_update_dimensions(_sapp.android.current.window, false); _sapp_frame(); eglSwapBuffers(_sapp.android.display, _sapp.android.surface); @@ -7980,22 +8058,17 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { sapp_event_type type = SAPP_EVENTTYPE_INVALID; switch (action) { case AMOTION_EVENT_ACTION_DOWN: - SOKOL_LOG("Touch: down"); case AMOTION_EVENT_ACTION_POINTER_DOWN: - SOKOL_LOG("Touch: ptr down"); type = SAPP_EVENTTYPE_TOUCHES_BEGAN; break; case AMOTION_EVENT_ACTION_MOVE: type = SAPP_EVENTTYPE_TOUCHES_MOVED; break; case AMOTION_EVENT_ACTION_UP: - SOKOL_LOG("Touch: up"); case AMOTION_EVENT_ACTION_POINTER_UP: - SOKOL_LOG("Touch: ptr up"); type = SAPP_EVENTTYPE_TOUCHES_ENDED; break; case AMOTION_EVENT_ACTION_CANCEL: - SOKOL_LOG("Touch: cancel"); type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; break; default: @@ -8015,7 +8088,7 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; - + dst->android_tooltype = (sapp_android_tooltype) AMotionEvent_getToolType(e, (size_t)i); if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_POINTER_UP) { dst->changed = (i == idx); @@ -8043,8 +8116,10 @@ _SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { } _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { + _SOKOL_UNUSED(fd); + _SOKOL_UNUSED(data); if ((events & ALOOPER_EVENT_INPUT) == 0) { - SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event"); + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB); return 1; } SOKOL_ASSERT(_sapp.android.current.input); @@ -8063,14 +8138,15 @@ _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { } _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { + _SOKOL_UNUSED(data); if ((events & ALOOPER_EVENT_INPUT) == 0) { - SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event"); + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB); return 1; } _sapp_android_msg_t msg; if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { - SOKOL_LOG("Could not write to read_from_main_fd"); + _SAPP_ERROR(ANDROID_READ_MSG_FAILED); return 1; } @@ -8078,45 +8154,42 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { switch (msg) { case _SOKOL_ANDROID_MSG_CREATE: { - SOKOL_LOG("MSG_CREATE"); + _SAPP_INFO(ANDROID_MSG_CREATE); SOKOL_ASSERT(!_sapp.valid); bool result = _sapp_android_init_egl(); - SOKOL_ASSERT(result); + SOKOL_ASSERT(result); _SOKOL_UNUSED(result); _sapp.valid = true; _sapp.android.has_created = true; } break; case _SOKOL_ANDROID_MSG_RESUME: - SOKOL_LOG("MSG_RESUME"); + _SAPP_INFO(ANDROID_MSG_RESUME); _sapp.android.has_resumed = true; _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); break; case _SOKOL_ANDROID_MSG_PAUSE: - SOKOL_LOG("MSG_PAUSE"); + _SAPP_INFO(ANDROID_MSG_PAUSE); _sapp.android.has_resumed = false; _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); break; case _SOKOL_ANDROID_MSG_FOCUS: - SOKOL_LOG("MSG_FOCUS"); + _SAPP_INFO(ANDROID_MSG_FOCUS); _sapp.android.has_focus = true; break; case _SOKOL_ANDROID_MSG_NO_FOCUS: - SOKOL_LOG("MSG_NO_FOCUS"); + _SAPP_INFO(ANDROID_MSG_NO_FOCUS); _sapp.android.has_focus = false; break; case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: - SOKOL_LOG("MSG_SET_NATIVE_WINDOW"); + _SAPP_INFO(ANDROID_MSG_SET_NATIVE_WINDOW); if (_sapp.android.current.window != _sapp.android.pending.window) { if (_sapp.android.current.window != NULL) { _sapp_android_cleanup_egl_surface(); } if (_sapp.android.pending.window != NULL) { - SOKOL_LOG("Creating egl surface ..."); if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { - SOKOL_LOG("... ok!"); _sapp_android_update_dimensions(_sapp.android.pending.window, true); } else { - SOKOL_LOG("... failed!"); _sapp_android_shutdown(); } } @@ -8124,7 +8197,7 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _sapp.android.current.window = _sapp.android.pending.window; break; case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: - SOKOL_LOG("MSG_SET_INPUT_QUEUE"); + _SAPP_INFO(ANDROID_MSG_SET_INPUT_QUEUE); if (_sapp.android.current.input != _sapp.android.pending.input) { if (_sapp.android.current.input != NULL) { AInputQueue_detachLooper(_sapp.android.current.input); @@ -8141,13 +8214,13 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _sapp.android.current.input = _sapp.android.pending.input; break; case _SOKOL_ANDROID_MSG_DESTROY: - SOKOL_LOG("MSG_DESTROY"); + _SAPP_INFO(ANDROID_MSG_DESTROY); _sapp_android_cleanup(); _sapp.valid = false; _sapp.android.is_thread_stopping = true; break; default: - SOKOL_LOG("Unknown msg type received"); + _SAPP_WARN(ANDROID_UNKNOWN_MSG); break; } pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ @@ -8165,17 +8238,15 @@ _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { SOKOL_ASSERT(_sapp.valid); /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ if (shown) { - SOKOL_LOG("Showing keyboard"); ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); } else { - SOKOL_LOG("Hiding keyboard"); ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); } } _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { _SOKOL_UNUSED(arg); - SOKOL_LOG("Loop thread started"); + _SAPP_INFO(ANDROID_LOOP_THREAD_STARTED); _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); ALooper_addFd(_sapp.android.looper, @@ -8220,34 +8291,39 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { _sapp.android.is_thread_stopped = true; pthread_cond_broadcast(&_sapp.android.pt.cond); pthread_mutex_unlock(&_sapp.android.pt.mutex); - SOKOL_LOG("Loop thread done"); + + _SAPP_INFO(ANDROID_LOOP_THREAD_DONE); return NULL; } /* android main/ui thread */ _SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { - SOKOL_LOG("Could not write to write_from_main_fd"); + _SAPP_ERROR(ANDROID_WRITE_MSG_FAILED); } } _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onStart()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTART); } _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onResume()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONRESUME); _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); } _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { - SOKOL_LOG("NativeActivity onSaveInstanceState()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE); *out_size = 0; return NULL; } _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { - SOKOL_LOG("NativeActivity onWindowFocusChanged()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED); if (has_focus) { _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); } else { @@ -8256,12 +8332,14 @@ _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activ } _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onPause()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONPAUSE); _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); } _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onStop()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTOP); } _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { @@ -8275,12 +8353,15 @@ _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { } _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { - SOKOL_LOG("NativeActivity onNativeWindowCreated()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED); _sapp_android_msg_set_native_window(window); } _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { - SOKOL_LOG("NativeActivity onNativeWindowDestroyed()"); + _SOKOL_UNUSED(activity); + _SOKOL_UNUSED(window); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED); _sapp_android_msg_set_native_window(NULL); } @@ -8295,22 +8376,27 @@ _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { } _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { - SOKOL_LOG("NativeActivity onInputQueueCreated()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED); _sapp_android_msg_set_input_queue(queue); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { - SOKOL_LOG("NativeActivity onInputQueueDestroyed()"); + _SOKOL_UNUSED(activity); + _SOKOL_UNUSED(queue); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED); _sapp_android_msg_set_input_queue(NULL); } _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onConfigurationChanged()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED); /* see android:configChanges in manifest */ } _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onLowMemory()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY); } _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { @@ -8322,7 +8408,8 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { * However, if ANativeActivity_finish() is explicitly called from for example * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? */ - SOKOL_LOG("NativeActivity onDestroy()"); + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONDESTROY); /* send destroy msg */ pthread_mutex_lock(&_sapp.android.pt.mutex); @@ -8339,7 +8426,7 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { close(_sapp.android.pt.read_from_main_fd); close(_sapp.android.pt.write_from_main_fd); - SOKOL_LOG("NativeActivity done"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_DONE); /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ exit(0); @@ -8347,17 +8434,23 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { JNIEXPORT void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { - SOKOL_LOG("NativeActivity onCreate()"); + _SOKOL_UNUSED(saved_state); + _SOKOL_UNUSED(saved_state_size); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCREATE); + // the NativeActity pointer needs to be available inside sokol_main() + // (see https://github.com/floooh/sokol/issues/708), however _sapp_init_state() + // will clear the global _sapp_t struct, so we need to initialize the native + // activity pointer twice, once before sokol_main() and once after _sapp_init_state() + _sapp_clear(&_sapp, sizeof(_sapp)); + _sapp.android.activity = activity; sapp_desc desc = sokol_main(0, NULL); _sapp_init_state(&desc); - - /* start loop thread */ _sapp.android.activity = activity; int pipe_fd[2]; if (pipe(pipe_fd) != 0) { - SOKOL_LOG("Could not create thread pipe"); + _SAPP_ERROR(ANDROID_CREATE_THREAD_PIPE_FAILED); return; } _sapp.android.pt.read_from_main_fd = pipe_fd[0]; @@ -8405,14 +8498,20 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; activity->callbacks->onLowMemory = _sapp_android_on_low_memory; - SOKOL_LOG("NativeActivity successfully created"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS); /* NOT A BUG: do NOT call sapp_discard_state() */ } #endif /* _SAPP_ANDROID */ -/*== LINUX ==================================================================*/ +// ██ ██ ███ ██ ██ ██ ██ ██ +// ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ████ ██████ ██ ██ +// +// >>linux #if defined(_SAPP_LINUX) /* see GLFW's xkb_unicode.c */ @@ -9312,6 +9411,7 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { consistent user experience (matches Qt, Gtk, etc), although not always the most accurate one */ + bool dpi_ok = false; char* rms = XResourceManagerString(_sapp.x11.display); if (rms) { XrmDatabase db = XrmGetStringDatabase(rms); @@ -9321,13 +9421,21 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { if (type && strcmp(type, "String") == 0) { _sapp.x11.dpi = atof(value.addr); + dpi_ok = true; } } XrmDestroyDatabase(db); } } + // fallback if querying DPI had failed: assume the standard DPI 96.0f + if (!dpi_ok) { + _sapp.x11.dpi = 96.0f; + _SAPP_WARN(LINUX_X11_QUERY_SYSTEM_DPI_FAILED); + } } +#if defined(_SAPP_GLX) + _SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) { SOKOL_ASSERT(ext); const char* start = extensions; @@ -9378,7 +9486,7 @@ _SOKOL_PRIVATE void _sapp_glx_init() { } } if (!_sapp.glx.libgl) { - _sapp_fail("GLX: failed to load libGL"); + _SAPP_PANIC(LINUX_GLX_LOAD_LIBGL_FAILED); } _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); @@ -9409,17 +9517,17 @@ _SOKOL_PRIVATE void _sapp_glx_init() { !_sapp.glx.GetProcAddressARB || !_sapp.glx.GetVisualFromFBConfig) { - _sapp_fail("GLX: failed to load required entry points"); + _SAPP_PANIC(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED); } if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { - _sapp_fail("GLX: GLX extension not found"); + _SAPP_PANIC(LINUX_GLX_EXTENSION_NOT_FOUND); } if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { - _sapp_fail("GLX: Failed to query GLX version"); + _SAPP_PANIC(LINUX_GLX_QUERY_VERSION_FAILED); } if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { - _sapp_fail("GLX: GLX version 1.3 is required"); + _SAPP_PANIC(LINUX_GLX_VERSION_TOO_LOW); } const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { @@ -9462,10 +9570,10 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); if (!native_configs || !native_count) { - _sapp_fail("GLX: No GLXFBConfigs returned"); + _SAPP_PANIC(LINUX_GLX_NO_GLXFBCONFIGS); } - usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig)); + usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); usable_count = 0; for (i = 0; i < native_count; i++) { const GLXFBConfig n = native_configs[i]; @@ -9513,18 +9621,18 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { result = (GLXFBConfig) closest->handle; } XFree(native_configs); - SOKOL_FREE(usable_configs); + _sapp_free(usable_configs); return result; } _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native) { - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); } XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); if (!result) { - _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); + _SAPP_PANIC(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED); } *visual = result->visual; *depth = result->depth; @@ -9534,27 +9642,27 @@ _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { _SOKOL_PRIVATE void _sapp_glx_create_context(void) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native){ - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); } if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { - _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); + _SAPP_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING); } _sapp_x11_grab_error_handler(); const int attribs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, + GLX_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 0, 0 }; _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); if (!_sapp.glx.ctx) { - _sapp_fail("GLX: failed to create GL context"); + _SAPP_PANIC(LINUX_GLX_CREATE_CONTEXT_FAILED); } _sapp_x11_release_error_handler(); _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); if (!_sapp.glx.window) { - _sapp_fail("GLX: failed to create window"); + _SAPP_PANIC(LINUX_GLX_CREATE_WINDOW_FAILED); } } @@ -9587,9 +9695,11 @@ _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { } } +#endif /* _SAPP_GLX */ + _SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { XEvent event; - memset(&event, 0, sizeof(event)); + _sapp_clear(&event, sizeof(event)); event.type = ClientMessage; event.xclient.window = _sapp.x11.window; @@ -9646,24 +9756,77 @@ _SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { img->xhot = 0; img->yhot = 0; const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel); - memset(img->pixels, 0, num_bytes); + _sapp_clear(img->pixels, num_bytes); _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img); XcursorImageDestroy(img); } + _SOKOL_PRIVATE void _sapp_x11_create_standard_cursor(sapp_mouse_cursor cursor, const char* name, const char* theme, int size, uint32_t fallback_native) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(_sapp.x11.display); + if (theme) { + XcursorImage* img = XcursorLibraryLoadImage(name, theme, size); + if (img) { + _sapp.x11.cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); + } + } + if (0 == _sapp.x11.cursors[cursor]) { + _sapp.x11.cursors[cursor] = XCreateFontCursor(_sapp.x11.display, fallback_native); + } +} + +_SOKOL_PRIVATE void _sapp_x11_create_cursors(void) { + SOKOL_ASSERT(_sapp.x11.display); + const char* cursor_theme = XcursorGetTheme(_sapp.x11.display); + const int size = XcursorGetDefaultSize(_sapp.x11.display); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_ARROW, "default", cursor_theme, size, XC_left_ptr); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_IBEAM, "text", cursor_theme, size, XC_xterm); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_CROSSHAIR, "crosshair", cursor_theme, size, XC_crosshair); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_POINTING_HAND, "pointer", cursor_theme, size, XC_hand2); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_EW, "ew-resize", cursor_theme, size, XC_sb_h_double_arrow); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NS, "ns-resize", cursor_theme, size, XC_sb_v_double_arrow); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NWSE, "nwse-resize", cursor_theme, size, 0); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NESW, "nesw-resize", cursor_theme, size, 0); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_ALL, "all-scroll", cursor_theme, size, XC_fleur); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_NOT_ALLOWED, "no-allowed", cursor_theme, size, 0); + _sapp_x11_create_hidden_cursor(); +} + +_SOKOL_PRIVATE void _sapp_x11_destroy_cursors(void) { + SOKOL_ASSERT(_sapp.x11.display); + if (_sapp.x11.hidden_cursor) { + XFreeCursor(_sapp.x11.display, _sapp.x11.hidden_cursor); + _sapp.x11.hidden_cursor = 0; + } + for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { + if (_sapp.x11.cursors[i]) { + XFreeCursor(_sapp.x11.display, _sapp.x11.cursors[i]); + _sapp.x11.cursors[i] = 0; + } + } +} + _SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { _sapp.fullscreen = !_sapp.fullscreen; _sapp_x11_set_fullscreen(_sapp.fullscreen); _sapp_x11_query_window_size(); } -_SOKOL_PRIVATE void _sapp_x11_show_mouse(bool show) { - if (show) { - XUndefineCursor(_sapp.x11.display, _sapp.x11.window); +_SOKOL_PRIVATE void _sapp_x11_update_cursor(sapp_mouse_cursor cursor, bool shown) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (shown) { + if (_sapp.x11.cursors[cursor]) { + XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.cursors[cursor]); + } + else { + XUndefineCursor(_sapp.x11.display, _sapp.x11.window); + } } else { XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); } + XFlush(_sapp.x11.display); } _SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { @@ -9733,7 +9896,7 @@ _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_ const sapp_image_desc* img_desc = &icon_desc->images[i]; long_count += 2 + (img_desc->width * img_desc->height); } - long* icon_data = (long*) SOKOL_CALLOC((size_t)long_count, sizeof(long)); + long* icon_data = (long*) _sapp_malloc_clear((size_t)long_count * sizeof(long)); SOKOL_ASSERT(icon_data); long* dst = icon_data; for (int img_index = 0; img_index < num_images; img_index++) { @@ -9755,14 +9918,14 @@ _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_ PropModeReplace, (unsigned char*)icon_data, long_count); - SOKOL_FREE(icon_data); + _sapp_free(icon_data); XFlush(_sapp.x11.display); } _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); XSetWindowAttributes wa; - memset(&wa, 0, sizeof(wa)); + _sapp_clear(&wa, sizeof(wa)); const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; wa.colormap = _sapp.x11.colormap; wa.border_pixel = 0; @@ -9770,12 +9933,32 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | FocusChangeMask | VisibilityChangeMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + int display_width = DisplayWidth(_sapp.x11.display, _sapp.x11.screen); + int display_height = DisplayHeight(_sapp.x11.display, _sapp.x11.screen); + int window_width = _sapp.window_width; + int window_height = _sapp.window_height; + if (0 == window_width) { + window_width = (display_width * 4) / 5; + } + if (0 == window_height) { + window_height = (display_height * 4) / 5; + } + int window_xpos = (display_width - window_width) / 2; + int window_ypos = (display_height - window_height) / 2; + if (window_xpos < 0) { + window_xpos = 0; + } + if (window_ypos < 0) { + window_ypos = 0; + } _sapp_x11_grab_error_handler(); _sapp.x11.window = XCreateWindow(_sapp.x11.display, _sapp.x11.root, - 0, 0, - (uint32_t)_sapp.window_width, - (uint32_t)_sapp.window_height, + window_xpos, + window_ypos, + (uint32_t)window_width, + (uint32_t)window_height, 0, /* border width */ depth, /* color depth */ InputOutput, @@ -9784,7 +9967,7 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { &wa); _sapp_x11_release_error_handler(); if (!_sapp.x11.window) { - _sapp_fail("X11: Failed to create window"); + _SAPP_PANIC(LINUX_X11_CREATE_WINDOW_FAILED); } Atom protocols[] = { _sapp.x11.WM_DELETE_WINDOW @@ -9792,8 +9975,12 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); XSizeHints* hints = XAllocSizeHints(); - hints->flags |= PWinGravity; + hints->flags = (PWinGravity | PPosition | PSize); hints->win_gravity = StaticGravity; + hints->x = window_xpos; + hints->y = window_ypos; + hints->width = window_width; + hints->height = window_height; XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); XFree(hints); @@ -9802,8 +9989,8 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { const Atom version = _SAPP_X11_XDND_VERSION; XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); } - _sapp_x11_update_window_title(); + _sapp_x11_query_window_size(); } _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { @@ -9943,6 +10130,20 @@ _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) } } +_SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y) { + if (!_sapp.mouse.locked) { + const float new_x = (float) x; + const float new_y = (float) y; + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { if (_sapp_events_enabled()) { _sapp_init_event(type); @@ -10192,7 +10393,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { ((src_count == 6) && (src_chr != '/')) || ((src_count == 7) && (src_chr != '/'))) { - SOKOL_LOG("sokol_app.h: dropped file URI doesn't start with file://"); + _SAPP_ERROR(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME); err = true; break; } @@ -10201,7 +10402,6 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { // skip } else if (src_chr == '\n') { - src_chr = 0; src_count = 0; _sapp.drop.num_files++; // too many files is not an error @@ -10212,7 +10412,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); } else if ((src_chr == '%') && src[0] && src[1]) { - // a percent-encoded byte (most like UTF-8 multibyte sequence) + // a percent-encoded byte (most likely UTF-8 multibyte sequence) const char digits[3] = { src[0], src[1], 0 }; src += 2; dst_chr = (char) strtol(digits, 0, 16); @@ -10226,7 +10426,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { *dst_ptr++ = dst_chr; } else { - SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); err = true; break; } @@ -10273,7 +10473,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } break; case FocusIn: - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); } @@ -10283,7 +10483,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { if (_sapp.mouse.locked) { _sapp_x11_lock_mouse(false); } - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); } @@ -10323,6 +10523,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case ButtonPress: { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y); const sapp_mousebutton btn = _sapp_x11_translate_button(event); uint32_t mods = _sapp_x11_mods(event->xbutton.state); // X11 doesn't set modifier bit on button down, so emulate that @@ -10344,6 +10545,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case ButtonRelease: { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y); const sapp_mousebutton btn = _sapp_x11_translate_button(event); if (btn != SAPP_MOUSEBUTTON_INVALID) { uint32_t mods = _sapp_x11_mods(event->xbutton.state); @@ -10357,25 +10559,19 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { case EnterNotify: /* don't send enter/leave events while mouse button held down */ if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); } break; case LeaveNotify: if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); } break; case MotionNotify: if (!_sapp.mouse.locked) { - const float new_x = (float) event->xmotion.x; - const float new_y = (float) event->xmotion.y; - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; + _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); } break; @@ -10459,9 +10655,9 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } else if (_sapp.x11.xdnd.version >= 2) { XEvent reply; - memset(&reply, 0, sizeof(reply)); + _sapp_clear(&reply, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; + reply.xclient.window = _sapp.x11.xdnd.source; reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; reply.xclient.format = 32; reply.xclient.data.l[0] = (long)_sapp.x11.window; @@ -10480,7 +10676,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { return; } XEvent reply; - memset(&reply, 0, sizeof(reply)); + _sapp_clear(&reply, sizeof(reply)); reply.type = ClientMessage; reply.xclient.window = _sapp.x11.xdnd.source; reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; @@ -10514,9 +10710,9 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } if (_sapp.x11.xdnd.version >= 2) { XEvent reply; - memset(&reply, 0, sizeof(reply)); + _sapp_clear(&reply, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; + reply.xclient.window = _sapp.x11.xdnd.source; reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; reply.xclient.format = 32; reply.xclient.data.l[0] = (long)_sapp.x11.window; @@ -10532,6 +10728,147 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } } +#if !defined(_SAPP_GLX) + +_SOKOL_PRIVATE void _sapp_egl_init(void) { +#if defined(SOKOL_GLCORE33) + if (!eglBindAPI(EGL_OPENGL_API)) { + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); + } +#else + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED); + } +#endif + + _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display); + if (EGL_NO_DISPLAY == _sapp.egl.display) { + _SAPP_PANIC(LINUX_EGL_GET_DISPLAY_FAILED); + } + + EGLint major, minor; + if (!eglInitialize(_sapp.egl.display, &major, &minor)) { + _SAPP_PANIC(LINUX_EGL_INITIALIZE_FAILED); + } + + EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0; + EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; + const EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + #if defined(SOKOL_GLCORE33) + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #elif defined(SOKOL_GLES3) + EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT, + #else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + #endif + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, alpha_size, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_SAMPLE_BUFFERS, _sapp.desc.sample_count > 1 ? 1 : 0, + EGL_SAMPLES, sample_count, + EGL_NONE, + }; + + EGLConfig egl_configs[32]; + EGLint config_count; + if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) { + _SAPP_PANIC(LINUX_EGL_NO_CONFIGS); + } + + EGLConfig config = egl_configs[0]; + for (int i = 0; i < config_count; ++i) { + EGLConfig c = egl_configs[i]; + EGLint r, g, b, a, d, s, n; + if (eglGetConfigAttrib(_sapp.egl.display, c, EGL_RED_SIZE, &r) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_GREEN_SIZE, &g) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_BLUE_SIZE, &b) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_ALPHA_SIZE, &a) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_DEPTH_SIZE, &d) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_STENCIL_SIZE, &s) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_SAMPLES, &n) && + (r == 8) && (g == 8) && (b == 8) && (a == alpha_size) && (d == 24) && (s == 8) && (n == sample_count)) { + config = c; + break; + } + } + + EGLint visual_id; + if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) { + _SAPP_PANIC(LINUX_EGL_NO_NATIVE_VISUAL); + } + + XVisualInfo visual_info_template; + _sapp_clear(&visual_info_template, sizeof(visual_info_template)); + visual_info_template.visualid = (VisualID)visual_id; + + int num_visuals; + XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals); + if (!visual_info) { + _SAPP_PANIC(LINUX_EGL_GET_VISUAL_INFO_FAILED); + } + + _sapp_x11_create_window(visual_info->visual, visual_info->depth); + XFree(visual_info); + + _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL); + if (EGL_NO_SURFACE == _sapp.egl.surface) { + _SAPP_PANIC(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED); + } + + EGLint ctx_attrs[] = { + #if defined(SOKOL_GLCORE33) + EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl_major_version, + EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + #elif defined(SOKOL_GLES3) + EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, + #else + EGL_CONTEXT_CLIENT_VERSION, 2, + #endif + EGL_NONE, + }; + + _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs); + if (EGL_NO_CONTEXT == _sapp.egl.context) { + _SAPP_PANIC(LINUX_EGL_CREATE_CONTEXT_FAILED); + } + + if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) { + _SAPP_PANIC(LINUX_EGL_MAKE_CURRENT_FAILED); + } + + eglSwapInterval(_sapp.egl.display, _sapp.swap_interval); + +#if defined(SOKOL_GLES3) + _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; +#endif +} + +_SOKOL_PRIVATE void _sapp_egl_destroy(void) { + if (_sapp.egl.display != EGL_NO_DISPLAY) { + eglMakeCurrent(_sapp.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (_sapp.egl.context != EGL_NO_CONTEXT) { + eglDestroyContext(_sapp.egl.display, _sapp.egl.context); + _sapp.egl.context = EGL_NO_CONTEXT; + } + + if (_sapp.egl.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.egl.display, _sapp.egl.surface); + _sapp.egl.surface = EGL_NO_SURFACE; + } + + eglTerminate(_sapp.egl.display); + _sapp.egl.display = EGL_NO_DISPLAY; + } +} + +#endif /* _SAPP_GLX */ + _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { /* The following lines are here to trigger a linker error instead of an obscure runtime error if the user has forgotten to add -pthread to @@ -10548,7 +10885,7 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { XrmInitialize(); _sapp.x11.display = XOpenDisplay(NULL); if (!_sapp.x11.display) { - _sapp_fail("XOpenDisplay() failed!\n"); + _SAPP_PANIC(LINUX_X11_OPEN_DISPLAY_FAILED); } _sapp.x11.screen = DefaultScreen(_sapp.x11.display); _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); @@ -10556,24 +10893,28 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp_x11_query_system_dpi(); _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; _sapp_x11_init_extensions(); - _sapp_x11_create_hidden_cursor(); + _sapp_x11_create_cursors(); +#if defined(_SAPP_GLX) _sapp_glx_init(); Visual* visual = 0; int depth = 0; _sapp_glx_choose_visual(&visual, &depth); _sapp_x11_create_window(visual, depth); _sapp_glx_create_context(); + _sapp_glx_swapinterval(_sapp.swap_interval); +#else + _sapp_egl_init(); +#endif sapp_set_icon(&desc->icon); _sapp.valid = true; _sapp_x11_show_window(); if (_sapp.fullscreen) { _sapp_x11_set_fullscreen(true); } - _sapp_x11_query_window_size(); - _sapp_glx_swapinterval(_sapp.swap_interval); + XFlush(_sapp.x11.display); while (!_sapp.quit_ordered) { - _sapp_glx_make_current(); + _sapp_timing_measure(&_sapp.timing); int count = XPending(_sapp.x11.display); while (count--) { XEvent event; @@ -10581,7 +10922,11 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp_x11_process_event(&event); } _sapp_frame(); +#if defined(_SAPP_GLX) _sapp_glx_swap_buffers(); +#else + eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface); +#endif XFlush(_sapp.x11.display); /* handle quit-requested, either from window or from sapp_request_quit() */ if (_sapp.quit_requested && !_sapp.quit_ordered) { @@ -10594,8 +10939,13 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { } } _sapp_call_cleanup(); +#if defined(_SAPP_GLX) _sapp_glx_destroy_context(); +#else + _sapp_egl_destroy(); +#endif _sapp_x11_destroy_window(); + _sapp_x11_destroy_cursors(); XCloseDisplay(_sapp.x11.display); _sapp_discard_state(); } @@ -10609,7 +10959,13 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ #endif /* _SAPP_LINUX */ -/*== PUBLIC API FUNCTIONS ====================================================*/ +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public #if defined(SOKOL_NO_ENTRY) SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { SOKOL_ASSERT(desc); @@ -10621,13 +10977,10 @@ SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { _sapp_emsc_run(desc); #elif defined(_SAPP_WIN32) _sapp_win32_run(desc); - #elif defined(_SAPP_UWP) - _sapp_uwp_run(desc); #elif defined(_SAPP_LINUX) _sapp_linux_run(desc); #else - // calling sapp_run() directly is not supported on Android) - _sapp_fail("sapp_run() not supported on this platform!"); + #error "sapp_run() not supported on this platform" #endif } @@ -10636,7 +10989,7 @@ sapp_desc sokol_main(int argc, char* argv[]) { _SOKOL_UNUSED(argc); _SOKOL_UNUSED(argv); sapp_desc desc; - memset(&desc, 0, sizeof(desc)); + _sapp_clear(&desc, sizeof(desc)); return desc; } #else @@ -10662,6 +11015,10 @@ SOKOL_API_IMPL uint64_t sapp_frame_count(void) { return _sapp.frame_count; } +SOKOL_API_IMPL double sapp_frame_duration(void) { + return _sapp_timing_get_avg(&_sapp.timing); +} + SOKOL_API_IMPL int sapp_width(void) { return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; } @@ -10712,6 +11069,28 @@ SOKOL_API_IMPL float sapp_dpi_scale(void) { return _sapp.dpi_scale; } +SOKOL_APP_IMPL const void* sapp_egl_get_display(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANDROID) + return _sapp.android.display; + #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) + return _sapp.egl.display; + #else + return 0; + #endif +} + +SOKOL_APP_IMPL const void* sapp_egl_get_context(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANDROID) + return _sapp.android.context; + #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) + return _sapp.egl.context; + #else + return 0; + #endif +} + SOKOL_API_IMPL bool sapp_gles2(void) { return _sapp.gles2_fallback; } @@ -10741,8 +11120,6 @@ SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { _sapp_macos_toggle_fullscreen(); #elif defined(_SAPP_WIN32) _sapp_win32_toggle_fullscreen(); - #elif defined(_SAPP_UWP) - _sapp_uwp_toggle_fullscreen(); #elif defined(_SAPP_LINUX) _sapp_x11_toggle_fullscreen(); #endif @@ -10752,13 +11129,13 @@ SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { SOKOL_API_IMPL void sapp_show_mouse(bool show) { if (_sapp.mouse.shown != show) { #if defined(_SAPP_MACOS) - _sapp_macos_show_mouse(show); + _sapp_macos_update_cursor(_sapp.mouse.current_cursor, show); #elif defined(_SAPP_WIN32) - _sapp_win32_show_mouse(show); + _sapp_win32_update_cursor(_sapp.mouse.current_cursor, show, false); #elif defined(_SAPP_LINUX) - _sapp_x11_show_mouse(show); - #elif defined(_SAPP_UWP) - _sapp_uwp_show_mouse(show); + _sapp_x11_update_cursor(_sapp.mouse.current_cursor, show); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_update_cursor(_sapp.mouse.current_cursor, show); #endif _sapp.mouse.shown = show; } @@ -10786,6 +11163,26 @@ SOKOL_API_IMPL bool sapp_mouse_locked(void) { return _sapp.mouse.locked; } +SOKOL_API_IMPL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (_sapp.mouse.current_cursor != cursor) { + #if defined(_SAPP_MACOS) + _sapp_macos_update_cursor(cursor, _sapp.mouse.shown); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_cursor(cursor, _sapp.mouse.shown, false); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_cursor(cursor, _sapp.mouse.shown); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_update_cursor(cursor, _sapp.mouse.shown); + #endif + _sapp.mouse.current_cursor = cursor; + } +} + +SOKOL_API_IMPL sapp_mouse_cursor sapp_get_mouse_cursor(void) { + return _sapp.mouse.current_cursor; +} + SOKOL_API_IMPL void sapp_request_quit(void) { _sapp.quit_requested = true; } @@ -10804,7 +11201,6 @@ SOKOL_API_IMPL void sapp_consume_event(void) { /* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */ SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { - SOKOL_ASSERT(_sapp.clipboard.enabled); if (!_sapp.clipboard.enabled) { return; } @@ -10822,7 +11218,6 @@ SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { } SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.enabled); if (!_sapp.clipboard.enabled) { return ""; } @@ -10914,15 +11309,15 @@ SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request SOKOL_ASSERT(_sapp.drop.enabled); SOKOL_ASSERT(request); SOKOL_ASSERT(request->callback); - SOKOL_ASSERT(request->buffer_ptr); - SOKOL_ASSERT(request->buffer_size > 0); + SOKOL_ASSERT(request->buffer.ptr); + SOKOL_ASSERT(request->buffer.size > 0); #if defined(_SAPP_EMSCRIPTEN) const int index = request->dropped_file_index; sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR; if ((index < 0) || (index >= _sapp.drop.num_files)) { error_code = SAPP_HTML5_FETCH_ERROR_OTHER; } - if (sapp_html5_get_dropped_file_size(index) > request->buffer_size) { + if (sapp_html5_get_dropped_file_size(index) > request->buffer.size) { error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL; } if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) { @@ -10931,15 +11326,15 @@ SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request (int)error_code, request->callback, 0, // fetched_size - request->buffer_ptr, - request->buffer_size, + (void*)request->buffer.ptr, + request->buffer.size, request->user_data); } else { sapp_js_fetch_dropped_file(index, request->callback, - request->buffer_ptr, - request->buffer_size, + (void*)request->buffer.ptr, + request->buffer.size, request->user_data); } #else @@ -11118,7 +11513,8 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) { } SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { - SOKOL_ASSERT(_sapp.valid); + // NOTE: _sapp.valid is not asserted here because sapp_android_get_native_activity() + // needs to be callable from within sokol_main() (see: https://github.com/floooh/sokol/issues/708) #if defined(_SAPP_ANDROID) return (void*)_sapp.android.activity; #else diff --git a/3rdparty/sokol/sokol_args.h b/3rdparty/sokol/sokol_args.h index e3639a8..4d37907 100644 --- a/3rdparty/sokol/sokol_args.h +++ b/3rdparty/sokol/sokol_args.h @@ -16,9 +16,6 @@ Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_LOG(msg) - your own logging functions (default: puts(msg)) - SOKOL_CALLOC(n,s) - your own calloc() implementation (default: calloc(n,s)) - SOKOL_FREE(p) - your own free() implementation (default: free(p)) SOKOL_ARGS_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_ARGS_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) @@ -218,6 +215,36 @@ Return the value of argument at index. Returns empty string if index is outside range. + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sargs_setup(&(sargs_desc){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_args.h + itself though, not any allocations in OS libraries. + TODO ==== - parsing errors? @@ -251,6 +278,7 @@ #define SOKOL_ARGS_INCLUDED (1) #include #include +#include // size_t #if defined(SOKOL_API_DECL) && !defined(SOKOL_ARGS_API_DECL) #define SOKOL_ARGS_API_DECL SOKOL_API_DECL @@ -269,11 +297,26 @@ extern "C" { #endif +/* + sargs_allocator + + Used in sargs_desc to provide custom memory-alloc and -free functions + 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 + override one function but not the other). +*/ +typedef struct sargs_allocator { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sargs_allocator; + typedef struct sargs_desc { int argc; char** argv; int max_args; int buf_size; + sargs_allocator allocator; } sargs_desc; /* setup sokol-args */ @@ -313,7 +356,13 @@ inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); } /*--- IMPLEMENTATION ---------------------------------------------------------*/ #ifdef SOKOL_ARGS_IMPL #define SOKOL_ARGS_IMPL_INCLUDED (1) -#include /* memset, strcmp */ + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sargs_desc.allocator to override memory allocation functions" +#endif + +#include // memset, strcmp +#include // malloc, free #if defined(__EMSCRIPTEN__) #include @@ -324,30 +373,13 @@ inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); } #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#if !defined(SOKOL_CALLOC) && !defined(SOKOL_FREE) - #include -#endif -#if !defined(SOKOL_CALLOC) - #define SOKOL_CALLOC(n,s) calloc(n,s) -#endif -#if !defined(SOKOL_FREE) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) @@ -388,10 +420,43 @@ typedef struct { uint32_t parse_state; char quote; /* current quote char, 0 if not in a quote */ bool in_escape; /* currently in an escape sequence */ + sargs_allocator allocator; } _sargs_state_t; static _sargs_state_t _sargs; /*== PRIVATE IMPLEMENTATION FUNCTIONS ========================================*/ +_SOKOL_PRIVATE void _sargs_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sargs_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sargs.allocator.alloc) { + ptr = _sargs.allocator.alloc(size, _sargs.allocator.user_data); + } + else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +_SOKOL_PRIVATE void* _sargs_malloc_clear(size_t size) { + void* ptr = _sargs_malloc(size); + _sargs_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sargs_free(void* ptr) { + if (_sargs.allocator.free) { + _sargs.allocator.free(ptr, _sargs.allocator.user_data); + } + else { + free(ptr); + } +} _SOKOL_PRIVATE void _sargs_putc(char c) { if ((_sargs.buf_pos+2) < _sargs.buf_size) { @@ -617,6 +682,11 @@ _SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) { #ifdef __cplusplus extern "C" { #endif + +#if defined(EM_JS_DEPS) +EM_JS_DEPS(sokol_audio, "$withStackSave,$allocateUTF8OnStack"); +#endif + EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) { SOKOL_ASSERT(_sargs.valid && key && val); if (_sargs.num_args >= _sargs.max_args) { @@ -648,11 +718,15 @@ EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) { /* JS function to extract arguments from the page URL */ EM_JS(void, sargs_js_parse_url, (void), { - var params = new URLSearchParams(window.location.search).entries(); - for (var p = params.next(); !p.done; p = params.next()) { - var key = p.value[0]; - var val = p.value[1]; - var res = ccall('_sargs_add_kvp', 'void', ['string','string'], [key,val]); + const params = new URLSearchParams(window.location.search).entries(); + for (let p = params.next(); !p.done; p = params.next()) { + const key = p.value[0]; + const val = p.value[1]; + withStackSave(() => { + const key_cstr = allocateUTF8OnStack(key); + const val_cstr = allocateUTF8OnStack(val); + __sargs_add_kvp(key_cstr, val_cstr) + }); } }); @@ -661,14 +735,15 @@ EM_JS(void, sargs_js_parse_url, (void), { /*== PUBLIC IMPLEMENTATION FUNCTIONS =========================================*/ SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) { SOKOL_ASSERT(desc); - memset(&_sargs, 0, sizeof(_sargs)); + _sargs_clear(&_sargs, sizeof(_sargs)); _sargs.max_args = _sargs_def(desc->max_args, _SARGS_MAX_ARGS_DEF); _sargs.buf_size = _sargs_def(desc->buf_size, _SARGS_BUF_SIZE_DEF); SOKOL_ASSERT(_sargs.buf_size > 8); - _sargs.args = (_sargs_kvp_t*) SOKOL_CALLOC((size_t)_sargs.max_args, sizeof(_sargs_kvp_t)); - _sargs.buf = (char*) SOKOL_CALLOC((size_t)_sargs.buf_size, sizeof(char)); + _sargs.args = (_sargs_kvp_t*) _sargs_malloc_clear((size_t)_sargs.max_args * sizeof(_sargs_kvp_t)); + _sargs.buf = (char*) _sargs_malloc_clear((size_t)_sargs.buf_size * sizeof(char)); /* the first character in buf is reserved and always zero, this is the 'empty string' */ _sargs.buf_pos = 1; + _sargs.allocator = desc->allocator; _sargs.valid = true; /* parse argc/argv */ @@ -683,11 +758,11 @@ SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) { SOKOL_API_IMPL void sargs_shutdown(void) { SOKOL_ASSERT(_sargs.valid); if (_sargs.args) { - SOKOL_FREE(_sargs.args); + _sargs_free(_sargs.args); _sargs.args = 0; } if (_sargs.buf) { - SOKOL_FREE(_sargs.buf); + _sargs_free(_sargs.buf); _sargs.buf = 0; } _sargs.valid = false; diff --git a/3rdparty/sokol/sokol_audio.h b/3rdparty/sokol/sokol_audio.h index faea817..bdd6953 100644 --- a/3rdparty/sokol/sokol_audio.h +++ b/3rdparty/sokol/sokol_audio.h @@ -17,9 +17,6 @@ SOKOL_DUMMY_BACKEND - use a dummy backend SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) - SOKOL_MALLOC(s) - your own malloc() implementation (default: malloc(s)) - SOKOL_FREE(p) - your own free() implementation (default: free(p)) SOKOL_AUDIO_API_DECL- public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) @@ -27,6 +24,8 @@ SAUDIO_RING_MAX_SLOTS - max number of slots in the push-audio ring buffer (default 1024) SAUDIO_OSX_USE_SYSTEM_HEADERS - define this to force inclusion of system headers on macOS instead of using embedded CoreAudio declarations + SAUDIO_ANDROID_AAUDIO - on Android, select the AAudio backend (default) + SAUDIO_ANDROID_SLES - on Android, select the OpenSLES backend If sokol_audio.h is compiled as a DLL, define the following before including the declaration or implementation: @@ -41,7 +40,7 @@ - on macOS: AudioToolbox - on iOS: AudioToolbox, AVFoundation - on Linux: asound - - on Android: link with OpenSLES + - on Android: link with OpenSLES or aaudio - on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' and link with -lole32 @@ -55,7 +54,7 @@ - macOS: CoreAudio - iOS: CoreAudio+AVAudioSession - emscripten: WebAudio with ScriptProcessorNode - - Android: OpenSLES + - Android: AAudio (default) or OpenSLES, select at build time Sokol Audio will not do any buffer mixing or volume control, if you have multiple independent input streams of sample data you need to perform the @@ -133,18 +132,30 @@ a good balance between low-latency and glitch-free playback on all audio backends. + You should always provide a logging callback to be aware of any + warnings and errors. The easiest way is to use sokol_log.h for this: + + #include "sokol_log.h" + // ... + saudio_setup(&(saudio_desc){ + .logger = { + .func = slog_func, + } + }); + If you want to use the callback-model, you need to provide a stream callback function either in saudio_desc.stream_cb or saudio_desc.stream_userdata_cb, otherwise keep both function pointers zero-initialized. Use push model and default playback parameters: - saudio_setup(&(saudio_desc){0}); + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); Use stream callback model and default playback parameters: saudio_setup(&(saudio_desc){ .stream_cb = my_stream_callback + .logger.func = slog_func, }); The standard stream callback doesn't have a user data argument, if you want @@ -153,6 +164,7 @@ saudio_setup(&(saudio_desc){ .stream_userdata_cb = my_stream_callback, .user_data = &my_data + .logger.func = slog_func, }); The following playback parameters can be provided through the @@ -316,6 +328,8 @@ "Blob URLs": https://www.html5rocks.com/en/tutorials/workers/basics/ + Also see: https://blog.paul.cx/post/a-wait-free-spsc-ringbuffer-for-the-web/ + THE COREAUDIO BACKEND ===================== The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined). @@ -365,6 +379,78 @@ header must be present (usually both are installed with some sort of ALSA development package). + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + saudio_setup(&(saudio_desc){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_audio.h + itself though, not any allocations in OS libraries. + + Memory allocation will only happen on the same thread where saudio_setup() + was called, so you don't need to worry about thread-safety. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'saudio' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAUDIO_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_audio.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-audio like this: + + saudio_setup(&(saudio_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + LICENSE ======= @@ -392,6 +478,7 @@ distribution. */ #define SOKOL_AUDIO_INCLUDED (1) +#include // size_t #include #include @@ -412,15 +499,102 @@ extern "C" { #endif +/* + saudio_log_item + + Log items are defined via X-Macros, and expanded to an + enum 'saudio_log_item', and in debug mode only, + corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SAUDIO_LOG_ITEMS \ + _SAUDIO_LOGITEM_XMACRO(OK, "Ok") \ + _SAUDIO_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_OPEN_FAILED, "snd_pcm_open() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED, "floating point sample format not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED, "requested buffer size not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED, "requested channel count not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED, "snd_pcm_hw_params_set_rate_near() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_HW_PARAMS_FAILED, "snd_pcm_hw_params() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_PTHREAD_CREATE_FAILED, "pthread_create() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_EVENT_FAILED, "CreateEvent() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED, "CoCreateInstance() for IMMDeviceEnumerator failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED, "IMMDeviceEnumerator.GetDefaultAudioEndpoint() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_DEVICE_ACTIVATE_FAILED, "IMMDevice.Activate() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED, "IAudioClient.Initialize() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED, "IAudioClient.GetBufferSize() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED, "IAudioClient.GetService() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED, "IAudioClient.SetEventHandle() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_THREAD_FAILED, "CreateThread() failed") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED, "AAudioStreamBuilder_openStream() failed") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_PTHREAD_CREATE_FAILED, "pthread_create() failed after AAUDIO_ERROR_DISCONNECTED") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_RESTARTING_STREAM_AFTER_ERROR, "restarting AAudio stream after error") \ + _SAUDIO_LOGITEM_XMACRO(USING_AAUDIO_BACKEND, "using AAudio backend") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_CREATE_STREAMBUILDER_FAILED, "AAudio_createStreamBuilder() failed") \ + _SAUDIO_LOGITEM_XMACRO(USING_SLES_BACKEND, "using OpenSLES backend") \ + _SAUDIO_LOGITEM_XMACRO(SLES_CREATE_ENGINE_FAILED, "slCreateEngine() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED, "GetInterface() for SL_IID_ENGINE failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_CREATE_OUTPUT_MIX_FAILED, "CreateOutputMix() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED, "GetInterface() for SL_IID_VOLUME failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED, "CreateAudioPlayer() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED, "GetInterface() for SL_IID_PLAY failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED, "GetInterface() for SL_IID_VOLUME failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED, "GetInterface() for SL_IID_ANDROIDSIMPLEBUFFERQUEUE failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_NEW_OUTPUT_FAILED, "AudioQueueNewOutput() failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_ALLOCATE_BUFFER_FAILED, "AudioQueueAllocateBuffer() failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_START_FAILED, "AudioQueueStart() failed") \ + _SAUDIO_LOGITEM_XMACRO(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE, "backend buffer size isn't multiple of packet size") \ + +#define _SAUDIO_LOGITEM_XMACRO(item,msg) SAUDIO_LOGITEM_##item, +typedef enum saudio_log_item { + _SAUDIO_LOG_ITEMS +} saudio_log_item; +#undef _SAUDIO_LOGITEM_XMACRO + +/* + saudio_logger + + Used in saudio_desc to provide a custom logging and error reporting + callback to sokol-audio. +*/ +typedef struct saudio_logger { + void (*func)( + const char* tag, // always "saudio" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAUDIO_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_audio.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} saudio_logger; + +/* + saudio_allocator + + Used in saudio_desc to provide custom memory-alloc and -free functions + 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 + override one function but not the other). +*/ +typedef struct saudio_allocator { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} saudio_allocator; + typedef struct saudio_desc { - int sample_rate; /* requested sample rate */ - int num_channels; /* number of channels, default: 1 (mono) */ - int buffer_frames; /* number of frames in streaming buffer */ - int packet_frames; /* number of frames in a packet */ - int num_packets; /* number of packets in packet queue */ - void (*stream_cb)(float* buffer, int num_frames, int num_channels); /* optional streaming callback (no user data) */ - void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); /*... and with user data */ - void* user_data; /* optional user data argument for stream_userdata_cb */ + int sample_rate; // requested sample rate + int num_channels; // number of channels, default: 1 (mono) + int buffer_frames; // number of frames in streaming buffer + int packet_frames; // number of frames in a packet + int num_packets; // number of packets in packet queue + void (*stream_cb)(float* buffer, int num_frames, int num_channels); // optional streaming callback (no user data) + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); //... and with user data + void* user_data; // optional user data argument for stream_userdata_cb + saudio_allocator allocator; // optional allocation override functions + saudio_logger logger; // optional logging function (default: NO LOGGING!) } saudio_desc; /* setup sokol-audio */ @@ -439,6 +613,8 @@ SOKOL_AUDIO_API_DECL int saudio_sample_rate(void); SOKOL_AUDIO_API_DECL int saudio_buffer_frames(void); /* actual number of channels */ SOKOL_AUDIO_API_DECL int saudio_channels(void); +/* return true if audio context is currently suspended (only in WebAudio backend, all other backends return false) */ +SOKOL_AUDIO_API_DECL bool saudio_suspended(void); /* get current number of frames to fill packet queue */ SOKOL_AUDIO_API_DECL int saudio_expect(void); /* push sample frames from main thread, returns number of frames actually pushed */ @@ -453,9 +629,21 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #endif #endif // SOKOL_AUDIO_INCLUDED -/*=== IMPLEMENTATION =========================================================*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_AUDIO_IMPL #define SOKOL_AUDIO_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use saudio_desc.allocator to override memory allocation functions" +#endif + +#include // alloc, free #include // memset, memcpy #include // size_t @@ -464,26 +652,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) @@ -509,17 +684,18 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #define _SAUDIO_MACOS (1) #endif #elif defined(__EMSCRIPTEN__) - #define _SAUDIO_EMSCRIPTEN + #define _SAUDIO_EMSCRIPTEN (1) #elif defined(_WIN32) #define _SAUDIO_WINDOWS (1) #include #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #define _SAUDIO_UWP (1) - #else - #define _SAUDIO_WIN32 (1) + #error "sokol_audio.h no longer supports UWP" #endif #elif defined(__ANDROID__) #define _SAUDIO_ANDROID (1) + #if !defined(SAUDIO_ANDROID_SLES) && !defined(SAUDIO_ANDROID_AAUDIO) + #define SAUDIO_ANDROID_AAUDIO (1) + #endif #elif defined(__linux__) || defined(__unix__) #define _SAUDIO_LINUX (1) #else @@ -539,12 +715,8 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #endif #include #include - #if defined(_SAUDIO_UWP) - #pragma comment (lib, "WindowsApp") - #else - #pragma comment (lib, "kernel32") - #pragma comment (lib, "ole32") - #endif + #pragma comment (lib, "kernel32") + #pragma comment (lib, "ole32") #ifndef CINTERFACE #define CINTERFACE #endif @@ -556,12 +728,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #endif #include #include - static const IID _saudio_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; - static const IID _saudio_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, { 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } }; - static const CLSID _saudio_CLSID_IMMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, { 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } }; - static const IID _saudio_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; - static const IID _saudio_IID_Devinterface_Audio_Render = { 0xe6327cad, 0xdcec, 0x4949, {0xae, 0x8a, 0x99, 0x1e, 0x97, 0x6a, 0x79, 0xd2 } }; + static const IID _saudio_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2} }; + static const IID _saudio_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, {0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6} }; + static const CLSID _saudio_CLSID_IMMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, {0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e} }; + static const IID _saudio_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2} }; + static const IID _saudio_IID_Devinterface_Audio_Render = { 0xe6327cad, 0xdcec, 0x4949, {0xae, 0x8a, 0x99, 0x1e, 0x97, 0x6a, 0x79, 0xd2} }; static const IID _saudio_IID_IActivateAudioInterface_Completion_Handler = { 0x94ea2b94, 0xe9cc, 0x49e0, {0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90} }; + static const GUID _saudio_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} }; #if defined(__cplusplus) #define _SOKOL_AUDIO_WIN32COM_ID(x) (x) #else @@ -601,8 +774,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #elif defined(_SAUDIO_ANDROID) #define _SAUDIO_PTHREADS (1) #include - #include "SLES/OpenSLES_Android.h" + #if defined(SAUDIO_ANDROID_SLES) + #include "SLES/OpenSLES_Android.h" + #elif defined(SAUDIO_ANDROID_AAUDIO) + #include "aaudio/AAudio.h" + #endif #elif defined(_SAUDIO_LINUX) + #include #define _SAUDIO_PTHREADS (1) #include #define ALSA_PCM_NEW_HW_PARAMS_API @@ -624,7 +802,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #define SAUDIO_RING_MAX_SLOTS (1024) #endif -/*=== MUTEX WRAPPER DECLARATIONS =============================================*/ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs #if defined(_SAUDIO_PTHREADS) typedef struct { @@ -645,14 +829,12 @@ typedef struct { #endif -/*=== DUMMY BACKEND DECLARATIONS =============================================*/ #if defined(SOKOL_DUMMY_BACKEND) typedef struct { - int dummy_backend; -} _saudio_backend_t; + int dummy; +} _saudio_dummy_backend_t; -/*=== COREAUDIO BACKEND DECLARATIONS =========================================*/ #elif defined(_SAUDIO_APPLE) #if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) @@ -667,6 +849,9 @@ typedef OSStatus _saudio_OSStatus; #define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked) #else +#ifdef __cplusplus +extern "C" { +#endif // embedded AudioToolbox declarations typedef uint32_t _saudio_AudioFormatID; @@ -742,6 +927,11 @@ extern _saudio_OSStatus AudioQueueAllocateBuffer(_saudio_AudioQueueRef inAQ, uin extern _saudio_OSStatus AudioQueueEnqueueBuffer(_saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer, uint32_t inNumPacketDescs, const _saudio_AudioStreamPacketDescription* inPacketDescs); extern _saudio_OSStatus AudioQueueStart(_saudio_AudioQueueRef inAQ, const _saudio_AudioTimeStamp * inStartTime); extern _saudio_OSStatus AudioQueueStop(_saudio_AudioQueueRef inAQ, bool inImmediate); + +#ifdef __cplusplus +} // extern "C" +#endif + #endif // SAUDIO_OSX_USE_SYSTEM_HEADERS typedef struct { @@ -749,9 +939,8 @@ typedef struct { #if defined(_SAUDIO_IOS) id ca_interruption_handler; #endif -} _saudio_backend_t; +} _saudio_apple_backend_t; -/*=== ALSA BACKEND DECLARATIONS ==============================================*/ #elif defined(_SAUDIO_LINUX) typedef struct { @@ -761,18 +950,17 @@ typedef struct { int buffer_frames; pthread_t thread; bool thread_stop; -} _saudio_backend_t; +} _saudio_alsa_backend_t; -/*=== OpenSLES BACKEND DECLARATIONS ==============================================*/ -#elif defined(_SAUDIO_ANDROID) +#elif defined(SAUDIO_ANDROID_SLES) -#define SAUDIO_NUM_BUFFERS 2 +#define SAUDIO_SLES_NUM_BUFFERS (2) typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; int count; -} _saudio_semaphore_t; +} _saudio_sles_semaphore_t; typedef struct { SLObjectItf engine_obj; @@ -786,16 +974,24 @@ typedef struct { SLVolumeItf player_vol; SLAndroidSimpleBufferQueueItf player_buffer_queue; - int16_t* output_buffers[SAUDIO_NUM_BUFFERS]; + int16_t* output_buffers[SAUDIO_SLES_NUM_BUFFERS]; float* src_buffer; int active_buffer; - _saudio_semaphore_t buffer_sem; + _saudio_sles_semaphore_t buffer_sem; pthread_t thread; volatile int thread_stop; SLDataLocator_AndroidSimpleBufferQueue in_locator; -} _saudio_backend_t; +} _saudio_sles_backend_t; + +#elif defined(SAUDIO_ANDROID_AAUDIO) + +typedef struct { + AAudioStreamBuilder* builder; + AAudioStream* stream; + pthread_t thread; + pthread_mutex_t mutex; +} _saudio_aaudio_backend_t; -/*=== WASAPI BACKEND DECLARATIONS ============================================*/ #elif defined(_SAUDIO_WINDOWS) typedef struct { @@ -810,33 +1006,38 @@ typedef struct { } _saudio_wasapi_thread_data_t; typedef struct { - #if defined(_SAUDIO_UWP) - LPOLESTR interface_activation_audio_interface_uid_string; - IActivateAudioInterfaceAsyncOperation* interface_activation_operation; - BOOL interface_activation_success; - HANDLE interface_activation_mutex; - #else - IMMDeviceEnumerator* device_enumerator; - IMMDevice* device; - #endif + IMMDeviceEnumerator* device_enumerator; + IMMDevice* device; IAudioClient* audio_client; IAudioRenderClient* render_client; - int si16_bytes_per_frame; _saudio_wasapi_thread_data_t thread; -} _saudio_backend_t; +} _saudio_wasapi_backend_t; -/*=== WEBAUDIO BACKEND DECLARATIONS ==========================================*/ #elif defined(_SAUDIO_EMSCRIPTEN) typedef struct { uint8_t* buffer; -} _saudio_backend_t; +} _saudio_web_backend_t; #else #error "unknown platform" #endif -/*=== GENERAL DECLARATIONS ===================================================*/ +#if defined(SOKOL_DUMMY_BACKEND) +typedef _saudio_dummy_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_APPLE) +typedef _saudio_apple_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_EMSCRIPTEN) +typedef _saudio_web_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_WINDOWS) +typedef _saudio_wasapi_backend_t _saudio_backend_t; +#elif defined(SAUDIO_ANDROID_SLES) +typedef _saudio_sles_backend_t _saudio_backend_t; +#elif defined(SAUDIO_ANDROID_AAUDIO) +typedef _saudio_aaudio_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_LINUX) +typedef _saudio_alsa_backend_t _saudio_backend_t; +#endif /* a ringbuffer structure */ typedef struct { @@ -876,7 +1077,7 @@ typedef struct { _saudio_backend_t backend; } _saudio_state_t; -static _saudio_state_t _saudio; +_SOKOL_PRIVATE _saudio_state_t _saudio; _SOKOL_PRIVATE bool _saudio_has_callback(void) { return (_saudio.stream_cb || _saudio.stream_userdata_cb); @@ -891,7 +1092,94 @@ _SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int n } } -/*=== MUTEX IMPLEMENTATION ===================================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAUDIO_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _saudio_log_messages[] = { + _SAUDIO_LOG_ITEMS +}; +#undef _SAUDIO_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAUDIO_PANIC(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 0, __LINE__) +#define _SAUDIO_ERROR(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 1, __LINE__) +#define _SAUDIO_WARN(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 2, __LINE__) +#define _SAUDIO_INFO(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 3, __LINE__) + +static void _saudio_log(saudio_log_item log_item, uint32_t log_level, uint32_t line_nr) { + if (_saudio.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _saudio_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _saudio.desc.logger.func("saudio", log_level, log_item, message, line_nr, filename, _saudio.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +_SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _saudio_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_saudio.desc.allocator.alloc) { + ptr = _saudio.desc.allocator.alloc(size, _saudio.desc.allocator.user_data); + } + else { + ptr = malloc(size); + } + if (0 == ptr) { + _SAUDIO_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) { + void* ptr = _saudio_malloc(size); + _saudio_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _saudio_free(void* ptr) { + if (_saudio.desc.allocator.free) { + _saudio.desc.allocator.free(ptr, _saudio.desc.allocator.user_data); + } + else { + free(ptr); + } +} + +// ███ ███ ██ ██ ████████ ███████ ██ ██ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ██ ██ ██ █████ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██████ ██ ███████ ██ ██ +// +// >>mutex #if defined(_SAUDIO_NOTHREADS) _SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { (void)m; } @@ -937,10 +1225,16 @@ _SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { LeaveCriticalSection(&m->critsec); } #else -#error "unknown platform!" +#error "sokol_audio.h: unknown platform!" #endif -/*=== RING-BUFFER QUEUE IMPLEMENTATION =======================================*/ +// ██████ ██ ███ ██ ██████ ██████ ██ ██ ███████ ███████ ███████ ██████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ███ ██████ ██ ██ █████ █████ █████ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ████ ██████ ██████ ██████ ██ ██ ███████ ██ ██ +// +// >>ringbuffer _SOKOL_PRIVATE int _saudio_ring_idx(_saudio_ring_t* ring, int i) { return (i % ring->num); } @@ -986,12 +1280,22 @@ _SOKOL_PRIVATE int _saudio_ring_dequeue(_saudio_ring_t* ring) { return val; } -/*--- a packet fifo for queueing audio data from main thread ----------------*/ +// ███████ ██ ███████ ██████ +// ██ ██ ██ ██ ██ +// █████ ██ █████ ██ ██ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ +// +// >>fifo _SOKOL_PRIVATE void _saudio_fifo_init_mutex(_saudio_fifo_t* fifo) { /* this must be called before initializing both the backend and the fifo itself! */ _saudio_mutex_init(&fifo->mutex); } +_SOKOL_PRIVATE void _saudio_fifo_destroy_mutex(_saudio_fifo_t* fifo) { + _saudio_mutex_destroy(&fifo->mutex); +} + _SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int num_packets) { /* NOTE: there's a chicken-egg situation during the init phase where the streaming thread must be started before the fifo is actually initialized, @@ -1001,8 +1305,7 @@ _SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int SOKOL_ASSERT((packet_size > 0) && (num_packets > 0)); fifo->packet_size = packet_size; fifo->num_packets = num_packets; - fifo->base_ptr = (uint8_t*) SOKOL_MALLOC((size_t)(packet_size * num_packets)); - SOKOL_ASSERT(fifo->base_ptr); + fifo->base_ptr = (uint8_t*) _saudio_malloc((size_t)(packet_size * num_packets)); fifo->cur_packet = -1; fifo->cur_offset = 0; _saudio_ring_init(&fifo->read_queue, num_packets); @@ -1020,10 +1323,9 @@ _SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int _SOKOL_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) { SOKOL_ASSERT(fifo->base_ptr); - SOKOL_FREE(fifo->base_ptr); + _saudio_free(fifo->base_ptr); fifo->base_ptr = 0; fifo->valid = false; - _saudio_mutex_destroy(&fifo->mutex); } _SOKOL_PRIVATE int _saudio_fifo_writable_bytes(_saudio_fifo_t* fifo) { @@ -1114,15 +1416,866 @@ _SOKOL_PRIVATE int _saudio_fifo_read(_saudio_fifo_t* fifo, uint8_t* ptr, int num return num_bytes_copied; } -/*=== DUMMY BACKEND IMPLEMENTATION ===========================================*/ +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ +// +// >>dummy #if defined(SOKOL_DUMMY_BACKEND) -_SOKOL_PRIVATE bool _saudio_backend_init(void) { +_SOKOL_PRIVATE bool _saudio_dummy_backend_init(void) { _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); return true; }; -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { }; +_SOKOL_PRIVATE void _saudio_dummy_backend_shutdown(void) { }; -/*=== COREAUDIO BACKEND IMPLEMENTATION =======================================*/ +// █████ ██ ███████ █████ +// ██ ██ ██ ██ ██ ██ +// ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ██ +// +// >>alsa +#elif defined(_SAUDIO_LINUX) + +/* the streaming callback runs in a separate thread */ +_SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) { + _SOKOL_UNUSED(param); + while (!_saudio.backend.thread_stop) { + /* snd_pcm_writei() will be blocking until it needs data */ + int write_res = snd_pcm_writei(_saudio.backend.device, _saudio.backend.buffer, (snd_pcm_uframes_t)_saudio.backend.buffer_frames); + if (write_res < 0) { + /* underrun occurred */ + snd_pcm_prepare(_saudio.backend.device); + } + else { + /* fill the streaming buffer with new data */ + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.buffer, _saudio.backend.buffer_frames, _saudio.num_channels); + } + else { + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.buffer, (size_t)_saudio.backend.buffer_byte_size); + } + } + } + } + return 0; +} + +_SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) { + int dir; uint32_t rate; + int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (rc < 0) { + _SAUDIO_ERROR(ALSA_SND_PCM_OPEN_FAILED); + return false; + } + + /* configuration works by restricting the 'configuration space' step + by step, we require all parameters except the sample rate to + match perfectly + */ + snd_pcm_hw_params_t* params = 0; + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(_saudio.backend.device, params); + snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) { + _SAUDIO_ERROR(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED); + goto error; + } + if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) { + _SAUDIO_ERROR(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED); + goto error; + } + if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) { + _SAUDIO_ERROR(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED); + goto error; + } + /* let ALSA pick a nearby sampling rate */ + rate = (uint32_t) _saudio.sample_rate; + dir = 0; + if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) { + _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED); + goto error; + } + if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) { + _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_FAILED); + goto error; + } + + /* read back actual sample rate and channels */ + _saudio.sample_rate = (int)rate; + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + /* allocate the streaming buffer */ + _saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame; + _saudio.backend.buffer_frames = _saudio.buffer_frames; + _saudio.backend.buffer = (float*) _saudio_malloc_clear((size_t)_saudio.backend.buffer_byte_size); + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) { + _SAUDIO_ERROR(ALSA_PTHREAD_CREATE_FAILED); + goto error; + } + + return true; +error: + if (_saudio.backend.device) { + snd_pcm_close(_saudio.backend.device); + _saudio.backend.device = 0; + } + return false; +}; + +_SOKOL_PRIVATE void _saudio_alsa_backend_shutdown(void) { + SOKOL_ASSERT(_saudio.backend.device); + _saudio.backend.thread_stop = true; + pthread_join(_saudio.backend.thread, 0); + snd_pcm_drain(_saudio.backend.device); + snd_pcm_close(_saudio.backend.device); + _saudio_free(_saudio.backend.buffer); +}; + +// ██ ██ █████ ███████ █████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ███████ ███████ ███████ ██████ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ██ ██ ███████ ██ ██ ██ ██ +// +// >>wasapi +#elif defined(_SAUDIO_WINDOWS) + +/* fill intermediate buffer with new data and reset buffer_pos */ +_SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) { + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_frames, _saudio.num_channels); + } + else { + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.thread.src_buffer, (size_t)_saudio.backend.thread.src_buffer_byte_size); + } + } +} + +_SOKOL_PRIVATE int _saudio_wasapi_min(int a, int b) { + return (a < b) ? a : b; +} + +_SOKOL_PRIVATE void _saudio_wasapi_submit_buffer(int num_frames) { + BYTE* wasapi_buffer = 0; + if (FAILED(IAudioRenderClient_GetBuffer(_saudio.backend.render_client, num_frames, &wasapi_buffer))) { + return; + } + SOKOL_ASSERT(wasapi_buffer); + + /* copy samples to WASAPI buffer, refill source buffer if needed */ + int num_remaining_samples = num_frames * _saudio.num_channels; + int buffer_pos = _saudio.backend.thread.src_buffer_pos; + const int buffer_size_in_samples = _saudio.backend.thread.src_buffer_byte_size / (int)sizeof(float); + float* dst = (float*)wasapi_buffer; + const float* dst_end = dst + num_remaining_samples; + _SOKOL_UNUSED(dst_end); // suppress unused warning in release mode + const float* src = _saudio.backend.thread.src_buffer; + + while (num_remaining_samples > 0) { + if (0 == buffer_pos) { + _saudio_wasapi_fill_buffer(); + } + const int samples_to_copy = _saudio_wasapi_min(num_remaining_samples, buffer_size_in_samples - buffer_pos); + SOKOL_ASSERT((buffer_pos + samples_to_copy) <= buffer_size_in_samples); + SOKOL_ASSERT((dst + samples_to_copy) <= dst_end); + memcpy(dst, &src[buffer_pos], (size_t)samples_to_copy * sizeof(float)); + num_remaining_samples -= samples_to_copy; + SOKOL_ASSERT(num_remaining_samples >= 0); + buffer_pos += samples_to_copy; + dst += samples_to_copy; + + SOKOL_ASSERT(buffer_pos <= buffer_size_in_samples); + if (buffer_pos == buffer_size_in_samples) { + buffer_pos = 0; + } + } + _saudio.backend.thread.src_buffer_pos = buffer_pos; + IAudioRenderClient_ReleaseBuffer(_saudio.backend.render_client, num_frames, 0); +} + +_SOKOL_PRIVATE DWORD WINAPI _saudio_wasapi_thread_fn(LPVOID param) { + (void)param; + _saudio_wasapi_submit_buffer(_saudio.backend.thread.src_buffer_frames); + IAudioClient_Start(_saudio.backend.audio_client); + while (!_saudio.backend.thread.stop) { + WaitForSingleObject(_saudio.backend.thread.buffer_end_event, INFINITE); + UINT32 padding = 0; + if (FAILED(IAudioClient_GetCurrentPadding(_saudio.backend.audio_client, &padding))) { + continue; + } + SOKOL_ASSERT(_saudio.backend.thread.dst_buffer_frames >= padding); + int num_frames = (int)_saudio.backend.thread.dst_buffer_frames - (int)padding; + if (num_frames > 0) { + _saudio_wasapi_submit_buffer(num_frames); + } + } + return 0; +} + +_SOKOL_PRIVATE void _saudio_wasapi_release(void) { + if (_saudio.backend.thread.src_buffer) { + _saudio_free(_saudio.backend.thread.src_buffer); + _saudio.backend.thread.src_buffer = 0; + } + if (_saudio.backend.render_client) { + IAudioRenderClient_Release(_saudio.backend.render_client); + _saudio.backend.render_client = 0; + } + if (_saudio.backend.audio_client) { + IAudioClient_Release(_saudio.backend.audio_client); + _saudio.backend.audio_client = 0; + } + if (_saudio.backend.device) { + IMMDevice_Release(_saudio.backend.device); + _saudio.backend.device = 0; + } + if (_saudio.backend.device_enumerator) { + IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator); + _saudio.backend.device_enumerator = 0; + } + if (0 != _saudio.backend.thread.buffer_end_event) { + CloseHandle(_saudio.backend.thread.buffer_end_event); + _saudio.backend.thread.buffer_end_event = 0; + } +} + +_SOKOL_PRIVATE bool _saudio_wasapi_backend_init(void) { + REFERENCE_TIME dur; + /* CoInitializeEx could have been called elsewhere already, in which + case the function returns with S_FALSE (thus it does not make much + sense to check the result) + */ + HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); + _SOKOL_UNUSED(hr); + _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0); + if (0 == _saudio.backend.thread.buffer_end_event) { + _SAUDIO_ERROR(WASAPI_CREATE_EVENT_FAILED); + goto error; + } + if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator), + 0, CLSCTX_ALL, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), + (void**)&_saudio.backend.device_enumerator))) + { + _SAUDIO_ERROR(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED); + goto error; + } + if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, + eRender, eConsole, + &_saudio.backend.device))) + { + _SAUDIO_ERROR(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED); + goto error; + } + if (FAILED(IMMDevice_Activate(_saudio.backend.device, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), + CLSCTX_ALL, 0, + (void**)&_saudio.backend.audio_client))) + { + _SAUDIO_ERROR(WASAPI_DEVICE_ACTIVATE_FAILED); + goto error; + } + + WAVEFORMATEXTENSIBLE fmtex; + _saudio_clear(&fmtex, sizeof(fmtex)); + fmtex.Format.nChannels = (WORD)_saudio.num_channels; + fmtex.Format.nSamplesPerSec = (DWORD)_saudio.sample_rate; + fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + fmtex.Format.wBitsPerSample = 32; + fmtex.Format.nBlockAlign = (fmtex.Format.nChannels * fmtex.Format.wBitsPerSample) / 8; + fmtex.Format.nAvgBytesPerSec = fmtex.Format.nSamplesPerSec * fmtex.Format.nBlockAlign; + fmtex.Format.cbSize = 22; /* WORD + DWORD + GUID */ + fmtex.Samples.wValidBitsPerSample = 32; + if (_saudio.num_channels == 1) { + fmtex.dwChannelMask = SPEAKER_FRONT_CENTER; + } + else { + fmtex.dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT; + } + fmtex.SubFormat = _saudio_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + dur = (REFERENCE_TIME) + (((double)_saudio.buffer_frames) / (((double)_saudio.sample_rate) * (1.0/10000000.0))); + if (FAILED(IAudioClient_Initialize(_saudio.backend.audio_client, + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, + dur, 0, (WAVEFORMATEX*)&fmtex, 0))) + { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED); + goto error; + } + if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED); + goto error; + } + if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient), + (void**)&_saudio.backend.render_client))) + { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED); + goto error; + } + if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED); + goto error; + } + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + _saudio.backend.thread.src_buffer_frames = _saudio.buffer_frames; + _saudio.backend.thread.src_buffer_byte_size = _saudio.backend.thread.src_buffer_frames * _saudio.bytes_per_frame; + + /* allocate an intermediate buffer for sample format conversion */ + _saudio.backend.thread.src_buffer = (float*) _saudio_malloc((size_t)_saudio.backend.thread.src_buffer_byte_size); + + /* create streaming thread */ + _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0); + if (0 == _saudio.backend.thread.thread_handle) { + _SAUDIO_ERROR(WASAPI_CREATE_THREAD_FAILED); + goto error; + } + return true; +error: + _saudio_wasapi_release(); + return false; +} + +_SOKOL_PRIVATE void _saudio_wasapi_backend_shutdown(void) { + if (_saudio.backend.thread.thread_handle) { + _saudio.backend.thread.stop = true; + SetEvent(_saudio.backend.thread.buffer_end_event); + WaitForSingleObject(_saudio.backend.thread.thread_handle, INFINITE); + CloseHandle(_saudio.backend.thread.thread_handle); + _saudio.backend.thread.thread_handle = 0; + } + if (_saudio.backend.audio_client) { + IAudioClient_Stop(_saudio.backend.audio_client); + } + _saudio_wasapi_release(); + CoUninitialize(); +} + +// ██ ██ ███████ ██████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ █████ ██████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██ ██ ██████ ██████ ██ ██████ +// +// >>webaudio +#elif defined(_SAUDIO_EMSCRIPTEN) + +#ifdef __cplusplus +extern "C" { +#endif + +EMSCRIPTEN_KEEPALIVE int _saudio_emsc_pull(int num_frames) { + SOKOL_ASSERT(_saudio.backend.buffer); + if (num_frames == _saudio.buffer_frames) { + if (_saudio_has_callback()) { + _saudio_stream_callback((float*)_saudio.backend.buffer, num_frames, _saudio.num_channels); + } + else { + const int num_bytes = num_frames * _saudio.bytes_per_frame; + if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.buffer, (size_t)num_bytes); + } + } + int res = (int) _saudio.backend.buffer; + return res; + } + else { + return 0; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* setup the WebAudio context and attach a ScriptProcessorNode */ +EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size), { + Module._saudio_context = null; + Module._saudio_node = null; + if (typeof AudioContext !== 'undefined') { + Module._saudio_context = new AudioContext({ + sampleRate: sample_rate, + latencyHint: 'interactive', + }); + } + else { + Module._saudio_context = null; + console.log('sokol_audio.h: no WebAudio support'); + } + if (Module._saudio_context) { + console.log('sokol_audio.h: sample rate ', Module._saudio_context.sampleRate); + Module._saudio_node = Module._saudio_context.createScriptProcessor(buffer_size, 0, num_channels); + Module._saudio_node.onaudioprocess = (event) => { + const num_frames = event.outputBuffer.length; + const ptr = __saudio_emsc_pull(num_frames); + if (ptr) { + const num_channels = event.outputBuffer.numberOfChannels; + for (let chn = 0; chn < num_channels; chn++) { + const chan = event.outputBuffer.getChannelData(chn); + for (let i = 0; i < num_frames; i++) { + chan[i] = HEAPF32[(ptr>>2) + ((num_channels*i)+chn)] + } + } + } + }; + Module._saudio_node.connect(Module._saudio_context.destination); + + // in some browsers, WebAudio needs to be activated on a user action + const resume_webaudio = () => { + if (Module._saudio_context) { + if (Module._saudio_context.state === 'suspended') { + Module._saudio_context.resume(); + } + } + }; + document.addEventListener('click', resume_webaudio, {once:true}); + document.addEventListener('touchend', resume_webaudio, {once:true}); + document.addEventListener('keydown', resume_webaudio, {once:true}); + return 1; + } + else { + return 0; + } +}); + +/* shutdown the WebAudioContext and ScriptProcessorNode */ +EM_JS(void, saudio_js_shutdown, (void), { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const ctx = Module._saudio_context; + if (ctx !== null) { + if (Module._saudio_node) { + Module._saudio_node.disconnect(); + } + ctx.close(); + Module._saudio_context = null; + Module._saudio_node = null; + } +}); + +/* get the actual sample rate back from the WebAudio context */ +EM_JS(int, saudio_js_sample_rate, (void), { + if (Module._saudio_context) { + return Module._saudio_context.sampleRate; + } + else { + return 0; + } +}); + +/* get the actual buffer size in number of frames */ +EM_JS(int, saudio_js_buffer_frames, (void), { + if (Module._saudio_node) { + return Module._saudio_node.bufferSize; + } + else { + return 0; + } +}); + +/* return 1 if the WebAudio context is currently suspended, else 0 */ +EM_JS(int, saudio_js_suspended, (void), { + if (Module._saudio_context) { + if (Module._saudio_context.state === 'suspended') { + return 1; + } + else { + return 0; + } + } +}); + +_SOKOL_PRIVATE bool _saudio_webaudio_backend_init(void) { + if (saudio_js_init(_saudio.sample_rate, _saudio.num_channels, _saudio.buffer_frames)) { + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; + _saudio.sample_rate = saudio_js_sample_rate(); + _saudio.buffer_frames = saudio_js_buffer_frames(); + const size_t buf_size = (size_t) (_saudio.buffer_frames * _saudio.bytes_per_frame); + _saudio.backend.buffer = (uint8_t*) _saudio_malloc(buf_size); + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _saudio_webaudio_backend_shutdown(void) { + saudio_js_shutdown(); + if (_saudio.backend.buffer) { + _saudio_free(_saudio.backend.buffer); + _saudio.backend.buffer = 0; + } +} + +// █████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ██ ██████ +// +// >>aaudio +#elif defined(SAUDIO_ANDROID_AAUDIO) + +_SOKOL_PRIVATE aaudio_data_callback_result_t _saudio_aaudio_data_callback(AAudioStream* stream, void* user_data, void* audio_data, int32_t num_frames) { + _SOKOL_UNUSED(user_data); + _SOKOL_UNUSED(stream); + if (_saudio_has_callback()) { + _saudio_stream_callback((float*)audio_data, (int)num_frames, _saudio.num_channels); + } + else { + uint8_t* ptr = (uint8_t*)audio_data; + int num_bytes = _saudio.bytes_per_frame * num_frames; + if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { + // not enough read data available, fill the entire buffer with silence + memset(ptr, 0, (size_t)num_bytes); + } + } + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +_SOKOL_PRIVATE bool _saudio_aaudio_start_stream(void) { + if (AAudioStreamBuilder_openStream(_saudio.backend.builder, &_saudio.backend.stream) != AAUDIO_OK) { + _SAUDIO_ERROR(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED); + return false; + } + AAudioStream_requestStart(_saudio.backend.stream); + return true; +} + +_SOKOL_PRIVATE void _saudio_aaudio_stop_stream(void) { + if (_saudio.backend.stream) { + AAudioStream_requestStop(_saudio.backend.stream); + AAudioStream_close(_saudio.backend.stream); + _saudio.backend.stream = 0; + } +} + +_SOKOL_PRIVATE void* _saudio_aaudio_restart_stream_thread_fn(void* param) { + _SOKOL_UNUSED(param); + _SAUDIO_WARN(AAUDIO_RESTARTING_STREAM_AFTER_ERROR); + pthread_mutex_lock(&_saudio.backend.mutex); + _saudio_aaudio_stop_stream(); + _saudio_aaudio_start_stream(); + pthread_mutex_unlock(&_saudio.backend.mutex); + return 0; +} + +_SOKOL_PRIVATE void _saudio_aaudio_error_callback(AAudioStream* stream, void* user_data, aaudio_result_t error) { + _SOKOL_UNUSED(stream); + _SOKOL_UNUSED(user_data); + if (error == AAUDIO_ERROR_DISCONNECTED) { + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_aaudio_restart_stream_thread_fn, 0)) { + _SAUDIO_ERROR(AAUDIO_PTHREAD_CREATE_FAILED); + } + } +} + +_SOKOL_PRIVATE void _saudio_aaudio_backend_shutdown(void) { + pthread_mutex_lock(&_saudio.backend.mutex); + _saudio_aaudio_stop_stream(); + pthread_mutex_unlock(&_saudio.backend.mutex); + if (_saudio.backend.builder) { + AAudioStreamBuilder_delete(_saudio.backend.builder); + _saudio.backend.builder = 0; + } + pthread_mutex_destroy(&_saudio.backend.mutex); +} + +_SOKOL_PRIVATE bool _saudio_aaudio_backend_init(void) { + _SAUDIO_INFO(USING_AAUDIO_BACKEND); + + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&_saudio.backend.mutex, &attr); + + if (AAudio_createStreamBuilder(&_saudio.backend.builder) != AAUDIO_OK) { + _SAUDIO_ERROR(AAUDIO_CREATE_STREAMBUILDER_FAILED); + _saudio_aaudio_backend_shutdown(); + return false; + } + + AAudioStreamBuilder_setFormat(_saudio.backend.builder, AAUDIO_FORMAT_PCM_FLOAT); + AAudioStreamBuilder_setSampleRate(_saudio.backend.builder, _saudio.sample_rate); + AAudioStreamBuilder_setChannelCount(_saudio.backend.builder, _saudio.num_channels); + AAudioStreamBuilder_setBufferCapacityInFrames(_saudio.backend.builder, _saudio.buffer_frames * 2); + AAudioStreamBuilder_setFramesPerDataCallback(_saudio.backend.builder, _saudio.buffer_frames); + AAudioStreamBuilder_setDataCallback(_saudio.backend.builder, _saudio_aaudio_data_callback, 0); + AAudioStreamBuilder_setErrorCallback(_saudio.backend.builder, _saudio_aaudio_error_callback, 0); + + if (!_saudio_aaudio_start_stream()) { + _saudio_aaudio_backend_shutdown(); + return false; + } + + return true; +} + +// ██████ ██████ ███████ ███ ██ ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ███████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ███████ ███████ ███████ ███████ +// +// >>opensles +// >>sles +#elif defined(SAUDIO_ANDROID_SLES) + +_SOKOL_PRIVATE void _saudio_sles_semaphore_init(_saudio_sles_semaphore_t* sem) { + sem->count = 0; + int r = pthread_mutex_init(&sem->mutex, NULL); + SOKOL_ASSERT(r == 0); + r = pthread_cond_init(&sem->cond, NULL); + SOKOL_ASSERT(r == 0); + (void)(r); +} + +_SOKOL_PRIVATE void _saudio_sles_semaphore_destroy(_saudio_sles_semaphore_t* sem) { + pthread_cond_destroy(&sem->cond); + pthread_mutex_destroy(&sem->mutex); +} + +_SOKOL_PRIVATE void _saudio_sles_semaphore_post(_saudio_sles_semaphore_t* sem, int count) { + int r = pthread_mutex_lock(&sem->mutex); + SOKOL_ASSERT(r == 0); + for (int ii = 0; ii < count; ii++) { + r = pthread_cond_signal(&sem->cond); + SOKOL_ASSERT(r == 0); + } + sem->count += count; + r = pthread_mutex_unlock(&sem->mutex); + SOKOL_ASSERT(r == 0); + (void)(r); +} + +_SOKOL_PRIVATE bool _saudio_sles_semaphore_wait(_saudio_sles_semaphore_t* sem) { + int r = pthread_mutex_lock(&sem->mutex); + SOKOL_ASSERT(r == 0); + while (r == 0 && sem->count <= 0) { + r = pthread_cond_wait(&sem->cond, &sem->mutex); + } + bool ok = (r == 0); + if (ok) { + --sem->count; + } + r = pthread_mutex_unlock(&sem->mutex); + (void)(r); + return ok; +} + +/* fill intermediate buffer with new data and reset buffer_pos */ +_SOKOL_PRIVATE void _saudio_sles_fill_buffer(void) { + int src_buffer_frames = _saudio.buffer_frames; + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.src_buffer, src_buffer_frames, _saudio.num_channels); + } + else { + const int src_buffer_byte_size = src_buffer_frames * _saudio.num_channels * (int)sizeof(float); + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.src_buffer, src_buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.src_buffer, (size_t)src_buffer_byte_size); + } + } +} + +_SOKOL_PRIVATE void SLAPIENTRY _saudio_sles_play_cb(SLPlayItf player, void *context, SLuint32 event) { + _SOKOL_UNUSED(context); + _SOKOL_UNUSED(player); + if (event & SL_PLAYEVENT_HEADATEND) { + _saudio_sles_semaphore_post(&_saudio.backend.buffer_sem, 1); + } +} + +_SOKOL_PRIVATE void* _saudio_sles_thread_fn(void* param) { + _SOKOL_UNUSED(param); + while (!_saudio.backend.thread_stop) { + /* get next output buffer, advance, next buffer. */ + int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_SLES_NUM_BUFFERS; + int16_t* next_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; + + /* queue this buffer */ + const int buffer_size_bytes = _saudio.buffer_frames * _saudio.num_channels * (int)sizeof(short); + (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, out_buffer, (SLuint32)buffer_size_bytes); + + /* fill the next buffer */ + _saudio_sles_fill_buffer(); + const int num_samples = _saudio.num_channels * _saudio.buffer_frames; + for (int i = 0; i < num_samples; ++i) { + next_buffer[i] = (int16_t) (_saudio.backend.src_buffer[i] * 0x7FFF); + } + + _saudio_sles_semaphore_wait(&_saudio.backend.buffer_sem); + } + + return 0; +} + +_SOKOL_PRIVATE void _saudio_sles_backend_shutdown(void) { + _saudio.backend.thread_stop = 1; + pthread_join(_saudio.backend.thread, 0); + + if (_saudio.backend.player_obj) { + (*_saudio.backend.player_obj)->Destroy(_saudio.backend.player_obj); + } + + if (_saudio.backend.output_mix_obj) { + (*_saudio.backend.output_mix_obj)->Destroy(_saudio.backend.output_mix_obj); + } + + if (_saudio.backend.engine_obj) { + (*_saudio.backend.engine_obj)->Destroy(_saudio.backend.engine_obj); + } + + for (int i = 0; i < SAUDIO_SLES_NUM_BUFFERS; i++) { + _saudio_free(_saudio.backend.output_buffers[i]); + } + _saudio_free(_saudio.backend.src_buffer); +} + +_SOKOL_PRIVATE bool _saudio_sles_backend_init(void) { + _SAUDIO_INFO(USING_SLES_BACKEND); + + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; + + for (int i = 0; i < SAUDIO_SLES_NUM_BUFFERS; ++i) { + const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; + _saudio.backend.output_buffers[i] = (int16_t*) _saudio_malloc_clear((size_t)buffer_size_bytes); + } + + { + const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames; + _saudio.backend.src_buffer = (float*) _saudio_malloc_clear((size_t)buffer_size_bytes); + } + + /* Create engine */ + const SLEngineOption opts[] = { { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } }; + if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_CREATE_ENGINE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + + (*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE); + if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + + /* Create output mix. */ + { + const SLInterfaceID ids[] = { SL_IID_VOLUME }; + const SLboolean req[] = { SL_BOOLEAN_FALSE }; + + if ((*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_CREATE_OUTPUT_MIX_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + (*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE); + + if ((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) { + _SAUDIO_WARN(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED); + } + } + + /* android buffer queue */ + _saudio.backend.in_locator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + _saudio.backend.in_locator.numBuffers = SAUDIO_SLES_NUM_BUFFERS; + + /* data format */ + SLDataFormat_PCM format; + format.formatType = SL_DATAFORMAT_PCM; + format.numChannels = (SLuint32)_saudio.num_channels; + format.samplesPerSec = (SLuint32) (_saudio.sample_rate * 1000); + format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + format.containerSize = 16; + format.endianness = SL_BYTEORDER_LITTLEENDIAN; + + if (_saudio.num_channels == 2) { + format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + } else { + format.channelMask = SL_SPEAKER_FRONT_CENTER; + } + + SLDataSource src; + src.pLocator = &_saudio.backend.in_locator; + src.pFormat = &format; + + /* Output mix. */ + _saudio.backend.out_locator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + _saudio.backend.out_locator.outputMix = _saudio.backend.output_mix_obj; + + _saudio.backend.dst_data_sink.pLocator = &_saudio.backend.out_locator; + _saudio.backend.dst_data_sink.pFormat = NULL; + + /* setup player */ + { + const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; + const SLboolean req[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE }; + + if ((*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req) != SL_RESULT_SUCCESS) + { + _SAUDIO_ERROR(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + (*_saudio.backend.player_obj)->Realize(_saudio.backend.player_obj, SL_BOOLEAN_FALSE); + + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED); + } + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + } + + /* begin */ + { + const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; + (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, _saudio.backend.output_buffers[0], (SLuint32)buffer_size_bytes); + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_SLES_NUM_BUFFERS; + + (*_saudio.backend.player)->RegisterCallback(_saudio.backend.player, _saudio_sles_play_cb, NULL); + (*_saudio.backend.player)->SetCallbackEventsMask(_saudio.backend.player, SL_PLAYEVENT_HEADATEND); + (*_saudio.backend.player)->SetPlayState(_saudio.backend.player, SL_PLAYSTATE_PLAYING); + } + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_sles_thread_fn, 0)) { + _saudio_sles_backend_shutdown(); + return false; + } + + return true; +} + +// ██████ ██████ ██████ ███████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ █████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ███████ ██ ██ ██████ ██████ ██ ██████ +// +// >>coreaudio #elif defined(_SAUDIO_APPLE) #if defined(_SAUDIO_IOS) @@ -1162,12 +2315,16 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { }; NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; switch (type) { case AVAudioSessionInterruptionTypeBegan: - AudioQueuePause(_saudio.backend.ca_audio_queue); + if (_saudio.backend.ca_audio_queue) { + AudioQueuePause(_saudio.backend.ca_audio_queue); + } [session setActive:false error:nil]; break; case AVAudioSessionInterruptionTypeEnded: [session setActive:true error:nil]; - AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + if (_saudio.backend.ca_audio_queue) { + AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + } break; default: break; @@ -1189,65 +2346,18 @@ _SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQue int num_bytes = (int) buffer->mAudioDataByteSize; if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { /* not enough read data available, fill the entire buffer with silence */ - memset(ptr, 0, (size_t)num_bytes); + _saudio_clear(ptr, (size_t)num_bytes); } } AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); } -_SOKOL_PRIVATE bool _saudio_backend_init(void) { - SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); - - #if defined(_SAUDIO_IOS) - /* activate audio session */ - AVAudioSession* session = [AVAudioSession sharedInstance]; - SOKOL_ASSERT(session != nil); - [session setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; - [session setActive:true error:nil]; - - /* create interruption handler */ - _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; - #endif // _SAUDIO_IOS - - /* create an audio queue with fp32 samples */ - _saudio_AudioStreamBasicDescription fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.mSampleRate = (double) _saudio.sample_rate; - fmt.mFormatID = _saudio_kAudioFormatLinearPCM; - fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; - fmt.mFramesPerPacket = 1; - fmt.mChannelsPerFrame = (uint32_t) _saudio.num_channels; - fmt.mBytesPerFrame = (uint32_t)sizeof(float) * (uint32_t)_saudio.num_channels; - fmt.mBytesPerPacket = fmt.mBytesPerFrame; - fmt.mBitsPerChannel = 32; - _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); - SOKOL_ASSERT((res == 0) && _saudio.backend.ca_audio_queue); - - /* create 2 audio buffers */ - for (int i = 0; i < 2; i++) { - _saudio_AudioQueueBufferRef buf = NULL; - const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; - res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); - SOKOL_ASSERT((res == 0) && buf); - buf->mAudioDataByteSize = buf_byte_size; - memset(buf->mAudioData, 0, buf->mAudioDataByteSize); - AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); +_SOKOL_PRIVATE void _saudio_coreaudio_backend_shutdown(void) { + if (_saudio.backend.ca_audio_queue) { + AudioQueueStop(_saudio.backend.ca_audio_queue, true); + AudioQueueDispose(_saudio.backend.ca_audio_queue, false); + _saudio.backend.ca_audio_queue = 0; } - - /* init or modify actual playback parameters */ - _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; - - /* ...and start playback */ - res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); - SOKOL_ASSERT(0 == res); - - return true; -} - -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { - AudioQueueStop(_saudio.backend.ca_audio_queue, true); - AudioQueueDispose(_saudio.backend.ca_audio_queue, false); - _saudio.backend.ca_audio_queue = NULL; #if defined(_SAUDIO_IOS) /* remove interruption handler */ if (_saudio.backend.ca_interruption_handler != nil) { @@ -1261,800 +2371,122 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { #endif // _SAUDIO_IOS } -/*=== ALSA BACKEND IMPLEMENTATION ============================================*/ -#elif defined(_SAUDIO_LINUX) +_SOKOL_PRIVATE bool _saudio_coreaudio_backend_init(void) { + SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); -/* the streaming callback runs in a separate thread */ -_SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) { - _SOKOL_UNUSED(param); - while (!_saudio.backend.thread_stop) { - /* snd_pcm_writei() will be blocking until it needs data */ - int write_res = snd_pcm_writei(_saudio.backend.device, _saudio.backend.buffer, (snd_pcm_uframes_t)_saudio.backend.buffer_frames); - if (write_res < 0) { - /* underrun occurred */ - snd_pcm_prepare(_saudio.backend.device); - } - else { - /* fill the streaming buffer with new data */ - if (_saudio_has_callback()) { - _saudio_stream_callback(_saudio.backend.buffer, _saudio.backend.buffer_frames, _saudio.num_channels); - } - else { - if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) { - /* not enough read data available, fill the entire buffer with silence */ - memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size); - } - } - } - } - return 0; -} + #if defined(_SAUDIO_IOS) + /* activate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session != nil); + [session setCategory: AVAudioSessionCategoryPlayback error:nil]; + [session setActive:true error:nil]; -_SOKOL_PRIVATE bool _saudio_backend_init(void) { - int dir; uint32_t rate; - int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0); - if (rc < 0) { - SOKOL_LOG("sokol_audio.h: snd_pcm_open() failed"); + /* create interruption handler */ + _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; + #endif + + /* create an audio queue with fp32 samples */ + _saudio_AudioStreamBasicDescription fmt; + _saudio_clear(&fmt, sizeof(fmt)); + fmt.mSampleRate = (double) _saudio.sample_rate; + fmt.mFormatID = _saudio_kAudioFormatLinearPCM; + fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; + fmt.mFramesPerPacket = 1; + fmt.mChannelsPerFrame = (uint32_t) _saudio.num_channels; + fmt.mBytesPerFrame = (uint32_t)sizeof(float) * (uint32_t)_saudio.num_channels; + fmt.mBytesPerPacket = fmt.mBytesPerFrame; + fmt.mBitsPerChannel = 32; + _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_NEW_OUTPUT_FAILED); return false; } - - /* configuration works by restricting the 'configuration space' step - by step, we require all parameters except the sample rate to - match perfectly - */ - snd_pcm_hw_params_t* params = 0; - snd_pcm_hw_params_alloca(¶ms); - snd_pcm_hw_params_any(_saudio.backend.device, params); - snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED); - if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) { - SOKOL_LOG("sokol_audio.h: float samples not supported"); - goto error; - } - if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) { - SOKOL_LOG("sokol_audio.h: requested buffer size not supported"); - goto error; - } - if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) { - SOKOL_LOG("sokol_audio.h: requested channel count not supported"); - goto error; - } - /* let ALSA pick a nearby sampling rate */ - rate = (uint32_t) _saudio.sample_rate; - dir = 0; - if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) { - SOKOL_LOG("sokol_audio.h: snd_pcm_hw_params_set_rate_near() failed"); - goto error; - } - if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) { - SOKOL_LOG("sokol_audio.h: snd_pcm_hw_params() failed"); - goto error; - } - - /* read back actual sample rate and channels */ - _saudio.sample_rate = (int)rate; - _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); - - /* allocate the streaming buffer */ - _saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame; - _saudio.backend.buffer_frames = _saudio.buffer_frames; - _saudio.backend.buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.buffer_byte_size); - memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size); - - /* create the buffer-streaming start thread */ - if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) { - SOKOL_LOG("sokol_audio.h: pthread_create() failed"); - goto error; - } - - return true; -error: - if (_saudio.backend.device) { - snd_pcm_close(_saudio.backend.device); - _saudio.backend.device = 0; - } - return false; -}; - -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { - SOKOL_ASSERT(_saudio.backend.device); - _saudio.backend.thread_stop = true; - pthread_join(_saudio.backend.thread, 0); - snd_pcm_drain(_saudio.backend.device); - snd_pcm_close(_saudio.backend.device); - SOKOL_FREE(_saudio.backend.buffer); -}; - -/*=== WASAPI BACKEND IMPLEMENTATION ==========================================*/ -#elif defined(_SAUDIO_WINDOWS) - -#if defined(_SAUDIO_UWP) -/* Minimal implementation of an IActivateAudioInterfaceCompletionHandler COM object in plain C. - Meant to be a static singleton (always one reference when add/remove reference) - and implements IUnknown and IActivateAudioInterfaceCompletionHandler when queryinterface'd - - Do not know why but IActivateAudioInterfaceCompletionHandler's GUID is not the one system queries for, - so I'm advertising the one actually requested. -*/ -_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_interface_completion_handler_queryinterface(IActivateAudioInterfaceCompletionHandler* instance, REFIID riid, void** ppvObject) { - if (!ppvObject) { - return E_POINTER; - } - - if (IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IActivateAudioInterface_Completion_Handler)) || IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(IID_IUnknown))) - { - *ppvObject = (void*)instance; - return S_OK; - } - - *ppvObject = NULL; - return E_NOINTERFACE; -} - -_SOKOL_PRIVATE ULONG STDMETHODCALLTYPE _saudio_interface_completion_handler_addref_release(IActivateAudioInterfaceCompletionHandler* instance) { - _SOKOL_UNUSED(instance); - return 1; -} - -_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_backend_activate_audio_interface_cb(IActivateAudioInterfaceCompletionHandler* instance, IActivateAudioInterfaceAsyncOperation* activateOperation) { - _SOKOL_UNUSED(instance); - WaitForSingleObject(_saudio.backend.interface_activation_mutex, INFINITE); - _saudio.backend.interface_activation_success = TRUE; - HRESULT activation_result; - if (FAILED(activateOperation->lpVtbl->GetActivateResult(activateOperation, &activation_result, (IUnknown**)(&_saudio.backend.audio_client))) || FAILED(activation_result)) { - _saudio.backend.interface_activation_success = FALSE; - } - - ReleaseMutex(_saudio.backend.interface_activation_mutex); - return S_OK; -} -#endif // _SAUDIO_UWP - -/* fill intermediate buffer with new data and reset buffer_pos */ -_SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) { - if (_saudio_has_callback()) { - _saudio_stream_callback(_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_frames, _saudio.num_channels); - } - else { - if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) { - /* not enough read data available, fill the entire buffer with silence */ - memset(_saudio.backend.thread.src_buffer, 0, (size_t)_saudio.backend.thread.src_buffer_byte_size); - } - } -} - -_SOKOL_PRIVATE void _saudio_wasapi_submit_buffer(int num_frames) { - BYTE* wasapi_buffer = 0; - if (FAILED(IAudioRenderClient_GetBuffer(_saudio.backend.render_client, num_frames, &wasapi_buffer))) { - return; - } - SOKOL_ASSERT(wasapi_buffer); - - /* convert float samples to int16_t, refill float buffer if needed */ - const int num_samples = num_frames * _saudio.num_channels; - int16_t* dst = (int16_t*) wasapi_buffer; - int buffer_pos = _saudio.backend.thread.src_buffer_pos; - const int buffer_float_size = _saudio.backend.thread.src_buffer_byte_size / (int)sizeof(float); - float* src = _saudio.backend.thread.src_buffer; - for (int i = 0; i < num_samples; i++) { - if (0 == buffer_pos) { - _saudio_wasapi_fill_buffer(); - } - dst[i] = (int16_t) (src[buffer_pos] * 0x7FFF); - buffer_pos += 1; - if (buffer_pos == buffer_float_size) { - buffer_pos = 0; - } - } - _saudio.backend.thread.src_buffer_pos = buffer_pos; - - IAudioRenderClient_ReleaseBuffer(_saudio.backend.render_client, num_frames, 0); -} - -_SOKOL_PRIVATE DWORD WINAPI _saudio_wasapi_thread_fn(LPVOID param) { - (void)param; - _saudio_wasapi_submit_buffer(_saudio.backend.thread.src_buffer_frames); - IAudioClient_Start(_saudio.backend.audio_client); - while (!_saudio.backend.thread.stop) { - WaitForSingleObject(_saudio.backend.thread.buffer_end_event, INFINITE); - UINT32 padding = 0; - if (FAILED(IAudioClient_GetCurrentPadding(_saudio.backend.audio_client, &padding))) { - continue; - } - SOKOL_ASSERT(_saudio.backend.thread.dst_buffer_frames >= padding); - int num_frames = (int)_saudio.backend.thread.dst_buffer_frames - (int)padding; - if (num_frames > 0) { - _saudio_wasapi_submit_buffer(num_frames); - } - } - return 0; -} - -_SOKOL_PRIVATE void _saudio_wasapi_release(void) { - if (_saudio.backend.thread.src_buffer) { - SOKOL_FREE(_saudio.backend.thread.src_buffer); - _saudio.backend.thread.src_buffer = 0; - } - if (_saudio.backend.render_client) { - IAudioRenderClient_Release(_saudio.backend.render_client); - _saudio.backend.render_client = 0; - } - if (_saudio.backend.audio_client) { - IAudioClient_Release(_saudio.backend.audio_client); - _saudio.backend.audio_client = 0; - } - #if defined(_SAUDIO_UWP) - if (_saudio.backend.interface_activation_audio_interface_uid_string) { - CoTaskMemFree(_saudio.backend.interface_activation_audio_interface_uid_string); - _saudio.backend.interface_activation_audio_interface_uid_string = 0; - } - if (_saudio.backend.interface_activation_operation) { - IActivateAudioInterfaceAsyncOperation_Release(_saudio.backend.interface_activation_operation); - _saudio.backend.interface_activation_operation = 0; - } - #else - if (_saudio.backend.device) { - IMMDevice_Release(_saudio.backend.device); - _saudio.backend.device = 0; - } - if (_saudio.backend.device_enumerator) { - IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator); - _saudio.backend.device_enumerator = 0; - } - #endif - if (0 != _saudio.backend.thread.buffer_end_event) { - CloseHandle(_saudio.backend.thread.buffer_end_event); - _saudio.backend.thread.buffer_end_event = 0; - } -} - -_SOKOL_PRIVATE bool _saudio_backend_init(void) { - REFERENCE_TIME dur; - /* UWP Threads are CoInitialized by default with a different threading model, and this call fails - See https://github.com/Microsoft/cppwinrt/issues/6#issuecomment-253930637 */ - #if defined(_SAUDIO_WIN32) - /* CoInitializeEx could have been called elsewhere already, in which - case the function returns with S_FALSE (thus it does not make much - sense to check the result) - */ - HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); - _SOKOL_UNUSED(hr); - #endif - _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0); - if (0 == _saudio.backend.thread.buffer_end_event) { - SOKOL_LOG("sokol_audio wasapi: failed to create buffer_end_event"); - goto error; - } - #if defined(_SAUDIO_UWP) - _saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex"); - if (_saudio.backend.interface_activation_mutex == NULL) { - SOKOL_LOG("sokol_audio wasapi: failed to create interface activation mutex"); - goto error; - } - if (FAILED(StringFromIID(_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_Devinterface_Audio_Render), &_saudio.backend.interface_activation_audio_interface_uid_string))) { - SOKOL_LOG("sokol_audio wasapi: failed to get default audio device ID string"); - goto error; - } - - /* static instance of the fake COM object */ - static IActivateAudioInterfaceCompletionHandlerVtbl completion_handler_interface_vtable = { - _saudio_interface_completion_handler_queryinterface, - _saudio_interface_completion_handler_addref_release, - _saudio_interface_completion_handler_addref_release, - _saudio_backend_activate_audio_interface_cb - }; - static IActivateAudioInterfaceCompletionHandler completion_handler_interface = { &completion_handler_interface_vtable }; - - if (FAILED(ActivateAudioInterfaceAsync(_saudio.backend.interface_activation_audio_interface_uid_string, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), NULL, &completion_handler_interface, &_saudio.backend.interface_activation_operation))) { - SOKOL_LOG("sokol_audio wasapi: failed to get default audio device ID string"); - goto error; - } - while (!(_saudio.backend.audio_client)) { - if (WaitForSingleObject(_saudio.backend.interface_activation_mutex, 10) != WAIT_TIMEOUT) { - ReleaseMutex(_saudio.backend.interface_activation_mutex); - } - } - - if (!(_saudio.backend.interface_activation_success)) { - SOKOL_LOG("sokol_audio wasapi: interface activation failed. Unable to get audio client"); - goto error; - } - - #else - if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator), - 0, CLSCTX_ALL, - _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), - (void**)&_saudio.backend.device_enumerator))) - { - SOKOL_LOG("sokol_audio wasapi: failed to create device enumerator"); - goto error; - } - if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, - eRender, eConsole, - &_saudio.backend.device))) - { - SOKOL_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed"); - goto error; - } - if (FAILED(IMMDevice_Activate(_saudio.backend.device, - _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), - CLSCTX_ALL, 0, - (void**)&_saudio.backend.audio_client))) - { - SOKOL_LOG("sokol_audio wasapi: device activate failed"); - goto error; - } - #endif - WAVEFORMATEX fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.nChannels = (WORD)_saudio.num_channels; - fmt.nSamplesPerSec = (DWORD)_saudio.sample_rate; - fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.wBitsPerSample = 16; - fmt.nBlockAlign = (fmt.nChannels * fmt.wBitsPerSample) / 8; - fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; - dur = (REFERENCE_TIME) - (((double)_saudio.buffer_frames) / (((double)_saudio.sample_rate) * (1.0/10000000.0))); - if (FAILED(IAudioClient_Initialize(_saudio.backend.audio_client, - AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, - dur, 0, &fmt, 0))) - { - SOKOL_LOG("sokol_audio wasapi: audio client initialize failed"); - goto error; - } - if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) { - SOKOL_LOG("sokol_audio wasapi: audio client get buffer size failed"); - goto error; - } - if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client, - _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient), - (void**)&_saudio.backend.render_client))) - { - SOKOL_LOG("sokol_audio wasapi: audio client GetService failed"); - goto error; - } - if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) { - SOKOL_LOG("sokol_audio wasapi: audio client SetEventHandle failed"); - goto error; - } - _saudio.backend.si16_bytes_per_frame = _saudio.num_channels * (int)sizeof(int16_t); - _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); - _saudio.backend.thread.src_buffer_frames = _saudio.buffer_frames; - _saudio.backend.thread.src_buffer_byte_size = _saudio.backend.thread.src_buffer_frames * _saudio.bytes_per_frame; - - /* allocate an intermediate buffer for sample format conversion */ - _saudio.backend.thread.src_buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.thread.src_buffer_byte_size); - SOKOL_ASSERT(_saudio.backend.thread.src_buffer); - - /* create streaming thread */ - _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0); - if (0 == _saudio.backend.thread.thread_handle) { - SOKOL_LOG("sokol_audio wasapi: CreateThread failed"); - goto error; - } - return true; -error: - _saudio_wasapi_release(); - return false; -} - -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { - if (_saudio.backend.thread.thread_handle) { - _saudio.backend.thread.stop = true; - SetEvent(_saudio.backend.thread.buffer_end_event); - WaitForSingleObject(_saudio.backend.thread.thread_handle, INFINITE); - CloseHandle(_saudio.backend.thread.thread_handle); - _saudio.backend.thread.thread_handle = 0; - } - if (_saudio.backend.audio_client) { - IAudioClient_Stop(_saudio.backend.audio_client); - } - _saudio_wasapi_release(); - - #if defined(_SAUDIO_WIN32) - CoUninitialize(); - #endif -} - -/*=== EMSCRIPTEN BACKEND IMPLEMENTATION ======================================*/ -#elif defined(_SAUDIO_EMSCRIPTEN) - -#ifdef __cplusplus -extern "C" { -#endif - -EMSCRIPTEN_KEEPALIVE int _saudio_emsc_pull(int num_frames) { - SOKOL_ASSERT(_saudio.backend.buffer); - if (num_frames == _saudio.buffer_frames) { - if (_saudio_has_callback()) { - _saudio_stream_callback((float*)_saudio.backend.buffer, num_frames, _saudio.num_channels); - } - else { - const int num_bytes = num_frames * _saudio.bytes_per_frame; - if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) { - /* not enough read data available, fill the entire buffer with silence */ - memset(_saudio.backend.buffer, 0, (size_t)num_bytes); - } - } - int res = (int) _saudio.backend.buffer; - return res; - } - else { - return 0; - } -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -/* setup the WebAudio context and attach a ScriptProcessorNode */ -EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size), { - Module._saudio_context = null; - Module._saudio_node = null; - if (typeof AudioContext !== 'undefined') { - Module._saudio_context = new AudioContext({ - sampleRate: sample_rate, - latencyHint: 'interactive', - }); - } - else if (typeof webkitAudioContext !== 'undefined') { - Module._saudio_context = new webkitAudioContext({ - sampleRate: sample_rate, - latencyHint: 'interactive', - }); - } - else { - Module._saudio_context = null; - console.log('sokol_audio.h: no WebAudio support'); - } - if (Module._saudio_context) { - console.log('sokol_audio.h: sample rate ', Module._saudio_context.sampleRate); - Module._saudio_node = Module._saudio_context.createScriptProcessor(buffer_size, 0, num_channels); - Module._saudio_node.onaudioprocess = function pump_audio(event) { - var num_frames = event.outputBuffer.length; - var ptr = __saudio_emsc_pull(num_frames); - if (ptr) { - var num_channels = event.outputBuffer.numberOfChannels; - for (var chn = 0; chn < num_channels; chn++) { - var chan = event.outputBuffer.getChannelData(chn); - for (var i = 0; i < num_frames; i++) { - chan[i] = HEAPF32[(ptr>>2) + ((num_channels*i)+chn)] - } - } - } - }; - Module._saudio_node.connect(Module._saudio_context.destination); - - // in some browsers, WebAudio needs to be activated on a user action - var resume_webaudio = function() { - if (Module._saudio_context) { - if (Module._saudio_context.state === 'suspended') { - Module._saudio_context.resume(); - } - } - }; - document.addEventListener('click', resume_webaudio, {once:true}); - document.addEventListener('touchstart', resume_webaudio, {once:true}); - document.addEventListener('keydown', resume_webaudio, {once:true}); - return 1; - } - else { - return 0; - } -}); - -/* shutdown the WebAudioContext and ScriptProcessorNode */ -EM_JS(void, saudio_js_shutdown, (void), { - if (Module._saudio_context !== null) { - if (Module._saudio_node) { - Module._saudio_node.disconnect(); - } - Module._saudio_context.close(); - Module._saudio_context = null; - Module._saudio_node = null; - } -}); - -/* get the actual sample rate back from the WebAudio context */ -EM_JS(int, saudio_js_sample_rate, (void), { - if (Module._saudio_context) { - return Module._saudio_context.sampleRate; - } - else { - return 0; - } -}); - -/* get the actual buffer size in number of frames */ -EM_JS(int, saudio_js_buffer_frames, (void), { - if (Module._saudio_node) { - return Module._saudio_node.bufferSize; - } - else { - return 0; - } -}); - -_SOKOL_PRIVATE bool _saudio_backend_init(void) { - if (saudio_js_init(_saudio.sample_rate, _saudio.num_channels, _saudio.buffer_frames)) { - _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; - _saudio.sample_rate = saudio_js_sample_rate(); - _saudio.buffer_frames = saudio_js_buffer_frames(); - const size_t buf_size = (size_t) (_saudio.buffer_frames * _saudio.bytes_per_frame); - _saudio.backend.buffer = (uint8_t*) SOKOL_MALLOC(buf_size); - return true; - } - else { - return false; - } -} - -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { - saudio_js_shutdown(); - if (_saudio.backend.buffer) { - SOKOL_FREE(_saudio.backend.buffer); - _saudio.backend.buffer = 0; - } -} - -/*=== ANDROID BACKEND IMPLEMENTATION ======================================*/ -#elif defined(_SAUDIO_ANDROID) - -#ifdef __cplusplus -extern "C" { -#endif - -_SOKOL_PRIVATE void _saudio_semaphore_init(_saudio_semaphore_t* sem) { - sem->count = 0; - int r = pthread_mutex_init(&sem->mutex, NULL); - SOKOL_ASSERT(r == 0); - - r = pthread_cond_init(&sem->cond, NULL); - SOKOL_ASSERT(r == 0); - - (void)(r); -} - -_SOKOL_PRIVATE void _saudio_semaphore_destroy(_saudio_semaphore_t* sem) -{ - pthread_cond_destroy(&sem->cond); - pthread_mutex_destroy(&sem->mutex); -} - -_SOKOL_PRIVATE void _saudio_semaphore_post(_saudio_semaphore_t* sem, int count) -{ - int r = pthread_mutex_lock(&sem->mutex); - SOKOL_ASSERT(r == 0); - - for (int ii = 0; ii < count; ii++) { - r = pthread_cond_signal(&sem->cond); - SOKOL_ASSERT(r == 0); - } - - sem->count += count; - r = pthread_mutex_unlock(&sem->mutex); - SOKOL_ASSERT(r == 0); - - (void)(r); -} - -_SOKOL_PRIVATE bool _saudio_semaphore_wait(_saudio_semaphore_t* sem) -{ - int r = pthread_mutex_lock(&sem->mutex); - SOKOL_ASSERT(r == 0); - - while (r == 0 && sem->count <= 0) { - r = pthread_cond_wait(&sem->cond, &sem->mutex); - } - - bool ok = (r == 0); - if (ok) { - --sem->count; - } - r = pthread_mutex_unlock(&sem->mutex); - (void)(r); - return ok; -} - -/* fill intermediate buffer with new data and reset buffer_pos */ -_SOKOL_PRIVATE void _saudio_opensles_fill_buffer(void) { - int src_buffer_frames = _saudio.buffer_frames; - if (_saudio_has_callback()) { - _saudio_stream_callback(_saudio.backend.src_buffer, src_buffer_frames, _saudio.num_channels); - } - else { - const int src_buffer_byte_size = src_buffer_frames * _saudio.num_channels * (int)sizeof(float); - if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.src_buffer, src_buffer_byte_size)) { - /* not enough read data available, fill the entire buffer with silence */ - memset(_saudio.backend.src_buffer, 0x0, (size_t)src_buffer_byte_size); - } - } -} - -_SOKOL_PRIVATE void SLAPIENTRY _saudio_opensles_play_cb(SLPlayItf player, void *context, SLuint32 event) { - (void)(context); - (void)(player); - - if (event & SL_PLAYEVENT_HEADATEND) { - _saudio_semaphore_post(&_saudio.backend.buffer_sem, 1); - } -} - -_SOKOL_PRIVATE void* _saudio_opensles_thread_fn(void* param) { - while (!_saudio.backend.thread_stop) { - /* get next output buffer, advance, next buffer. */ - int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; - _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_NUM_BUFFERS; - int16_t* next_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; - - /* queue this buffer */ - const int buffer_size_bytes = _saudio.buffer_frames * _saudio.num_channels * (int)sizeof(short); - (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, out_buffer, (SLuint32)buffer_size_bytes); - - /* fill the next buffer */ - _saudio_opensles_fill_buffer(); - const int num_samples = _saudio.num_channels * _saudio.buffer_frames; - for (int i = 0; i < num_samples; ++i) { - next_buffer[i] = (int16_t) (_saudio.backend.src_buffer[i] * 0x7FFF); - } - - _saudio_semaphore_wait(&_saudio.backend.buffer_sem); - } - - return 0; -} - -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { - _saudio.backend.thread_stop = 1; - pthread_join(_saudio.backend.thread, 0); - - if (_saudio.backend.player_obj) { - (*_saudio.backend.player_obj)->Destroy(_saudio.backend.player_obj); - } - - if (_saudio.backend.output_mix_obj) { - (*_saudio.backend.output_mix_obj)->Destroy(_saudio.backend.output_mix_obj); - } - - if (_saudio.backend.engine_obj) { - (*_saudio.backend.engine_obj)->Destroy(_saudio.backend.engine_obj); - } - - for (int i = 0; i < SAUDIO_NUM_BUFFERS; i++) { - SOKOL_FREE(_saudio.backend.output_buffers[i]); - } - SOKOL_FREE(_saudio.backend.src_buffer); -} - -_SOKOL_PRIVATE bool _saudio_backend_init(void) { - _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; - - for (int i = 0; i < SAUDIO_NUM_BUFFERS; ++i) { - const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; - _saudio.backend.output_buffers[i] = (int16_t*) SOKOL_MALLOC((size_t)buffer_size_bytes); - SOKOL_ASSERT(_saudio.backend.output_buffers[i]); - memset(_saudio.backend.output_buffers[i], 0x0, (size_t)buffer_size_bytes); - } - - { - const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames; - _saudio.backend.src_buffer = (float*) SOKOL_MALLOC((size_t)buffer_size_bytes); - SOKOL_ASSERT(_saudio.backend.src_buffer); - memset(_saudio.backend.src_buffer, 0x0, (size_t)buffer_size_bytes); - } - - /* Create engine */ - const SLEngineOption opts[] = { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE }; - if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) { - SOKOL_LOG("sokol_audio opensles: slCreateEngine failed"); - _saudio_backend_shutdown(); - return false; - } - - (*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE); - if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) { - SOKOL_LOG("sokol_audio opensles: GetInterface->Engine failed"); - _saudio_backend_shutdown(); - return false; - } - - /* Create output mix. */ - { - const SLInterfaceID ids[] = { SL_IID_VOLUME }; - const SLboolean req[] = { SL_BOOLEAN_FALSE }; - - if( (*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) - { - SOKOL_LOG("sokol_audio opensles: CreateOutputMix failed"); - _saudio_backend_shutdown(); + SOKOL_ASSERT(_saudio.backend.ca_audio_queue); + + /* create 2 audio buffers */ + for (int i = 0; i < 2; i++) { + _saudio_AudioQueueBufferRef buf = NULL; + const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; + res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_ALLOCATE_BUFFER_FAILED); + _saudio_coreaudio_backend_shutdown(); return false; } - (*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE); - - if((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) { - SOKOL_LOG("sokol_audio opensles: GetInterface->OutputMixVol failed"); - } + buf->mAudioDataByteSize = buf_byte_size; + _saudio_clear(buf->mAudioData, buf->mAudioDataByteSize); + AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); } - /* android buffer queue */ - _saudio.backend.in_locator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - _saudio.backend.in_locator.numBuffers = SAUDIO_NUM_BUFFERS; + /* init or modify actual playback parameters */ + _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; - /* data format */ - SLDataFormat_PCM format; - format.formatType = SL_DATAFORMAT_PCM; - format.numChannels = (SLuint32)_saudio.num_channels; - format.samplesPerSec = (SLuint32) (_saudio.sample_rate * 1000); - format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - format.containerSize = 16; - format.endianness = SL_BYTEORDER_LITTLEENDIAN; - - if (_saudio.num_channels == 2) { - format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - } else { - format.channelMask = SL_SPEAKER_FRONT_CENTER; - } - - SLDataSource src; - src.pLocator = &_saudio.backend.in_locator; - src.pFormat = &format; - - /* Output mix. */ - _saudio.backend.out_locator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - _saudio.backend.out_locator.outputMix = _saudio.backend.output_mix_obj; - - _saudio.backend.dst_data_sink.pLocator = &_saudio.backend.out_locator; - _saudio.backend.dst_data_sink.pFormat = NULL; - - /* setup player */ - { - const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; - const SLboolean req[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE }; - - (*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req); - - (*_saudio.backend.player_obj)->Realize(_saudio.backend.player_obj, SL_BOOLEAN_FALSE); - - (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player); - (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol); - - (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue); - } - - /* begin */ - { - const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; - (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, _saudio.backend.output_buffers[0], (SLuint32)buffer_size_bytes); - _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_NUM_BUFFERS; - - (*_saudio.backend.player)->RegisterCallback(_saudio.backend.player, _saudio_opensles_play_cb, NULL); - (*_saudio.backend.player)->SetCallbackEventsMask(_saudio.backend.player, SL_PLAYEVENT_HEADATEND); - (*_saudio.backend.player)->SetPlayState(_saudio.backend.player, SL_PLAYSTATE_PLAYING); - } - - /* create the buffer-streaming start thread */ - if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_opensles_thread_fn, 0)) { - _saudio_backend_shutdown(); + /* ...and start playback */ + res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_START_FAILED); + _saudio_coreaudio_backend_shutdown(); return false; } - return true; } -#ifdef __cplusplus -} /* extern "C" */ -#endif - #else #error "unsupported platform" #endif -/*=== PUBLIC API FUNCTIONS ===================================================*/ +bool _saudio_backend_init(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return _saudio_dummy_backend_init(); + #elif defined(_SAUDIO_LINUX) + return _saudio_alsa_backend_init(); + #elif defined(_SAUDIO_WINDOWS) + return _saudio_wasapi_backend_init(); + #elif defined(_SAUDIO_EMSCRIPTEN) + return _saudio_webaudio_backend_init(); + #elif defined(SAUDIO_ANDROID_AAUDIO) + return _saudio_aaudio_backend_init(); + #elif defined(SAUDIO_ANDROID_SLES) + return _saudio_sles_backend_init(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_init(); + #else + #error "unknown platform" + #endif +} + +void _saudio_backend_shutdown(void) { + #if defined(SOKOL_DUMMY_BACKEND) + _saudio_dummy_backend_shutdown(); + #elif defined(_SAUDIO_LINUX) + _saudio_alsa_backend_shutdown(); + #elif defined(_SAUDIO_WINDOWS) + _saudio_wasapi_backend_shutdown(); + #elif defined(_SAUDIO_EMSCRIPTEN) + _saudio_webaudio_backend_shutdown(); + #elif defined(SAUDIO_ANDROID_AAUDIO) + _saudio_aaudio_backend_shutdown(); + #elif defined(SAUDIO_ANDROID_SLES) + _saudio_sles_backend_shutdown(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_shutdown(); + #else + #error "unknown platform" + #endif +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { SOKOL_ASSERT(!_saudio.valid); SOKOL_ASSERT(desc); - memset(&_saudio, 0, sizeof(_saudio)); + SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + _saudio_clear(&_saudio, sizeof(_saudio)); _saudio.desc = *desc; _saudio.stream_cb = desc->stream_cb; _saudio.stream_userdata_cb = desc->stream_userdata_cb; @@ -2071,7 +2503,7 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { the requested packet size */ if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) { - SOKOL_LOG("sokol_audio.h: actual backend buffer size isn't multiple of requested packet size"); + _SAUDIO_ERROR(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE); _saudio_backend_shutdown(); return; } @@ -2079,12 +2511,16 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { _saudio_fifo_init(&_saudio.fifo, _saudio.packet_frames * _saudio.bytes_per_frame, _saudio.num_packets); _saudio.valid = true; } + else { + _saudio_fifo_destroy_mutex(&_saudio.fifo); + } } SOKOL_API_IMPL void saudio_shutdown(void) { if (_saudio.valid) { _saudio_backend_shutdown(); _saudio_fifo_shutdown(&_saudio.fifo); + _saudio_fifo_destroy_mutex(&_saudio.fifo); _saudio.valid = false; } } @@ -2113,6 +2549,19 @@ SOKOL_API_IMPL int saudio_channels(void) { return _saudio.num_channels; } +SOKOL_API_IMPL bool saudio_suspended(void) { + #if defined(_SAUDIO_EMSCRIPTEN) + if (_saudio.valid) { + return 1 == saudio_js_suspended(); + } + else { + return false; + } + #else + return false; + #endif +} + SOKOL_API_IMPL int saudio_expect(void) { if (_saudio.valid) { const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame; diff --git a/3rdparty/sokol/sokol_fetch.h b/3rdparty/sokol/sokol_fetch.h index 9ff62d3..21e9b5d 100644 --- a/3rdparty/sokol/sokol_fetch.h +++ b/3rdparty/sokol/sokol_fetch.h @@ -16,9 +16,6 @@ Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_FETCH_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_FETCH_API_DECL @@ -78,9 +75,11 @@ (1) initialize sokol-fetch with default parameters (but NOTE that the default setup parameters provide a safe-but-slow "serialized" - operation): + operation). In order to see any logging output in case or errors + you should always provide a logging function + (such as 'slog_func' from sokol_log.h): - sfetch_setup(&(sfetch_desc_t){ 0 }); + sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func }); (2) send a fetch-request to load a file from the current directory into a buffer big enough to hold the entire file content: @@ -90,8 +89,19 @@ sfetch_send(&(sfetch_request_t){ .path = "my_file.txt", .callback = response_callback, - .buffer_ptr = buf, - .buffer_size = sizeof(buf) + .buffer = { + .ptr = buf, + .size = sizeof(buf) + } + }); + + If 'buf' is a value (e.g. an array or struct item), the .buffer item can + be initialized with the SFETCH_RANGE() helper macro: + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = response_callback, + .buffer = SFETCH_RANGE(buf) }); (3) write a 'response-callback' function, this will be called whenever @@ -100,10 +110,10 @@ void response_callback(const sfetch_response_t* response) { if (response->fetched) { - // data has been loaded, and is available via - // 'buffer_ptr' and 'fetched_size': - const void* data = response->buffer_ptr; - uint64_t num_bytes = response->fetched_size; + // data has been loaded, and is available via the + // sfetch_range_t struct item 'data': + const void* ptr = response->data.ptr; + size_t num_bytes = response->data.size; } if (response->finished) { // the 'finished'-flag is the catch-all flag for when the request @@ -121,7 +131,7 @@ (4) pump the sokol-fetch message queues, and invoke response callbacks by calling: - sfetch_dowork(); + sfetch_dowork(); In an event-driven app this should be called in the event loop. If you use sokol-app this would be in your frame_cb function. @@ -245,7 +255,7 @@ important information how streaming works if the web server is serving compressed data. - - buffer_ptr, buffer_size (void*, uint64_t, optional) + - buffer (sfetch_range_t) This is a optional pointer/size pair describing a chunk of memory where data will be loaded into (if no buffer is provided upfront, this must happen in the response callback). If a buffer is provided, @@ -253,15 +263,15 @@ is zero), or the *uncompressed* data for one downloaded chunk (if chunk_size is > 0). - - user_data_ptr, user_data_size (const void*, uint32_t, both optional) - user_data_ptr and user_data_size describe an optional POD (plain-old-data) - associated with the request which will be copied(!) into an internal - memory block. The maximum default size of this memory block is - 128 bytes (but can be overridden by defining SFETCH_MAX_USERDATA_UINT64 - before including the notification, note that this define is in - "number of uint64_t", not number of bytes). The user-data - block is 8-byte aligned, and will be copied via memcpy() (so don't - put any C++ "smart members" in there). + - user_data (sfetch_range_t) + The user_data ptr/size range struct describe an optional POD blob + (plain-old-data) associated with the request which will be copied(!) + into an internal memory block. The maximum default size of this + memory block is 128 bytes (but can be overridden by defining + SFETCH_MAX_USERDATA_UINT64 before including the notification, note + that this define is in "number of uint64_t", not number of bytes). + The user-data block is 8-byte aligned, and will be copied via + memcpy() (so don't put any C++ "smart members" in there). NOTE that request handles are strictly thread-local and only unique within the thread the handle was created on, and all function calls @@ -348,9 +358,9 @@ --------------------------------------------- Continues a paused request, counterpart to the sfetch_pause() function. - void sfetch_bind_buffer(sfetch_handle_t request, void* buffer_ptr, uint64_t buffer_size) + void sfetch_bind_buffer(sfetch_handle_t request, sfetch_range_t buffer) ---------------------------------------------------------------------------------------- - This "binds" a new buffer (pointer/size pair) to an active request. The + This "binds" a new buffer (as pointer/size pair) to an active request. The function *must* be called from inside the response-callback, and there must not already be another buffer bound. @@ -477,9 +487,9 @@ The response callback will be called so that the user-code can process the loaded data using the following sfetch_response_t struct members: - - fetched_size: the number of bytes in the provided buffer - - buffer_ptr: pointer to the start of fetched data - - fetched_offset: the byte offset of the loaded data chunk in the + - data.ptr: pointer to the start of fetched data + - data.size: the number of bytes in the provided buffer + - data_offset: the byte offset of the loaded data chunk in the overall file (this is only set to a non-zero value in a streaming scenario) @@ -503,10 +513,10 @@ void response_callback(const sfetch_response_t* response) { if (response->fetched) { // request is in FETCHED state, the loaded data is available - // in .buffer_ptr, and the number of bytes that have been - // loaded in .fetched_size: - const void* data = response->buffer_ptr; - const uint64_t num_bytes = response->fetched_size; + // in .data.ptr, and the number of bytes that have been + // loaded in .data.size: + const void* data = response->data.ptr; + size_t num_bytes = response->data.size; } if (response->finished) { // the finished flag is set either when all data @@ -632,12 +642,12 @@ Channels and lanes are (somewhat artificial) concepts to manage parallelization, prioritization and rate-limiting. - Channels can be used to parallelize message processing for better - 'pipeline throughput', and to prioritize messages: user-code could - reserve one channel for "small and big" streaming downloads, - another channel for "regular" downloads and yet another high-priority channel - which would only be used for small files which need to start loading - immediately. + Channels can be used to parallelize message processing for better 'pipeline + throughput', and to prioritize requests: user-code could reserve one + channel for streaming downloads which need to run in parallel to other + requests, another channel for "regular" downloads and yet another + high-priority channel which would only be used for small files which need + to start loading immediately. Each channel comes with its own IO thread and message queues for pumping messages in and out of the thread. The channel where a request is @@ -709,7 +719,7 @@ the 'inherent latency' of a request: - if a buffer is provided upfront, the response-callback won't be - called in the OPENED state, but start right with the FETCHED state + called in the DISPATCHED state, but start right with the FETCHED state where data has already been loaded into the buffer - there is no separate CLOSED state where the callback is invoked @@ -722,7 +732,7 @@ the next frame (or two calls to sfetch_dowork()). If no buffer is provided upfront, one frame of latency is added because - the response callback needs to be invoked in the OPENED state so that + the response callback needs to be invoked in the DISPATCHED state so that the user code can bind a buffer. This means the best case for a request without an upfront-provided @@ -747,39 +757,39 @@ 1 LANE (8 frames): Lane 0: ------------- - REQ 0 OPENED + REQ 0 DISPATCHED REQ 0 FETCHED - REQ 1 OPENED + REQ 1 DISPATCHED REQ 1 FETCHED - REQ 2 OPENED + REQ 2 DISPATCHED REQ 2 FETCHED - REQ 3 OPENED + REQ 3 DISPATCHED REQ 3 FETCHED Note how the request don't overlap, so they can all use the same buffer. 2 LANES (4 frames): - Lane 0: Lane 1: - --------------------------------- - REQ 0 OPENED REQ 1 OPENED - REQ 0 FETCHED REQ 1 FETCHED - REQ 2 OPENED REQ 3 OPENED - REQ 2 FETCHED REQ 3 FETCHED + Lane 0: Lane 1: + ------------------------------------ + REQ 0 DISPATCHED REQ 1 DISPATCHED + REQ 0 FETCHED REQ 1 FETCHED + REQ 2 DISPATCHED REQ 3 DISPATCHED + REQ 2 FETCHED REQ 3 FETCHED This reduces the overall time to 4 frames, but now you need 2 buffers so that requests don't scribble over each other. 4 LANES (2 frames): - Lane 0: Lane 1: Lane 2: Lane 3: - ------------------------------------------------------------- - REQ 0 OPENED REQ 1 OPENED REQ 2 OPENED REQ 3 OPENED - REQ 0 FETCHED REQ 1 FETCHED REQ 2 FETCHED REQ 3 FETCHED + Lane 0: Lane 1: Lane 2: Lane 3: + ---------------------------------------------------------------------------- + REQ 0 DISPATCHED REQ 1 DISPATCHED REQ 2 DISPATCHED REQ 3 DISPATCHED + REQ 0 FETCHED REQ 1 FETCHED REQ 2 FETCHED REQ 3 FETCHED Now we're down to the same 'best-case' latency as sending a single request. Apart from the memory requirements for the streaming buffers (which is - under your control), you can be generous with the number of channels, + under your control), you can be generous with the number of lanes, they don't add any processing overhead. The last option for tweaking latency and throughput is channels. Each @@ -793,6 +803,81 @@ the blocking traditional file IO functions, not for performance reasons. + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sfetch_setup(&(sfetch_desc_t){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_fetch.h + itself though, not any allocations in OS libraries. + + Memory allocation will only happen on the same thread where sfetch_setup() + was called, so you don't need to worry about thread-safety. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sfetch_setup(&(sfetch_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sfetch' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SFETCH_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_fetch.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-fetch like this: + + sfetch_setup(&(sfetch_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + FUTURE PLANS / V2.0 IDEA DUMP ============================= - An optional polling API (as alternative to callback API) @@ -837,6 +922,7 @@ distribution. */ #define SOKOL_FETCH_INCLUDED (1) +#include // size_t #include #include @@ -857,13 +943,99 @@ extern "C" { #endif +/* + sfetch_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sfetch_log_item', and in debug mode only, + corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SFETCH_LOG_ITEMS \ + _SFETCH_LOGITEM_XMACRO(OK, "Ok") \ + _SFETCH_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SFETCH_LOGITEM_XMACRO(FILE_PATH_UTF8_DECODING_FAILED, "failed converting file path from UTF8 to wide") \ + _SFETCH_LOGITEM_XMACRO(SEND_QUEUE_FULL, "send queue full (adjust via sfetch_desc_t.max_requests)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CHANNEL_INDEX_TOO_BIG, "channel index too big (adjust via sfetch_desc_t.num_channels)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_PATH_IS_NULL, "file path is nullptr (sfetch_request_t.path)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_PATH_TOO_LONG, "file path is too long (SFETCH_MAX_PATH)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CALLBACK_MISSING, "no callback provided (sfetch_request_t.callback)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CHUNK_SIZE_GREATER_BUFFER_SIZE, "chunk size is greater buffer size (sfetch_request_t.chunk_size vs .buffer.size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_PTR_IS_SET_BUT_USERDATA_SIZE_IS_NULL, "user data ptr is set but user data size is null (sfetch_request_t.user_data.ptr vs .size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_PTR_IS_NULL_BUT_USERDATA_SIZE_IS_NOT, "user data ptr is null but size is not (sfetch_request_t.user_data.ptr vs .size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_SIZE_TOO_BIG, "user data size too big (see SFETCH_MAX_USERDATA_UINT64)") \ + _SFETCH_LOGITEM_XMACRO(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS, "clamping num channels to SFETCH_MAX_CHANNELS") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_POOL_EXHAUSTED, "request pool exhausted (tweak via sfetch_desc_t.max_requests)") \ + +#define _SFETCH_LOGITEM_XMACRO(item,msg) SFETCH_LOGITEM_##item, +typedef enum sfetch_log_item_t { + _SFETCH_LOG_ITEMS +} sfetch_log_item_t; +#undef _SFETCH_LOGITEM_XMACRO + +/* + sfetch_logger_t + + Used in sfetch_desc_t to provide a custom logging and error reporting + callback to sokol-fetch. +*/ +typedef struct sfetch_logger_t { + void (*func)( + const char* tag, // always "sfetch" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SFETCH_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_fetch.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sfetch_logger_t; + +/* + sfetch_range_t + + A pointer-size pair struct to pass memory ranges into and out of sokol-fetch. + When initialized from a value type (array or struct) you can use the + SFETCH_RANGE() helper macro to build an sfetch_range_t struct. +*/ +typedef struct sfetch_range_t { + const void* ptr; + size_t size; +} sfetch_range_t; + +// disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' +#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer +#endif +#if defined(__cplusplus) +#define SFETCH_RANGE(x) sfetch_range_t{ &x, sizeof(x) } +#else +#define SFETCH_RANGE(x) (sfetch_range_t){ &x, sizeof(x) } +#endif + +/* + sfetch_allocator_t + + Used in sfetch_desc_t to provide custom memory-alloc and -free functions + to sokol_fetch.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sfetch_allocator_t { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sfetch_allocator_t; + /* configuration values for sfetch_setup() */ typedef struct sfetch_desc_t { - uint32_t _start_canary; - uint32_t max_requests; /* max number of active requests across all channels, default is 128 */ - uint32_t num_channels; /* number of channels to fetch requests in parallel, default is 1 */ - uint32_t num_lanes; /* max number of requests active on the same channel, default is 1 */ - uint32_t _end_canary; + uint32_t max_requests; // max number of active requests across all channels (default: 128) + uint32_t num_channels; // number of channels to fetch requests in parallel (default: 1) + uint32_t num_lanes; // max number of requests active on the same channel (default: 1) + sfetch_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sfetch_logger_t logger; // optional log function overrides (default: NO LOGGING!) } sfetch_desc_t; /* a request handle to identify an active fetch request, returned by sfetch_send() */ @@ -882,22 +1054,21 @@ typedef enum sfetch_error_t { /* the response struct passed to the response callback */ typedef struct sfetch_response_t { - sfetch_handle_t handle; /* request handle this response belongs to */ - bool dispatched; /* true when request is in DISPATCHED state (lane has been assigned) */ - bool fetched; /* true when request is in FETCHED state (fetched data is available) */ - bool paused; /* request is currently in paused state */ - bool finished; /* this is the last response for this request */ - bool failed; /* request has failed (always set together with 'finished') */ - bool cancelled; /* request was cancelled (always set together with 'finished') */ - sfetch_error_t error_code; /* more detailed error code when failed is true */ - uint32_t channel; /* the channel which processes this request */ - uint32_t lane; /* the lane this request occupies on its channel */ - const char* path; /* the original filesystem path of the request (FIXME: this is unsafe, wrap in API call?) */ - void* user_data; /* pointer to read/write user-data area (FIXME: this is unsafe, wrap in API call?) */ - uint32_t fetched_offset; /* current offset of fetched data chunk in file data */ - uint32_t fetched_size; /* size of fetched data chunk in number of bytes */ - void* buffer_ptr; /* pointer to buffer with fetched data */ - uint32_t buffer_size; /* overall buffer size (may be >= than fetched_size!) */ + sfetch_handle_t handle; // request handle this response belongs to + bool dispatched; // true when request is in DISPATCHED state (lane has been assigned) + bool fetched; // true when request is in FETCHED state (fetched data is available) + bool paused; // request is currently in paused state + bool finished; // this is the last response for this request + bool failed; // request has failed (always set together with 'finished') + bool cancelled; // request was cancelled (always set together with 'finished') + sfetch_error_t error_code; // more detailed error code when failed is true + uint32_t channel; // the channel which processes this request + uint32_t lane; // the lane this request occupies on its channel + const char* path; // the original filesystem path of the request + void* user_data; // pointer to read/write user-data area + uint32_t data_offset; // current offset of fetched data chunk in the overall file data + sfetch_range_t data; // the fetched data as ptr/size pair (data.ptr == buffer.ptr, data.size <= buffer.size) + sfetch_range_t buffer; // the user-provided buffer which holds the fetched data } sfetch_response_t; /* response callback function signature */ @@ -905,16 +1076,12 @@ typedef void(*sfetch_callback_t)(const sfetch_response_t*); /* request parameters passed to sfetch_send() */ typedef struct sfetch_request_t { - uint32_t _start_canary; - uint32_t channel; /* index of channel this request is assigned to (default: 0) */ - const char* path; /* filesystem path or HTTP URL (required) */ - sfetch_callback_t callback; /* response callback function pointer (required) */ - void* buffer_ptr; /* buffer pointer where data will be loaded into (optional) */ - uint32_t buffer_size; /* buffer size in number of bytes (optional) */ - uint32_t chunk_size; /* number of bytes to load per stream-block (optional) */ - const void* user_data_ptr; /* pointer to a POD user-data block which will be memcpy'd(!) (optional) */ - uint32_t user_data_size; /* size of user-data block (optional) */ - uint32_t _end_canary; + uint32_t channel; // index of channel this request is assigned to (default: 0) + const char* path; // filesystem path or HTTP URL (required) + sfetch_callback_t callback; // response callback function pointer (required) + uint32_t chunk_size; // number of bytes to load per stream-block (optional) + sfetch_range_t buffer; // a memory buffer where the data will be loaded into (optional) + sfetch_range_t user_data; // ptr/size of a POD user data block which will be memcpy'd (optional) } sfetch_request_t; /* setup sokol-fetch (can be called on multiple threads) */ @@ -938,7 +1105,7 @@ SOKOL_FETCH_API_DECL bool sfetch_handle_valid(sfetch_handle_t h); SOKOL_FETCH_API_DECL void sfetch_dowork(void); /* bind a data buffer to a request (request must not currently have a buffer bound, must be called from response callback */ -SOKOL_FETCH_API_DECL void sfetch_bind_buffer(sfetch_handle_t h, void* buffer_ptr, uint32_t buffer_size); +SOKOL_FETCH_API_DECL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer); /* clear the 'buffer binding' of a request, returns previous buffer pointer (can be 0), must be called from response callback */ SOKOL_FETCH_API_DECL void* sfetch_unbind_buffer(sfetch_handle_t h); /* cancel a request that's in flight (will call response callback with .cancelled + .finished) */ @@ -958,9 +1125,21 @@ inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfe #endif #endif // SOKOL_FETCH_INCLUDED -/*--- IMPLEMENTATION ---------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_FETCH_IMPL #define SOKOL_FETCH_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfetch_desc_t.allocator to override memory allocation functions" +#endif + +#include /* malloc, free */ #include /* memset, memcpy */ #ifndef SFETCH_MAX_PATH @@ -978,26 +1157,13 @@ inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfe #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) @@ -1038,16 +1204,17 @@ inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfe #define _SFETCH_HAS_THREADS (1) #endif -/*=== private type definitions ===============================================*/ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs typedef struct _sfetch_path_t { char buf[SFETCH_MAX_PATH]; } _sfetch_path_t; -typedef struct _sfetch_buffer_t { - uint8_t* ptr; - uint32_t size; -} _sfetch_buffer_t; - /* a thread with incoming and outgoing message queue syncing */ #if _SFETCH_PLATFORM_POSIX typedef struct { @@ -1095,7 +1262,7 @@ typedef struct { sfetch_error_t error_code; bool finished; /* user thread only */ - uint32_t user_data_size; + size_t user_data_size; uint64_t user_data[SFETCH_MAX_USERDATA_UINT64]; } _sfetch_item_user_t; @@ -1136,7 +1303,7 @@ typedef struct { uint32_t lane; uint32_t chunk_size; sfetch_callback_t callback; - _sfetch_buffer_t buffer; + sfetch_range_t buffer; /* updated by IO-thread, off-limits to user thread */ _sfetch_item_thread_t thread; @@ -1169,8 +1336,7 @@ typedef struct { /* an IO channel with its own IO thread */ struct _sfetch_t; typedef struct { - struct _sfetch_t* ctx; /* back-pointer to thread-local _sfetch state pointer, - since this isn't accessible from the IO threads */ + struct _sfetch_t* ctx; // back-pointer to thread-local _sfetch state pointer, since this isn't accessible from the IO threads _sfetch_ring_t free_lanes; _sfetch_ring_t user_sent; _sfetch_ring_t user_incoming; @@ -1202,10 +1368,93 @@ static __thread _sfetch_t* _sfetch; #else static _sfetch_t* _sfetch; #endif - -/*=== general helper functions and macros =====================================*/ #define _sfetch_def(val, def) (((val) == 0) ? (def) : (val)) +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SFETCH_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sfetch_log_messages[] = { + _SFETCH_LOG_ITEMS +}; +#undef _SFETCH_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SFETCH_PANIC(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 0, __LINE__) +#define _SFETCH_ERROR(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 1, __LINE__) +#define _SFETCH_WARN(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 2, __LINE__) +#define _SFETCH_INFO(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 3, __LINE__) + +static void _sfetch_log(sfetch_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sfetch->desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sfetch_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sfetch->desc.logger.func("sfetch", log_level, log_item, message, line_nr, filename, _sfetch->desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +_SOKOL_PRIVATE void _sfetch_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (allocator->alloc) { + ptr = allocator->alloc(size, allocator->user_data); + } + else { + ptr = malloc(size); + } + if (0 == ptr) { + _SFETCH_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sfetch_malloc(size_t size) { + return _sfetch_malloc_with_allocator(&_sfetch->desc.allocator, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) { + void* ptr = _sfetch_malloc(size); + _sfetch_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sfetch_free(void* ptr) { + if (_sfetch->desc.allocator.free) { + _sfetch->desc.allocator.free(ptr, _sfetch->desc.allocator.user_data); + } + else { + free(ptr); + } +} + _SOKOL_PRIVATE _sfetch_t* _sfetch_ctx(void) { return _sfetch; } @@ -1221,7 +1470,7 @@ _SOKOL_PRIVATE void _sfetch_path_copy(_sfetch_path_t* dst, const char* src) { dst->buf[SFETCH_MAX_PATH-1] = 0; } else { - memset(dst->buf, 0, SFETCH_MAX_PATH); + _sfetch_clear(dst->buf, SFETCH_MAX_PATH); } } @@ -1231,21 +1480,13 @@ _SOKOL_PRIVATE _sfetch_path_t _sfetch_path_make(const char* str) { return res; } -_SOKOL_PRIVATE uint32_t _sfetch_make_id(uint32_t index, uint32_t gen_ctr) { - return (gen_ctr<<16) | (index & 0xFFFF); -} - -_SOKOL_PRIVATE sfetch_handle_t _sfetch_make_handle(uint32_t slot_id) { - sfetch_handle_t h; - h.id = slot_id; - return h; -} - -_SOKOL_PRIVATE uint32_t _sfetch_slot_index(uint32_t slot_id) { - return slot_id & 0xFFFF; -} - -/*=== a circular message queue ===============================================*/ +// ███ ███ ███████ ███████ ███████ █████ ██████ ███████ ██████ ██ ██ ███████ ██ ██ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ███████ ███████ ███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ███████ ██ ██ ██████ ███████ ██████ ██████ ███████ ██████ ███████ +// ▀▀ +// >>message queue _SOKOL_PRIVATE uint32_t _sfetch_ring_wrap(const _sfetch_ring_t* rb, uint32_t i) { return i % rb->num; } @@ -1253,7 +1494,7 @@ _SOKOL_PRIVATE uint32_t _sfetch_ring_wrap(const _sfetch_ring_t* rb, uint32_t i) _SOKOL_PRIVATE void _sfetch_ring_discard(_sfetch_ring_t* rb) { SOKOL_ASSERT(rb); if (rb->buf) { - SOKOL_FREE(rb->buf); + _sfetch_free(rb->buf); rb->buf = 0; } rb->head = 0; @@ -1269,9 +1510,8 @@ _SOKOL_PRIVATE bool _sfetch_ring_init(_sfetch_ring_t* rb, uint32_t num_slots) { /* one slot reserved to detect full vs empty */ rb->num = num_slots + 1; const size_t queue_size = rb->num * sizeof(sfetch_handle_t); - rb->buf = (uint32_t*) SOKOL_MALLOC(queue_size); + rb->buf = (uint32_t*) _sfetch_malloc_clear(queue_size); if (rb->buf) { - memset(rb->buf, 0, queue_size); return true; } else { @@ -1328,49 +1568,68 @@ _SOKOL_PRIVATE uint32_t _sfetch_ring_peek(const _sfetch_ring_t* rb, uint32_t ind return rb->buf[rb_index]; } -/*=== request pool implementation ============================================*/ +// ██████ ███████ ██████ ██ ██ ███████ ███████ ████████ ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ██ ██ ██ ██ █████ ███████ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██████ ██████ ███████ ███████ ██ ██ ██████ ██████ ███████ +// ▀▀ +// >>request pool +_SOKOL_PRIVATE uint32_t _sfetch_make_id(uint32_t index, uint32_t gen_ctr) { + return (gen_ctr<<16) | (index & 0xFFFF); +} + +_SOKOL_PRIVATE sfetch_handle_t _sfetch_make_handle(uint32_t slot_id) { + sfetch_handle_t h; + h.id = slot_id; + return h; +} + +_SOKOL_PRIVATE uint32_t _sfetch_slot_index(uint32_t slot_id) { + return slot_id & 0xFFFF; +} + _SOKOL_PRIVATE void _sfetch_item_init(_sfetch_item_t* item, uint32_t slot_id, const sfetch_request_t* request) { SOKOL_ASSERT(item && (0 == item->handle.id)); SOKOL_ASSERT(request && request->path); - memset(item, 0, sizeof(_sfetch_item_t)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); item->handle.id = slot_id; item->state = _SFETCH_STATE_INITIAL; item->channel = request->channel; item->chunk_size = request->chunk_size; item->lane = _SFETCH_INVALID_LANE; item->callback = request->callback; - item->buffer.ptr = (uint8_t*) request->buffer_ptr; - item->buffer.size = request->buffer_size; + item->buffer = request->buffer; item->path = _sfetch_path_make(request->path); #if !_SFETCH_PLATFORM_EMSCRIPTEN item->thread.file_handle = _SFETCH_INVALID_FILE_HANDLE; #endif - if (request->user_data_ptr && - (request->user_data_size > 0) && - (request->user_data_size <= (SFETCH_MAX_USERDATA_UINT64*8))) + if (request->user_data.ptr && + (request->user_data.size > 0) && + (request->user_data.size <= (SFETCH_MAX_USERDATA_UINT64*8))) { - item->user.user_data_size = request->user_data_size; - memcpy(item->user.user_data, request->user_data_ptr, request->user_data_size); + item->user.user_data_size = request->user_data.size; + memcpy(item->user.user_data, request->user_data.ptr, request->user_data.size); } } _SOKOL_PRIVATE void _sfetch_item_discard(_sfetch_item_t* item) { SOKOL_ASSERT(item && (0 != item->handle.id)); - memset(item, 0, sizeof(_sfetch_item_t)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); } _SOKOL_PRIVATE void _sfetch_pool_discard(_sfetch_pool_t* pool) { SOKOL_ASSERT(pool); if (pool->free_slots) { - SOKOL_FREE(pool->free_slots); + _sfetch_free(pool->free_slots); pool->free_slots = 0; } if (pool->gen_ctrs) { - SOKOL_FREE(pool->gen_ctrs); + _sfetch_free(pool->gen_ctrs); pool->gen_ctrs = 0; } if (pool->items) { - SOKOL_FREE(pool->items); + _sfetch_free(pool->items); pool->items = 0; } pool->size = 0; @@ -1385,17 +1644,15 @@ _SOKOL_PRIVATE bool _sfetch_pool_init(_sfetch_pool_t* pool, uint32_t num_items) pool->size = num_items + 1; pool->free_top = 0; const size_t items_size = pool->size * sizeof(_sfetch_item_t); - pool->items = (_sfetch_item_t*) SOKOL_MALLOC(items_size); + pool->items = (_sfetch_item_t*) _sfetch_malloc_clear(items_size); /* generation counters indexable by pool slot index, slot 0 is reserved */ const size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; - pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + pool->gen_ctrs = (uint32_t*) _sfetch_malloc_clear(gen_ctrs_size); SOKOL_ASSERT(pool->gen_ctrs); /* NOTE: it's not a bug to only reserve num_items here */ const size_t free_slots_size = num_items * sizeof(int); - pool->free_slots = (uint32_t*) SOKOL_MALLOC(free_slots_size); + pool->free_slots = (uint32_t*) _sfetch_malloc_clear(free_slots_size); if (pool->items && pool->free_slots) { - memset(pool->items, 0, items_size); - memset(pool->gen_ctrs, 0, gen_ctrs_size); /* never allocate the 0-th item, this is the reserved 'invalid item' */ for (uint32_t i = pool->size - 1; i >= 1; i--) { pool->free_slots[pool->free_top++] = i; @@ -1461,7 +1718,13 @@ _SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_lookup(_sfetch_pool_t* pool, ui return 0; } -/*=== PLATFORM WRAPPER FUNCTIONS =============================================*/ +// ██████ ██████ ███████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ███████ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ███████ ██ ██ ██ +// +// >>posix #if _SFETCH_PLATFORM_POSIX _SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { return fopen(path->buf, "rb"); @@ -1618,10 +1881,17 @@ _SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _s } #endif /* _SFETCH_PLATFORM_POSIX */ +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows #if _SFETCH_PLATFORM_WINDOWS _SOKOL_PRIVATE bool _sfetch_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); - memset(dst, 0, (size_t)dst_num_bytes); + _sfetch_clear(dst, (size_t)dst_num_bytes); const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); if ((dst_needed > 0) && (dst_needed < dst_chars)) { @@ -1637,7 +1907,7 @@ _SOKOL_PRIVATE bool _sfetch_win32_utf8_to_wide(const char* src, wchar_t* dst, in _SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { wchar_t w_path[SFETCH_MAX_PATH]; if (!_sfetch_win32_utf8_to_wide(path->buf, w_path, sizeof(w_path))) { - SOKOL_LOG("_sfetch_file_open: error converting UTF-8 path to wide string"); + _SFETCH_ERROR(FILE_PATH_UTF8_DECODING_FAILED); return 0; } _sfetch_file_handle_t h = CreateFileW( @@ -1797,7 +2067,13 @@ _SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _s } #endif /* _SFETCH_PLATFORM_WINDOWS */ -/*=== IO CHANNEL implementation ==============================================*/ +// ██████ ██ ██ █████ ███ ██ ███ ██ ███████ ██ ███████ +// ██ ██ ██ ██ ██ ████ ██ ████ ██ ██ ██ ██ +// ██ ███████ ███████ ██ ██ ██ ██ ██ ██ █████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ████ ██ ████ ███████ ███████ ███████ +// +// >>channels /* per-channel request handler for native platforms accessing the local filesystem */ #if _SFETCH_HAS_THREADS @@ -1805,7 +2081,7 @@ _SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { _sfetch_state_t state; _sfetch_path_t* path; _sfetch_item_thread_t* thread; - _sfetch_buffer_t* buffer; + sfetch_range_t* buffer; uint32_t chunk_size; { _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); @@ -1874,7 +2150,7 @@ _SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { } } if (!thread->failed) { - if (_sfetch_file_read(thread->file_handle, read_offset, bytes_to_read, buffer->ptr)) { + if (_sfetch_file_read(thread->file_handle, read_offset, bytes_to_read, (void*)buffer->ptr)) { thread->fetched_size = bytes_to_read; thread->fetched_offset += bytes_to_read; } @@ -1921,19 +2197,18 @@ _SOKOL_PRIVATE void* _sfetch_channel_thread_func(void* arg) { #endif /* _SFETCH_HAS_THREADS */ #if _SFETCH_PLATFORM_EMSCRIPTEN -/*=== embedded Javascript helper functions ===================================*/ EM_JS(void, sfetch_js_send_head_request, (uint32_t slot_id, const char* path_cstr), { - var path_str = UTF8ToString(path_cstr); - var req = new XMLHttpRequest(); + const path_str = UTF8ToString(path_cstr); + const req = new XMLHttpRequest(); req.open('HEAD', path_str); req.onreadystatechange = function() { - if (this.readyState == this.DONE) { - if (this.status == 200) { - var content_length = this.getResponseHeader('Content-Length'); + if (req.readyState == XMLHttpRequest.DONE) { + if (req.status == 200) { + const content_length = req.getResponseHeader('Content-Length'); __sfetch_emsc_head_response(slot_id, content_length); } else { - __sfetch_emsc_failed_http_status(slot_id, this.status); + __sfetch_emsc_failed_http_status(slot_id, req.status); } } }; @@ -1942,19 +2217,19 @@ EM_JS(void, sfetch_js_send_head_request, (uint32_t slot_id, const char* path_cst /* if bytes_to_read != 0, a range-request will be sent, otherwise a normal request */ EM_JS(void, sfetch_js_send_get_request, (uint32_t slot_id, const char* path_cstr, uint32_t offset, uint32_t bytes_to_read, void* buf_ptr, uint32_t buf_size), { - var path_str = UTF8ToString(path_cstr); - var req = new XMLHttpRequest(); + const path_str = UTF8ToString(path_cstr); + const req = new XMLHttpRequest(); req.open('GET', path_str); req.responseType = 'arraybuffer'; - var need_range_request = (bytes_to_read > 0); + const need_range_request = (bytes_to_read > 0); if (need_range_request) { req.setRequestHeader('Range', 'bytes='+offset+'-'+(offset+bytes_to_read-1)); } req.onreadystatechange = function() { - if (this.readyState == this.DONE) { - if ((this.status == 206) || ((this.status == 200) && !need_range_request)) { - var u8_array = new Uint8Array(req.response); - var content_fetched_size = u8_array.length; + if (req.readyState == XMLHttpRequest.DONE) { + if ((req.status == 206) || ((req.status == 200) && !need_range_request)) { + const u8_array = new Uint8Array(\x2F\x2A\x2A @type {!ArrayBuffer} \x2A\x2F (req.response)); + const content_fetched_size = u8_array.length; if (content_fetched_size <= buf_size) { HEAPU8.set(u8_array, buf_ptr); __sfetch_emsc_get_response(slot_id, bytes_to_read, content_fetched_size); @@ -1964,7 +2239,7 @@ EM_JS(void, sfetch_js_send_get_request, (uint32_t slot_id, const char* path_cstr } } else { - __sfetch_emsc_failed_http_status(slot_id, this.status); + __sfetch_emsc_failed_http_status(slot_id, req.status); } } }; @@ -1994,7 +2269,7 @@ void _sfetch_emsc_send_get_request(uint32_t slot_id, _sfetch_item_t* item) { SOKOL_ASSERT(bytes_to_read > 0); offset = item->thread.http_range_offset; } - sfetch_js_send_get_request(slot_id, item->path.buf, offset, bytes_to_read, item->buffer.ptr, item->buffer.size); + sfetch_js_send_get_request(slot_id, item->path.buf, offset, bytes_to_read, (void*)item->buffer.ptr, item->buffer.size); } } @@ -2088,7 +2363,7 @@ _SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { } else { /* just move all other items (e.g. paused or cancelled) - into the outgoing queue, so they wont get lost + into the outgoing queue, so they won't get lost */ _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); } @@ -2155,14 +2430,14 @@ _SOKOL_PRIVATE bool _sfetch_channel_send(_sfetch_channel_t* chn, uint32_t slot_i return true; } else { - SOKOL_LOG("sfetch_send: user_sent queue is full)"); + _SFETCH_ERROR(SEND_QUEUE_FULL); return false; } } _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { sfetch_response_t response; - memset(&response, 0, sizeof(response)); + _sfetch_clear(&response, sizeof(response)); response.handle = item->handle; response.dispatched = (item->state == _SFETCH_STATE_DISPATCHED); response.fetched = (item->state == _SFETCH_STATE_FETCHED); @@ -2175,10 +2450,10 @@ _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { response.lane = item->lane; response.path = item->path.buf; response.user_data = item->user.user_data; - response.fetched_offset = item->user.fetched_offset - item->user.fetched_size; - response.fetched_size = item->user.fetched_size; - response.buffer_ptr = item->buffer.ptr; - response.buffer_size = item->buffer.size; + response.data_offset = item->user.fetched_offset - item->user.fetched_size; + response.data.ptr = item->buffer.ptr; + response.data.size = item->user.fetched_size; + response.buffer = item->buffer; item->callback(&response); } @@ -2296,68 +2571,75 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_ } } -/*=== private high-level functions ===========================================*/ _SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_request_t* req) { - #if defined(SOKOL_DEBUG) - if (req->channel >= ctx->desc.num_channels) { - SOKOL_LOG("_sfetch_validate_request: request.channel too big!"); - return false; - } - if (!req->path) { - SOKOL_LOG("_sfetch_validate_request: request.path is null!"); - return false; - } - if (strlen(req->path) >= (SFETCH_MAX_PATH-1)) { - SOKOL_LOG("_sfetch_validate_request: request.path is too long (must be < SFETCH_MAX_PATH-1)"); - return false; - } - if (!req->callback) { - SOKOL_LOG("_sfetch_validate_request: request.callback missing"); - return false; - } - if (req->chunk_size > req->buffer_size) { - SOKOL_LOG("_sfetch_validate_request: request.chunk_size is greater request.buffer_size)"); - return false; - } - if (req->user_data_ptr && (req->user_data_size == 0)) { - SOKOL_LOG("_sfetch_validate_request: request.user_data_ptr is set, but request.user_data_size is null"); - return false; - } - if (!req->user_data_ptr && (req->user_data_size > 0)) { - SOKOL_LOG("_sfetch_validate_request: request.user_data_ptr is null, but request.user_data_size is not"); - return false; - } - if (req->user_data_size > SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) { - SOKOL_LOG("_sfetch_validate_request: request.user_data_size is too big (see SFETCH_MAX_USERDATA_UINT64"); - return false; - } - #else - /* silence unused warnings in release*/ - (void)(ctx && req); - #endif + if (req->channel >= ctx->desc.num_channels) { + _SFETCH_ERROR(REQUEST_CHANNEL_INDEX_TOO_BIG); + return false; + } + if (!req->path) { + _SFETCH_ERROR(REQUEST_PATH_IS_NULL); + return false; + } + if (strlen(req->path) >= (SFETCH_MAX_PATH-1)) { + _SFETCH_ERROR(REQUEST_PATH_TOO_LONG); + return false; + } + if (!req->callback) { + _SFETCH_ERROR(REQUEST_CALLBACK_MISSING); + return false; + } + if (req->chunk_size > req->buffer.size) { + _SFETCH_ERROR(REQUEST_CHUNK_SIZE_GREATER_BUFFER_SIZE); + return false; + } + if (req->user_data.ptr && (req->user_data.size == 0)) { + _SFETCH_ERROR(REQUEST_USERDATA_PTR_IS_SET_BUT_USERDATA_SIZE_IS_NULL); + return false; + } + if (!req->user_data.ptr && (req->user_data.size > 0)) { + _SFETCH_ERROR(REQUEST_USERDATA_PTR_IS_NULL_BUT_USERDATA_SIZE_IS_NOT); + return false; + } + if (req->user_data.size > SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) { + _SFETCH_ERROR(REQUEST_USERDATA_SIZE_TOO_BIG); + return false; + } return true; } -/*=== PUBLIC API FUNCTIONS ===================================================*/ -SOKOL_API_IMPL void sfetch_setup(const sfetch_desc_t* desc) { - SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); +_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)); + sfetch_desc_t res = *desc; + res.max_requests = _sfetch_def(desc->max_requests, 128); + res.num_channels = _sfetch_def(desc->num_channels, 1); + res.num_lanes = _sfetch_def(desc->num_lanes, 1); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sfetch_setup(const sfetch_desc_t* desc_) { + SOKOL_ASSERT(desc_); SOKOL_ASSERT(0 == _sfetch); - _sfetch = (_sfetch_t*) SOKOL_MALLOC(sizeof(_sfetch_t)); + + sfetch_desc_t desc = _sfetch_desc_defaults(desc_); + _sfetch = (_sfetch_t*) _sfetch_malloc_with_allocator(&desc.allocator, sizeof(_sfetch_t)); SOKOL_ASSERT(_sfetch); - memset(_sfetch, 0, sizeof(_sfetch_t)); _sfetch_t* ctx = _sfetch_ctx(); - ctx->desc = *desc; + _sfetch_clear(ctx, sizeof(_sfetch_t)); + ctx->desc = desc; ctx->setup = true; ctx->valid = true; /* replace zero-init items with default values */ - ctx->desc.max_requests = _sfetch_def(ctx->desc.max_requests, 128); - ctx->desc.num_channels = _sfetch_def(ctx->desc.num_channels, 1); - ctx->desc.num_lanes = _sfetch_def(ctx->desc.num_lanes, 1); if (ctx->desc.num_channels > SFETCH_MAX_CHANNELS) { ctx->desc.num_channels = SFETCH_MAX_CHANNELS; - SOKOL_LOG("sfetch_setup: clamping num_channels to SFETCH_MAX_CHANNELS"); + _SFETCH_WARN(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS); } /* setup the global request item pool */ @@ -2381,7 +2663,7 @@ SOKOL_API_IMPL void sfetch_shutdown(void) { } _sfetch_pool_discard(&ctx->pool); ctx->setup = false; - SOKOL_FREE(ctx); + _sfetch_free(ctx); _sfetch = 0; } @@ -2417,7 +2699,6 @@ SOKOL_API_IMPL bool sfetch_handle_valid(sfetch_handle_t h) { SOKOL_API_IMPL sfetch_handle_t sfetch_send(const sfetch_request_t* request) { _sfetch_t* ctx = _sfetch_ctx(); SOKOL_ASSERT(ctx && ctx->setup); - SOKOL_ASSERT(request && (request->_start_canary == 0) && (request->_end_canary == 0)); const sfetch_handle_t invalid_handle = _sfetch_make_handle(0); if (!ctx->valid) { @@ -2430,7 +2711,7 @@ SOKOL_API_IMPL sfetch_handle_t sfetch_send(const sfetch_request_t* request) { uint32_t slot_id = _sfetch_pool_item_alloc(&ctx->pool, request); if (0 == slot_id) { - SOKOL_LOG("sfetch_send: request pool exhausted (too many active requests)"); + _SFETCH_WARN(REQUEST_POOL_EXHAUSTED); return invalid_handle; } if (!_sfetch_channel_send(&ctx->chn[request->channel], slot_id)) { @@ -2460,15 +2741,15 @@ SOKOL_API_IMPL void sfetch_dowork(void) { ctx->in_callback = false; } -SOKOL_API_IMPL void sfetch_bind_buffer(sfetch_handle_t h, void* buffer_ptr, uint32_t buffer_size) { +SOKOL_API_IMPL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer) { _sfetch_t* ctx = _sfetch_ctx(); SOKOL_ASSERT(ctx && ctx->valid); SOKOL_ASSERT(ctx->in_callback); + SOKOL_ASSERT(buffer.ptr && (buffer.size > 0)); _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); if (item) { SOKOL_ASSERT((0 == item->buffer.ptr) && (0 == item->buffer.size)); - item->buffer.ptr = (uint8_t*) buffer_ptr; - item->buffer.size = buffer_size; + item->buffer = buffer; } } @@ -2478,7 +2759,7 @@ SOKOL_API_IMPL void* sfetch_unbind_buffer(sfetch_handle_t h) { SOKOL_ASSERT(ctx->in_callback); _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); if (item) { - void* prev_buf_ptr = item->buffer.ptr; + void* prev_buf_ptr = (void*)item->buffer.ptr; item->buffer.ptr = 0; item->buffer.size = 0; return prev_buf_ptr; @@ -2518,6 +2799,4 @@ SOKOL_API_IMPL void sfetch_cancel(sfetch_handle_t h) { item->user.cancel = true; } } - #endif /* SOKOL_FETCH_IMPL */ - diff --git a/3rdparty/sokol/sokol_gfx.h b/3rdparty/sokol/sokol_gfx.h index e084e24..ecd79d9 100644 --- a/3rdparty/sokol/sokol_gfx.h +++ b/3rdparty/sokol/sokol_gfx.h @@ -40,9 +40,6 @@ Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GFX_API_DECL @@ -84,6 +81,7 @@ offline shader cross-compiler, see here: https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) + STEP BY STEP ============ --- to initialize sokol_gfx, after creating a window and a 3D-API @@ -91,6 +89,30 @@ sg_setup(const sg_desc*) + Depending on the selected 3D backend, sokol-gfx requires some + information, like a device pointer framebuffer pixel formats + and so on. If you are using sokol_app.h for the window system + glue, you can use a helper function provided in the sokol_glue.h + header: + + #include "sokol_gfx.h" + #include "sokol_app.h" + #include "sokol_glue.h" + //... + sg_setup(&(sg_desc){ + .context = sapp_sgcontext(), + }); + + To get any logging output for errors and from the validation layer, you + need to provide a logging callback. Easiest way is through sokol_log.h: + + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + --- create resource objects (at least buffers, shaders and pipelines, and optionally images and passes): @@ -129,7 +151,10 @@ --- optionally update shader uniform data with: - sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) + sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) + + Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout + of the uniform data passed into sg_apply_uniforms(). --- kick off a draw call with: @@ -169,7 +194,7 @@ sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) - ...or if you want to specifiy the viewport rectangle with float values: + ...or if you want to specify the viewport rectangle with float values: sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) @@ -246,6 +271,11 @@ bool sg_query_buffer_overflow(sg_buffer buf) + You can manually check to see if an overflow would occur before adding + any data to a buffer by calling + + bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size) + NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of data will be 4-byte aligned in the destination buffer. This means that there will be gaps in index buffers containing 16-bit indices @@ -271,7 +301,40 @@ by calling sg_query_desc(). This will return an sg_desc struct with the default values patched in instead of any zero-initialized values - --- you can inspect various internal resource attributes via: + --- you can get a desc struct matching the creation attributes of a + specific resource object via: + + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf) + sg_image_desc sg_query_image_desc(sg_image img) + sg_shader_desc sq_query_shader_desc(sg_shader shd) + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) + sg_pass_desc sg_query_pass_desc(sg_pass pass) + + ...but NOTE that the returned desc structs may be incomplete, only + creation attributes that are kept around internally after resource + creation will be filled in, and in some cases (like shaders) that's + very little. Any missing attributes will be set to zero. The returned + desc structs might still be useful as partial blueprint for creating + similar resources if filled up with the missing attributes. + + Calling the query-desc functions on an invalid resource will return + completely zeroed structs (it makes sense to check the resource state + with sg_query_*_state() first) + + --- you can query the default resource creation parameters through the functions + + sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) + sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) + sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) + sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) + + These functions take a pointer to a desc structure which may contain + zero-initialized items for default values. These zero-init values + will be replaced with their concrete values in the returned desc + struct. + + --- you can inspect various internal resource runtime values via: sg_buffer_info sg_query_buffer_info(sg_buffer buf) sg_image_info sg_query_image_info(sg_image img) @@ -288,18 +351,6 @@ sg_backend sg_query_backend(void) - --- you can query the default resource creation parameters through the functions - - sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) - sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) - sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) - sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) - sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) - - These functions take a pointer to a desc structure which may contain - zero-initialized items for default values. These zero-init values - will be replaced with their concrete values in the returned desc - struct. ON INITIALIZATION: ================== @@ -334,30 +385,129 @@ a convenience function to get a sg_context_desc struct filled out with context information provided by sokol_app.h - See the documention block of the sg_desc struct below for more information. + See the documentation block of the sg_desc struct below for more information. + + + UNIFORM DATA LAYOUT: + ==================== + NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry + about the following details. + + The data that's passed into the sg_apply_uniforms() function must adhere to + specific layout rules so that the GPU shader finds the uniform block + items at the right offset. + + For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform + blocks, but not about the internal layout. The data will just be copied into + a uniform/constant buffer in a single operation and it's up you to arrange the + CPU-side layout so that it matches the GPU side layout. This also means that with + the D3D11 and Metal backends you are not limited to a 'cross-platform' subset + of uniform variable types. + + If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here. + + For the GL backends, the internal layout of uniform blocks matters though, + and you are limited to a small number of uniform variable types. This is + because sokol-gfx must be able to locate the uniform block members in order + to upload them to the GPU with glUniformXXX() calls. + + To describe the uniform block layout to sokol-gfx, the following information + must be passed to the sg_make_shader() call in the sg_shader_desc struct: + + - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or + SG_UNIFORMLAYOUT_STD140) + - a list of the uniform block members types in the correct order they + appear on the CPU side + + For example if the GLSL shader has the following uniform declarations: + + uniform mat4 mvp; + uniform vec2 offset0; + uniform vec2 offset1; + uniform vec2 offset2; + + ...and on the CPU side, there's a similar C struct: + + typedef struct { + float mvp[16]; + float offset0[2]; + float offset1[2]; + float offset2[2]; + } params_t; + + ...the uniform block description in the sg_shader_desc must look like this: + + sg_shader_desc desc = { + .vs.uniform_blocks[0] = { + .size = sizeof(params_t), + .layout = SG_UNIFORMLAYOUT_NATIVE, // this is the default and can be omitted + .uniforms = { + // order must be the same as in 'params_t': + [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 }, + [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 }, + [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 }, + [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 }, + } + } + }; + + With this information sokol-gfx can now compute the correct offsets of the data items + within the uniform block struct. + + The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used, + but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is + described in the next section: + + + CROSS-BACKEND COMMON UNIFORM DATA LAYOUT + ======================================== + For cross-platform / cross-3D-backend code it is important that the same uniform block + layout on the CPU side can be used for all sokol-gfx backends. To achieve this, + a common subset of the std140 layout must be used: + + - The uniform block layout hint in sg_shader_desc must be explicitly set to + SG_UNIFORMLAYOUT_STD140. + - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): + - float => SG_UNIFORMTYPE_FLOAT + - vec2 => SG_UNIFORMTYPE_FLOAT2 + - vec3 => SG_UNIFORMTYPE_FLOAT3 + - vec4 => SG_UNIFORMTYPE_FLOAT4 + - int => SG_UNIFORMTYPE_INT + - ivec2 => SG_UNIFORMTYPE_INT2 + - ivec3 => SG_UNIFORMTYPE_INT3 + - ivec4 => SG_UNIFORMTYPE_INT4 + - mat4 => SG_UNIFORMTYPE_MAT4 + - Alignment for those types must be as follows (in bytes): + - float => 4 + - vec2 => 8 + - vec3 => 16 + - vec4 => 16 + - int => 4 + - ivec2 => 8 + - ivec3 => 16 + - ivec4 => 16 + - mat4 => 16 + - Arrays are only allowed for the following types: vec4, int4, mat4. + + Note that the HLSL cbuffer layout rules are slightly different from the + std140 layout rules, this means that the cbuffer declarations in HLSL code + must be tweaked so that the layout is compatible with std140. + + The by far easiest way to tacke the common uniform block layout problem is + to use the sokol-shdc shader cross-compiler tool! + BACKEND-SPECIFIC TOPICS: ======================== - --- the GL backends need to know about the internal structure of uniform - blocks, and the texture sampler-name and -type: - - typedef struct { - float mvp[16]; // model-view-projection matrix - float offset0[2]; // some 2D vectors - float offset1[2]; - float offset2[2]; - } params_t; + --- The GL backends need to know about the internal structure of uniform + blocks, and the texture sampler-name and -type. The uniform layout details + are described in the UNIFORM DATA LAYOUT section above. // uniform block structure and texture image definition in sg_shader_desc: sg_shader_desc desc = { // uniform block description (size and internal structure) .vs.uniform_blocks[0] = { - .size = sizeof(params_t), - .uniforms = { - [0] = { .name="mvp", .type=SG_UNIFORMTYPE_MAT4 }, - [1] = { .name="offset0", .type=SG_UNIFORMTYPE_VEC2 }, - ... - } + ... }, // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } @@ -434,6 +584,7 @@ } }; + WORKING WITH CONTEXTS ===================== sokol-gfx allows to switch between different rendering contexts and @@ -477,6 +628,7 @@ https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c + TRACE HOOKS: ============ sokol_gfx.h optionally allows to install "trace hook" callbacks for @@ -506,6 +658,7 @@ imgui/sokol_gfx_imgui.h header which implements a realtime debugging UI for sokol_gfx.h on top of Dear ImGui. + A NOTE ON PORTABLE PACKED VERTEX FORMATS: ========================================= There are two things to consider when using packed @@ -534,9 +687,11 @@ - SG_VERTEXFORMAT_SHORT2 - SG_VERTEXFORMAT_SHORT4 - - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) + - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) or the following: - - SG_VERTEXFORMAT_UINT10_N2 is not supported on WebGL/GLES2 + - SG_VERTEXFORMAT_UINT10_N2 + - SG_VERTEXFORMAT_HALF2, SG_VERTEXFORMAT_HALF4 + (commonly supported extension: OES_vertex_half_float) So for a vertex input layout which works on all platforms, only use the following vertex formats, and if needed "expand" the normalized vertex shader @@ -554,10 +709,278 @@ - SG_VERTEXFORMAT_SHORT4N, - SG_VERTEXFORMAT_USHORT4N - TODO: - ==== - - talk about asynchronous resource creation + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sg_setup(&(sg_desc){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_gfx.h + itself though, not any allocations in OS libraries. + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sg_setup(&(sg_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sg' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-gfx like this: + + sg_setup(&(sg_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + COMMIT LISTENERS + ================ + It's possible to hook callback functions into sokol-gfx which are called from + inside sg_commit() in unspecified order. This is mainly useful for libraries + that build on top of sokol_gfx.h to be notified about the end/start of a frame. + + To add a commit listener, call: + + static void my_commit_listener(void* user_data) { + ... + } + + bool success = sg_add_commit_listener((sg_commit_listener){ + .func = my_commit_listener, + .user_data = ..., + }); + + The function returns false if the internal array of commit listeners is full, + or the same commit listener had already been added. + + If the function returns true, my_commit_listener() will be called each frame + from inside sg_commit(). + + By default, 1024 distinct commit listeners can be added, but this number + can be tweaked in the sg_setup() call: + + sg_setup(&(sg_desc){ + .max_commit_listeners = 2048, + }); + + An sg_commit_listener item is equal to another if both the function + pointer and user_data field are equal. + + To remove a commit listener: + + bool success = sg_remove_commit_listener((sg_commit_listener){ + .func = my_commit_listener, + .user_data = ..., + }); + + ...where the .func and .user_data field are equal to a previous + sg_add_commit_listener() call. The function returns true if the commit + listener item was found and removed, and false otherwise. + + + RESOURCE CREATION AND DESTRUCTION IN DETAIL + =========================================== + The 'vanilla' way to create resource objects is with the 'make functions': + + sg_buffer sg_make_buffer(const sg_buffer_desc* desc) + sg_image sg_make_image(const sg_image_desc* desc) + sg_shader sg_make_shader(const sg_shader_desc* desc) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) + sg_pass sg_make_pass(const sg_pass_desc* desc) + + This will result in one of three cases: + + 1. The returned handle is invalid. This happens when there are no more + free slots in the resource pool for this resource type. An invalid + handle is associated with the INVALID resource state, for instance: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID) { + // buffer pool is exhausted + } + + 2. The returned handle is valid, but creating the underlying resource + has failed for some reason. This results in a resource object in the + FAILED state. The reason *why* resource creation has failed differ + by resource type. Look for log messages with more details. A failed + resource state can be checked with: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED) { + // creating the resource has failed + } + + 3. And finally, if everything goes right, the returned resource is + in resource state VALID and ready to use. This can be checked + with: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID) { + // creating the resource has failed + } + + When calling the 'make functions', the created resource goes through a number + of states: + + - INITIAL: the resource slot associated with the new resource is currently + free (technically, there is no resource yet, just an empty pool slot) + - ALLOC: a handle for the new resource has been allocated, this just means + a pool slot has been reserved. + - VALID or FAILED: in VALID state any 3D API backend resource objects have + been successfully created, otherwise if anything went wrong, the resource + will be in FAILED state. + + Sometimes it makes sense to first grab a handle, but initialize the + underlying resource at a later time. For instance when loading data + asynchronously from a slow data source, you may know what buffers and + textures are needed at an early stage of the loading process, but actually + loading the buffer or texture content can only be completed at a later time. + + For such situations, sokol-gfx resource objects can be created in two steps. + You can allocate a handle upfront with one of the 'alloc functions': + + sg_buffer sg_alloc_buffer(void) + sg_image sg_alloc_image(void) + sg_shader sg_alloc_shader(void) + sg_pipeline sg_alloc_pipeline(void) + sg_pass sg_alloc_pass(void) + + This will return a handle with the underlying resource object in the + ALLOC state: + + sg_image img = sg_alloc_image(); + if (sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC) { + // allocating an image handle has succeeded, otherwise + // the image pool is full + } + + Such an 'incomplete' handle can be used in most sokol-gfx rendering functions + without doing any harm, sokol-gfx will simply skip any rendering operation + that involve resources which are not in VALID state. + + At a later time (for instance once the texture has completed loading + asynchronously), the resource creation can be completed by calling one of + the 'init functions', those functions take an existing resource handle and + 'desc struct': + + void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc) + void sg_init_image(sg_image img, const sg_image_desc* desc) + void sg_init_shader(sg_shader shd, const sg_shader_desc* desc) + void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc) + void sg_init_pass(sg_pass pass, const sg_pass_desc* desc) + + The init functions expect a resource in ALLOC state, and after the function + returns, the resource will be either in VALID or FAILED state. Calling + an 'alloc function' followed by the matching 'init function' is fully + equivalent with calling the 'make function' alone. + + Destruction can also happen as a two-step process. The 'uninit functions' + will put a resource object from the VALID or FAILED state back into the + ALLOC state: + + void sg_uninit_buffer(sg_buffer buf) + void sg_uninit_image(sg_image img) + void sg_uninit_shader(sg_shader shd) + void sg_uninit_pipeline(sg_pipeline pip) + void sg_uninit_pass(sg_pass pass) + + Calling the 'uninit functions' with a resource that is not in the VALID or + FAILED state is a no-op. + + To finally free the pool slot for recycling call the 'dealloc functions': + + void sg_dealloc_buffer(sg_buffer buf) + void sg_dealloc_image(sg_image img) + void sg_dealloc_shader(sg_shader shd) + void sg_dealloc_pipeline(sg_pipeline pip) + void sg_dealloc_pass(sg_pass pass) + + Calling the 'dealloc functions' on a resource that's not in ALLOC state is + a no-op, but will generate a warning log message. + + Calling an 'uninit function' and 'dealloc function' in sequence is equivalent + with calling the associated 'destroy function': + + void sg_destroy_buffer(sg_buffer buf) + void sg_destroy_image(sg_image img) + void sg_destroy_shader(sg_shader shd) + void sg_destroy_pipeline(sg_pipeline pip) + void sg_destroy_pass(sg_pass pass) + + The 'destroy functions' can be called on resources in any state and generally + do the right thing (for instance if the resource is in ALLOC state, the destroy + function will be equivalent to the 'dealloc function' and skip the 'uninit part'). + + And finally to close the circle, the 'fail functions' can be called to manually + put a resource in ALLOC state into the FAILED state: + + sg_fail_buffer(sg_buffer buf) + sg_fail_image(sg_image img) + sg_fail_shader(sg_shader shd) + sg_fail_pipeline(sg_pipeline pip) + sg_fail_pass(sg_pass pass) + + This is recommended if anything went wrong outside of sokol-gfx during asynchronous + resource creation (for instance the file loading operation failed). In this case, + the 'fail function' should be called instead of the 'init function'. + + Calling a 'fail function' on a resource that's not in ALLOC state is a no-op, + but will generate a warning log message. + + NOTE: that two-step resource creation usually only makes sense for buffers + and images, but not for shaders, pipelines or passes. Most notably, trying + to create a pipeline object with a shader that's not in VALID state will + trigger a validation layer error, or if the validation layer is disabled, + result in a pipeline object in FAILED state. Same when trying to create + a pass object with image invalid image objects. + + LICENSE + ======= zlib/libpng license Copyright (c) 2018 Andre Weissflog @@ -780,6 +1203,7 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_RG16SI, SG_PIXELFORMAT_RG16F, SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_SRGB8A8, SG_PIXELFORMAT_RGBA8SN, SG_PIXELFORMAT_RGBA8UI, SG_PIXELFORMAT_RGBA8SI, @@ -823,6 +1247,8 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_ETC2_RG11, SG_PIXELFORMAT_ETC2_RG11SN, + SG_PIXELFORMAT_RGB9E5, + _SG_PIXELFORMAT_NUM, _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF } sg_pixel_format; @@ -873,6 +1299,7 @@ typedef struct sg_limits { int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images int max_vertex_attrs; // <= SG_MAX_VERTEX_ATTRIBUTES or less (on some GLES2 impls) int gl_max_vertex_uniform_vectors; // <= GL_MAX_VERTEX_UNIFORM_VECTORS (only on GL backends) + int gl_max_combined_texture_image_units; // <= GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends) } sg_limits; /* @@ -1172,6 +1599,8 @@ typedef enum sg_vertex_format { SG_VERTEXFORMAT_SHORT4N, SG_VERTEXFORMAT_USHORT4N, SG_VERTEXFORMAT_UINT10_N2, + SG_VERTEXFORMAT_HALF2, + SG_VERTEXFORMAT_HALF4, _SG_VERTEXFORMAT_NUM, _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF } sg_vertex_format; @@ -1208,11 +1637,57 @@ typedef enum sg_uniform_type { SG_UNIFORMTYPE_FLOAT2, SG_UNIFORMTYPE_FLOAT3, SG_UNIFORMTYPE_FLOAT4, + SG_UNIFORMTYPE_INT, + SG_UNIFORMTYPE_INT2, + SG_UNIFORMTYPE_INT3, + SG_UNIFORMTYPE_INT4, SG_UNIFORMTYPE_MAT4, _SG_UNIFORMTYPE_NUM, _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF } sg_uniform_type; +/* + sg_uniform_layout + + A hint for the interior memory layout of uniform blocks. This is + only really relevant for the GL backend where the internal layout + of uniform blocks must be known to sokol-gfx. For all other backends the + internal memory layout of uniform blocks doesn't matter, sokol-gfx + will just pass uniform data as a single memory blob to the + 3D backend. + + SG_UNIFORMLAYOUT_NATIVE (default) + Native layout means that a 'backend-native' memory layout + is used. For the GL backend this means that uniforms + are packed tightly in memory (e.g. there are no padding + bytes). + + SG_UNIFORMLAYOUT_STD140 + The memory layout is a subset of std140. Arrays are only + allowed for the FLOAT4, INT4 and MAT4. Alignment is as + is as follows: + + FLOAT, INT: 4 byte alignment + FLOAT2, INT2: 8 byte alignment + FLOAT3, INT3: 16 byte alignment(!) + FLOAT4, INT4: 16 byte alignment + MAT4: 16 byte alignment + FLOAT4[], INT4[]: 16 byte alignment + + The overall size of the uniform block must be a multiple + of 16. + + For more information search for 'UNIFORM DATA LAYOUT' in the documentation block + at the start of the header. +*/ +typedef enum sg_uniform_layout { + _SG_UNIFORMLAYOUT_DEFAULT, /* value 0 reserved for default-init */ + SG_UNIFORMLAYOUT_NATIVE, /* default: layout depends on currently active backend */ + SG_UNIFORMLAYOUT_STD140, /* std140: memory layout according to std140 */ + _SG_UNIFORMLAYOUT_NUM, + _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_layout; + /* sg_cull_mode @@ -1714,6 +2189,7 @@ typedef struct sg_image_desc { defaults are "vs_4_0" and "ps_4_0") - reflection info for each uniform block used by the shader stage: - the size of the uniform block in bytes + - a memory layout hint (native vs std140, only required for GL backends) - reflection info for each uniform block member (only required for GL backends): - member name - member type (SG_UNIFORMTYPE_xxx) @@ -1746,6 +2222,7 @@ typedef struct sg_shader_uniform_desc { typedef struct sg_shader_uniform_block_desc { size_t size; + sg_uniform_layout layout; sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; } sg_shader_uniform_block_desc; @@ -1815,9 +2292,9 @@ typedef struct sg_shader_desc { .enabled: false .front/back: .compare: SG_COMPAREFUNC_ALWAYS + .fail_op: SG_STENCILOP_KEEP .depth_fail_op: SG_STENCILOP_KEEP .pass_op: SG_STENCILOP_KEEP - .compare: SG_COMPAREFUNC_ALWAYS .read_mask: 0 .write_mask: 0 .ref: 0 @@ -2079,12 +2556,10 @@ typedef struct sg_image_info { uint32_t upd_frame_index; /* frame index of last sg_update_image() */ int num_slots; /* number of renaming-slots for dynamically updated images */ int active_slot; /* currently active write-slot for dynamically updated images */ - int width; /* image width */ - int height; /* image height */ } sg_image_info; typedef struct sg_shader_info { - sg_slot_info slot; /* resoure pool slot info */ + sg_slot_info slot; /* resource pool slot info */ } sg_shader_info; typedef struct sg_pipeline_info { @@ -2095,6 +2570,198 @@ typedef struct sg_pass_info { sg_slot_info slot; /* resource pool slot info */ } sg_pass_info; +/* + sg_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SG_LOG_ITEMS \ + _SG_LOGITEM_XMACRO(OK, "Ok") \ + _SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \ + _SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_INCOMPLETE, "framebuffer completeness check failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_MSAA_FRAMEBUFFER_INCOMPLETE, "completeness check failed for msaa resolve framebuffer (gl)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (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_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \ + _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (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_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND, "vertex shader entry function 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_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(WGPU_MAP_UNIFORM_BUFFER_FAILED, "mapping uniform buffer failed (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER, "per frame staging buffer full when copying to buffer (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE, "per frame staging buffer full when copying to texture (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_RESET_STATE_CACHE_FIXME, "_sg_wgpu_reset_state_cache: fixme") \ + _SG_LOGITEM_XMACRO(WGPU_ACTIVATE_CONTEXT_FIXME, "_sg_wgpu_activate_context: fixme") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in buffer uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in image uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in shader uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in pipeline uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in pass uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \ + _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \ + _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \ + _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SHADER_INVALID_STATE, "sg_dealloc_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_PIPELINE_INVALID_STATE, "sg_dealloc_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_PASS_INVALID_STATE, "sg_dealloc_pass(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_BUFFER_INVALID_STATE, "sg_init_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_IMAGE_INVALID_STATE, "sg_init_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SHADER_INVALID_STATE, "sg_init_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_PIPELINE_INVALID_STATE, "sg_init_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_PASS_INVALID_STATE, "sg_init_pass(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_PASS_INVALID_STATE, "sg_uninit_pass(): pass must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(FAIL_BUFFER_INVALID_STATE, "sg_fail_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_IMAGE_INVALID_STATE, "sg_fail_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SHADER_INVALID_STATE, "sg_fail_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_PIPELINE_INVALID_STATE, "sg_fail_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_PASS_INVALID_STATE, "sg_fail_pass(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(BUFFER_POOL_EXHAUSTED, "buffer pool exhausted") \ + _SG_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + _SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \ + _SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \ + _SG_LOGITEM_XMACRO(PASS_POOL_EXHAUSTED, "pass pool exhausted") \ + _SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SIZE, "sg_buffer_desc.size cannot be 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA, "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA_SIZE, "immutable buffer data size differs from buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_NO_DATA, "dynamic/stream usage buffers cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_PIXELFORMAT, "invalid pixel format for render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, "non-render-target images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, "shader source or byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UBS, "shader uniform blocks must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, "uniform block members must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_UB_MEMBERS, "GL backend requires uniform block member declarations") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_MEMBER_NAME, "uniform block member name missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMGS, "shader images must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMG_NAME, "GL backend requires uniform block member names") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_NAMES, "GLES2 backend requires vertex attribute names") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_SEMANTICS, "D3D11 backend requires vertex attribute semantics") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_ATTRS, "sg_pipeline_desc.layout.attrs is empty or not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_NAME, "GLES2/WebGL missing vertex attribute name in shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_CANARY, "sg_pass_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_NO_COLOR_ATTS, "sg_pass_desc.color_attachments[0] must be valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, "color attachments must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE, "pass attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_MIPLEVEL, "pass attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_FACE, "pass attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_LAYER, "pass attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_SLICE, "pass attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_NO_RT, "pass attachment image must be render targets") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, "pass color-attachment images must have a renderable pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, "pass depth-attachment image must have depth pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_SIZES, "all pass attachments must have the same size") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, "all pass attachments must have the same sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_PASS, "sg_begin_pass: pass must be valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_IMAGE, "sg_begin_pass: one or more attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID_ID, "sg_apply_pipeline: invalid pipeline id provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_EXISTS, "sg_apply_pipeline: pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID, "sg_apply_pipeline: pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_EXISTS, "sg_apply_pipeline: shader object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_VALID, "sg_apply_pipeline: shader object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATT_COUNT, "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLOR_FORMAT, "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTH_FORMAT, "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_EXISTS, "sg_apply_bindings: currently applied pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBS, "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_EXISTS, "sg_apply_bindings: vertex buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_TYPE, "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_IB, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMGS, "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_EXISTS, "sg_apply_bindings: vertex shader image no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_TYPES, "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMGS, "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: fragment shader image no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_TYPES, "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc") \ + _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_SIZE, "sg_apply_uniforms: data size exceeds declared uniform block size") \ + _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_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \ + _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \ + +#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item, +typedef enum sg_log_item { + _SG_LOG_ITEMS +} sg_log_item; +#undef _SG_LOGITEM_XMACRO + /* sg_desc @@ -2116,9 +2783,15 @@ typedef struct sg_pass_info { .pipeline_pool_size 64 .pass_pool_size 16 .context_pool_size 16 - .sampler_cache_size 64 .uniform_buffer_size 4 MB (4*1024*1024) .staging_buffer_size 8 MB (8*1024*1024) + .sampler_cache_size 64 + .max_commit_listeners 1024 + .disable_validation false + + .allocator.alloc 0 (in this case, malloc() will be called) + .allocator.free 0 (in this case, free() will be called) + .allocator.user_data 0 .context.color_format: default value depends on selected backend: all GL backends: SG_PIXELFORMAT_RGBA8 @@ -2175,7 +2848,7 @@ typedef struct sg_pass_info { ID3D11DepthStencilView object of the default framebuffer, this function will be called in sg_begin_pass() when rendering to the default framebuffer - .context.metal.user_data + .context.d3d11.user_data optional user data pointer passed to the userdata versions of callback functions @@ -2196,7 +2869,7 @@ typedef struct sg_pass_info { .context.wgpu.depth_stencil_view_userdata_cb callback to get current default-pass depth-stencil-surface WGPUTextureView the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 - .context.metal.user_data + .context.wgpu.user_data optional user data pointer passed to the userdata versions of callback functions @@ -2250,6 +2923,56 @@ typedef struct sg_context_desc { sg_wgpu_context_desc wgpu; } sg_context_desc; +/* + sg_commit_listener + + Used with function sg_add_commit_listener() to add a callback + which will be called in sg_commit(). This is useful for libraries + building on top of sokol-gfx to be notified about when a frame + ends (instead of having to guess, or add a manual 'new-frame' + function. +*/ +typedef struct sg_commit_listener { + void (*func)(void* user_data); + void* user_data; +} sg_commit_listener; + +/* + sg_allocator + + Used in sg_desc to provide custom memory-alloc and -free functions + 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 + override one function but not the other). +*/ +typedef struct sg_allocator { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sg_allocator; + +/* + sg_logger + + Used in sg_desc to provide a logging function. Please be aware + that without logging function, sokol-gfx will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and install + a logger (for instance the standard logging function from sokol_log.h). +*/ +typedef struct sg_logger { + void (*func)( + const char* tag, // always "sg" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sg_logger; + typedef struct sg_desc { uint32_t _start_canary; int buffer_pool_size; @@ -2261,6 +2984,10 @@ typedef struct sg_desc { int uniform_buffer_size; int staging_buffer_size; int sampler_cache_size; + int max_commit_listeners; + bool disable_validation; // disable validation layer even in debug mode, useful for tests + sg_allocator allocator; + sg_logger logger; // optional log function override sg_context_desc context; uint32_t _end_canary; } sg_desc; @@ -2273,6 +3000,8 @@ SOKOL_GFX_API_DECL void sg_reset_state_cache(void); SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name); SOKOL_GFX_API_DECL void sg_pop_debug_group(void); +SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener); +SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener); /* resource creation, destruction and updating */ SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); @@ -2289,6 +3018,7 @@ SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data); SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data); SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); +SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size); /* rendering functions */ SOKOL_GFX_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); @@ -2323,6 +3053,12 @@ SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); SOKOL_GFX_API_DECL sg_pass_info sg_query_pass_info(sg_pass pass); +/* get desc structs matching a specific resource (NOTE that not all creation attributes may be provided) */ +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_desc(sg_image img); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_desc(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_pass_desc sg_query_pass_desc(sg_pass pass); /* get resource creation desc struct with their default values replaced */ SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); @@ -2336,26 +3072,26 @@ SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); SOKOL_GFX_API_DECL sg_pass sg_alloc_pass(void); -SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf_id); -SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img_id); -SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd_id); -SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip_id); -SOKOL_GFX_API_DECL void sg_dealloc_pass(sg_pass pass_id); -SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc); -SOKOL_GFX_API_DECL void sg_init_image(sg_image img_id, const sg_image_desc* desc); -SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc); -SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc); -SOKOL_GFX_API_DECL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc); -SOKOL_GFX_API_DECL bool sg_uninit_buffer(sg_buffer buf_id); -SOKOL_GFX_API_DECL bool sg_uninit_image(sg_image img_id); -SOKOL_GFX_API_DECL bool sg_uninit_shader(sg_shader shd_id); -SOKOL_GFX_API_DECL bool sg_uninit_pipeline(sg_pipeline pip_id); -SOKOL_GFX_API_DECL bool sg_uninit_pass(sg_pass pass_id); -SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf_id); -SOKOL_GFX_API_DECL void sg_fail_image(sg_image img_id); -SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd_id); -SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); -SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass_id); +SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img); +SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_dealloc_pass(sg_pass pass); +SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL void sg_init_image(sg_image img, const sg_image_desc* desc); +SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd, const sg_shader_desc* desc); +SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL void sg_init_pass(sg_pass pass, const sg_pass_desc* desc); +SOKOL_GFX_API_DECL void sg_uninit_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_uninit_image(sg_image img); +SOKOL_GFX_API_DECL void sg_uninit_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_uninit_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_uninit_pass(sg_pass pass); +SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_fail_image(sg_image img); +SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass); /* rendering contexts (optional) */ SOKOL_GFX_API_DECL sg_context sg_setup_context(void); @@ -2413,53 +3149,42 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #endif // SOKOL_GFX_INCLUDED -/*--- IMPLEMENTATION ---------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_GFX_IMPL #define SOKOL_GFX_IMPL_INCLUDED (1) #if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) #error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" #endif -#include /* memset */ -#include /* FLT_MAX */ +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions" +#endif + +#include // malloc, free +#include // memset +#include // FLT_MAX #ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_VALIDATE_BEGIN - #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() -#endif -#ifndef SOKOL_VALIDATE - #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) -#endif -#ifndef SOKOL_VALIDATE_END - #define SOKOL_VALIDATE_END() _sg_validate_end() -#endif #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) @@ -2683,6 +3408,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_RGB10_A2 0x8059 #define GL_RGBA8 0x8058 + #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_RGBA4 0x8056 #define GL_RGB8 0x8051 @@ -2778,6 +3504,8 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_R11F_G11F_B10F 0x8C3A #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B + #define GL_RGB9_E5 0x8C3D + #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_RGBA32UI 0x8D70 #define GL_RGB32UI 0x8D71 #define GL_RGBA16UI 0x8D76 @@ -2832,6 +3560,8 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_CURRENT_PROGRAM 0x8B8D #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB + #define GL_UNPACK_ALIGNMENT 0x0CF5 + #define GL_FRAMEBUFFER_SRGB 0x8DB9 #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -2942,8 +3672,13 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } #endif -/*=== COMMON BACKEND STUFF ===================================================*/ - +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs /* resource pool slots */ typedef struct { uint32_t id; @@ -2966,6 +3701,7 @@ enum { _SG_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, + _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, }; /* fixed-size string */ @@ -2981,31 +3717,57 @@ typedef struct { #define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v))) #define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta))) +_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size); +_SOKOL_PRIVATE void _sg_free(void* ptr); +_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size); + +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ + int max_lod; + uintptr_t sampler_handle; +} _sg_sampler_cache_item_t; + +typedef struct { + int capacity; + int num_items; + _sg_sampler_cache_item_t* items; +} _sg_sampler_cache_t; + typedef struct { int size; int append_pos; bool append_overflow; - sg_buffer_type type; - sg_usage usage; uint32_t update_frame_index; uint32_t append_frame_index; int num_slots; int active_slot; + sg_buffer_type type; + sg_usage usage; } _sg_buffer_common_t; _SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { cmn->size = (int)desc->size; cmn->append_pos = 0; cmn->append_overflow = false; - cmn->type = desc->type; - cmn->usage = desc->usage; cmn->update_frame_index = 0; cmn->append_frame_index = 0; - cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; cmn->active_slot = 0; + cmn->type = desc->type; + cmn->usage = desc->usage; } typedef struct { + uint32_t upd_frame_index; + int num_slots; + int active_slot; sg_image_type type; bool render_target; int width; @@ -3022,12 +3784,14 @@ typedef struct { sg_wrap wrap_w; sg_border_color border_color; uint32_t max_anisotropy; - uint32_t upd_frame_index; - int num_slots; - int active_slot; + float min_lod; + float max_lod; } _sg_image_common_t; _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->upd_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; cmn->type = desc->type; cmn->render_target = desc->render_target; cmn->width = desc->width; @@ -3044,14 +3808,13 @@ _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_imag cmn->wrap_w = desc->wrap_w; cmn->border_color = desc->border_color; cmn->max_anisotropy = desc->max_anisotropy; - cmn->upd_frame_index = 0; - cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - cmn->active_slot = 0; + cmn->min_lod = desc->min_lod; + cmn->max_lod = desc->max_lod; } typedef struct { size_t size; -} _sg_uniform_block_t; +} _sg_shader_uniform_block_t; typedef struct { sg_image_type image_type; @@ -3061,7 +3824,7 @@ typedef struct { typedef struct { int num_uniform_blocks; int num_images; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; } _sg_shader_stage_t; @@ -3096,36 +3859,44 @@ _SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_sh } typedef struct { - sg_shader shader_id; - sg_index_type index_type; bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS]; - sg_pixel_format depth_format; + bool use_instanced_draw; + sg_shader shader_id; + sg_layout_desc layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; int sample_count; - float depth_bias; - float depth_bias_slope_scale; - float depth_bias_clamp; sg_color blend_color; + bool alpha_to_coverage_enabled; } _sg_pipeline_common_t; _SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { SOKOL_ASSERT((desc->color_count >= 1) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); - cmn->shader_id = desc->shader; - cmn->index_type = desc->index_type; for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { cmn->vertex_layout_valid[i] = false; } - cmn->color_attachment_count = desc->color_count; - for (int i = 0; i < cmn->color_attachment_count; i++) { - cmn->color_formats[i] = desc->colors[i].pixel_format; + cmn->use_instanced_draw = false; + cmn->shader_id = desc->shader; + cmn->layout = desc->layout; + cmn->depth = desc->depth; + cmn->stencil = desc->stencil; + cmn->color_count = desc->color_count; + for (int i = 0; i < desc->color_count; i++) { + cmn->colors[i] = desc->colors[i]; } - cmn->depth_format = desc->depth.pixel_format; + cmn->primitive_type = desc->primitive_type; + cmn->index_type = desc->index_type; + cmn->cull_mode = desc->cull_mode; + cmn->face_winding = desc->face_winding; cmn->sample_count = desc->sample_count; - cmn->depth_bias = desc->depth.bias; - cmn->depth_bias_slope_scale = desc->depth.bias_slope_scale; - cmn->depth_bias_clamp = desc->depth.bias_clamp; cmn->blend_color = desc->blend_color; + cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; } typedef struct { @@ -3162,107 +3933,6 @@ _SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_d } } -/*=== GENERIC SAMPLER CACHE ==================================================*/ - -/* - this is used by the Metal and WGPU backends to reduce the - number of sampler state objects created through the backend API -*/ -typedef struct { - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ - int max_lod; - uintptr_t sampler_handle; -} _sg_sampler_cache_item_t; - -typedef struct { - int capacity; - int num_items; - _sg_sampler_cache_item_t* items; -} _sg_sampler_cache_t; - -_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { - SOKOL_ASSERT(cache && (capacity > 0)); - memset(cache, 0, sizeof(_sg_sampler_cache_t)); - cache->capacity = capacity; - const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); - cache->items = (_sg_sampler_cache_item_t*) SOKOL_MALLOC(size); - SOKOL_ASSERT(cache->items); - memset(cache->items, 0, size); -} - -_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_FREE(cache->items); - cache->items = 0; - cache->num_items = 0; - cache->capacity = 0; -} - -_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { - return (int) (min_lod * 1000.0f); -} - -_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { - return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); -} - -_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { - /* return matching sampler cache item index or -1 */ - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(img_desc); - const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); - const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); - for (int i = 0; i < cache->num_items; i++) { - const _sg_sampler_cache_item_t* item = &cache->items[i]; - if ((img_desc->min_filter == item->min_filter) && - (img_desc->mag_filter == item->mag_filter) && - (img_desc->wrap_u == item->wrap_u) && - (img_desc->wrap_v == item->wrap_v) && - (img_desc->wrap_w == item->wrap_w) && - (img_desc->max_anisotropy == item->max_anisotropy) && - (img_desc->border_color == item->border_color) && - (min_lod == item->min_lod) && - (max_lod == item->max_lod)) - { - return i; - } - } - /* fallthrough: no matching cache item found */ - return -1; -} - -_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(img_desc); - SOKOL_ASSERT(cache->num_items < cache->capacity); - const int item_index = cache->num_items++; - _sg_sampler_cache_item_t* item = &cache->items[item_index]; - item->min_filter = img_desc->min_filter; - item->mag_filter = img_desc->mag_filter; - item->wrap_u = img_desc->wrap_u; - item->wrap_v = img_desc->wrap_v; - item->wrap_w = img_desc->wrap_w; - item->border_color = img_desc->border_color; - item->max_anisotropy = img_desc->max_anisotropy; - item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); - item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); - item->sampler_handle = sampler_handle; -} - -_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(item_index < cache->num_items); - return cache->items[item_index].sampler_handle; -} - -/*=== DUMMY BACKEND DECLARATIONS =============================================*/ #if defined(SOKOL_DUMMY_BACKEND) typedef struct { _sg_slot_t slot; @@ -3309,7 +3979,6 @@ typedef struct { } _sg_dummy_context_t; typedef _sg_dummy_context_t _sg_context_t; -/*== GL BACKEND DECLARATIONS =================================================*/ #elif defined(_SOKOL_ANY_GL) typedef struct { _sg_slot_t slot; @@ -3435,6 +4104,8 @@ typedef struct { GLuint texture; } _sg_gl_texture_bind_slot; +#define _SG_GL_IMAGE_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGES * SG_NUM_SHADER_STAGES) + typedef struct { sg_depth_state depth; sg_stencil_state stencil; @@ -3452,7 +4123,7 @@ typedef struct { GLuint stored_vertex_buffer; GLuint stored_index_buffer; GLuint prog; - _sg_gl_texture_bind_slot textures[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_gl_texture_bind_slot textures[_SG_GL_IMAGE_CACHE_SIZE]; _sg_gl_texture_bind_slot stored_texture; int cur_ib_offset; GLenum cur_primitive_type; @@ -3474,13 +4145,11 @@ typedef struct { _sg_gl_state_cache_t cache; bool ext_anisotropic; GLint max_anisotropy; - GLint max_combined_texture_image_units; #if _SOKOL_USE_WIN32_GL_LOADER HINSTANCE opengl32_dll; #endif } _sg_gl_backend_t; -/*== D3D11 BACKEND DECLARATIONS ==============================================*/ #elif defined(SOKOL_D3D11) typedef struct { @@ -3584,6 +4253,7 @@ typedef struct { void* user_data; bool in_pass; bool use_indexed_draw; + bool use_instanced_draw; int cur_width; int cur_height; int num_rtvs; @@ -3601,7 +4271,6 @@ typedef struct { D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; } _sg_d3d11_backend_t; -/*=== METAL BACKEND DECLARATIONS =============================================*/ #elif defined(SOKOL_METAL) #if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) @@ -3737,11 +4406,11 @@ typedef struct { id device; id cmd_queue; id cmd_buffer; + id present_cmd_buffer; id cmd_encoder; id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; } _sg_mtl_backend_t; -/*=== WGPU BACKEND DECLARATIONS ==============================================*/ #elif defined(SOKOL_WGPU) #define _SG_WGPU_STAGING_ALIGN (256) @@ -3873,7 +4542,8 @@ typedef struct { } _sg_wgpu_backend_t; #endif -/*=== RESOURCE POOL DECLARATIONS =============================================*/ +// POOL STRUCTS + /* this *MUST* remain 0 */ #define _SG_INVALID_SLOT_INDEX (0) @@ -3900,134 +4570,11 @@ typedef struct { _sg_context_t* contexts; } _sg_pools_t; -/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ -typedef enum { - /* special case 'validation was successful' */ - _SG_VALIDATE_SUCCESS, - - /* buffer creation */ - _SG_VALIDATE_BUFFERDESC_CANARY, - _SG_VALIDATE_BUFFERDESC_SIZE, - _SG_VALIDATE_BUFFERDESC_DATA, - _SG_VALIDATE_BUFFERDESC_DATA_SIZE, - _SG_VALIDATE_BUFFERDESC_NO_DATA, - - /* image data (for image creation and updating) */ - _SG_VALIDATE_IMAGEDATA_NODATA, - _SG_VALIDATE_IMAGEDATA_DATA_SIZE, - - /* image creation */ - _SG_VALIDATE_IMAGEDESC_CANARY, - _SG_VALIDATE_IMAGEDESC_WIDTH, - _SG_VALIDATE_IMAGEDESC_HEIGHT, - _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, - _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, - _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, - _SG_VALIDATE_IMAGEDESC_RT_NO_DATA, - _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA, - _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, - _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, - - /* shader creation */ - _SG_VALIDATE_SHADERDESC_CANARY, - _SG_VALIDATE_SHADERDESC_SOURCE, - _SG_VALIDATE_SHADERDESC_BYTECODE, - _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, - _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, - _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, - _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, - _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, - _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, - _SG_VALIDATE_SHADERDESC_IMG_NAME, - _SG_VALIDATE_SHADERDESC_ATTR_NAMES, - _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, - _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, - - /* pipeline creation */ - _SG_VALIDATE_PIPELINEDESC_CANARY, - _SG_VALIDATE_PIPELINEDESC_SHADER, - _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, - _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, - _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, - _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, - - /* pass creation */ - _SG_VALIDATE_PASSDESC_CANARY, - _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_IMAGE, - _SG_VALIDATE_PASSDESC_MIPLEVEL, - _SG_VALIDATE_PASSDESC_FACE, - _SG_VALIDATE_PASSDESC_LAYER, - _SG_VALIDATE_PASSDESC_SLICE, - _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, - _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_IMAGE_SIZES, - _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, - - /* sg_begin_pass validation */ - _SG_VALIDATE_BEGINPASS_PASS, - _SG_VALIDATE_BEGINPASS_IMAGE, - - /* sg_apply_pipeline validation */ - _SG_VALIDATE_APIP_PIPELINE_VALID_ID, - _SG_VALIDATE_APIP_PIPELINE_EXISTS, - _SG_VALIDATE_APIP_PIPELINE_VALID, - _SG_VALIDATE_APIP_SHADER_EXISTS, - _SG_VALIDATE_APIP_SHADER_VALID, - _SG_VALIDATE_APIP_ATT_COUNT, - _SG_VALIDATE_APIP_COLOR_FORMAT, - _SG_VALIDATE_APIP_DEPTH_FORMAT, - _SG_VALIDATE_APIP_SAMPLE_COUNT, - - /* sg_apply_bindings validation */ - _SG_VALIDATE_ABND_PIPELINE, - _SG_VALIDATE_ABND_PIPELINE_EXISTS, - _SG_VALIDATE_ABND_PIPELINE_VALID, - _SG_VALIDATE_ABND_VBS, - _SG_VALIDATE_ABND_VB_EXISTS, - _SG_VALIDATE_ABND_VB_TYPE, - _SG_VALIDATE_ABND_VB_OVERFLOW, - _SG_VALIDATE_ABND_NO_IB, - _SG_VALIDATE_ABND_IB, - _SG_VALIDATE_ABND_IB_EXISTS, - _SG_VALIDATE_ABND_IB_TYPE, - _SG_VALIDATE_ABND_IB_OVERFLOW, - _SG_VALIDATE_ABND_VS_IMGS, - _SG_VALIDATE_ABND_VS_IMG_EXISTS, - _SG_VALIDATE_ABND_VS_IMG_TYPES, - _SG_VALIDATE_ABND_FS_IMGS, - _SG_VALIDATE_ABND_FS_IMG_EXISTS, - _SG_VALIDATE_ABND_FS_IMG_TYPES, - - /* sg_apply_uniforms validation */ - _SG_VALIDATE_AUB_NO_PIPELINE, - _SG_VALIDATE_AUB_NO_UB_AT_SLOT, - _SG_VALIDATE_AUB_SIZE, - - /* sg_update_buffer validation */ - _SG_VALIDATE_UPDATEBUF_USAGE, - _SG_VALIDATE_UPDATEBUF_SIZE, - _SG_VALIDATE_UPDATEBUF_ONCE, - _SG_VALIDATE_UPDATEBUF_APPEND, - - /* sg_append_buffer validation */ - _SG_VALIDATE_APPENDBUF_USAGE, - _SG_VALIDATE_APPENDBUF_SIZE, - _SG_VALIDATE_APPENDBUF_UPDATE, - - /* sg_update_image validation */ - _SG_VALIDATE_UPDIMG_USAGE, - _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, - _SG_VALIDATE_UPDIMG_ONCE -} _sg_validate_error_t; - -/*=== GENERIC BACKEND STATE ==================================================*/ +typedef struct { + int num; // number of allocated commit listener items + int upper; // the current upper index (no valid items past this point) + sg_commit_listener* items; +} _sg_commit_listeners_t; typedef struct { bool valid; @@ -4040,7 +4587,7 @@ typedef struct { bool bindings_valid; bool next_draw_valid; #if defined(SOKOL_DEBUG) - _sg_validate_error_t validate_error; + sg_log_item validate_error; #endif _sg_pools_t pools; sg_backend backend; @@ -4059,10 +4606,188 @@ typedef struct { #if defined(SOKOL_TRACE_HOOKS) sg_trace_hooks hooks; #endif + _sg_commit_listeners_t commit_listeners; } _sg_state_t; static _sg_state_t _sg; -/*-- helper functions --------------------------------------------------------*/ +// ███████ █████ ███ ███ ██████ ██ ███████ ██████ ██████ █████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ███████ ██ ████ ██ ██████ ██ █████ ██████ ██ ███████ ██ ███████ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ██ ███████ ███████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ +// +// >>sampler cache +/* + this is used by the Metal and WGPU backends to reduce the + number of sampler state objects created through the backend API +*/ +_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { + SOKOL_ASSERT(cache && (capacity > 0)); + _sg_clear(cache, sizeof(_sg_sampler_cache_t)); + cache->capacity = capacity; + const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); + cache->items = (_sg_sampler_cache_item_t*) _sg_malloc_clear(size); +} + +_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { + SOKOL_ASSERT(cache && cache->items); + _sg_free(cache->items); + cache->items = 0; + cache->num_items = 0; + cache->capacity = 0; +} + +_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { + return (int) (min_lod * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { + return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { + /* return matching sampler cache item index or -1 */ + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + for (int i = 0; i < cache->num_items; i++) { + const _sg_sampler_cache_item_t* item = &cache->items[i]; + if ((img_desc->min_filter == item->min_filter) && + (img_desc->mag_filter == item->mag_filter) && + (img_desc->wrap_u == item->wrap_u) && + (img_desc->wrap_v == item->wrap_v) && + (img_desc->wrap_w == item->wrap_w) && + (img_desc->max_anisotropy == item->max_anisotropy) && + (img_desc->border_color == item->border_color) && + (min_lod == item->min_lod) && + (max_lod == item->max_lod)) + { + return i; + } + } + /* fallthrough: no matching cache item found */ + return -1; +} + +_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + SOKOL_ASSERT(cache->num_items < cache->capacity); + const int item_index = cache->num_items++; + _sg_sampler_cache_item_t* item = &cache->items[item_index]; + item->min_filter = img_desc->min_filter; + item->mag_filter = img_desc->mag_filter; + item->wrap_u = img_desc->wrap_u; + item->wrap_v = img_desc->wrap_v; + item->wrap_w = img_desc->wrap_w; + item->border_color = img_desc->border_color; + item->max_anisotropy = img_desc->max_anisotropy; + item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + item->sampler_handle = sampler_handle; +} + +_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(item_index < cache->num_items); + return cache->items[item_index].sampler_handle; +} + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SG_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sg_log_messages[] = { + _SG_LOG_ITEMS +}; +#undef _SG_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SG_PANIC(code) _sg_log(SG_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SG_ERROR(code) _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SG_WARN(code) _sg_log(SG_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SG_INFO(code) _sg_log(SG_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SG_LOGMSG(code,msg) _sg_log(SG_LOGITEM_ ##code, 3, msg, __LINE__) +#define _SG_VALIDATE(cond,code) if (!(cond)){ _sg.validate_error = SG_LOGITEM_ ##code; _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__); } + +static void _sg_log(sg_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sg.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sg_log_messages[log_item]; + } + #endif + _sg.desc.logger.func("sg", log_level, log_item, msg, line_nr, filename, _sg.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory + +// a helper macro to clear a struct with potentially ARC'ed ObjC references +#if defined(SOKOL_METAL) + #if defined(__cplusplus) + #define _SG_CLEAR_ARC_STRUCT(type, item) { item = type(); } + #else + #define _SG_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SG_CLEAR_ARC_STRUCT(type, item) { _sg_clear(&item, sizeof(item)); } +#endif + +_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sg_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sg.desc.allocator.alloc) { + ptr = _sg.desc.allocator.alloc(size, _sg.desc.allocator.user_data); + } + else { + ptr = malloc(size); + } + if (0 == ptr) { + _SG_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) { + void* ptr = _sg_malloc(size); + _sg_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sg_free(void* ptr) { + if (_sg.desc.allocator.free) { + _sg.desc.allocator.free(ptr, _sg.desc.allocator.user_data); + } + else { + free(ptr); + } +} _SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { return 0 == str->buf[0]; @@ -4083,10 +4808,22 @@ _SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { dst->buf[_SG_STRING_SIZE-1] = 0; } else { - memset(dst->buf, 0, _SG_STRING_SIZE); + _sg_clear(dst->buf, _SG_STRING_SIZE); } } +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers +_SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); +} + /* return byte size of a vertex format */ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { switch (fmt) { @@ -4105,6 +4842,8 @@ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return 8; case SG_VERTEXFORMAT_USHORT4N: return 8; case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 4; + case SG_VERTEXFORMAT_HALF4: return 8; case SG_VERTEXFORMAT_INVALID: return 0; default: SOKOL_UNREACHABLE; @@ -4112,18 +4851,101 @@ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { } } -/* return the byte size of a shader uniform */ -_SOKOL_PRIVATE int _sg_uniform_size(sg_uniform_type type, int count) { - switch (type) { - case SG_UNIFORMTYPE_INVALID: return 0; - case SG_UNIFORMTYPE_FLOAT: return 4 * count; - case SG_UNIFORMTYPE_FLOAT2: return 8 * count; - case SG_UNIFORMTYPE_FLOAT3: return 12 * count; /* FIXME: std140??? */ - case SG_UNIFORMTYPE_FLOAT4: return 16 * count; - case SG_UNIFORMTYPE_MAT4: return 64 * count; - default: - SOKOL_UNREACHABLE; - return -1; +_SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + return 1; + } + else { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 16; + default: + SOKOL_UNREACHABLE; + return 1; + } + } + else { + return 16; + } + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 64; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + else { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + else { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT: + case SG_UNIFORMTYPE_INT2: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } } } @@ -4203,12 +5025,14 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: case SG_PIXELFORMAT_RG16F: case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8SN: case SG_PIXELFORMAT_RGBA8UI: case SG_PIXELFORMAT_RGBA8SI: case SG_PIXELFORMAT_BGRA8: case SG_PIXELFORMAT_RGB10A2: case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: return 4; case SG_PIXELFORMAT_RG32UI: @@ -4429,7 +5253,13 @@ _SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, } } -/*== DUMMY BACKEND IMPL ======================================================*/ +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>dummy backend #if defined(SOKOL_DUMMY_BACKEND) _SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { @@ -4461,7 +5291,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_dummy_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); } @@ -4477,7 +5307,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, cons return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _SOKOL_UNUSED(buf); } @@ -4488,7 +5318,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); _SOKOL_UNUSED(img); } @@ -4499,7 +5329,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, cons return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _SOKOL_UNUSED(shd); } @@ -4519,7 +5349,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _SOKOL_UNUSED(pip); } @@ -4551,7 +5381,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pass(_sg_pass_t* pass, _sg_ima return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_dummy_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); } @@ -4663,10 +5493,16 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data } } -/*== GL BACKEND ==============================================================*/ +// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>opengl backend #elif defined(_SOKOL_ANY_GL) -/*=== OPTIONAL GL LOADER FOR WIN32 ===========================================*/ +// optional GL loader for win32 #if defined(_SOKOL_USE_WIN32_GL_LOADER) // X Macro list of GL function names and signatures @@ -4682,8 +5518,15 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ _SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ _SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ - _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ _SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ _SG_XMACRO(glUseProgram, void, (GLuint program)) \ _SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ _SG_XMACRO(glLinkProgram, void, (GLuint program)) \ @@ -4708,7 +5551,6 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ _SG_XMACRO(glActiveTexture, void, (GLenum texture)) \ _SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ _SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ _SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ _SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ @@ -4739,7 +5581,6 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ _SG_XMACRO(glClearStencil, void, (GLint s)) \ _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ - _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ _SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ @@ -4753,21 +5594,21 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ - _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ + _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \ _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ _SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ - _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ _SG_XMACRO(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * data)) \ _SG_XMACRO(glClear, void, (GLbitfield mask)) \ _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ - _SG_XMACRO(glCullFace, void, (GLenum mode)) + _SG_XMACRO(glCullFace, void, (GLenum mode)) \ + _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -4864,6 +5705,8 @@ _SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return 4; case SG_VERTEXFORMAT_USHORT4N: return 4; case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 2; + case SG_VERTEXFORMAT_HALF4: return 4; default: SOKOL_UNREACHABLE; return 0; } } @@ -4891,6 +5734,9 @@ _SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { return GL_UNSIGNED_SHORT; case SG_VERTEXFORMAT_UINT10_N2: return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_VERTEXFORMAT_HALF2: + case SG_VERTEXFORMAT_HALF4: + return GL_HALF_FLOAT; default: SOKOL_UNREACHABLE; return 0; } @@ -5022,6 +5868,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG8: case SG_PIXELFORMAT_RG8UI: case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8UI: case SG_PIXELFORMAT_BGRA8: return GL_UNSIGNED_BYTE; @@ -5067,6 +5914,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { return GL_UNSIGNED_INT_2_10_10_10_REV; case SG_PIXELFORMAT_RG11B10F: return GL_UNSIGNED_INT_10F_11F_11F_REV; + case SG_PIXELFORMAT_RGB9E5: + return GL_UNSIGNED_INT_5_9_9_9_REV; #endif case SG_PIXELFORMAT_DEPTH: return GL_UNSIGNED_SHORT; @@ -5119,6 +5968,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_RG_INTEGER; #endif case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8SN: case SG_PIXELFORMAT_RGBA16: case SG_PIXELFORMAT_RGBA16SN: @@ -5136,6 +5986,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_RGBA_INTEGER; #endif case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: return GL_RGB; case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT; @@ -5219,11 +6070,13 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: return GL_RG16I; case SG_PIXELFORMAT_RG16F: return GL_RG16F; case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8; case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; case SG_PIXELFORMAT_RG32SI: return GL_RG32I; case SG_PIXELFORMAT_RG32F: return GL_RG32F; @@ -5324,6 +6177,7 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); @@ -5336,6 +6190,7 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { if (!_sg.gl.gles2) { _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); #if !defined(SOKOL_GLES3) @@ -5547,7 +6402,7 @@ _SOKOL_PRIVATE void _sg_gl_init_limits(void) { } glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); _SG_GL_CHECK_ERROR(); - _sg.gl.max_combined_texture_image_units = gl_int; + _sg.limits.gl_max_combined_texture_image_units = gl_int; } #if defined(SOKOL_GLCORE33) @@ -5869,7 +6724,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { } } -/* called when from _sg_gl_destroy_buffer() */ +/* called when _sg_gl_deinit_buffer() */ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { if (buf == _sg.gl.cache.vertex_buffer) { _sg.gl.cache.vertex_buffer = 0; @@ -5900,7 +6755,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { } _SOKOL_PRIVATE void _sg_gl_cache_clear_texture_bindings(bool force) { - for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { + for (int i = 0; (i < _SG_GL_IMAGE_CACHE_SIZE) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) { if (force || (_sg.gl.cache.textures[i].texture != 0)) { GLenum gl_texture_slot = (GLenum) (GL_TEXTURE0 + i); glActiveTexture(gl_texture_slot); @@ -5924,8 +6779,8 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLu target=0 will unbind the previous binding, texture=0 will clear the new binding */ - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - if (slot_index >= _sg.gl.max_combined_texture_image_units) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_IMAGE_CACHE_SIZE)); + if (slot_index >= _sg.limits.gl_max_combined_texture_image_units) { return; } _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; @@ -5945,12 +6800,12 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLu } _SOKOL_PRIVATE void _sg_gl_cache_store_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_IMAGE_CACHE_SIZE)); _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; } _SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_IMAGE_CACHE_SIZE)); _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; if (slot->texture != 0) { /* we only care restoring valid ids */ @@ -5963,7 +6818,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { /* called from _sg_gl_destroy_texture() */ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + for (int i = 0; i < _SG_GL_IMAGE_CACHE_SIZE; i++) { _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[i]; if (tex == slot->texture) { _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); @@ -5978,7 +6833,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { } } -/* called from _sg_gl_destroy_shader() */ +/* called from _sg_gl_discard_shader() */ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { if (prog == _sg.gl.cache.prog) { _sg.gl.cache.prog = 0; @@ -5986,7 +6841,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { } } -/* called from _sg_gl_destroy_pipeline() */ +/* called from _sg_gl_discard_pipeline() */ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { if (pip == _sg.gl.cache.cur_pipeline) { _sg.gl.cache.cur_pipeline = 0; @@ -6003,7 +6858,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { _SG_GL_CHECK_ERROR(); } #endif - memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); + _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache)); _sg_gl_cache_clear_buffer_bindings(true); _SG_GL_CHECK_ERROR(); _sg_gl_cache_clear_texture_bindings(true); @@ -6137,10 +6992,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { _SG_GL_CHECK_ERROR(); } #endif + // incoming texture data is generally expected to be packed tightly + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_gl_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { @@ -6185,7 +7042,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const s return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_gl_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _SG_GL_CHECK_ERROR(); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { @@ -6213,16 +7070,16 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ /* check if texture format is support */ if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { - SOKOL_LOG("texture format not supported by GL context\n"); + _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED); return SG_RESOURCESTATE_FAILED; } /* check for optional texture types */ if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { - SOKOL_LOG("3D textures not supported by GL context\n"); + _SG_ERROR(GL_3D_TEXTURES_NOT_SUPPORTED); return SG_RESOURCESTATE_FAILED; } if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { - SOKOL_LOG("array textures not supported by GL context\n"); + _SG_ERROR(GL_ARRAY_TEXTURES_NOT_SUPPORTED); return SG_RESOURCESTATE_FAILED; } @@ -6391,7 +7248,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_gl_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); _SG_GL_CHECK_ERROR(); for (int slot = 0; slot < img->cmn.num_slots; slot++) { @@ -6424,10 +7281,11 @@ _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* s GLint log_len = 0; glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); if (log_len > 0) { - GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); - SOKOL_LOG(log_buf); - SOKOL_FREE(log_buf); + _SG_ERROR(GL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(GL_SHADER_COMPILATION_FAILED, log_buf); + _sg_free(log_buf); } glDeleteShader(gl_shd); gl_shd = 0; @@ -6467,10 +7325,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s GLint log_len = 0; glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); if (log_len > 0) { - GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); - SOKOL_LOG(log_buf); - SOKOL_FREE(log_buf); + _SG_ERROR(GL_SHADER_LINKING_FAILED); + _SG_LOGMSG(GL_SHADER_LINKING_FAILED, log_buf); + _sg_free(log_buf); } glDeleteProgram(gl_prog); return SG_RESOURCESTATE_FAILED; @@ -6487,17 +7346,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s SOKOL_ASSERT(ub_desc->size > 0); _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; SOKOL_ASSERT(ub->num_uniforms == 0); - int cur_uniform_offset = 0; + uint32_t cur_uniform_offset = 0; for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type == SG_UNIFORMTYPE_INVALID) { break; } + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, u_desc->array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, u_desc->array_count, ub_desc->layout); + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, u_align); _sg_gl_uniform_t* u = &ub->uniforms[u_index]; u->type = u_desc->type; u->count = (uint16_t) u_desc->array_count; u->offset = (uint16_t) cur_uniform_offset; - cur_uniform_offset += _sg_uniform_size(u->type, u->count); + cur_uniform_offset += u_size; if (u_desc->name) { u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); } @@ -6506,7 +7368,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s } ub->num_uniforms++; } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, 16); + } SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset); + _SOKOL_UNUSED(cur_uniform_offset); } } @@ -6542,7 +7408,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _SG_GL_CHECK_ERROR(); if (shd->gl.prog) { @@ -6599,6 +7465,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg } else { gl_attr->divisor = (int8_t) step_rate; + pip->cmn.use_instanced_draw = true; } SOKOL_ASSERT(l_desc->stride > 0); gl_attr->stride = (uint8_t) l_desc->stride; @@ -6609,14 +7476,14 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; } else { - SOKOL_LOG("Vertex attribute not found in shader: "); - SOKOL_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); + _SG_ERROR(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, _sg_strptr(&shd->gl.attrs[attr_index].name)); } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _sg_gl_cache_invalidate_pipeline(pip); } @@ -6715,7 +7582,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ /* check if framebuffer is complete */ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SOKOL_LOG("Framebuffer completeness check failed!\n"); + _SG_ERROR(GL_FRAMEBUFFER_INCOMPLETE); return SG_RESOURCESTATE_FAILED; } @@ -6762,7 +7629,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ } /* check if framebuffer is complete */ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); + _SG_ERROR(GL_MSAA_FRAMEBUFFER_INCOMPLETE); return SG_RESOURCESTATE_FAILED; } /* setup color attachments for the framebuffer */ @@ -6782,7 +7649,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_gl_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); SOKOL_ASSERT(pass != _sg.gl.cur_pass); _SG_GL_CHECK_ERROR(); @@ -6832,15 +7699,29 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac /* number of color attachments */ const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; - /* bind the render pass framebuffer */ + // bind the render pass framebuffer + // + // FIXME: Disabling SRGB conversion for the default framebuffer is + // a crude hack to make behaviour for sRGB render target textures + // identical with the Metal and D3D11 swapchains created by sokol-app. + // + // This will need a cleaner solution (e.g. allowing to configure + // sokol_app.h with an sRGB or RGB framebuffer. if (pass) { - /* offscreen pass */ + // offscreen pass SOKOL_ASSERT(pass->gl.fb); + #if defined(SOKOL_GLCORE33) + glEnable(GL_FRAMEBUFFER_SRGB); + #endif glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + } else { - /* default pass */ + // default pass SOKOL_ASSERT(_sg.gl.cur_context); + #if defined(SOKOL_GLCORE33) + glDisable(GL_FRAMEBUFFER_SRGB); + #endif glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); } glViewport(0, 0, w, h); @@ -7145,7 +8026,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } /* standalone state */ - for (GLuint i = 0; i < (GLuint)pip->cmn.color_attachment_count; i++) { + for (GLuint i = 0; i < (GLuint)pip->cmn.color_count; i++) { if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { const sg_color_mask cm = pip->gl.color_write_mask[i]; _sg.gl.cache.color_write_mask[i] = cm; @@ -7327,24 +8208,37 @@ _SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_in if (u->gl_loc == -1) { continue; } - GLfloat* ptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); + GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); + GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset); switch (u->type) { case SG_UNIFORMTYPE_INVALID: break; case SG_UNIFORMTYPE_FLOAT: - glUniform1fv(u->gl_loc, u->count, ptr); + glUniform1fv(u->gl_loc, u->count, fptr); break; case SG_UNIFORMTYPE_FLOAT2: - glUniform2fv(u->gl_loc, u->count, ptr); + glUniform2fv(u->gl_loc, u->count, fptr); break; case SG_UNIFORMTYPE_FLOAT3: - glUniform3fv(u->gl_loc, u->count, ptr); + glUniform3fv(u->gl_loc, u->count, fptr); break; case SG_UNIFORMTYPE_FLOAT4: - glUniform4fv(u->gl_loc, u->count, ptr); + glUniform4fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_INT: + glUniform1iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT2: + glUniform2iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT3: + glUniform3iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT4: + glUniform4iv(u->gl_loc, u->count, iptr); break; case SG_UNIFORMTYPE_MAT4: - glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, ptr); + glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, fptr); break; default: SOKOL_UNREACHABLE; @@ -7354,6 +8248,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_in } _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; if (0 != i_type) { @@ -7361,25 +8256,25 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; const int ib_offset = _sg.gl.cache.cur_ib_offset; const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); - if (num_instances == 1) { - glDrawElements(p_type, num_elements, i_type, indices); - } - else { + if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { if (_sg.features.instancing) { glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); } } + else { + glDrawElements(p_type, num_elements, i_type, indices); + } } else { /* non-indexed rendering */ - if (num_instances == 1) { - glDrawArrays(p_type, base_element, num_elements); - } - else { + if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { if (_sg.features.instancing) { glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); } } + else { + glDrawArrays(p_type, base_element, num_elements); + } } } @@ -7484,7 +8379,13 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d _sg_gl_cache_restore_texture_binding(0); } -/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ +// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>d3d11 backend #elif defined(SOKOL_D3D11) #if defined(__cplusplus) @@ -7919,12 +8820,14 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_SRGB8A8: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; + case SG_PIXELFORMAT_RGB9E5: return DXGI_FORMAT_R9G9B9E5_SHAREDEXP; case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; @@ -8040,6 +8943,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_VERTEXFORMAT_HALF2: return DXGI_FORMAT_R16G16_FLOAT; + case SG_VERTEXFORMAT_HALF4: return DXGI_FORMAT_R16G16B16A16_FLOAT; default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; } } @@ -8225,7 +9130,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_d3d11_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); /* empty */ @@ -8242,27 +9147,29 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons } else { D3D11_BUFFER_DESC d3d11_desc; - memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); d3d11_desc.ByteWidth = (UINT)buf->cmn.size; d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; D3D11_SUBRESOURCE_DATA init_data; - memset(&init_data, 0, sizeof(init_data)); + _sg_clear(&init_data, sizeof(init_data)); if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->data.ptr); init_data.pSysMem = desc->data.ptr; init_data_ptr = &init_data; } HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11.buf); + if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { + _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); if (buf->d3d11.buf) { _sg_d3d11_Release(buf->d3d11.buf); @@ -8303,7 +9210,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa); SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp); HRESULT hr; - _SOKOL_UNUSED(hr); _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); @@ -8315,11 +9221,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const /* create only a depth-texture */ SOKOL_ASSERT(!injected); if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); + _SG_ERROR(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); return SG_RESOURCESTATE_FAILED; } D3D11_TEXTURE2D_DESC d3d11_desc; - memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); d3d11_desc.Width = (UINT)img->cmn.width; d3d11_desc.Height = (UINT)img->cmn.height; d3d11_desc.MipLevels = 1; @@ -8330,7 +9236,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texds); + if (!(SUCCEEDED(hr) && img->d3d11.texds)) { + _SG_ERROR(D3D11_CREATE_DEPTH_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } } else { /* create (or inject) color texture and shader-resource-view */ @@ -8368,7 +9277,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const /* if not injected, create texture */ if (0 == img->d3d11.tex2d) { D3D11_TEXTURE2D_DESC d3d11_tex_desc; - memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; d3d11_tex_desc.Height = (UINT)img->cmn.height; d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; @@ -8391,8 +9300,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - /* trying to create a texture format that's not supported by D3D */ - SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); return SG_RESOURCESTATE_FAILED; } d3d11_tex_desc.SampleDesc.Count = 1; @@ -8400,13 +9308,16 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex2d); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } } /* ...and similar, if not injected, create shader-resource-view */ if (0 == img->d3d11.srv) { D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); d3d11_srv_desc.Format = img->d3d11.format; switch (img->cmn.type) { case SG_IMAGETYPE_2D: @@ -8426,7 +9337,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const SOKOL_UNREACHABLE; break; } hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } } } else { @@ -8449,7 +9363,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const if (0 == img->d3d11.tex3d) { D3D11_TEXTURE3D_DESC d3d11_tex_desc; - memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; d3d11_tex_desc.Height = (UINT)img->cmn.height; d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; @@ -8468,29 +9382,34 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - /* trying to create a texture format that's not supported by D3D */ - SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); return SG_RESOURCESTATE_FAILED; } hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex3d); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } } if (0 == img->d3d11.srv) { D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); d3d11_srv_desc.Format = img->d3d11.format; d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } } } /* also need to create a separate MSAA render target texture? */ if (msaa) { D3D11_TEXTURE2D_DESC d3d11_tex_desc; - memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; d3d11_tex_desc.Height = (UINT)img->cmn.height; d3d11_tex_desc.MipLevels = 1; @@ -8502,12 +9421,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texmsaa); + if (!(SUCCEEDED(hr) && img->d3d11.texmsaa)) { + _SG_ERROR(D3D11_CREATE_MSAA_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } } /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ D3D11_SAMPLER_DESC d3d11_smp_desc; - memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); + _sg_clear(&d3d11_smp_desc, sizeof(d3d11_smp_desc)); d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->cmn.wrap_u); d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->cmn.wrap_v); @@ -8531,12 +9453,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const d3d11_smp_desc.MinLOD = desc->min_lod; d3d11_smp_desc.MaxLOD = desc->max_lod; hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.smp); + if (!(SUCCEEDED(hr) && img->d3d11.smp)) { + _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); if (img->d3d11.tex2d) { _sg_d3d11_Release(img->d3d11.tex2d); @@ -8568,7 +9493,7 @@ _SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); if (0 == _sg.d3d11.d3dcompiler_dll) { /* don't attempt to load missing DLL in the future */ - SOKOL_LOG("failed to load d3dcompiler_47.dll!\n"); + _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED); _sg.d3d11.d3dcompiler_dll_load_failed = true; return false; } @@ -8605,8 +9530,12 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st 0, /* Flags2 */ &output, /* ppCode */ &errors_or_warnings); /* ppErrorMsgs */ + if (FAILED(hr)) { + _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED); + } if (errors_or_warnings) { - SOKOL_LOG((LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _SG_WARN(D3D11_SHADER_COMPILATION_OUTPUT); + _SG_LOGMSG(D3D11_SHADER_COMPILATION_OUTPUT, (LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; } if (FAILED(hr)) { @@ -8623,7 +9552,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SOKOL_ASSERT(shd && desc); SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); HRESULT hr; - _SOKOL_UNUSED(hr); _sg_shader_common_init(&shd->cmn, desc); @@ -8638,17 +9566,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { - const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + const _sg_shader_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; /* create a D3D constant buffer for each uniform block */ SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); D3D11_BUFFER_DESC cb_desc; - memset(&cb_desc, 0, sizeof(cb_desc)); + _sg_clear(&cb_desc, sizeof(cb_desc)); cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16); cb_desc.Usage = D3D11_USAGE_DEFAULT; cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); - SOKOL_ASSERT(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index]); + if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { + _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } } } @@ -8684,7 +9615,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ if (vs_succeeded && fs_succeeded) { shd->d3d11.vs_blob_length = vs_length; - shd->d3d11.vs_blob = SOKOL_MALLOC((size_t)vs_length); + shd->d3d11.vs_blob = _sg_malloc((size_t)vs_length); SOKOL_ASSERT(shd->d3d11.vs_blob); memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); result = SG_RESOURCESTATE_VALID; @@ -8699,7 +9630,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons return result; } -_SOKOL_PRIVATE void _sg_d3d11_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_d3d11_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); if (shd->d3d11.vs) { _sg_d3d11_Release(shd->d3d11.vs); @@ -8708,7 +9639,7 @@ _SOKOL_PRIVATE void _sg_d3d11_destroy_shader(_sg_shader_t* shd) { _sg_d3d11_Release(shd->d3d11.fs); } if (shd->d3d11.vs_blob) { - SOKOL_FREE(shd->d3d11.vs_blob); + _sg_free(shd->d3d11.vs_blob); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; @@ -8736,9 +9667,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, /* create input layout object */ HRESULT hr; - _SOKOL_UNUSED(hr); D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; - memset(d3d11_comps, 0, sizeof(d3d11_comps)); + _sg_clear(d3d11_comps, sizeof(d3d11_comps)); int attr_index = 0; for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; @@ -8758,6 +9688,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { d3d11_comp->InstanceDataStepRate = (UINT)step_rate; + pip->cmn.use_instanced_draw = true; } pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; } @@ -8777,27 +9708,33 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */ shd->d3d11.vs_blob_length, /* BytecodeLength */ &pip->d3d11.il); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } /* create rasterizer state */ D3D11_RASTERIZER_DESC rs_desc; - memset(&rs_desc, 0, sizeof(rs_desc)); + _sg_clear(&rs_desc, sizeof(rs_desc)); rs_desc.FillMode = D3D11_FILL_SOLID; rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; - rs_desc.DepthBias = (INT) pip->cmn.depth_bias; - rs_desc.DepthBiasClamp = pip->cmn.depth_bias_clamp; - rs_desc.SlopeScaledDepthBias = pip->cmn.depth_bias_slope_scale; + rs_desc.DepthBias = (INT) pip->cmn.depth.bias; + rs_desc.DepthBiasClamp = pip->cmn.depth.bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth.bias_slope_scale; rs_desc.DepthClipEnable = TRUE; rs_desc.ScissorEnable = TRUE; rs_desc.MultisampleEnable = desc->sample_count > 1; rs_desc.AntialiasedLineEnable = FALSE; hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.rs); + if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { + _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } /* create depth-stencil state */ D3D11_DEPTH_STENCIL_DESC dss_desc; - memset(&dss_desc, 0, sizeof(dss_desc)); + _sg_clear(&dss_desc, sizeof(dss_desc)); dss_desc.DepthEnable = TRUE; dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare); @@ -8815,11 +9752,14 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.dss); + if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { + _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } /* create blend state */ D3D11_BLEND_DESC bs_desc; - memset(&bs_desc, 0, sizeof(bs_desc)); + _sg_clear(&bs_desc, sizeof(bs_desc)); bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; bs_desc.IndependentBlendEnable = TRUE; { @@ -8846,12 +9786,14 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, } } hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.bs); - + if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { + _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); if (pip == _sg.d3d11.cur_pipeline) { _sg.d3d11.cur_pipeline = 0; @@ -8894,7 +9836,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima ID3D11Resource* d3d11_res = 0; const bool is_msaa = att_img->cmn.sample_count > 1; D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; - memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); + _sg_clear(&d3d11_rtv_desc, sizeof(d3d11_rtv_desc)); d3d11_rtv_desc.Format = att_img->d3d11.format; if ((att_img->cmn.type == SG_IMAGETYPE_2D) || is_msaa) { if (is_msaa) { @@ -8924,8 +9866,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima } SOKOL_ASSERT(d3d11_res); HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv); + if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv)) { + _SG_ERROR(D3D11_CREATE_RTV_FAILED); + return SG_RESOURCESTATE_FAILED; + } } /* optional depth-stencil image */ @@ -8943,7 +9887,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima /* create D3D11 depth-stencil-view */ D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; - memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); + _sg_clear(&d3d11_dsv_desc, sizeof(d3d11_dsv_desc)); d3d11_dsv_desc.Format = att_img->d3d11.format; const bool is_msaa = att_img->cmn.sample_count > 1; if (is_msaa) { @@ -8955,13 +9899,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds; SOKOL_ASSERT(d3d11_res); HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv); + if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv)) { + _SG_ERROR(D3D11_CREATE_DSV_FAILED); + return SG_RESOURCESTATE_FAILED; + } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_d3d11_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); SOKOL_ASSERT(pass != _sg.d3d11.cur_pass); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { @@ -9033,7 +9979,7 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* /* set viewport and scissor rect to cover whole screen */ D3D11_VIEWPORT vp; - memset(&vp, 0, sizeof(vp)); + _sg_clear(&vp, sizeof(vp)); vp.Width = (FLOAT) w; vp.Height = (FLOAT) h; vp.MaxDepth = 1.0f; @@ -9138,6 +10084,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { _sg.d3d11.cur_pipeline = pip; _sg.d3d11.cur_pipeline_id.id = pip->slot.id; _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); + _sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw; _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); @@ -9222,19 +10169,19 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.d3d11.in_pass); if (_sg.d3d11.use_indexed_draw) { - if (1 == num_instances) { - _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); + if (_sg.d3d11.use_instanced_draw) { + _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); } else { - _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); + _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); } } else { - if (1 == num_instances) { - _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + if (_sg.d3d11.use_instanced_draw) { + _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); } else { - _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); + _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); } } } @@ -9249,10 +10196,13 @@ _SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* d SOKOL_ASSERT(buf->d3d11.buf); D3D11_MAPPED_SUBRESOURCE d3d11_msr; HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr)); - memcpy(d3d11_msr.pData, data->ptr, data->size); - _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + if (SUCCEEDED(hr)) { + memcpy(d3d11_msr.pData, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + } + else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); + } } _SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { @@ -9262,12 +10212,15 @@ _SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* da D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; D3D11_MAPPED_SUBRESOURCE d3d11_msr; HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr)); - uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; - memcpy(dst_ptr, data->ptr, data->size); - _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + if (SUCCEEDED(hr)) { + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + } + else { + _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); + } + /* NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend */ return _sg_roundup((int)data->size, 4); } @@ -9287,7 +10240,6 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; UINT subres_index = 0; HRESULT hr; - _SOKOL_UNUSED(hr); D3D11_MAPPED_SUBRESOURCE d3d11_msr; for (int face_index = 0; face_index < num_faces; face_index++) { for (int slice_index = 0; slice_index < num_slices; slice_index++) { @@ -9301,38 +10253,46 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data const size_t slice_offset = slice_size * (size_t)slice_index; const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; hr = _sg_d3d11_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); - SOKOL_ASSERT(SUCCEEDED(hr)); - /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ - if (src_pitch == (int)d3d11_msr.RowPitch) { - memcpy(d3d11_msr.pData, slice_ptr, slice_size); + if (SUCCEEDED(hr)) { + /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ + if (src_pitch == (int)d3d11_msr.RowPitch) { + memcpy(d3d11_msr.pData, slice_ptr, slice_size); + } + else { + SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_ptr, src_ptr, (size_t)src_pitch); + src_ptr += src_pitch; + dst_ptr += d3d11_msr.RowPitch; + } + } + _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); } else { - SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); - const uint8_t* src_ptr = slice_ptr; - uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; - for (int row_index = 0; row_index < mip_height; row_index++) { - memcpy(dst_ptr, src_ptr, (size_t)src_pitch); - src_ptr += src_pitch; - dst_ptr += d3d11_msr.RowPitch; - } + _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); } - _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); } } } } -/*== METAL BACKEND IMPLEMENTATION ============================================*/ +// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>metal backend #elif defined(SOKOL_METAL) #if __has_feature(objc_arc) #define _SG_OBJC_RETAIN(obj) { } #define _SG_OBJC_RELEASE(obj) { obj = nil; } -#define _SG_OBJC_RELEASE_WITH_NULL(obj) { obj = [NSNull null]; } #else #define _SG_OBJC_RETAIN(obj) { [obj retain]; } #define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } -#define _SG_OBJC_RELEASE_WITH_NULL(obj) { [obj release]; obj = [NSNull null]; } #endif /*-- enum translation functions ----------------------------------------------*/ @@ -9391,6 +10351,8 @@ _SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + case SG_VERTEXFORMAT_HALF2: return MTLVertexFormatHalf2; + case SG_VERTEXFORMAT_HALF4: return MTLVertexFormatHalf4; default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; } } @@ -9430,12 +10392,14 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return MTLPixelFormatRGBA8Unorm_sRGB; case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm; case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm; case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float; + case SG_PIXELFORMAT_RGB9E5: return MTLPixelFormatRGB9E5Float; case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint; case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint; case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float; @@ -9683,7 +10647,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); /* a queue of currently free slot indices */ _sg.mtl.idpool.free_queue_top = 0; - _sg.mtl.idpool.free_queue = (int*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); + _sg.mtl.idpool.free_queue = (int*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); /* pool slot 0 is reserved! */ for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; @@ -9694,7 +10658,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { */ _sg.mtl.idpool.release_queue_front = 0; _sg.mtl.idpool.release_queue_back = 0; - _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); + _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { _sg.mtl.idpool.release_queue[i].frame_index = 0; _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; @@ -9702,8 +10666,8 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { } _SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { - SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; - SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; + _sg_free(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; + _sg_free(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); } @@ -9728,6 +10692,7 @@ _SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { return _SG_MTL_INVALID_SLOT_INDEX; } const int slot_index = _sg_mtl_alloc_pool_slot(); + // NOTE: the NSMutableArray will take ownership of its items SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]); _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res; return slot_index; @@ -9767,8 +10732,11 @@ _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { /* safe to release this resource */ const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + /* note: the NSMutableArray takes ownership of its items, assigning an NSNull object will + release the object, no matter if using ARC or not + */ SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); - _SG_OBJC_RELEASE_WITH_NULL(_sg.mtl.idpool.pool[(NSUInteger)slot_index]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = [NSNull null]; /* put the now free pool index back on the free queue */ _sg_mtl_free_pool_slot(slot_index); /* reset the release queue slot and advance the back index */ @@ -9833,13 +10801,14 @@ _SOKOL_PRIVATE int _sg_mtl_create_sampler(id mtl_device, const sg_ima id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; _SG_OBJC_RELEASE(mtl_desc); int sampler_handle = _sg_mtl_add_resource(mtl_sampler); + _SG_OBJC_RELEASE(mtl_sampler); _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, (uintptr_t)sampler_handle); return sampler_handle; } } _SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { - memset(&_sg.mtl.state_cache, 0, sizeof(_sg.mtl.state_cache)); + _sg_clear(&_sg.mtl.state_cache, sizeof(_sg.mtl.state_cache)); } /* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ @@ -9919,6 +10888,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); @@ -9926,9 +10896,11 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); #else + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); #endif @@ -10036,6 +11008,7 @@ _SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { } /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ _sg.mtl.cmd_buffer = nil; + _sg.mtl.present_cmd_buffer = nil; _sg.mtl.cmd_encoder = nil; } @@ -10070,7 +11043,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_mtl_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_mtl_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); /* empty */ @@ -10102,11 +11075,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const } } buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); + _SG_OBJC_RELEASE(mtl_buf); } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_mtl_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_mtl_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { /* it's valid to call release resource with '0' */ @@ -10184,7 +11158,7 @@ _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { - SOKOL_LOG("Unsupported texture pixel format!\n"); + _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED); return false; } mtl_desc.width = (NSUInteger)img->cmn.width; @@ -10203,6 +11177,9 @@ _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, mtl_desc.arrayLength = 1; } mtl_desc.usage = MTLTextureUsageShaderRead; + if (img->cmn.render_target) { + mtl_desc.usage |= MTLTextureUsageRenderTarget; + } MTLResourceOptions res_options = 0; if (img->cmn.usage != SG_USAGE_IMMUTABLE) { res_options |= MTLResourceCPUCacheModeWriteCombined; @@ -10279,6 +11256,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; SOKOL_ASSERT(nil != tex); img->mtl.depth_tex = _sg_mtl_add_resource(tex); + _SG_OBJC_RELEASE(tex); } else { /* create the color texture @@ -10305,6 +11283,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg } } img->mtl.tex[slot] = _sg_mtl_add_resource(tex); + _SG_OBJC_RELEASE(tex); } /* if MSAA color render target, create an additional MSAA render-surface texture */ @@ -10312,6 +11291,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; img->mtl.msaa_tex = _sg_mtl_add_resource(tex); + _SG_OBJC_RELEASE(tex); } /* create (possibly shared) sampler state */ @@ -10321,7 +11301,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_mtl_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_mtl_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); /* it's valid to call release resource with a 'null resource' */ for (int slot = 0; slot < img->cmn.num_slots; slot++) { @@ -10340,7 +11320,8 @@ _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { error:&err ]; if (err) { - SOKOL_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); } return lib; } @@ -10350,7 +11331,8 @@ _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, siz dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; if (err) { - SOKOL_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_SHADER_CREATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); } _SG_OBJC_RELEASE(lib_data); return lib; @@ -10361,19 +11343,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const _sg_shader_common_init(&shd->cmn, desc); - /* create metal libray objects and lookup entry functions */ - id vs_lib; - id fs_lib; - id vs_func; - id fs_func; + /* create metal library objects and lookup entry functions */ + id vs_lib = nil; + id fs_lib = nil; + id vs_func = nil; + id fs_func = nil; const char* vs_entry = desc->vs.entry; const char* fs_entry = desc->fs.entry; if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { /* separate byte code provided */ vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); - if (nil == vs_lib || nil == fs_lib) { - return SG_RESOURCESTATE_FAILED; + if ((nil == vs_lib) || (nil == fs_lib)) { + goto failed; } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; @@ -10382,32 +11364,50 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const /* separate sources provided */ vs_lib = _sg_mtl_compile_library(desc->vs.source); fs_lib = _sg_mtl_compile_library(desc->fs.source); - if (nil == vs_lib || nil == fs_lib) { - return SG_RESOURCESTATE_FAILED; + if ((nil == vs_lib) || (nil == fs_lib)) { + goto failed; } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; } else { - return SG_RESOURCESTATE_FAILED; + goto failed; } if (nil == vs_func) { - SOKOL_LOG("vertex shader entry function not found\n"); - return SG_RESOURCESTATE_FAILED; + _SG_ERROR(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND); + goto failed; } if (nil == fs_func) { - SOKOL_LOG("fragment shader entry function not found\n"); - return SG_RESOURCESTATE_FAILED; + _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND); + goto failed; } /* 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); + _SG_OBJC_RELEASE(vs_lib); shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + _SG_OBJC_RELEASE(fs_lib); shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + _SG_OBJC_RELEASE(vs_func); shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + _SG_OBJC_RELEASE(fs_func); return SG_RESOURCESTATE_VALID; +failed: + if (vs_lib != nil) { + _SG_OBJC_RELEASE(vs_lib); + } + if (fs_lib != nil) { + _SG_OBJC_RELEASE(fs_lib); + } + if (vs_func != nil) { + _SG_OBJC_RELEASE(vs_func); + } + if (fs_func != nil) { + _SG_OBJC_RELEASE(fs_func); + } + return SG_RESOURCESTATE_FAILED; } -_SOKOL_PRIVATE void _sg_mtl_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); @@ -10454,6 +11454,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride; vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate; + if (SG_VERTEXSTEP_PER_INSTANCE == l_desc->step_func) { + // NOTE: not actually used in _sg_mtl_draw() + pip->cmn.use_instanced_draw = true; + } } } @@ -10464,7 +11468,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); - rp_desc.sampleCount = (NSUInteger)desc->sample_count; + rp_desc.rasterSampleCount = (NSUInteger)desc->sample_count; rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; rp_desc.alphaToOneEnabled = NO; rp_desc.rasterizationEnabled = YES; @@ -10498,7 +11502,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s _SG_OBJC_RELEASE(rp_desc); if (nil == mtl_rps) { SOKOL_ASSERT(err); - SOKOL_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_CREATE_RPS_FAILED); + _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]); return SG_RESOURCESTATE_FAILED; } @@ -10524,14 +11529,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.writeMask = desc->stencil.write_mask; } + // FIXME: can this actually fail? id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; _SG_OBJC_RELEASE(ds_desc); pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + _SG_OBJC_RELEASE(mtl_rps); pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); + _SG_OBJC_RELEASE(mtl_dss); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_mtl_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); /* it's valid to call release resource with a 'null resource' */ _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); @@ -10567,7 +11575,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_mtl_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_mtl_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); } @@ -10595,14 +11603,28 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a _sg.mtl.cur_height = h; _sg_mtl_clear_state_cache(); - /* if this is the first pass in the frame, create a command buffer */ + /* + if this is the first pass in the frame, create command buffers + + NOTE: we're creating two command buffers here, one with unretained references + for storing the regular commands, and one with retained references for + storing the presentDrawable call (this needs to hold on the drawable until + presentation has happened - and the easiest way to do this is to let the + command buffer manage the lifetime of the drawable). + + Also see: https://github.com/floooh/sokol/issues/762 + */ if (nil == _sg.mtl.cmd_buffer) { + SOKOL_ASSERT(nil == _sg.mtl.present_cmd_buffer); /* block until the oldest frame in flight has finished */ dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; - [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buffer) { + _sg.mtl.present_cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; + [_sg.mtl.cmd_buffer enqueue]; + [_sg.mtl.present_cmd_buffer enqueue]; + [_sg.mtl.present_cmd_buffer addCompletedHandler:^(id cmd_buf) { // NOTE: this code is called on a different thread! - _SOKOL_UNUSED(cmd_buffer); + _SOKOL_UNUSED(cmd_buf); dispatch_semaphore_signal(_sg.mtl.sem); }]; } @@ -10691,10 +11713,12 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a SOKOL_ASSERT(ds_att_img->mtl.depth_tex != _SG_MTL_INVALID_SLOT_INDEX); pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.storeAction = MTLStoreActionStore; pass_desc.depthAttachment.clearDepth = action->depth.value; if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.storeAction = MTLStoreActionStore; pass_desc.stencilAttachment.clearStencil = action->stencil.value; } } @@ -10738,6 +11762,7 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + SOKOL_ASSERT(nil != _sg.mtl.present_cmd_buffer); /* present, commit and signal semaphore when done */ id cur_drawable = nil; @@ -10748,9 +11773,10 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); } if (nil != cur_drawable) { - [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; + [_sg.mtl.present_cmd_buffer presentDrawable:cur_drawable]; } [_sg.mtl.cmd_buffer commit]; + [_sg.mtl.present_cmd_buffer commit]; /* garbage-collect resources pending for release */ _sg_mtl_garbage_collect(_sg.mtl.frame_index); @@ -10764,6 +11790,7 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { _sg.mtl.cur_ub_base_ptr = 0; /* NOTE: MTLCommandBuffer is autoreleased */ _sg.mtl.cmd_buffer = nil; + _sg.mtl.present_cmd_buffer = nil; } _SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { @@ -10825,7 +11852,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; - [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth_bias slopeScale:pip->cmn.depth_bias_slope_scale clamp:pip->cmn.depth_bias_clamp]; + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp]; SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); @@ -10917,7 +11944,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_i SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(data->size <= _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(data->size == _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; @@ -11000,7 +12027,13 @@ _SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* _sg_mtl_copy_image_data(img, mtl_tex, data); } -/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ +// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>webgpu backend #elif defined(SOKOL_WGPU) _SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { @@ -11130,6 +12163,8 @@ _SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Short4; case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Short4Norm; case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; + case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Half2; + case SG_VERTEXFORMAT_HALF3: return WGPUVertexFormat_Half4; /* FIXME! UINT10_N2 */ case SG_VERTEXFORMAT_UINT10_N2: default: @@ -11218,6 +12253,8 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_RG16SN: case SG_PIXELFORMAT_RGBA16: case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGB9E5: case SG_PIXELFORMAT_PVRTC_RGB_2BPP: case SG_PIXELFORMAT_PVRTC_RGB_4BPP: case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: @@ -11365,6 +12402,7 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + /* FIXME: missing SG_PIXELFORMAT_RG11B10F */ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); @@ -11416,14 +12454,14 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { _sg.wgpu.ub.num_bytes = desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; WGPUBufferDescriptor ub_desc; - memset(&ub_desc, 0, sizeof(ub_desc)); + _sg_clear(&ub_desc, sizeof(ub_desc)); ub_desc.size = _sg.wgpu.ub.num_bytes; ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; _sg.wgpu.ub.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); SOKOL_ASSERT(_sg.wgpu.ub.buf); WGPUBindGroupLayoutBinding ub_bglb_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - memset(ub_bglb_desc, 0, sizeof(ub_bglb_desc)); + _sg_clear(ub_bglb_desc, sizeof(ub_bglb_desc)); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { @@ -11436,14 +12474,14 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { } WGPUBindGroupLayoutDescriptor ub_bgl_desc; - memset(&ub_bgl_desc, 0, sizeof(ub_bgl_desc)); + _sg_clear(&ub_bgl_desc, sizeof(ub_bgl_desc)); ub_bgl_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; ub_bgl_desc.bindings = &ub_bglb_desc[0][0]; _sg.wgpu.ub.bindgroup_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); SOKOL_ASSERT(_sg.wgpu.ub.bindgroup_layout); WGPUBindGroupBinding ub_bgb[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - memset(ub_bgb, 0, sizeof(ub_bgb)); + _sg_clear(ub_bgb, sizeof(ub_bgb)); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; @@ -11454,7 +12492,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { } } WGPUBindGroupDescriptor bg_desc; - memset(&bg_desc, 0, sizeof(bg_desc)); + _sg_clear(&bg_desc, sizeof(bg_desc)); bg_desc.layout = _sg.wgpu.ub.bindgroup_layout; bg_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; bg_desc.bindings = &ub_bgb[0][0]; @@ -11490,7 +12528,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus sta } /* FIXME: better handling for this */ if (WGPUBufferMapAsyncStatus_Success != status) { - SOKOL_LOG("Mapping uniform buffer failed!\n"); + _SG_ERROR(WGPU_MAP_UNIFORM_BUFFER_FAILED); SOKOL_ASSERT(false); } SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); @@ -11510,7 +12548,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { /* rewind per-frame offsets */ _sg.wgpu.ub.offset = 0; - memset(&_sg.wgpu.ub.bind_offsets, 0, sizeof(_sg.wgpu.ub.bind_offsets)); + _sg_clear(&_sg.wgpu.ub.bind_offsets, sizeof(_sg.wgpu.ub.bind_offsets)); /* check if a mapped staging buffer is available, otherwise create one */ for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { @@ -11526,7 +12564,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { const int cur = _sg.wgpu.ub.stage.cur; WGPUBufferDescriptor desc; - memset(&desc, 0, sizeof(desc)); + _sg_clear(&desc, sizeof(desc)); desc.size = _sg.wgpu.ub.num_bytes; desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); @@ -11578,13 +12616,13 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; const sg_pixel_format fmt = img->cmn.pixel_format; WGPUBufferCopyView src_view; - memset(&src_view, 0, sizeof(src_view)); + _sg_clear(&src_view, sizeof(src_view)); src_view.buffer = stg_buf; WGPUTextureCopyView dst_view; - memset(&dst_view, 0, sizeof(dst_view)); + _sg_clear(&dst_view, sizeof(dst_view)); dst_view.texture = img->wgpu.tex; WGPUExtent3D extent; - memset(&extent, 0, sizeof(extent)); + _sg_clear(&extent, sizeof(extent)); for (uint32_t face_index = 0; face_index < num_faces; face_index++) { for (uint32_t mip_index = 0; mip_index < (uint32_t)img->cmn.num_mipmaps; mip_index++) { @@ -11724,7 +12762,7 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { const int cur = _sg.wgpu.staging.cur; WGPUBufferDescriptor desc; - memset(&desc, 0, sizeof(desc)); + _sg_clear(&desc, sizeof(desc)); desc.size = _sg.wgpu.staging.num_bytes; desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); @@ -11749,7 +12787,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint SOKOL_ASSERT(data_num_bytes > 0); uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { - SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); + _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER); return false; } const int cur = _sg.wgpu.staging.cur; @@ -11768,7 +12806,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_ SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); uint32_t num_bytes = _sg_wgpu_image_data_buffer_size(img); if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { - SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); + _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE); return false; } const int cur = _sg.wgpu.staging.cur; @@ -11817,7 +12855,7 @@ _SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc /* create a new WGPU sampler and add to sampler cache */ /* FIXME: anisotropic filtering not supported? */ WGPUSamplerDescriptor smp_desc; - memset(&smp_desc, 0, sizeof(smp_desc)); + _sg_clear(&smp_desc, sizeof(smp_desc)); smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); @@ -11867,11 +12905,11 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { /* create an empty bind group for shader stages without bound images */ WGPUBindGroupLayoutDescriptor bgl_desc; - memset(&bgl_desc, 0, sizeof(bgl_desc)); + _sg_clear(&bgl_desc, sizeof(bgl_desc)); WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); SOKOL_ASSERT(empty_bgl); WGPUBindGroupDescriptor bg_desc; - memset(&bg_desc, 0, sizeof(bg_desc)); + _sg_clear(&bg_desc, sizeof(bg_desc)); bg_desc.layout = empty_bgl; _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); SOKOL_ASSERT(_sg.wgpu.empty_bind_group); @@ -11879,7 +12917,7 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { /* create initial per-frame command encoders */ WGPUCommandEncoderDescriptor cmd_enc_desc; - memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); @@ -11906,7 +12944,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { } _SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { - SOKOL_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); + _SG_WARN(WGPU_RESET_STATE_CACHE_FIXME); } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { @@ -11915,14 +12953,14 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_wgpu_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_wgpu_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); } _SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { (void)ctx; - SOKOL_LOG("_sg_wgpu_activate_context: FIXME\n"); + _SG_WARN(WGPU_ACTIVATE_CONTEXT_FIXME); } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { @@ -11935,7 +12973,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const } else { WGPUBufferDescriptor wgpu_buf_desc; - memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); wgpu_buf_desc.size = buf->cmn.size; if (SG_USAGE_IMMUTABLE == buf->cmn.usage) { @@ -11953,7 +12991,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_wgpu_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); WGPUBuffer wgpu_buf = buf->wgpu.buf; if (0 != wgpu_buf) { @@ -11993,7 +13031,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s const bool injected = (0 != desc->wgpu_texture); const bool is_msaa = desc->sample_count > 1; WGPUTextureDescriptor wgpu_tex_desc; - memset(&wgpu_tex_desc, 0, sizeof(wgpu_tex_desc)); + _sg_clear(&wgpu_tex_desc, sizeof(wgpu_tex_desc)); _sg_wgpu_init_texdesc_common(&wgpu_tex_desc, desc); if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { SOKOL_ASSERT(img->cmn.render_target); @@ -12026,7 +13064,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s /* copy content into texture via a throw-away staging buffer */ if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { WGPUBufferDescriptor wgpu_buf_desc; - memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); wgpu_buf_desc.size = _sg_wgpu_image_data_buffer_size(img); wgpu_buf_desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_CopyDst; WGPUCreateBufferMappedResult map = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); @@ -12041,7 +13079,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s /* create texture view object */ WGPUTextureViewDescriptor wgpu_view_desc; - memset(&wgpu_view_desc, 0, sizeof(wgpu_view_desc)); + _sg_clear(&wgpu_view_desc, sizeof(wgpu_view_desc)); wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); img->wgpu.tex_view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_view_desc); @@ -12067,7 +13105,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_wgpu_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); if (img->wgpu.tex) { wgpuTextureRelease(img->wgpu.tex); @@ -12127,7 +13165,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); WGPUShaderModuleDescriptor wgpu_shdmod_desc; - memset(&wgpu_shdmod_desc, 0, sizeof(wgpu_shdmod_desc)); + _sg_clear(&wgpu_shdmod_desc, sizeof(wgpu_shdmod_desc)); wgpu_shdmod_desc.codeSize = stage_desc->bytecode.size >> 2; wgpu_shdmod_desc.code = (const uint32_t*) stage_desc->bytecode.ptr; wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); @@ -12142,7 +13180,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const num_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; } WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; - memset(bglb_desc, 0, sizeof(bglb_desc)); + _sg_clear(bglb_desc, sizeof(bglb_desc)); for (int img_index = 0; img_index < num_imgs; img_index++) { /* texture- and sampler-bindings */ WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; @@ -12159,7 +13197,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const smp_desc->type = WGPUBindingType_Sampler; } WGPUBindGroupLayoutDescriptor img_bgl_desc; - memset(&img_bgl_desc, 0, sizeof(img_bgl_desc)); + _sg_clear(&img_bgl_desc, sizeof(img_bgl_desc)); img_bgl_desc.bindingCount = num_imgs * 2; img_bgl_desc.bindings = &bglb_desc[0]; wgpu_stage->bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &img_bgl_desc); @@ -12168,7 +13206,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const return success ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; } -_SOKOL_PRIVATE void _sg_wgpu_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_wgpu_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; @@ -12198,15 +13236,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout }; WGPUPipelineLayoutDescriptor pl_desc; - memset(&pl_desc, 0, sizeof(pl_desc)); + _sg_clear(&pl_desc, sizeof(pl_desc)); pl_desc.bindGroupLayoutCount = 3; pl_desc.bindGroupLayouts = &pip_bgl[0]; WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; - memset(&vb_desc, 0, sizeof(vb_desc)); + _sg_clear(&vb_desc, sizeof(vb_desc)); WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; - memset(&va_desc, 0, sizeof(va_desc)); + _sg_clear(&va_desc, sizeof(va_desc)); int vb_idx = 0; for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; @@ -12236,13 +13274,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ vb_desc[vb_idx].attributes = &va_desc[vb_idx][0]; } WGPUVertexStateDescriptor vx_state_desc; - memset(&vx_state_desc, 0, sizeof(vx_state_desc)); + _sg_clear(&vx_state_desc, sizeof(vx_state_desc)); vx_state_desc.indexFormat = _sg_wgpu_indexformat(desc->index_type); vx_state_desc.vertexBufferCount = vb_idx; vx_state_desc.vertexBuffers = vb_desc; WGPURasterizationStateDescriptor rs_desc; - memset(&rs_desc, 0, sizeof(rs_desc)); + _sg_clear(&rs_desc, sizeof(rs_desc)); rs_desc.frontFace = _sg_wgpu_frontface(desc->face_winding); rs_desc.cullMode = _sg_wgpu_cullmode(desc->cull_mode); rs_desc.depthBias = (int32_t) desc->depth.bias; @@ -12250,7 +13288,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ rs_desc.depthBiasSlopeScale = desc->depth.bias_slope_scale; WGPUDepthStencilStateDescriptor ds_desc; - memset(&ds_desc, 0, sizeof(ds_desc)); + _sg_clear(&ds_desc, sizeof(ds_desc)); ds_desc.format = _sg_wgpu_textureformat(desc->depth.pixel_format); ds_desc.depthWriteEnabled = desc->depth.write_enabled; ds_desc.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); @@ -12266,12 +13304,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ ds_desc.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); WGPUProgrammableStageDescriptor fs_desc; - memset(&fs_desc, 0, sizeof(fs_desc)); + _sg_clear(&fs_desc, sizeof(fs_desc)); fs_desc.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; fs_desc.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; WGPUColorStateDescriptor cs_desc[SG_MAX_COLOR_ATTACHMENTS]; - memset(cs_desc, 0, sizeof(cs_desc)); + _sg_clear(cs_desc, sizeof(cs_desc)); for (uint32_t i = 0; i < desc->color_count; i++) { SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); cs_desc[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); @@ -12285,7 +13323,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ } WGPURenderPipelineDescriptor pip_desc; - memset(&pip_desc, 0, sizeof(pip_desc)); + _sg_clear(&pip_desc, sizeof(pip_desc)); pip_desc.layout = pip_layout; pip_desc.vertexStage.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; pip_desc.vertexStage.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; @@ -12307,7 +13345,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_wgpu_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); if (pip == _sg.wgpu.cur_pipeline) { _sg.wgpu.cur_pipeline = 0; @@ -12338,7 +13376,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag /* create a render-texture-view to render into the right sub-surface */ const bool is_msaa = img->cmn.sample_count > 1; WGPUTextureViewDescriptor view_desc; - memset(&view_desc, 0, sizeof(view_desc)); + _sg_clear(&view_desc, sizeof(view_desc)); view_desc.baseMipLevel = is_msaa ? 0 : att_desc->mip_level; view_desc.mipLevelCount = 1; view_desc.baseArrayLayer = is_msaa ? 0 : att_desc->slice; @@ -12369,7 +13407,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag SOKOL_ASSERT(0 == att_desc->mip_level); SOKOL_ASSERT(0 == att_desc->slice); WGPUTextureViewDescriptor view_desc; - memset(&view_desc, 0, sizeof(view_desc)); + _sg_clear(&view_desc, sizeof(view_desc)); WGPUTexture wgpu_tex = ds_img->wgpu.tex; SOKOL_ASSERT(wgpu_tex); pass->wgpu.ds_att.render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); @@ -12378,7 +13416,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_wgpu_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_wgpu_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { if (pass->wgpu.color_atts[i].render_tex_view) { @@ -12425,9 +13463,9 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); if (pass) { WGPURenderPassDescriptor wgpu_pass_desc; - memset(&wgpu_pass_desc, 0, sizeof(wgpu_pass_desc)); + _sg_clear(&wgpu_pass_desc, sizeof(wgpu_pass_desc)); WGPURenderPassColorAttachmentDescriptor wgpu_color_att_desc[SG_MAX_COLOR_ATTACHMENTS]; - memset(&wgpu_color_att_desc, 0, sizeof(wgpu_color_att_desc)); + _sg_clear(&wgpu_color_att_desc, sizeof(wgpu_color_att_desc)); SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { const _sg_wgpu_attachment_t* wgpu_att = &pass->wgpu.color_atts[i]; @@ -12446,7 +13484,7 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* wgpu_pass_desc.colorAttachments = &wgpu_color_att_desc[0]; if (pass->wgpu.ds_att.image) { WGPURenderPassDepthStencilAttachmentDescriptor wgpu_ds_att_desc; - memset(&wgpu_ds_att_desc, 0, sizeof(wgpu_ds_att_desc)); + _sg_clear(&wgpu_ds_att_desc, sizeof(wgpu_ds_att_desc)); wgpu_ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); wgpu_ds_att_desc.clearDepth = action->depth.value; wgpu_ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); @@ -12463,9 +13501,9 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); WGPURenderPassDescriptor pass_desc; - memset(&pass_desc, 0, sizeof(pass_desc)); + _sg_clear(&pass_desc, sizeof(pass_desc)); WGPURenderPassColorAttachmentDescriptor color_att_desc; - memset(&color_att_desc, 0, sizeof(color_att_desc)); + _sg_clear(&color_att_desc, sizeof(color_att_desc)); color_att_desc.loadOp = _sg_wgpu_load_op(action->colors[0].action); color_att_desc.clearColor.r = action->colors[0].value.r; color_att_desc.clearColor.g = action->colors[0].value.g; @@ -12476,7 +13514,7 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* pass_desc.colorAttachmentCount = 1; pass_desc.colorAttachments = &color_att_desc; WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; - memset(&ds_att_desc, 0, sizeof(ds_att_desc)); + _sg_clear(&ds_att_desc, sizeof(ds_att_desc)); ds_att_desc.attachment = wgpu_depth_stencil_view; SOKOL_ASSERT(0 != ds_att_desc.attachment); ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); @@ -12518,7 +13556,7 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { WGPUCommandBuffer cmd_bufs[2]; WGPUCommandBufferDescriptor cmd_buf_desc; - memset(&cmd_buf_desc, 0, sizeof(cmd_buf_desc)); + _sg_clear(&cmd_buf_desc, sizeof(cmd_buf_desc)); cmd_bufs[0] = wgpuCommandEncoderFinish(_sg.wgpu.staging_cmd_enc, &cmd_buf_desc); SOKOL_ASSERT(cmd_bufs[0]); wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); @@ -12536,7 +13574,7 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { /* create a new render- and staging-command-encoders for next frame */ WGPUCommandEncoderDescriptor cmd_enc_desc; - memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); @@ -12597,7 +13635,7 @@ _SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayou SOKOL_ASSERT(_sg.wgpu.dev); SOKOL_ASSERT(num_imgs <= _SG_WGPU_MAX_SHADERSTAGE_IMAGES); WGPUBindGroupBinding img_bgb[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; - memset(&img_bgb, 0, sizeof(img_bgb)); + _sg_clear(&img_bgb, sizeof(img_bgb)); for (int img_index = 0; img_index < num_imgs; img_index++) { WGPUBindGroupBinding* tex_bdg = &img_bgb[img_index*2 + 0]; WGPUBindGroupBinding* smp_bdg = &img_bgb[img_index*2 + 1]; @@ -12607,7 +13645,7 @@ _SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayou smp_bdg->sampler = imgs[img_index]->wgpu.sampler; } WGPUBindGroupDescriptor bg_desc; - memset(&bg_desc, 0, sizeof(bg_desc)); + _sg_clear(&bg_desc, sizeof(bg_desc)); bg_desc.layout = bgl; bg_desc.bindingCount = 2 * num_imgs; bg_desc.bindings = &img_bgb[0]; @@ -12723,7 +13761,13 @@ _SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* } #endif -/*== BACKEND API WRAPPERS ====================================================*/ +// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>generic backend static inline void _sg_setup_backend(const sg_desc* desc) { #if defined(_SOKOL_ANY_GL) _sg_gl_setup_backend(desc); @@ -12804,17 +13848,17 @@ static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { #endif } -static inline void _sg_destroy_context(_sg_context_t* ctx) { +static inline void _sg_discard_context(_sg_context_t* ctx) { #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_context(ctx); + _sg_gl_discard_context(ctx); #elif defined(SOKOL_METAL) - _sg_mtl_destroy_context(ctx); + _sg_mtl_discard_context(ctx); #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_context(ctx); + _sg_d3d11_discard_context(ctx); #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_context(ctx); + _sg_wgpu_discard_context(ctx); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_context(ctx); + _sg_dummy_discard_context(ctx); #else #error("INVALID BACKEND"); #endif @@ -12836,17 +13880,17 @@ static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_bu #endif } -static inline void _sg_destroy_buffer(_sg_buffer_t* buf) { +static inline void _sg_discard_buffer(_sg_buffer_t* buf) { #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_buffer(buf); + _sg_gl_discard_buffer(buf); #elif defined(SOKOL_METAL) - _sg_mtl_destroy_buffer(buf); + _sg_mtl_discard_buffer(buf); #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_buffer(buf); + _sg_d3d11_discard_buffer(buf); #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_buffer(buf); + _sg_wgpu_discard_buffer(buf); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_buffer(buf); + _sg_dummy_discard_buffer(buf); #else #error("INVALID BACKEND"); #endif @@ -12868,17 +13912,17 @@ static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_imag #endif } -static inline void _sg_destroy_image(_sg_image_t* img) { +static inline void _sg_discard_image(_sg_image_t* img) { #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_image(img); + _sg_gl_discard_image(img); #elif defined(SOKOL_METAL) - _sg_mtl_destroy_image(img); + _sg_mtl_discard_image(img); #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_image(img); + _sg_d3d11_discard_image(img); #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_image(img); + _sg_wgpu_discard_image(img); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_image(img); + _sg_dummy_discard_image(img); #else #error("INVALID BACKEND"); #endif @@ -12900,17 +13944,17 @@ static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_sh #endif } -static inline void _sg_destroy_shader(_sg_shader_t* shd) { +static inline void _sg_discard_shader(_sg_shader_t* shd) { #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_shader(shd); + _sg_gl_discard_shader(shd); #elif defined(SOKOL_METAL) - _sg_mtl_destroy_shader(shd); + _sg_mtl_discard_shader(shd); #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_shader(shd); + _sg_d3d11_discard_shader(shd); #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_shader(shd); + _sg_wgpu_discard_shader(shd); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_shader(shd); + _sg_dummy_discard_shader(shd); #else #error("INVALID BACKEND"); #endif @@ -12932,17 +13976,17 @@ static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sha #endif } -static inline void _sg_destroy_pipeline(_sg_pipeline_t* pip) { +static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) { #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_pipeline(pip); + _sg_gl_discard_pipeline(pip); #elif defined(SOKOL_METAL) - _sg_mtl_destroy_pipeline(pip); + _sg_mtl_discard_pipeline(pip); #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_pipeline(pip); + _sg_d3d11_discard_pipeline(pip); #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_pipeline(pip); + _sg_wgpu_discard_pipeline(pip); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_pipeline(pip); + _sg_dummy_discard_pipeline(pip); #else #error("INVALID BACKEND"); #endif @@ -12964,17 +14008,17 @@ static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** #endif } -static inline void _sg_destroy_pass(_sg_pass_t* pass) { +static inline void _sg_discard_pass(_sg_pass_t* pass) { #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_pass(pass); + _sg_gl_discard_pass(pass); #elif defined(SOKOL_METAL) - _sg_mtl_destroy_pass(pass); + _sg_mtl_discard_pass(pass); #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_pass(pass); + _sg_d3d11_discard_pass(pass); #elif defined(SOKOL_WGPU) - return _sg_wgpu_destroy_pass(pass); + return _sg_wgpu_discard_pass(pass); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_pass(pass); + _sg_dummy_discard_pass(pass); #else #error("INVALID BACKEND"); #endif @@ -13210,8 +14254,13 @@ static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) #endif } -/*== RESOURCE POOLS ==========================================================*/ - +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ @@ -13219,12 +14268,9 @@ _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { pool->queue_top = 0; /* generation counters indexable by pool slot index, slot 0 is reserved */ size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; - pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); - SOKOL_ASSERT(pool->gen_ctrs); - memset(pool->gen_ctrs, 0, gen_ctrs_size); + pool->gen_ctrs = (uint32_t*)_sg_malloc_clear(gen_ctrs_size); /* it's not a bug to only reserve 'num' here */ - pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); - SOKOL_ASSERT(pool->free_queue); + pool->free_queue = (int*) _sg_malloc_clear(sizeof(int) * (size_t)num); /* never allocate the zero-th pool item since the invalid id is 0 */ for (int i = pool->size-1; i >= 1; i--) { pool->free_queue[pool->queue_top++] = i; @@ -13234,10 +14280,10 @@ _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { _SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { SOKOL_ASSERT(pool); SOKOL_ASSERT(pool->free_queue); - SOKOL_FREE(pool->free_queue); + _sg_free(pool->free_queue); pool->free_queue = 0; SOKOL_ASSERT(pool->gen_ctrs); - SOKOL_FREE(pool->gen_ctrs); + _sg_free(pool->gen_ctrs); pool->gen_ctrs = 0; pool->size = 0; pool->queue_top = 0; @@ -13274,53 +14320,53 @@ _SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { _SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { SOKOL_ASSERT(slot); - memset(slot, 0, sizeof(_sg_slot_t)); + _sg_clear(slot, sizeof(_sg_slot_t)); } -_SOKOL_PRIVATE void _sg_reset_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _sg_slot_t slot = buf->slot; - memset(buf, 0, sizeof(_sg_buffer_t)); + _sg_clear(buf, sizeof(_sg_buffer_t)); buf->slot = slot; buf->slot.state = SG_RESOURCESTATE_ALLOC; } -_SOKOL_PRIVATE void _sg_reset_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { SOKOL_ASSERT(img); _sg_slot_t slot = img->slot; - memset(img, 0, sizeof(_sg_image_t)); + _sg_clear(img, sizeof(_sg_image_t)); img->slot = slot; img->slot.state = SG_RESOURCESTATE_ALLOC; } -_SOKOL_PRIVATE void _sg_reset_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _sg_slot_t slot = shd->slot; - memset(shd, 0, sizeof(_sg_shader_t)); + _sg_clear(shd, sizeof(_sg_shader_t)); shd->slot = slot; shd->slot.state = SG_RESOURCESTATE_ALLOC; } -_SOKOL_PRIVATE void _sg_reset_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_reset_pipeline_to_alloc_state(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _sg_slot_t slot = pip->slot; - memset(pip, 0, sizeof(_sg_pipeline_t)); + _sg_clear(pip, sizeof(_sg_pipeline_t)); pip->slot = slot; pip->slot.state = SG_RESOURCESTATE_ALLOC; } -_SOKOL_PRIVATE void _sg_reset_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_reset_pass_to_alloc_state(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _sg_slot_t slot = pass->slot; - memset(pass, 0, sizeof(_sg_pass_t)); + _sg_clear(pass, sizeof(_sg_pass_t)); pass->slot = slot; pass->slot.state = SG_RESOURCESTATE_ALLOC; } -_SOKOL_PRIVATE void _sg_reset_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_reset_context_to_alloc_state(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _sg_slot_t slot = ctx->slot; - memset(ctx, 0, sizeof(_sg_context_t)); + _sg_clear(ctx, sizeof(_sg_context_t)); ctx->slot = slot; ctx->slot.state = SG_RESOURCESTATE_ALLOC; } @@ -13332,54 +14378,42 @@ _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; - p->buffers = (_sg_buffer_t*) SOKOL_MALLOC(buffer_pool_byte_size); - SOKOL_ASSERT(p->buffers); - memset(p->buffers, 0, buffer_pool_byte_size); + p->buffers = (_sg_buffer_t*) _sg_malloc_clear(buffer_pool_byte_size); SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->image_pool, desc->image_pool_size); size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; - p->images = (_sg_image_t*) SOKOL_MALLOC(image_pool_byte_size); - SOKOL_ASSERT(p->images); - memset(p->images, 0, image_pool_byte_size); + p->images = (_sg_image_t*) _sg_malloc_clear(image_pool_byte_size); SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->shader_pool, desc->shader_pool_size); size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; - p->shaders = (_sg_shader_t*) SOKOL_MALLOC(shader_pool_byte_size); - SOKOL_ASSERT(p->shaders); - memset(p->shaders, 0, shader_pool_byte_size); + p->shaders = (_sg_shader_t*) _sg_malloc_clear(shader_pool_byte_size); SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; - p->pipelines = (_sg_pipeline_t*) SOKOL_MALLOC(pipeline_pool_byte_size); - SOKOL_ASSERT(p->pipelines); - memset(p->pipelines, 0, pipeline_pool_byte_size); + p->pipelines = (_sg_pipeline_t*) _sg_malloc_clear(pipeline_pool_byte_size); SOKOL_ASSERT((desc->pass_pool_size > 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->pass_pool, desc->pass_pool_size); size_t pass_pool_byte_size = sizeof(_sg_pass_t) * (size_t)p->pass_pool.size; - p->passes = (_sg_pass_t*) SOKOL_MALLOC(pass_pool_byte_size); - SOKOL_ASSERT(p->passes); - memset(p->passes, 0, pass_pool_byte_size); + p->passes = (_sg_pass_t*) _sg_malloc_clear(pass_pool_byte_size); SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->context_pool, desc->context_pool_size); size_t context_pool_byte_size = sizeof(_sg_context_t) * (size_t)p->context_pool.size; - p->contexts = (_sg_context_t*) SOKOL_MALLOC(context_pool_byte_size); - SOKOL_ASSERT(p->contexts); - memset(p->contexts, 0, context_pool_byte_size); + p->contexts = (_sg_context_t*) _sg_malloc_clear(context_pool_byte_size); } _SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { SOKOL_ASSERT(p); - SOKOL_FREE(p->contexts); p->contexts = 0; - SOKOL_FREE(p->passes); p->passes = 0; - SOKOL_FREE(p->pipelines); p->pipelines = 0; - SOKOL_FREE(p->shaders); p->shaders = 0; - SOKOL_FREE(p->images); p->images = 0; - SOKOL_FREE(p->buffers); p->buffers = 0; + _sg_free(p->contexts); p->contexts = 0; + _sg_free(p->passes); p->passes = 0; + _sg_free(p->pipelines); p->pipelines = 0; + _sg_free(p->shaders); p->shaders = 0; + _sg_free(p->images); p->images = 0; + _sg_free(p->buffers); p->buffers = 0; _sg_discard_pool(&p->context_pool); _sg_discard_pool(&p->pass_pool); _sg_discard_pool(&p->pipeline_pool); @@ -13524,7 +14558,7 @@ _SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t return 0; } -_SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { +_SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { /* this is a bit dumb since it loops over all pool slots to find the occupied slots, on the other hand it is only ever executed at shutdown @@ -13536,7 +14570,7 @@ _SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { if (p->buffers[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->buffers[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_buffer(&p->buffers[i]); + _sg_discard_buffer(&p->buffers[i]); } } } @@ -13544,7 +14578,7 @@ _SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { if (p->images[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->images[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_image(&p->images[i]); + _sg_discard_image(&p->images[i]); } } } @@ -13552,7 +14586,7 @@ _SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { if (p->shaders[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->shaders[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_shader(&p->shaders[i]); + _sg_discard_shader(&p->shaders[i]); } } } @@ -13560,7 +14594,7 @@ _SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { if (p->pipelines[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->pipelines[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_pipeline(&p->pipelines[i]); + _sg_discard_pipeline(&p->pipelines[i]); } } } @@ -13568,162 +14602,32 @@ _SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { if (p->passes[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->passes[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_pass(&p->passes[i]); + _sg_discard_pass(&p->passes[i]); } } } } -/*== VALIDATION LAYER ========================================================*/ -#if defined(SOKOL_DEBUG) -/* return a human readable string for an _sg_validate_error */ -_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { - switch (err) { - /* buffer creation validation errors */ - case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; - case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; - case _SG_VALIDATE_BUFFERDESC_DATA: return "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)"; - case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size"; - case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data"; - - /* image data (in image creation and updating) */ - case _SG_VALIDATE_IMAGEDATA_NODATA: return "sg_image_data: no data (.ptr and/or .size is zero)"; - case _SG_VALIDATE_IMAGEDATA_DATA_SIZE: return "sg_image_data: data size doesn't match expected surface size"; - - /* image creation validation errros */ - case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; - case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; - case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; - case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; - case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; - case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; - case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format"; - case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; - case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA: return "images with injected textures cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA: return "dynamic/stream images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE: return "compressed images must be immutable"; - - /* shader creation */ - case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; - case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; - case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; - case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; - case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; - case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; - case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; - case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; - case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; - case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; - - /* pipeline creation */ - case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; - case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; - case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; - case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; - - /* pass creation */ - case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; - case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; - case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; - case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; - case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; - case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; - case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; - case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; - case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; - case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; - case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; - case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; - case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; - - /* sg_begin_pass */ - case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; - case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; - - /* sg_apply_pipeline */ - case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; - case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; - case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; - case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; - case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; - case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments"; - case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format"; - case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format"; - case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count"; - - /* sg_apply_bindings */ - case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; - case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; - case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; - case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; - case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; - case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; - case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; - case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; - case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; - case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; - case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; - case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; - case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; - case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; - case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; - - /* sg_apply_uniforms */ - case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; - case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; - case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; - - /* sg_update_buffer */ - case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; - case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; - case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; - case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; - - /* sg_append_buffer */ - case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; - case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; - case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; - - /* sg_update_image */ - case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; - case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; - - default: return "unknown validation error"; - } -} -#endif /* defined(SOKOL_DEBUG) */ - -/*-- validation checks -------------------------------------------------------*/ +// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ +// +// >>validation #if defined(SOKOL_DEBUG) _SOKOL_PRIVATE void _sg_validate_begin(void) { - _sg.validate_error = _SG_VALIDATE_SUCCESS; -} - -_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { - if (!cond) { - _sg.validate_error = err; - SOKOL_LOG(_sg_validate_string(err)); - } + _sg.validate_error = SG_LOGITEM_OK; } _SOKOL_PRIVATE bool _sg_validate_end(void) { - if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { + if (_sg.validate_error != SG_LOGITEM_OK) { #if !defined(SOKOL_VALIDATE_NON_FATAL) - SOKOL_LOG("^^^^ SOKOL-GFX VALIDATION FAILED, TERMINATING ^^^^"); - SOKOL_ASSERT(false); + _SG_PANIC(VALIDATION_FAILED); + return false; + #else + return false; #endif - return false; } else { return true; @@ -13736,23 +14640,26 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { _SOKOL_UNUSED(desc); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); - SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->size > 0, VALIDATE_BUFFERDESC_SIZE); bool injected = (0 != desc->gl_buffers[0]) || (0 != desc->mtl_buffers[0]) || (0 != desc->d3d11_buffer) || (0 != desc->wgpu_buffer); if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { - SOKOL_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), _SG_VALIDATE_BUFFERDESC_DATA); - SOKOL_VALIDATE(desc->size == desc->data.size, _SG_VALIDATE_BUFFERDESC_DATA_SIZE); + _SG_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), VALIDATE_BUFFERDESC_DATA); + _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_DATA_SIZE); } else { - SOKOL_VALIDATE(0 == desc->data.ptr, _SG_VALIDATE_BUFFERDESC_NO_DATA); + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -13770,12 +14677,12 @@ _SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_ for (int mip_index = 0; mip_index < num_mips; mip_index++) { const bool has_data = data->subimage[face_index][mip_index].ptr != 0; const bool has_size = data->subimage[face_index][mip_index].size > 0; - SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDATA_NODATA); + _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); const int mip_width = _sg_max(width >> mip_index, 1); const int mip_height = _sg_max(height >> mip_index, 1); const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); const int expected_size = bytes_per_slice * num_slices; - SOKOL_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, _SG_VALIDATE_IMAGEDATA_DATA_SIZE); + _SG_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); } } #endif @@ -13786,12 +14693,15 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { _SOKOL_UNUSED(desc); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); - SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); + _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); const sg_pixel_format fmt = desc->pixel_format; const sg_usage usage = desc->usage; const bool injected = (0 != desc->gl_textures[0]) || @@ -13800,28 +14710,28 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { (0 != desc->wgpu_texture); if (desc->render_target) { SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); - SOKOL_VALIDATE(_sg.formats[fmt].render, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_RT_PIXELFORMAT); /* on GLES2, sample count for render targets is completely ignored */ #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) if (!_sg.gl.gles2) { #endif if (desc->sample_count > 1) { - SOKOL_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + _SG_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); } #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) } #endif - SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); - SOKOL_VALIDATE(desc->data.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_DATA); + _SG_VALIDATE(usage == SG_USAGE_IMMUTABLE, VALIDATE_IMAGEDESC_RT_IMMUTABLE); + _SG_VALIDATE(desc->data.subimage[0][0].ptr==0, VALIDATE_IMAGEDESC_RT_NO_DATA); } else { - SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + _SG_VALIDATE(desc->sample_count <= 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); - SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); if (is_compressed) { - SOKOL_VALIDATE(is_immutable, _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + _SG_VALIDATE(is_immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); } if (!injected && is_immutable) { // image desc must have valid data @@ -13840,16 +14750,16 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; if (injected) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); } if (!is_immutable) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); } } } } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -13858,44 +14768,47 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { _SOKOL_UNUSED(desc); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); + _SG_VALIDATE(0 != desc->attrs[0].name, VALIDATE_SHADERDESC_ATTR_NAMES); #elif defined(SOKOL_D3D11) - SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); + _SG_VALIDATE(0 != desc->attrs[0].sem_name, VALIDATE_SHADERDESC_ATTR_SEMANTICS); #endif #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) /* on GL, must provide shader source code */ - SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); - SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); + _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) /* on Metal or D3D11, must provide shader source code or byte code */ - SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); #elif defined(SOKOL_WGPU) /* on WGPU byte code must be provided */ - SOKOL_VALIDATE((0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); + _SG_VALIDATE((0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); + _SG_VALIDATE((0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); #else /* Dummy Backend, don't require source or bytecode */ #endif for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { if (desc->attrs[i].name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + _SG_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } if (desc->attrs[i].sem_name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + _SG_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } } /* if shader byte code, the size must also be provided */ if (0 != desc->vs.bytecode.ptr) { - SOKOL_VALIDATE(desc->vs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + _SG_VALIDATE(desc->vs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } if (0 != desc->fs.bytecode.ptr) { - SOKOL_VALIDATE(desc->fs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + _SG_VALIDATE(desc->fs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; @@ -13903,31 +14816,41 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (ub_desc->size > 0) { - SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); + _SG_VALIDATE(uniform_blocks_continuous, VALIDATE_SHADERDESC_NO_CONT_UBS); + #if defined(_SOKOL_ANY_GL) bool uniforms_continuous = true; - int uniform_offset = 0; + uint32_t uniform_offset = 0; int num_uniforms = 0; for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type != SG_UNIFORMTYPE_INVALID) { - SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - SOKOL_VALIDATE(0 != u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); + _SG_VALIDATE(0 != u_desc->name, VALIDATE_SHADERDESC_UB_MEMBER_NAME); #endif const int array_count = u_desc->array_count; - uniform_offset += _sg_uniform_size(u_desc->type, array_count); + _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UB_ARRAY_COUNT); + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); + uniform_offset = _sg_align_u32(uniform_offset, u_align); + uniform_offset += u_size; num_uniforms++; + // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + if (array_count > 1) { + _SG_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); + } + } } else { uniforms_continuous = false; } } - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); - SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); - #else - _SOKOL_UNUSED(uniform_offset); - _SOKOL_UNUSED(num_uniforms); + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + uniform_offset = _sg_align_u32(uniform_offset, 16); + } + _SG_VALIDATE((size_t)uniform_offset == ub_desc->size, VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_NO_UB_MEMBERS); #endif } else { @@ -13938,9 +14861,9 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; if (img_desc->image_type != _SG_IMAGETYPE_DEFAULT) { - SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); + _SG_VALIDATE(images_continuous, VALIDATE_SHADERDESC_NO_CONT_IMGS); #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); + _SG_VALIDATE(0 != img_desc->name, VALIDATE_SHADERDESC_IMG_NAME); #endif } else { @@ -13948,7 +14871,7 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { } } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -13957,23 +14880,26 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { _SOKOL_UNUSED(desc); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); - SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->shader.id != SG_INVALID_ID, VALIDATE_PIPELINEDESC_SHADER); for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; if (l_desc->stride == 0) { continue; } - SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + _SG_VALIDATE((l_desc->stride & 3) == 0, VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } - SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + _SG_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, VALIDATE_PIPELINEDESC_NO_ATTRS); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); - SOKOL_VALIDATE(0 != shd, _SG_VALIDATE_PIPELINEDESC_SHADER); + _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); if (shd) { - SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PIPELINEDESC_SHADER); bool attrs_cont = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; @@ -13981,18 +14907,18 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { attrs_cont = false; continue; } - SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_ATTRS); SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); #if defined(SOKOL_GLES2) /* on GLES2, vertex attribute names must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); + _SG_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), VALIDATE_PIPELINEDESC_ATTR_NAME); #elif defined(SOKOL_D3D11) /* on D3D11, semantic names (and semantic indices) must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); #endif } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14001,68 +14927,71 @@ _SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { _SOKOL_UNUSED(desc); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PASSDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PASSDESC_CANARY); bool atts_cont = true; int width = -1, height = -1, sample_count = -1; for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { const sg_pass_attachment_desc* att = &desc->color_attachments[att_index]; if (att->image.id == SG_INVALID_ID) { - SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); + _SG_VALIDATE(att_index > 0, VALIDATE_PASSDESC_NO_COLOR_ATTS); atts_cont = false; continue; } - SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + _SG_VALIDATE(atts_cont, VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_IMAGE); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_PASSDESC_MIPLEVEL); if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); + _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_FACE); } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_LAYER); } else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_SLICE); } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_PASSDESC_IMAGE_NO_RT); if (att_index == 0) { width = img->cmn.width >> att->mip_level; height = img->cmn.height >> att->mip_level; sample_count = img->cmn.sample_count; } else { - SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + _SG_VALIDATE(width == img->cmn.width >> att->mip_level, VALIDATE_PASSDESC_IMAGE_SIZES); + _SG_VALIDATE(height == img->cmn.height >> att->mip_level, VALIDATE_PASSDESC_IMAGE_SIZES); + _SG_VALIDATE(sample_count == img->cmn.sample_count, VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); } - SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + _SG_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); } if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { const sg_pass_attachment_desc* att = &desc->depth_stencil_attachment; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_IMAGE); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_PASSDESC_MIPLEVEL); if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); + _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_FACE); } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_LAYER); } else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_SLICE); } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); - SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_PASSDESC_IMAGE_NO_RT); + _SG_VALIDATE(width == img->cmn.width >> att->mip_level, VALIDATE_PASSDESC_IMAGE_SIZES); + _SG_VALIDATE(height == img->cmn.height >> att->mip_level, VALIDATE_PASSDESC_IMAGE_SIZES); + _SG_VALIDATE(sample_count == img->cmn.sample_count, VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + _SG_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14071,24 +15000,27 @@ _SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { _SOKOL_UNUSED(pass); return true; #else - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_PASS); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { const _sg_pass_attachment_t* att = &pass->cmn.color_atts[i]; const _sg_image_t* img = _sg_pass_color_image(pass, i); if (img) { - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_IMAGE); + _SG_VALIDATE(img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_IMAGE); } } const _sg_image_t* ds_img = _sg_pass_ds_image(pass); if (ds_img) { const _sg_pass_attachment_t* att = &pass->cmn.ds_att; - SOKOL_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(ds_img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_IMAGE); + _SG_VALIDATE(ds_img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_IMAGE); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14097,45 +15029,48 @@ _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { _SOKOL_UNUSED(pip_id); return true; #else - SOKOL_VALIDATE_BEGIN(); - /* the pipeline object must be alive and valid */ - SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); - const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); - if (!pip) { - return SOKOL_VALIDATE_END(); + if (_sg.desc.disable_validation) { + return true; } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); + _sg_validate_begin(); + /* the pipeline object must be alive and valid */ + _SG_VALIDATE(pip_id.id != SG_INVALID_ID, VALIDATE_APIP_PIPELINE_VALID_ID); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + _SG_VALIDATE(pip != 0, VALIDATE_APIP_PIPELINE_EXISTS); + if (!pip) { + return _sg_validate_end(); + } + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_VALID); /* the pipeline's shader must be alive and valid */ SOKOL_ASSERT(pip->shader); - SOKOL_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); - SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); + _SG_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, VALIDATE_APIP_SHADER_EXISTS); + _SG_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_SHADER_VALID); /* check that pipeline attributes match current pass attributes */ const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); if (pass) { /* an offscreen pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); - for (int i = 0; i < pip->cmn.color_attachment_count; i++) { + _SG_VALIDATE(pip->cmn.color_count == pass->cmn.num_color_atts, VALIDATE_APIP_ATT_COUNT); + for (int i = 0; i < pip->cmn.color_count; i++) { const _sg_image_t* att_img = _sg_pass_color_image(pass, i); - SOKOL_VALIDATE(pip->cmn.color_formats[i] == att_img->cmn.pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + _SG_VALIDATE(pip->cmn.colors[i].pixel_format == att_img->cmn.pixel_format, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, VALIDATE_APIP_SAMPLE_COUNT); } const _sg_image_t* att_dsimg = _sg_pass_ds_image(pass); if (att_dsimg) { - SOKOL_VALIDATE(pip->cmn.depth_format == att_dsimg->cmn.pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == att_dsimg->cmn.pixel_format, VALIDATE_APIP_DEPTH_FORMAT); } else { - SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTH_FORMAT); } } else { /* default pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); - SOKOL_VALIDATE(pip->cmn.color_formats[0] == _sg.desc.context.color_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->cmn.depth_format == _sg.desc.context.depth_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); - SOKOL_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_ATT_COUNT); + _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.desc.context.color_format, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.desc.context.depth_format, VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, VALIDATE_APIP_SAMPLE_COUNT); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14144,52 +15079,55 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { _SOKOL_UNUSED(bindings); return true; #else - SOKOL_VALIDATE_BEGIN(); + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); /* a pipeline object must have been applied */ - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_ABND_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); + _SG_VALIDATE(pip != 0, VALIDATE_ABND_PIPELINE_EXISTS); if (!pip) { - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); /* has expected vertex buffers, and vertex buffers still exist */ for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + _SG_VALIDATE(pip->cmn.vertex_layout_valid[i], VALIDATE_ABND_VBS); /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_VB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + _SG_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, VALIDATE_ABND_VB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VB_OVERFLOW); } } else { /* vertex buffer provided in a slot which has no vertex layout in pipeline */ - SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + _SG_VALIDATE(!pip->cmn.vertex_layout_valid[i], VALIDATE_ABND_VBS); } } /* index buffer expected or not, and index buffer still exists */ if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { /* pipeline defines non-indexed rendering, but index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_IB); } else { /* pipeline defines indexed rendering, but no index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); + _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_NO_IB); } if (bindings->index_buffer.id != SG_INVALID_ID) { /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_IB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + _SG_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, VALIDATE_ABND_IB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IB_OVERFLOW); } } @@ -14197,15 +15135,15 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; if (bindings->vs_images[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + _SG_VALIDATE(i < stage->num_images, VALIDATE_ABND_VS_IMGS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); - SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); + _SG_VALIDATE(img != 0, VALIDATE_ABND_VS_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMG_TYPES); } } else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + _SG_VALIDATE(i >= stage->num_images, VALIDATE_ABND_VS_IMGS); } } @@ -14213,18 +15151,18 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; if (bindings->fs_images[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + _SG_VALIDATE(i < stage->num_images, VALIDATE_ABND_FS_IMGS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); - SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); + _SG_VALIDATE(img != 0, VALIDATE_ABND_FS_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMG_TYPES); } } else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + _SG_VALIDATE(i >= stage->num_images, VALIDATE_ABND_FS_IMGS); } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14235,22 +15173,25 @@ _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int _SOKOL_UNUSED(data); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_AUB_NO_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); /* check that there is a uniform block at 'stage' and 'ub_index' */ const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; - SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_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 */ - SOKOL_VALIDATE(data->size <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); + _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE); - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14260,13 +15201,16 @@ _SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg _SOKOL_UNUSED(data); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (int)data->size, _SG_VALIDATE_UPDATEBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); - SOKOL_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); - return SOKOL_VALIDATE_END(); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDATEBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (int)data->size, VALIDATE_UPDATEBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_ONCE); + _SG_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_APPEND); + return _sg_validate_end(); #endif } @@ -14276,12 +15220,15 @@ _SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg _SOKOL_UNUSED(data); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), _SG_VALIDATE_APPENDBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); - return SOKOL_VALIDATE_END(); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_APPENDBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), VALIDATE_APPENDBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_APPENDBUF_UPDATE); + return _sg_validate_end(); #endif } @@ -14291,10 +15238,13 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i _SOKOL_UNUSED(data); return true; #else + if (_sg.desc.disable_validation) { + return true; + } SOKOL_ASSERT(img && data); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); - SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); + _sg_validate_begin(); + _SG_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDIMG_USAGE); + _SG_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, VALIDATE_UPDIMG_ONCE); _sg_validate_image_data(data, img->cmn.pixel_format, img->cmn.width, @@ -14302,11 +15252,17 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, img->cmn.num_mipmaps, img->cmn.num_slices); - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } -/*== fill in desc default values =============================================*/ +// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████ +// +// >>resources _SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { sg_buffer_desc def = *desc; def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); @@ -14369,6 +15325,7 @@ _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* des if (0 == ub_desc->size) { break; } + ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE); for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type == SG_UNIFORMTYPE_INVALID) { @@ -14438,7 +15395,7 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des /* resolve vertex layout strides and offsets */ int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; - memset(auto_offset, 0, sizeof(auto_offset)); + _sg_clear(auto_offset, sizeof(auto_offset)); bool use_auto_offset = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { /* to use computed offsets, *all* attr offsets must be 0 */ @@ -14474,7 +15431,6 @@ _SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { return def; } -/*== allocate/initialize resource private functions ==========================*/ _SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { sg_buffer res; int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); @@ -14482,8 +15438,9 @@ _SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); } else { - /* pool is exhausted */ res.id = SG_INVALID_ID; + _SG_ERROR(BUFFER_POOL_EXHAUSTED); + _SG_TRACE_NOARGS(err_buffer_pool_exhausted); } return res; } @@ -14495,8 +15452,9 @@ _SOKOL_PRIVATE sg_image _sg_alloc_image(void) { res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); } else { - /* pool is exhausted */ res.id = SG_INVALID_ID; + _SG_ERROR(IMAGE_POOL_EXHAUSTED); + _SG_TRACE_NOARGS(err_image_pool_exhausted); } return res; } @@ -14508,8 +15466,9 @@ _SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); } else { - /* pool is exhausted */ res.id = SG_INVALID_ID; + _SG_ERROR(SHADER_POOL_EXHAUSTED); + _SG_TRACE_NOARGS(err_shader_pool_exhausted); } return res; } @@ -14521,8 +15480,9 @@ _SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); } else { - /* pool is exhausted */ res.id = SG_INVALID_ID; + _SG_ERROR(PIPELINE_POOL_EXHAUSTED); + _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); } return res; } @@ -14534,56 +15494,46 @@ _SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); } else { - /* pool is exhausted */ res.id = SG_INVALID_ID; + _SG_ERROR(PASS_POOL_EXHAUSTED); + _SG_TRACE_NOARGS(err_pass_pool_exhausted); } return res; } -_SOKOL_PRIVATE void _sg_dealloc_buffer(sg_buffer buf_id) { - SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_dealloc_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC) && (buf->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf->slot.id)); _sg_reset_slot(&buf->slot); - _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf_id.id)); } -_SOKOL_PRIVATE void _sg_dealloc_image(sg_image img_id) { - SOKOL_ASSERT(img_id.id != SG_INVALID_ID); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_dealloc_image(_sg_image_t* img) { + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC) && (img->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img->slot.id)); _sg_reset_slot(&img->slot); - _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img_id.id)); } -_SOKOL_PRIVATE void _sg_dealloc_shader(sg_shader shd_id) { - SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_dealloc_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC) && (shd->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd->slot.id)); _sg_reset_slot(&shd->slot); - _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd_id.id)); } -_SOKOL_PRIVATE void _sg_dealloc_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_dealloc_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC) && (pip->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip->slot.id)); _sg_reset_slot(&pip->slot); - _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip_id.id)); } -_SOKOL_PRIVATE void _sg_dealloc_pass(sg_pass pass_id) { - SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_dealloc_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass && (pass->slot.state == SG_RESOURCESTATE_ALLOC) && (pass->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass->slot.id)); _sg_reset_slot(&pass->slot); - _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass_id.id)); } -_SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf_id.id != SG_INVALID_ID && desc); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); buf->slot.ctx_id = _sg.active_context.id; if (_sg_validate_buffer_desc(desc)) { buf->slot.state = _sg_create_buffer(buf, desc); @@ -14594,10 +15544,9 @@ _SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); } -_SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { - SOKOL_ASSERT(img_id.id != SG_INVALID_ID && desc); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); img->slot.ctx_id = _sg.active_context.id; if (_sg_validate_image_desc(desc)) { img->slot.state = _sg_create_image(img, desc); @@ -14608,10 +15557,9 @@ _SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); } -_SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd_id.id != SG_INVALID_ID && desc); - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); shd->slot.ctx_id = _sg.active_context.id; if (_sg_validate_shader_desc(desc)) { shd->slot.state = _sg_create_shader(shd, desc); @@ -14622,10 +15570,9 @@ _SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); } -_SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip_id.id != SG_INVALID_ID && desc); - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); pip->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pipeline_desc(desc)) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); @@ -14642,10 +15589,9 @@ _SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); } -_SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass_id.id != SG_INVALID_ID && desc); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); +_SOKOL_PRIVATE void _sg_init_pass(_sg_pass_t* pass, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + SOKOL_ASSERT(desc); pass->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pass_desc(desc)) { /* lookup pass attachment image pointers */ @@ -14653,8 +15599,10 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (desc->color_attachments[i].image.id) { att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); - /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ - SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); + if (!(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID)) { + pass->slot.state = SG_RESOURCESTATE_FAILED; + return; + } } else { att_imgs[i] = 0; @@ -14663,8 +15611,10 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; if (desc->depth_stencil_attachment.image.id) { att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); - /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ - SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); + if (!(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID)) { + pass->slot.state = SG_RESOURCESTATE_FAILED; + return; + } } else { att_imgs[ds_att_index] = 0; @@ -14677,129 +15627,185 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); } -_SOKOL_PRIVATE bool _sg_uninit_buffer(sg_buffer buf_id) { - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - if (buf) { - if (buf->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_buffer(buf); - _sg_reset_buffer(buf); - return true; +_SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf && ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED))); + if (buf->slot.ctx_id == _sg.active_context.id) { + _sg_discard_buffer(buf); + _sg_reset_buffer_to_alloc_state(buf); + } + else { + _SG_WARN(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH); + _SG_TRACE_NOARGS(err_context_mismatch); + } +} + +_SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) { + SOKOL_ASSERT(img && ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED))); + if (img->slot.ctx_id == _sg.active_context.id) { + _sg_discard_image(img); + _sg_reset_image_to_alloc_state(img); + } + else { + _SG_WARN(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH); + _SG_TRACE_NOARGS(err_context_mismatch); + } +} + +_SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd && ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED))); + if (shd->slot.ctx_id == _sg.active_context.id) { + _sg_discard_shader(shd); + _sg_reset_shader_to_alloc_state(shd); + } + else { + _SG_WARN(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH); + _SG_TRACE_NOARGS(err_context_mismatch); + } +} + +_SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip && ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED))); + if (pip->slot.ctx_id == _sg.active_context.id) { + _sg_discard_pipeline(pip); + _sg_reset_pipeline_to_alloc_state(pip); + } + else { + _SG_WARN(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH); + _SG_TRACE_NOARGS(err_context_mismatch); + } +} + +_SOKOL_PRIVATE void _sg_uninit_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass && ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED))); + if (pass->slot.ctx_id == _sg.active_context.id) { + _sg_discard_pass(pass); + _sg_reset_pass_to_alloc_state(pass); + } + else { + _SG_WARN(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH); + _SG_TRACE_NOARGS(err_context_mismatch); + } +} + +_SOKOL_PRIVATE void _sg_setup_commit_listeners(const sg_desc* desc) { + SOKOL_ASSERT(desc->max_commit_listeners > 0); + SOKOL_ASSERT(0 == _sg.commit_listeners.items); + SOKOL_ASSERT(0 == _sg.commit_listeners.num); + SOKOL_ASSERT(0 == _sg.commit_listeners.upper); + _sg.commit_listeners.num = desc->max_commit_listeners; + const size_t size = (size_t)_sg.commit_listeners.num * sizeof(sg_commit_listener); + _sg.commit_listeners.items = (sg_commit_listener*)_sg_malloc_clear(size); +} + +_SOKOL_PRIVATE void _sg_discard_commit_listeners(void) { + SOKOL_ASSERT(0 != _sg.commit_listeners.items); + _sg_free(_sg.commit_listeners.items); + _sg.commit_listeners.items = 0; +} + +_SOKOL_PRIVATE void _sg_notify_commit_listeners(void) { + SOKOL_ASSERT(_sg.commit_listeners.items); + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + const sg_commit_listener* listener = &_sg.commit_listeners.items[i]; + if (listener->func) { + listener->func(listener->user_data); } - else { - SOKOL_LOG("_sg_uninit_buffer: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } +} + +_SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listener) { + SOKOL_ASSERT(new_listener && new_listener->func); + SOKOL_ASSERT(_sg.commit_listeners.items); + // first check if the listener hadn't been added already + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + const sg_commit_listener* slot = &_sg.commit_listeners.items[i]; + if ((slot->func == new_listener->func) && (slot->user_data == new_listener->user_data)) { + _SG_ERROR(IDENTICAL_COMMIT_LISTENER); + return false; + } + } + // first try to plug a hole + sg_commit_listener* slot = 0; + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + if (_sg.commit_listeners.items[i].func == 0) { + slot = &_sg.commit_listeners.items[i]; + break; + } + } + if (!slot) { + // append to end + if (_sg.commit_listeners.upper < _sg.commit_listeners.num) { + slot = &_sg.commit_listeners.items[_sg.commit_listeners.upper++]; + } + } + if (!slot) { + _SG_ERROR(COMMIT_LISTENER_ARRAY_FULL); + return false; + } + *slot = *new_listener; + return true; +} + +_SOKOL_PRIVATE bool _sg_remove_commit_listener(const sg_commit_listener* listener) { + SOKOL_ASSERT(listener && listener->func); + SOKOL_ASSERT(_sg.commit_listeners.items); + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + sg_commit_listener* slot = &_sg.commit_listeners.items[i]; + // both the function pointer and user data must match! + if ((slot->func == listener->func) && (slot->user_data == listener->user_data)) { + slot->func = 0; + slot->user_data = 0; + // NOTE: since _sg_add_commit_listener() already catches duplicates, + // we don't need to worry about them here + return true; } } return false; } -_SOKOL_PRIVATE bool _sg_uninit_image(sg_image img_id) { - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - if (img) { - if (img->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_image(img); - _sg_reset_image(img); - return true; - } - else { - SOKOL_LOG("_sg_uninit_image: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_shader(sg_shader shd_id) { - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - if (shd) { - if (shd->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_shader(shd); - _sg_reset_shader(shd); - return true; - } - else { - SOKOL_LOG("_sg_uninit_shader: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_pipeline(sg_pipeline pip_id) { - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - if (pip) { - if (pip->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_pipeline(pip); - _sg_reset_pipeline(pip); - return true; - } - else { - SOKOL_LOG("_sg_uninit_pipeline: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_pass(sg_pass pass_id) { - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if (pass->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_pass(pass); - _sg_reset_pass(pass); - return true; - } - else { - SOKOL_LOG("_sg_uninit_pass: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -/*== PUBLIC API FUNCTIONS ====================================================*/ - -#if defined(SOKOL_METAL) - // this is ARC compatible - #if defined(__cplusplus) - #define _SG_CLEAR(type, item) { item = (type) { }; } - #else - #define _SG_CLEAR(type, item) { item = (type) { 0 }; } - #endif -#else - #define _SG_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } -#endif - -SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { - SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); - _SG_CLEAR(_sg_state_t, _sg); - _sg.desc = *desc; - - /* replace zero-init items with their default values +_SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { + /* NOTE: on WebGPU, the default color pixel format MUST be provided, cannot be a default compile-time constant. */ + sg_desc res = *desc; #if defined(SOKOL_WGPU) - SOKOL_ASSERT(SG_PIXELFORMAT_NONE != _sg.desc.context.color_format); + SOKOL_ASSERT(SG_PIXELFORMAT_NONE != res.context.color_format); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_BGRA8); + res.context.color_format = _sg_def(res.context.color_format, SG_PIXELFORMAT_BGRA8); #else - _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_RGBA8); + res.context.color_format = _sg_def(res.context.color_format, SG_PIXELFORMAT_RGBA8); #endif - _sg.desc.context.depth_format = _sg_def(_sg.desc.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); - _sg.desc.context.sample_count = _sg_def(_sg.desc.context.sample_count, 1); - _sg.desc.buffer_pool_size = _sg_def(_sg.desc.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); - _sg.desc.image_pool_size = _sg_def(_sg.desc.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); - _sg.desc.shader_pool_size = _sg_def(_sg.desc.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); - _sg.desc.pipeline_pool_size = _sg_def(_sg.desc.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); - _sg.desc.pass_pool_size = _sg_def(_sg.desc.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); - _sg.desc.context_pool_size = _sg_def(_sg.desc.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); - _sg.desc.uniform_buffer_size = _sg_def(_sg.desc.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); - _sg.desc.staging_buffer_size = _sg_def(_sg.desc.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); - _sg.desc.sampler_cache_size = _sg_def(_sg.desc.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); + res.context.depth_format = _sg_def(res.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + res.context.sample_count = _sg_def(res.context.sample_count, 1); + res.buffer_pool_size = _sg_def(res.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); + res.image_pool_size = _sg_def(res.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); + res.shader_pool_size = _sg_def(res.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); + res.pipeline_pool_size = _sg_def(res.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); + res.pass_pool_size = _sg_def(res.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); + res.context_pool_size = _sg_def(res.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); + res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + res.staging_buffer_size = _sg_def(res.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); + res.sampler_cache_size = _sg_def(res.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); + res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); + return res; +} +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); + _sg.desc = _sg_desc_defaults(desc); _sg_setup_pools(&_sg.pools, &_sg.desc); + _sg_setup_commit_listeners(&_sg.desc); _sg.frame_index = 1; _sg_setup_backend(&_sg.desc); _sg.valid = true; @@ -14814,13 +15820,14 @@ SOKOL_API_IMPL void sg_shutdown(void) { if (_sg.active_context.id != SG_INVALID_ID) { _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); if (ctx) { - _sg_destroy_all_resources(&_sg.pools, _sg.active_context.id); - _sg_destroy_context(ctx); + _sg_discard_all_resources(&_sg.pools, _sg.active_context.id); + _sg_discard_context(ctx); } } _sg_discard_backend(); + _sg_discard_commit_listeners(); _sg_discard_pools(&_sg.pools); - _sg.valid = false; + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); } SOKOL_API_IMPL bool sg_isvalid(void) { @@ -14875,11 +15882,11 @@ SOKOL_API_IMPL sg_context sg_setup_context(void) { SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { SOKOL_ASSERT(_sg.valid); - _sg_destroy_all_resources(&_sg.pools, ctx_id.id); + _sg_discard_all_resources(&_sg.pools, ctx_id.id); _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); if (ctx) { - _sg_destroy_context(ctx); - _sg_reset_context(ctx); + _sg_discard_context(ctx); + _sg_reset_context_to_alloc_state(ctx); _sg_reset_slot(&ctx->slot); _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); } @@ -14904,7 +15911,7 @@ SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace _sg.hooks = *trace_hooks; #else static sg_trace_hooks old_hooks; - SOKOL_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); + _SG_WARN(TRACE_HOOKS_NOT_ENABLED); #endif return old_hooks; } @@ -14946,152 +15953,302 @@ SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); - _sg_dealloc_buffer(buf_id); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_buffer(buf); + } + else { + _SG_ERROR(DEALLOC_BUFFER_INVALID_STATE); + } + } _SG_TRACE_ARGS(dealloc_buffer, buf_id); } SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { SOKOL_ASSERT(_sg.valid); - _sg_dealloc_image(img_id); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_image(img); + } + else { + _SG_ERROR(DEALLOC_IMAGE_INVALID_STATE); + } + } _SG_TRACE_ARGS(dealloc_image, img_id); } SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); - _sg_dealloc_shader(shd_id); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_shader(shd); + } + else { + _SG_ERROR(DEALLOC_SHADER_INVALID_STATE); + } + } _SG_TRACE_ARGS(dealloc_shader, shd_id); } SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); - _sg_dealloc_pipeline(pip_id); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pipeline(pip); + } + else { + _SG_ERROR(DEALLOC_PIPELINE_INVALID_STATE); + } + } _SG_TRACE_ARGS(dealloc_pipeline, pip_id); } SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { SOKOL_ASSERT(_sg.valid); - _sg_dealloc_pass(pass_id); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pass(pass); + } + else { + _SG_ERROR(DEALLOC_PASS_INVALID_STATE); + } + } _SG_TRACE_ARGS(dealloc_pass, pass_id); } SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); - _sg_init_buffer(buf_id, &desc_def); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_buffer(buf, &desc_def); + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); + } + else { + _SG_ERROR(INIT_BUFFER_INVALID_STATE); + } + } _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); } SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_image_desc desc_def = _sg_image_desc_defaults(desc); - _sg_init_image(img_id, &desc_def); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); + } + else { + _SG_ERROR(INIT_IMAGE_INVALID_STATE); + } + } _SG_TRACE_ARGS(init_image, img_id, &desc_def); } SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); - _sg_init_shader(shd_id, &desc_def); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_shader(shd, &desc_def); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); + } + else { + _SG_ERROR(INIT_SHADER_INVALID_STATE); + } + } _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); } SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); - _sg_init_pipeline(pip_id, &desc_def); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_pipeline(pip, &desc_def); + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); + } + else { + _SG_ERROR(INIT_PIPELINE_INVALID_STATE); + } + } _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); } SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); - _sg_init_pass(pass_id, &desc_def); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_pass(pass, &desc_def); + SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); + } + else { + _SG_ERROR(INIT_PASS_INVALID_STATE); + } + } _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); } -SOKOL_API_IMPL bool sg_uninit_buffer(sg_buffer buf_id) { +SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_buffer(buf_id); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); + } + else { + _SG_ERROR(UNINIT_BUFFER_INVALID_STATE); + } + } _SG_TRACE_ARGS(uninit_buffer, buf_id); - return res; } -SOKOL_API_IMPL bool sg_uninit_image(sg_image img_id) { +SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) { SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_image(img_id); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); + } + else { + _SG_ERROR(UNINIT_IMAGE_INVALID_STATE); + } + } _SG_TRACE_ARGS(uninit_image, img_id); - return res; } -SOKOL_API_IMPL bool sg_uninit_shader(sg_shader shd_id) { +SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_shader(shd_id); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); + } + else { + _SG_ERROR(UNINIT_SHADER_INVALID_STATE); + } + } _SG_TRACE_ARGS(uninit_shader, shd_id); - return res; } -SOKOL_API_IMPL bool sg_uninit_pipeline(sg_pipeline pip_id) { +SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_pipeline(pip_id); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); + } + else { + _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE); + } + } _SG_TRACE_ARGS(uninit_pipeline, pip_id); - return res; } -SOKOL_API_IMPL bool sg_uninit_pass(sg_pass pass_id) { +SOKOL_API_IMPL void sg_uninit_pass(sg_pass pass_id) { SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_pass(pass_id); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pass(pass); + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_ALLOC); + } + else { + _SG_ERROR(UNINIT_PASS_INVALID_STATE); + } + } _SG_TRACE_ARGS(uninit_pass, pass_id); - return res; } /*-- set allocated resource to failed state ----------------------------------*/ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); - buf->slot.ctx_id = _sg.active_context.id; - buf->slot.state = SG_RESOURCESTATE_FAILED; + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + buf->slot.ctx_id = _sg.active_context.id; + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + else { + _SG_ERROR(FAIL_BUFFER_INVALID_STATE); + } + } _SG_TRACE_ARGS(fail_buffer, buf_id); } SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(img_id.id != SG_INVALID_ID); _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); - img->slot.ctx_id = _sg.active_context.id; - img->slot.state = SG_RESOURCESTATE_FAILED; + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + img->slot.ctx_id = _sg.active_context.id; + img->slot.state = SG_RESOURCESTATE_FAILED; + } + else { + _SG_ERROR(FAIL_IMAGE_INVALID_STATE); + } + } _SG_TRACE_ARGS(fail_image, img_id); } SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); - shd->slot.ctx_id = _sg.active_context.id; - shd->slot.state = SG_RESOURCESTATE_FAILED; + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + shd->slot.ctx_id = _sg.active_context.id; + shd->slot.state = SG_RESOURCESTATE_FAILED; + } + else { + _SG_ERROR(FAIL_SHADER_INVALID_STATE); + } + } _SG_TRACE_ARGS(fail_shader, shd_id); } SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); - pip->slot.ctx_id = _sg.active_context.id; - pip->slot.state = SG_RESOURCESTATE_FAILED; + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + pip->slot.ctx_id = _sg.active_context.id; + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + else { + _SG_ERROR(FAIL_PIPELINE_INVALID_STATE); + } + } _SG_TRACE_ARGS(fail_pipeline, pip_id); } SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); - pass->slot.ctx_id = _sg.active_context.id; - pass->slot.state = SG_RESOURCESTATE_FAILED; + if (pass) { + if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { + pass->slot.ctx_id = _sg.active_context.id; + pass->slot.state = SG_RESOURCESTATE_FAILED; + } + else { + _SG_ERROR(FAIL_PASS_INVALID_STATE); + } + } _SG_TRACE_ARGS(fail_pass, pass_id); } @@ -15138,11 +16295,10 @@ SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); sg_buffer buf_id = _sg_alloc_buffer(); if (buf_id.id != SG_INVALID_ID) { - _sg_init_buffer(buf_id, &desc_def); - } - else { - SOKOL_LOG("buffer pool exhausted!"); - _SG_TRACE_NOARGS(err_buffer_pool_exhausted); + _sg_buffer_t* buf = _sg_buffer_at(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_buffer(buf, &desc_def); + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); } _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); return buf_id; @@ -15154,11 +16310,10 @@ SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { sg_image_desc desc_def = _sg_image_desc_defaults(desc); sg_image img_id = _sg_alloc_image(); if (img_id.id != SG_INVALID_ID) { - _sg_init_image(img_id, &desc_def); - } - else { - SOKOL_LOG("image pool exhausted!"); - _SG_TRACE_NOARGS(err_image_pool_exhausted); + _sg_image_t* img = _sg_image_at(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); } _SG_TRACE_ARGS(make_image, &desc_def, img_id); return img_id; @@ -15170,11 +16325,10 @@ SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); sg_shader shd_id = _sg_alloc_shader(); if (shd_id.id != SG_INVALID_ID) { - _sg_init_shader(shd_id, &desc_def); - } - else { - SOKOL_LOG("shader pool exhausted!"); - _SG_TRACE_NOARGS(err_shader_pool_exhausted); + _sg_shader_t* shd = _sg_shader_at(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_shader(shd, &desc_def); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); } _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); return shd_id; @@ -15186,11 +16340,10 @@ SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); sg_pipeline pip_id = _sg_alloc_pipeline(); if (pip_id.id != SG_INVALID_ID) { - _sg_init_pipeline(pip_id, &desc_def); - } - else { - SOKOL_LOG("pipeline pool exhausted!"); - _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); + _sg_pipeline_t* pip = _sg_pipeline_at(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_pipeline(pip, &desc_def); + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); } _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); return pip_id; @@ -15202,11 +16355,10 @@ SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); sg_pass pass_id = _sg_alloc_pass(); if (pass_id.id != SG_INVALID_ID) { - _sg_init_pass(pass_id, &desc_def); - } - else { - SOKOL_LOG("pass pool exhausted!"); - _SG_TRACE_NOARGS(err_pass_pool_exhausted); + _sg_pass_t* pass = _sg_pass_at(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && (pass->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_pass(pass, &desc_def); + SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); } _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); return pass_id; @@ -15216,40 +16368,80 @@ SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_buffer, buf_id); - if (_sg_uninit_buffer(buf_id)) { - _sg_dealloc_buffer(buf_id); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_INITIAL); + } } } SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_image, img_id); - if (_sg_uninit_image(img_id)) { - _sg_dealloc_image(img_id); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_INITIAL); + } } } SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_shader, shd_id); - if (_sg_uninit_shader(shd_id)) { - _sg_dealloc_shader(shd_id); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_INITIAL); + } } } SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_pipeline, pip_id); - if (_sg_uninit_pipeline(pip_id)) { - _sg_dealloc_pipeline(pip_id); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_INITIAL); + } } } SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_pass, pass_id); - if (_sg_uninit_pass(pass_id)) { - _sg_dealloc_pass(pass_id); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pass(pass); + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pass(pass); + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_INITIAL); + } } } @@ -15442,7 +16634,7 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_ASSERT(num_instances >= 0); #if defined(SOKOL_DEBUG) if (!_sg.bindings_valid) { - SOKOL_LOG("attempting to draw without resource bindings"); + _SG_WARN(DRAW_WITHOUT_BINDINGS); } #endif if (!_sg.pass_valid) { @@ -15484,6 +16676,7 @@ SOKOL_API_IMPL void sg_end_pass(void) { SOKOL_API_IMPL void sg_commit(void) { SOKOL_ASSERT(_sg.valid); _sg_commit(); + _sg_notify_commit_listeners(); _SG_TRACE_NOARGS(commit); _sg.frame_index++; } @@ -15555,6 +16748,23 @@ SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { return result; } +SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = false; + if (buf) { + int append_pos = buf->cmn.append_pos; + /* rewind append cursor in a new frame */ + if (buf->cmn.append_frame_index != _sg.frame_index) { + append_pos = 0; + } + if ((append_pos + _sg_roundup((int)size, 4)) > buf->cmn.size) { + result = true; + } + } + return result; +} + SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { SOKOL_ASSERT(_sg.valid); _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); @@ -15580,10 +16790,20 @@ SOKOL_API_IMPL void sg_pop_debug_group(void) { _SG_TRACE_NOARGS(pop_debug_group); } +SOKOL_API_IMPL bool sg_add_commit_listener(sg_commit_listener listener) { + SOKOL_ASSERT(_sg.valid); + return _sg_add_commit_listener(&listener); +} + +SOKOL_API_IMPL bool sg_remove_commit_listener(sg_commit_listener listener) { + SOKOL_ASSERT(_sg.valid); + return _sg_remove_commit_listener(&listener); +} + SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); sg_buffer_info info; - memset(&info, 0, sizeof(info)); + _sg_clear(&info, sizeof(info)); const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if (buf) { info.slot.state = buf->slot.state; @@ -15607,7 +16827,7 @@ SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { SOKOL_ASSERT(_sg.valid); sg_image_info info; - memset(&info, 0, sizeof(info)); + _sg_clear(&info, sizeof(info)); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img) { info.slot.state = img->slot.state; @@ -15621,8 +16841,6 @@ SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { info.num_slots = img->cmn.num_slots; info.active_slot = img->cmn.active_slot; #endif - info.width = img->cmn.width; - info.height = img->cmn.height; } return info; } @@ -15630,7 +16848,7 @@ SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); sg_shader_info info; - memset(&info, 0, sizeof(info)); + _sg_clear(&info, sizeof(info)); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); if (shd) { info.slot.state = shd->slot.state; @@ -15643,7 +16861,7 @@ SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); sg_pipeline_info info; - memset(&info, 0, sizeof(info)); + _sg_clear(&info, sizeof(info)); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); if (pip) { info.slot.state = pip->slot.state; @@ -15656,7 +16874,7 @@ SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { SOKOL_ASSERT(_sg.valid); sg_pass_info info; - memset(&info, 0, sizeof(info)); + _sg_clear(&info, sizeof(info)); const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); if (pass) { info.slot.state = pass->slot.state; @@ -15666,6 +16884,115 @@ SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { return info; } +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + desc.size = (size_t)buf->cmn.size; + desc.type = buf->cmn.type; + desc.usage = buf->cmn.usage; + } + return desc; +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + desc.type = img->cmn.type; + desc.render_target = img->cmn.render_target; + desc.width = img->cmn.width; + desc.height = img->cmn.height; + desc.num_slices = img->cmn.num_slices; + desc.num_mipmaps = img->cmn.num_mipmaps; + desc.usage = img->cmn.usage; + desc.pixel_format = img->cmn.pixel_format; + desc.sample_count = img->cmn.sample_count; + desc.min_filter = img->cmn.min_filter; + desc.mag_filter = img->cmn.mag_filter; + desc.wrap_u = img->cmn.wrap_u; + desc.wrap_v = img->cmn.wrap_v; + desc.wrap_w = img->cmn.wrap_w; + desc.border_color = img->cmn.border_color; + desc.max_anisotropy = img->cmn.max_anisotropy; + desc.min_lod = img->cmn.min_lod; + desc.max_lod = img->cmn.max_lod; + } + return desc; +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int stage_idx = 0; stage_idx < SG_NUM_SHADER_STAGES; stage_idx++) { + sg_shader_stage_desc* stage_desc = (stage_idx == 0) ? &desc.vs : &desc.fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_idx]; + for (int ub_idx = 0; ub_idx < stage->num_uniform_blocks; ub_idx++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_idx]; + const _sg_shader_uniform_block_t* ub = &stage->uniform_blocks[ub_idx]; + ub_desc->size = ub->size; + } + for (int img_idx = 0; img_idx < stage->num_images; img_idx++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_idx]; + const _sg_shader_image_t* img = &stage->images[img_idx]; + img_desc->image_type = img->image_type; + img_desc->sampler_type = img->sampler_type; + } + } + } + return desc; +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + desc.shader = pip->cmn.shader_id; + desc.layout = pip->cmn.layout; + desc.depth = pip->cmn.depth; + desc.stencil = pip->cmn.stencil; + desc.color_count = pip->cmn.color_count; + for (int i = 0; i < pip->cmn.color_count; i++) { + desc.colors[i] = pip->cmn.colors[i]; + } + desc.primitive_type = pip->cmn.primitive_type; + desc.index_type = pip->cmn.index_type; + desc.cull_mode = pip->cmn.cull_mode; + desc.face_winding = pip->cmn.face_winding; + desc.sample_count = pip->cmn.sample_count; + desc.blend_color = pip->cmn.blend_color; + desc.alpha_to_coverage_enabled = pip->cmn.alpha_to_coverage_enabled; + } + return desc; +} + +SOKOL_API_IMPL sg_pass_desc sg_query_pass_desc(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_pass_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + desc.color_attachments[i].image = pass->cmn.color_atts[i].image_id; + desc.color_attachments[i].mip_level = pass->cmn.color_atts[i].mip_level; + desc.color_attachments[i].slice = pass->cmn.color_atts[i].slice; + } + desc.depth_stencil_attachment.image = pass->cmn.ds_att.image_id; + desc.depth_stencil_attachment.mip_level = pass->cmn.ds_att.mip_level; + desc.depth_stencil_attachment.slice = pass->cmn.ds_att.slice; + } + return desc; +} + SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { SOKOL_ASSERT(_sg.valid && desc); return _sg_buffer_desc_defaults(desc); diff --git a/3rdparty/sokol/sokol_log.h b/3rdparty/sokol/sokol_log.h new file mode 100644 index 0000000..87f78d7 --- /dev/null +++ b/3rdparty/sokol/sokol_log.h @@ -0,0 +1,343 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL) +#define SOKOL_LOG_IMPL +#endif +#ifndef SOKOL_LOG_INCLUDED +/* + sokol_log.h -- common logging callback for sokol headers + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_LOG_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines when building the implementation: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + Optionally define the following for verbose output: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + + OVERVIEW + ======== + sokol_log.h provides a default logging callback for other sokol headers. + + To use the default log callback, just include sokol_log.h and provide + a function pointer to the 'slog_func' function when setting up the + sokol library: + + For instance with sokol_audio.h: + + #include "sokol_log.h" + ... + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); + + Logging output goes to stderr and/or a platform specific logging subsystem + (which means that in some scenarios you might see logging messages duplicated): + + - Windows: stderr + OutputDebugStringA() + - macOS/iOS/Linux: stderr + syslog() + - Emscripten: console.info()/warn()/error() + - Android: __android_log_write() + + On Windows with sokol_app.h also note the runtime config items to make + stdout/stderr output visible on the console for WinMain() applications + via sapp_desc.win32_console_attach or sapp_desc.win32_console_create, + however when running in a debugger on Windows, the logging output should + show up on the debug output UI panel. + + In debug mode, a log message might look like this: + + [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: + SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas + + The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) + such error messages are clickable. + + In release mode, logging is less verbose as to not bloat the executable with string data, but you still get + enough information to identify the type and location of an error: + + [sspine][error][id:12][line:3472] + + RULES FOR WRITING YOUR OWN LOGGING FUNCTION + =========================================== + - must be re-entrant because it might be called from different threads + - must treat **all** provided string pointers as optional (can be null) + - don't store the string pointers, copy the string data instead + - must not return for log level panic + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2023 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_LOG_INCLUDED (1) +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL) +#define SOKOL_LOG_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_LOG_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL) +#define SOKOL_LOG_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_LOG_API_DECL __declspec(dllimport) +#else +#define SOKOL_LOG_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Plug this function into the 'logger.func' struct item when initializating any of the sokol + headers. For instance for sokol_audio.h it would loom like this: + + saudio_setup(&(saudio_desc){ + .logger = { + .func = slog_func + } + }); +*/ +SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // SOKOL_LOG_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_LOG_IMPL +#define SOKOL_LOG_IMPL_INCLUDED (1) + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +// platform detection +#if defined(__APPLE__) + #define _SLOG_APPLE (1) +#elif defined(__EMSCRIPTEN__) + #define _SLOG_EMSCRIPTEN (1) +#elif defined(_WIN32) + #define _SLOG_WINDOWS (1) +#elif defined(__ANDROID__) + #define _SLOG_ANDROID (1) +#elif defined(__linux__) || defined(__unix__) + #define _SLOG_LINUX (1) +#else +#error "sokol_log.h: unknown platform" +#endif + +#include // abort +#include // fputs +#include // size_t + +#if defined(_SLOG_EMSCRIPTEN) +#include +#elif defined(_SLOG_WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX + #define NOMINMAX +#endif +#include +#elif defined(_SLOG_ANDROID) +#include +#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) +#include +#endif + +// size of line buffer (on stack!) in bytes including terminating zero +#define _SLOG_LINE_LENGTH (512) + +_SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { + if (str) { + char c; + while (((c = *str++) != 0) && (dst < (end - 1))) { + *dst++ = c; + } + } + *dst = 0; + return dst; +} + +_SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { + const size_t max_digits_and_null = 11; + if (buf_size < max_digits_and_null) { + return 0; + } + char* p = buf + max_digits_and_null; + *--p = 0; + do { + *--p = '0' + (x % 10); + x /= 10; + } while (x != 0); + return p; +} + +#if defined(_SLOG_EMSCRIPTEN) +EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { + const str = UTF8ToString(c_str); + switch (level) { + case 0: console.error(str); break; + case 1: console.error(str); break; + case 2: console.warn(str); break; + default: console.info(str); break; + } +}); +#endif + +SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { + _SOKOL_UNUSED(user_data); + + const char* log_level_str; + switch (log_level) { + case 0: log_level_str = "panic"; break; + case 1: log_level_str = "error"; break; + case 2: log_level_str = "warning"; break; + default: log_level_str = "info"; break; + } + + // build log output line + char line_buf[_SLOG_LINE_LENGTH]; + char* str = line_buf; + char* end = line_buf + sizeof(line_buf); + char num_buf[32]; + if (tag) { + str = _slog_append("[", str, end); + str = _slog_append(tag, str, end); + str = _slog_append("]", str, end); + } + str = _slog_append("[", str, end); + str = _slog_append(log_level_str, str, end); + str = _slog_append("]", str, end); + str = _slog_append("[id:", str, end); + str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("]", str, end); + // if a filename is provided, build a clickable log message that's compatible with compiler error messages + if (filename) { + str = _slog_append(" ", str, end); + #if defined(_MSC_VER) + // MSVC compiler error format + str = _slog_append(filename, str, end); + str = _slog_append("(", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("): ", str, end); + #else + // gcc/clang compiler error format + str = _slog_append(filename, str, end); + str = _slog_append(":", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append(":0: ", str, end); + #endif + } + else { + str = _slog_append("[line:", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("] ", str, end); + } + if (message) { + str = _slog_append("\n\t", str, end); + str = _slog_append(message, str, end); + } + str = _slog_append("\n\n", str, end); + if (0 == log_level) { + str = _slog_append("ABORTING because of [panic]\n", str, end); + (void)str; + } + + // print to stderr? + #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) + fputs(line_buf, stderr); + #endif + + // platform specific logging calls + #if defined(_SLOG_WINDOWS) + OutputDebugStringA(line_buf); + #elif defined(_SLOG_ANDROID) + int prio; + switch (log_level) { + case 0: prio = ANDROID_LOG_FATAL; break; + case 1: prio = ANDROID_LOG_ERROR; break; + case 2: prio = ANDROID_LOG_WARN; break; + default: prio = ANDROID_LOG_INFO; break; + } + __android_log_write(prio, "SOKOL", line_buf); + #elif defined(_SLOG_EMSCRIPTEN) + slog_js_log(log_level, line_buf); + #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) + int prio; + switch (log_level) { + case 0: prio = LOG_CRIT; break; + case 1: prio = LOG_ERR; break; + case 2: prio = LOG_WARNING; break; + default: prio = LOG_INFO; break; + } + syslog(prio, "%s", line_buf); + #endif + if (0 == log_level) { + abort(); + } +} +#endif // SOKOL_LOG_IMPL diff --git a/3rdparty/sokol/sokol_time.h b/3rdparty/sokol/sokol_time.h index 3bf1167..2d4d456 100644 --- a/3rdparty/sokol/sokol_time.h +++ b/3rdparty/sokol/sokol_time.h @@ -61,6 +61,8 @@ The main purpose of this function is to remove jitter/inaccuracies from measured frame times, and instead use the display refresh rate as frame duration. + NOTE: for more robust frame timing, consider using the + sokol_app.h function sapp_frame_duration() Use the following functions to convert a duration in ticks into useful time units: @@ -77,7 +79,7 @@ Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() MacOS/iOS: mach_absolute_time() - emscripten: performance.now() + emscripten: emscripten_get_now() Linux+others: clock_gettime(CLOCK_MONOTONIC) zlib/libpng license @@ -199,19 +201,13 @@ static _stm_state_t _stm; see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 */ #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) -_SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { +_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { int64_t q = value / denom; int64_t r = value % denom; return q * numer + r * numer / denom; } #endif -#if defined(__EMSCRIPTEN__) -EM_JS(double, stm_js_perfnow, (void), { - return performance.now(); -}); -#endif - SOKOL_API_IMPL void stm_setup(void) { memset(&_stm, 0, sizeof(_stm)); _stm.initialized = 0xABCDABCD; @@ -222,7 +218,7 @@ SOKOL_API_IMPL void stm_setup(void) { mach_timebase_info(&_stm.timebase); _stm.start = mach_absolute_time(); #elif defined(__EMSCRIPTEN__) - _stm.start = stm_js_perfnow(); + _stm.start = emscripten_get_now(); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -236,13 +232,12 @@ SOKOL_API_IMPL uint64_t stm_now(void) { #if defined(_WIN32) LARGE_INTEGER qpc_t; QueryPerformanceCounter(&qpc_t); - now = (uint64_t) int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); + now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); #elif defined(__APPLE__) && defined(__MACH__) const uint64_t mach_now = mach_absolute_time() - _stm.start; - now = (uint64_t) int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); + now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); #elif defined(__EMSCRIPTEN__) - double js_now = stm_js_perfnow() - _stm.start; - SOKOL_ASSERT(js_now >= 0.0); + double js_now = emscripten_get_now() - _stm.start; now = (uint64_t) (js_now * 1000000.0); #else struct timespec ts; diff --git a/3rdparty/sokol/util/sokol_debugtext.h b/3rdparty/sokol/util/sokol_debugtext.h index 02f5d3b..78ae60d 100644 --- a/3rdparty/sokol/util/sokol_debugtext.h +++ b/3rdparty/sokol/util/sokol_debugtext.h @@ -28,12 +28,9 @@ SOKOL_VSNPRINTF - the function name of an alternative vsnprintf() function (default: vsnprintf) SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_DEBUGTEXT_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_DEBUGTEXT_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) If sokol_debugtext.h is compiled as a DLL, define the following before @@ -63,6 +60,15 @@ sdtx_setup(&(sdtx_desc_t){ ... }); + To see any warnings and errors, you should always install a logging callback. + The easiest way is via sokol_log.h: + + #include "sokol_log.h" + + sdtx_setup(&(sdtx_desc_t){ + .logger.func = slog_func, + }); + --- configure sokol-debugtext by populating the sdtx_desc_t struct: .context_pool_size (default: 8) @@ -96,6 +102,11 @@ be active right after sdtx_setup(), or when calling sdtx_set_context(SDTX_DEFAULT_CONTEXT): + .max_commands (default: 4096) + The max number of render commands that can be recorded + into the internal command buffer. This directly translates + to the number of render layer changes in a single frame. + .char_buf_size (default: 4096) The number of characters that can be rendered per frame in this context, defines the size of an internal fixed-size vertex @@ -217,20 +228,42 @@ have to be inside a render pass. - Note that character codes <32 are reserved as control characters and won't render anything. Currently only the following control - characters are implements: + characters are implemented: \r - carriage return (same as sdtx_pos_x(0)) \n - carriage return + line feed (same as stdx_crlf()) \t - a tab character + --- You can 'record' text into render layers, this allows to mix/interleave + sokol-debugtext rendering with other rendering operations inside + sokol-gfx render passes. To start recording text into a different render + layer, call: + + sdtx_layer(int layer_id) + + ...outside a sokol-gfx render pass. + --- finally, from within a sokol-gfx render pass, call: sdtx_draw() - ...to actually render the text. Calling sdtx_draw() will also rewind - the text context: + ...for non-layered rendering, or to draw a specific layer: - - the internal vertex buffer pointer is reset to the beginning + sdtx_draw_layer(int layer_id) + + NOTE that sdtx_draw() is equivalent to: + + sdtx_draw_layer(0) + + ...so sdtx_draw() will *NOT* render all text layers, instead it will + only render the 'default layer' 0. + + --- at the end of a frame (defined by the call to sg_commit()), sokol-debugtext + will rewind all contexts: + + - the internal vertex index is set to 0 + - the internal command index is set to 0 + - the current layer id is set to 0 - the current font is set to 0 - the cursor position is reset @@ -271,7 +304,8 @@ - the origin position - the current cursor position - the current tab width - - and the current color + - the current color + - and the current layer-id You can get the currently active context with: @@ -294,6 +328,12 @@ If a context is set as active that no longer exists, all sokol-debugtext functions that require an active context will silently fail. + You can directly draw the recorded text in a specific context without + setting the active context: + + sdtx_context_draw(ctx) + sdtx_context_draw_layer(ctx, layer_id) + USING YOUR OWN FONT DATA ======================== @@ -370,6 +410,76 @@ Character tiles that haven't been defined in the font will be rendered as a solid 8x8 quad. + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sdtx_setup(&(sdtx_desc_t){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sdtx_setup(&(sdtx_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sdtx' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SDTX_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_debugtext.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-debugtext like this: + + sdtx_setup(&(sdtx_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + LICENSE ======= zlib/libpng license @@ -428,6 +538,46 @@ extern "C" { #endif +/* + sdtx_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sdtx_log_item' - and in debug mode only - corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SDTX_LOG_ITEMS \ + _SDTX_LOGITEM_XMACRO(OK, "Ok") \ + _SDTX_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SDTX_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed") \ + _SDTX_LOGITEM_XMACRO(COMMAND_BUFFER_FULL, "command buffer full (adjust via sdtx_context_desc_t.max_commands)") \ + _SDTX_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (adjust via sdtx_desc_t.context_pool_size)") \ + _SDTX_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context") \ + +#define _SDTX_LOGITEM_XMACRO(item,msg) SDTX_LOGITEM_##item, +typedef enum sdtx_log_item_t { + _SDTX_LOG_ITEMS +} sdtx_log_item_t; +#undef _SDTX_LOGITEM_XMACRO + +/* + sdtx_logger_t + + Used in sdtx_desc_t to provide a custom logging and error reporting + callback to sokol-debugtext. +*/ +typedef struct sdtx_logger_t { + void (*func)( + const char* tag, // always "sdtx" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SDTX_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_debugtext.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sdtx_logger_t; + /* a rendering context handle */ typedef struct sdtx_context { uint32_t id; } sdtx_context; @@ -494,6 +644,7 @@ typedef struct sdtx_font_desc_t { of text. */ typedef struct sdtx_context_desc_t { + int max_commands; // max number of draw commands, each layer transition counts as a command, default: 4096 int char_buf_size; // max number of characters rendered in one frame, default: 4096 float canvas_width; // the initial virtual canvas width, default: 640 float canvas_height; // the initial virtual canvas height, default: 400 @@ -503,6 +654,20 @@ typedef struct sdtx_context_desc_t { int sample_count; // MSAA sample count of target render pass } sdtx_context_desc_t; +/* + sdtx_allocator_t + + 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 + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sdtx_allocator_t { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sdtx_allocator_t; + /* sdtx_desc_t @@ -524,6 +689,8 @@ typedef struct sdtx_desc_t { int printf_buf_size; // size of internal buffer for snprintf(), default: 4096 sdtx_font_desc_t fonts[SDTX_MAX_FONTS]; // up to 8 fonts descriptions sdtx_context_desc_t context; // the default context creation parameters + sdtx_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sdtx_logger_t logger; // optional log override function (default: NO LOGGING) } sdtx_desc_t; /* initialization/shutdown */ @@ -545,8 +712,14 @@ SOKOL_DEBUGTEXT_API_DECL void sdtx_set_context(sdtx_context ctx); SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_get_context(void); SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_default_context(void); -/* draw and rewind the current context */ +/* drawing functions (call inside sokol-gfx render pass) */ SOKOL_DEBUGTEXT_API_DECL void sdtx_draw(void); +SOKOL_DEBUGTEXT_API_DECL void sdtx_context_draw(sdtx_context ctx); +SOKOL_DEBUGTEXT_API_DECL void sdtx_draw_layer(int layer_id); +SOKOL_DEBUGTEXT_API_DECL void sdtx_context_draw_layer(sdtx_context ctx, int layer_id); + +/* switch render layer */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_layer(int layer_id); /* switch to a different font */ SOKOL_DEBUGTEXT_API_DECL void sdtx_font(int font_index); @@ -589,39 +762,38 @@ inline sdtx_context sdtx_make_context(const sdtx_context_desc_t& desc) { return #endif #endif /* SOKOL_DEBUGTEXT_INCLUDED */ -/*-- IMPLEMENTATION ----------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_DEBUGTEXT_IMPL #define SOKOL_DEBUGTEXT_IMPL_INCLUDED (1) +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sdtx_desc_t.allocator to override memory allocation functions" +#endif + #include // memset #include // fmodf #include // for vsnprintf +#include // malloc/free #ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif + #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif @@ -637,9 +809,10 @@ inline sdtx_context sdtx_make_context(const sdtx_context_desc_t& desc) { return #define _sdtx_def(val, def) (((val) == 0) ? (def) : (val)) #define _SDTX_INIT_COOKIE (0xACBAABCA) +#define _SDTX_DEFAULT_MAX_COMMANDS (4096) #define _SDTX_DEFAULT_CONTEXT_POOL_SIZE (8) -#define _SDTX_DEFAULT_CHAR_BUF_SIZE (1<<12) -#define _SDTX_DEFAULT_PRINTF_BUF_SIZE (1<<12) +#define _SDTX_DEFAULT_CHAR_BUF_SIZE (4096) +#define _SDTX_DEFAULT_PRINTF_BUF_SIZE (4096) #define _SDTX_DEFAULT_CANVAS_WIDTH (640) #define _SDTX_DEFAULT_CANVAS_HEIGHT (480) #define _SDTX_DEFAULT_TAB_WIDTH (4) @@ -3307,7 +3480,7 @@ static const uint8_t _sdtx_vs_bytecode_wgpu[1648] = { 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, 0x27,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, }; -static const uint8_t _sdtx_vs_bytecode_wgpu[940] = { +static const uint8_t _sdtx_fs_bytecode_wgpu[940] = { 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1a,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, @@ -3375,6 +3548,13 @@ static const char* _sdtx_fs_src_dummy = ""; #error "Please define one of SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs typedef struct { uint32_t id; sg_resource_state state; @@ -3397,15 +3577,31 @@ typedef struct { uint32_t color; } _sdtx_vertex_t; +typedef struct { + int layer_id; + int first_vertex; + int num_vertices; +} _sdtx_command_t; + typedef struct { _sdtx_slot_t slot; sdtx_context_desc_t desc; - _sdtx_vertex_t* cur_vertex_ptr; - const _sdtx_vertex_t* max_vertex_ptr; - _sdtx_vertex_t* vertices; + uint32_t frame_id; + uint32_t update_frame_id; + struct { + int cap; + int next; + _sdtx_vertex_t* ptr; + } vertices; + struct { + int cap; + int next; + _sdtx_command_t* ptr; + } commands; sg_buffer vbuf; sg_pipeline pip; int cur_font; + int cur_layer_id; _sdtx_float2_t canvas_size; _sdtx_float2_t glyph_size; _sdtx_float2_t origin; @@ -3434,7 +3630,94 @@ typedef struct { } _sdtx_t; static _sdtx_t _sdtx; -/*=== CONTEXT POOL ===========================================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SDTX_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sdtx_log_messages[] = { + _SDTX_LOG_ITEMS +}; +#undef _SDTX_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SDTX_PANIC(code) _sdtx_log(SDTX_LOGITEM_ ##code, 0, __LINE__) +#define _SDTX_ERROR(code) _sdtx_log(SDTX_LOGITEM_ ##code, 1, __LINE__) +#define _SDTX_WARN(code) _sdtx_log(SDTX_LOGITEM_ ##code, 2, __LINE__) +#define _SDTX_INFO(code) _sdtx_log(SDTX_LOGITEM_ ##code, 3, __LINE__) + +static void _sdtx_log(sdtx_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sdtx.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sdtx_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sdtx.desc.logger.func("sdtx", log_level, log_item, message, line_nr, filename, _sdtx.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _sdtx_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _sdtx_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sdtx.desc.allocator.alloc) { + ptr = _sdtx.desc.allocator.alloc(size, _sdtx.desc.allocator.user_data); + } + else { + ptr = malloc(size); + } + if (0 == ptr) { + _SDTX_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _sdtx_malloc_clear(size_t size) { + void* ptr = _sdtx_malloc(size); + _sdtx_clear(ptr, size); + return ptr; +} + +static void _sdtx_free(void* ptr) { + if (_sdtx.desc.allocator.free) { + _sdtx.desc.allocator.free(ptr, _sdtx.desc.allocator.user_data); + } + else { + free(ptr); + } +} + +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ ██████ ██████ ██████ ██ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>context pool static void _sdtx_init_pool(_sdtx_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ @@ -3442,12 +3725,9 @@ static void _sdtx_init_pool(_sdtx_pool_t* pool, int num) { pool->queue_top = 0; /* generation counters indexable by pool slot index, slot 0 is reserved */ size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; - pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); - SOKOL_ASSERT(pool->gen_ctrs); - memset(pool->gen_ctrs, 0, gen_ctrs_size); + pool->gen_ctrs = (uint32_t*) _sdtx_malloc_clear(gen_ctrs_size); /* it's not a bug to only reserve 'num' here */ - pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); - SOKOL_ASSERT(pool->free_queue); + pool->free_queue = (int*) _sdtx_malloc_clear(sizeof(int) * (size_t)num); /* never allocate the zero-th pool item since the invalid id is 0 */ for (int i = pool->size-1; i >= 1; i--) { pool->free_queue[pool->queue_top++] = i; @@ -3457,10 +3737,10 @@ static void _sdtx_init_pool(_sdtx_pool_t* pool, int num) { static void _sdtx_discard_pool(_sdtx_pool_t* pool) { SOKOL_ASSERT(pool); SOKOL_ASSERT(pool->free_queue); - SOKOL_FREE(pool->free_queue); + _sdtx_free(pool->free_queue); pool->free_queue = 0; SOKOL_ASSERT(pool->gen_ctrs); - SOKOL_FREE(pool->gen_ctrs); + _sdtx_free(pool->gen_ctrs); pool->gen_ctrs = 0; pool->size = 0; pool->queue_top = 0; @@ -3501,14 +3781,12 @@ static void _sdtx_setup_context_pool(const sdtx_desc_t* desc) { SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SDTX_MAX_POOL_SIZE)); _sdtx_init_pool(&_sdtx.context_pool.pool, desc->context_pool_size); size_t pool_byte_size = sizeof(_sdtx_context_t) * (size_t)_sdtx.context_pool.pool.size; - _sdtx.context_pool.contexts = (_sdtx_context_t*) SOKOL_MALLOC(pool_byte_size); - SOKOL_ASSERT(_sdtx.context_pool.contexts); - memset(_sdtx.context_pool.contexts, 0, pool_byte_size); + _sdtx.context_pool.contexts = (_sdtx_context_t*) _sdtx_malloc_clear(pool_byte_size); } static void _sdtx_discard_context_pool(void) { SOKOL_ASSERT(_sdtx.context_pool.contexts); - SOKOL_FREE(_sdtx.context_pool.contexts); + _sdtx_free(_sdtx.context_pool.contexts); _sdtx.context_pool.contexts = 0; _sdtx_discard_pool(&_sdtx.context_pool.pool); } @@ -3582,6 +3860,7 @@ static sdtx_context _sdtx_alloc_context(void) { static sdtx_context_desc_t _sdtx_context_desc_defaults(const sdtx_context_desc_t* desc) { sdtx_context_desc_t res = *desc; + res.max_commands = _sdtx_def(res.max_commands, _SDTX_DEFAULT_MAX_COMMANDS); res.char_buf_size = _sdtx_def(res.char_buf_size, _SDTX_DEFAULT_CHAR_BUF_SIZE); res.canvas_width = _sdtx_def(res.canvas_width, _SDTX_DEFAULT_CANVAS_WIDTH); res.canvas_height = _sdtx_def(res.canvas_height, _SDTX_DEFAULT_CANVAS_HEIGHT); @@ -3593,6 +3872,30 @@ static sdtx_context_desc_t _sdtx_context_desc_defaults(const sdtx_context_desc_t return res; } +static void _sdtx_set_layer(_sdtx_context_t* ctx, int layer_id); +static void _sdtx_rewind(_sdtx_context_t* ctx) { + SOKOL_ASSERT(ctx); + ctx->frame_id++; + ctx->vertices.next = 0; + ctx->commands.next = 0; + _sdtx_set_layer(ctx, 0); + ctx->cur_font = 0; + ctx->pos.x = 0.0f; + ctx->pos.y = 0.0f; +} + +static void _sdtx_commit_listener(void* userdata) { + _sdtx_context_t* ctx = _sdtx_lookup_context((uint32_t)(uintptr_t)userdata); + if (ctx) { + _sdtx_rewind(ctx); + } +} + +static sg_commit_listener _sdtx_make_commit_listener(_sdtx_context_t* ctx) { + sg_commit_listener listener = { _sdtx_commit_listener, (void*)(uintptr_t)(ctx->slot.id) }; + return listener; +} + static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* in_desc) { sg_push_debug_group("sokol-debugtext"); @@ -3600,16 +3903,19 @@ static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* i _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); SOKOL_ASSERT(ctx); ctx->desc = _sdtx_context_desc_defaults(in_desc); + // NOTE: frame_id must be non-zero, so that updates trigger in first frame + ctx->frame_id = 1; - const int max_vertices = 6 * ctx->desc.char_buf_size; - const size_t vbuf_size = (size_t)max_vertices * sizeof(_sdtx_vertex_t); - ctx->vertices = (_sdtx_vertex_t*) SOKOL_MALLOC(vbuf_size); - SOKOL_ASSERT(ctx->vertices); - ctx->cur_vertex_ptr = ctx->vertices; - ctx->max_vertex_ptr = ctx->vertices + max_vertices; + ctx->vertices.cap = 6 * ctx->desc.char_buf_size; + const size_t vbuf_size = (size_t)ctx->vertices.cap * sizeof(_sdtx_vertex_t); + ctx->vertices.ptr = (_sdtx_vertex_t*) _sdtx_malloc(vbuf_size); + + ctx->commands.cap = ctx->desc.max_commands; + ctx->commands.ptr = (_sdtx_command_t*) _sdtx_malloc((size_t)ctx->commands.cap * sizeof(_sdtx_command_t)); + _sdtx_set_layer(ctx, 0); sg_buffer_desc vbuf_desc; - memset(&vbuf_desc, 0, sizeof(vbuf_desc)); + _sdtx_clear(&vbuf_desc, sizeof(vbuf_desc)); vbuf_desc.size = vbuf_size; vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; vbuf_desc.usage = SG_USAGE_STREAM; @@ -3618,7 +3924,7 @@ static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* i SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id); sg_pipeline_desc pip_desc; - memset(&pip_desc, 0, sizeof(pip_desc)); + _sdtx_clear(&pip_desc, sizeof(pip_desc)); pip_desc.layout.buffers[0].stride = sizeof(_sdtx_vertex_t); pip_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2; pip_desc.layout.attrs[1].format = SG_VERTEXFORMAT_USHORT2N; @@ -3644,23 +3950,33 @@ static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* i ctx->tab_width = (float) ctx->desc.tab_width; ctx->color = _SDTX_DEFAULT_COLOR; + if (!sg_add_commit_listener(_sdtx_make_commit_listener(ctx))) { + _SDTX_ERROR(ADD_COMMIT_LISTENER_FAILED); + } sg_pop_debug_group(); } static void _sdtx_destroy_context(sdtx_context ctx_id) { _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); if (ctx) { - if (ctx->vertices) { - SOKOL_FREE(ctx->vertices); - ctx->vertices = 0; - ctx->cur_vertex_ptr = 0; - ctx->max_vertex_ptr = 0; + if (ctx->vertices.ptr) { + _sdtx_free(ctx->vertices.ptr); + ctx->vertices.ptr = 0; + ctx->vertices.cap = 0; + ctx->vertices.next = 0; + } + if (ctx->commands.ptr) { + _sdtx_free(ctx->commands.ptr); + ctx->commands.ptr = 0; + ctx->commands.cap = 0; + ctx->commands.next = 0; } sg_push_debug_group("sokol_debugtext"); sg_destroy_buffer(ctx->vbuf); sg_destroy_pipeline(ctx->pip); + sg_remove_commit_listener(_sdtx_make_commit_listener(ctx)); sg_pop_debug_group(); - memset(ctx, 0, sizeof(*ctx)); + _sdtx_clear(ctx, sizeof(*ctx)); _sdtx_pool_free_index(&_sdtx.context_pool.pool, _sdtx_slot_index(ctx_id.id)); } } @@ -3669,6 +3985,14 @@ static bool _sdtx_is_default_context(sdtx_context ctx_id) { return ctx_id.id == SDTX_DEFAULT_CONTEXT.id; } +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc + /* unpack linear 8x8 bits-per-pixel font data into 2D byte-per-pixel texture data */ static void _sdtx_unpack_font(const sdtx_font_desc_t* font_desc, uint8_t* out_pixels) { SOKOL_ASSERT(font_desc->data.ptr); @@ -3690,14 +4014,13 @@ static void _sdtx_setup_common(void) { /* common printf formatting buffer */ _sdtx.fmt_buf_size = (uint32_t) _sdtx.desc.printf_buf_size + 1; - _sdtx.fmt_buf = (char*) SOKOL_MALLOC(_sdtx.fmt_buf_size); - SOKOL_ASSERT(_sdtx.fmt_buf); + _sdtx.fmt_buf = (char*) _sdtx_malloc_clear(_sdtx.fmt_buf_size); sg_push_debug_group("sokol-debugtext"); /* common shader for all contexts */ sg_shader_desc shd_desc; - memset(&shd_desc, 0, sizeof(shd_desc)); + _sdtx_clear(&shd_desc, sizeof(shd_desc)); shd_desc.label = "sokol-debugtext-shader"; shd_desc.attrs[0].name = "position"; shd_desc.attrs[1].name = "texcoord0"; @@ -3738,10 +4061,8 @@ static void _sdtx_setup_common(void) { shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_d3d11); shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_d3d11); #elif defined(SOKOL_WGPU) - shd_desc.vs.byte_code = _sdtx_vs_bytecode_wgpu; - shd_desc.vs.byte_code_size = sizeof(_sdtx_vs_bytecode_wgpu); - shd_desc.fs.byte_code = _sdtx_fs_bytecode_wgpu; - shd_desc.fs.byte_code_size = sizeof(_sdtx_fs_bytecode_wgpu); + shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_wgpu); + shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_wgpu); #else shd_desc.vs.source = _sdtx_vs_src_dummy; shd_desc.fs.source = _sdtx_fs_src_dummy; @@ -3751,7 +4072,7 @@ static void _sdtx_setup_common(void) { /* unpack font data */ memset(_sdtx.font_pixels, 0xFF, sizeof(_sdtx.font_pixels)); - const int unpacked_font_size = 256 * 8 * 8; + const int unpacked_font_size = (int) (sizeof(_sdtx.font_pixels) / SDTX_MAX_FONTS); for (int i = 0; i < SDTX_MAX_FONTS; i++) { if (_sdtx.desc.fonts[i].data.ptr) { _sdtx_unpack_font(&_sdtx.desc.fonts[i], &_sdtx.font_pixels[i * unpacked_font_size]); @@ -3760,7 +4081,7 @@ static void _sdtx_setup_common(void) { /* create font texture */ sg_image_desc img_desc; - memset(&img_desc, 0, sizeof(img_desc)); + _sdtx_clear(&img_desc, sizeof(img_desc)); img_desc.width = 256 * 8; img_desc.height = SDTX_MAX_FONTS * 8; img_desc.pixel_format = SG_PIXELFORMAT_R8; @@ -3769,6 +4090,7 @@ static void _sdtx_setup_common(void) { img_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; img_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; img_desc.data.subimage[0][0] = SG_RANGE(_sdtx.font_pixels); + img_desc.label = "sdtx-font-texture"; _sdtx.font_img = sg_make_image(&img_desc); SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_img.id); @@ -3780,23 +4102,23 @@ static void _sdtx_discard_common(void) { sg_destroy_image(_sdtx.font_img); sg_destroy_shader(_sdtx.shader); if (_sdtx.fmt_buf) { - SOKOL_FREE(_sdtx.fmt_buf); + _sdtx_free(_sdtx.fmt_buf); _sdtx.fmt_buf = 0; } sg_pop_debug_group(); } -static inline uint32_t _sdtx_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { +static uint32_t _sdtx_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r); } -static inline float _sdtx_clamp(float v, float lo, float hi) { +static float _sdtx_clamp(float v, float lo, float hi) { if (v < lo) return lo; else if (v > hi) return hi; else return v; } -static inline uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) { +static uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) { uint8_t r_u8 = (uint8_t) (_sdtx_clamp(r, 0.0f, 1.0f) * 255.0f); uint8_t g_u8 = (uint8_t) (_sdtx_clamp(g, 0.0f, 1.0f) * 255.0f); uint8_t b_u8 = (uint8_t) (_sdtx_clamp(b, 0.0f, 1.0f) * 255.0f); @@ -3804,7 +4126,7 @@ static inline uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) { return _sdtx_pack_rgbab(r_u8, g_u8, b_u8, a_u8); } -static inline void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) { +static void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) { switch (c) { case '\r': ctx->pos.x = 0.0f; @@ -3822,31 +4144,88 @@ static inline void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) { } } -static inline void _sdtx_draw_char(_sdtx_context_t* ctx, uint8_t c) { - if ((ctx->cur_vertex_ptr + 6) <= ctx->max_vertex_ptr) { +static _sdtx_vertex_t* _sdtx_next_vertex(_sdtx_context_t* ctx) { + if ((ctx->vertices.next + 6) <= ctx->vertices.cap) { + _sdtx_vertex_t* vx = &ctx->vertices.ptr[ctx->vertices.next]; + ctx->vertices.next += 6; + return vx; + } + else { + return 0; + } +} + +static _sdtx_command_t* _sdtx_cur_command(_sdtx_context_t* ctx) { + if (ctx->commands.next > 0) { + return &ctx->commands.ptr[ctx->commands.next - 1]; + } + else { + return 0; + } +} + +static _sdtx_command_t* _sdtx_next_command(_sdtx_context_t* ctx) { + if (ctx->commands.next < ctx->commands.cap) { + return &ctx->commands.ptr[ctx->commands.next++]; + } + else { + _SDTX_ERROR(COMMAND_BUFFER_FULL); + return 0; + } +} + +static void _sdtx_set_layer(_sdtx_context_t* ctx, int layer_id) { + ctx->cur_layer_id = layer_id; + _sdtx_command_t* cur_cmd = _sdtx_cur_command(ctx); + if (cur_cmd) { + if ((cur_cmd->num_vertices == 0) || (cur_cmd->layer_id == layer_id)) { + // no vertices recorded in current draw command, or layer hasn't changed, can just reuse this + cur_cmd->layer_id = layer_id; + } + else { + // layer has changed, need to start a new draw command + _sdtx_command_t* next_cmd = _sdtx_next_command(ctx); + if (next_cmd) { + next_cmd->layer_id = layer_id; + next_cmd->first_vertex = cur_cmd->first_vertex + cur_cmd->num_vertices; + next_cmd->num_vertices = 0; + } + } + } + else { + // first draw command in frame + _sdtx_command_t* next_cmd = _sdtx_next_command(ctx); + if (next_cmd) { + next_cmd->layer_id = layer_id; + next_cmd->first_vertex = 0; + next_cmd->num_vertices = 0; + } + } +} + +static void _sdtx_render_char(_sdtx_context_t* ctx, uint8_t c) { + _sdtx_vertex_t* vx = _sdtx_next_vertex(ctx); + _sdtx_command_t* cmd = _sdtx_cur_command(ctx); + if (vx && cmd) { + // update vertex count in current draw command + cmd->num_vertices += 6; + const float x0 = (ctx->origin.x + ctx->pos.x) * ctx->glyph_size.x; const float y0 = (ctx->origin.y + ctx->pos.y) * ctx->glyph_size.y; const float x1 = x0 + ctx->glyph_size.x; const float y1 = y0 + ctx->glyph_size.y; // glyph width and heigth in font texture space + // NOTE: the '+1' and '-2' fixes texture bleeding into the neighboring font texture cell const uint16_t uvw = 0x10000 / 0x100; const uint16_t uvh = 0x10000 / SDTX_MAX_FONTS; - const uint16_t u0 = ((uint16_t)c) * uvw; - const uint16_t v0 = ((uint16_t)ctx->cur_font) * uvh; - uint16_t u1 = u0 + uvw; - uint16_t v1 = v0 + uvh; - if (u1 == 0x0000) { - u1 = 0xFFFF; - } - if (v1 == 0x0000) { - v1 = 0xFFFF; - } + const uint16_t u0 = (((uint16_t)c) * uvw) + 1; + const uint16_t v0 = (((uint16_t)ctx->cur_font) * uvh) + 1; + uint16_t u1 = (u0 + uvw) - 2; + uint16_t v1 = (v0 + uvh) - 2; const uint32_t color = ctx->color; // write 6 vertices - _sdtx_vertex_t* vx = ctx->cur_vertex_ptr; - vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++; vx->x=x1; vx->y=y0; vx->u = u1; vx->v = v0; vx->color = color; vx++; vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++; @@ -3854,43 +4233,78 @@ static inline void _sdtx_draw_char(_sdtx_context_t* ctx, uint8_t c) { vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++; vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++; vx->x=x0; vx->y=y1; vx->u = u0; vx->v = v1; vx->color = color; vx++; - - ctx->cur_vertex_ptr = vx; } ctx->pos.x += 1.0f; } -static inline void _sdtx_put_char(_sdtx_context_t* ctx, char c) { +static void _sdtx_put_char(_sdtx_context_t* ctx, char c) { uint8_t c_u8 = (uint8_t)c; if (c_u8 <= 32) { _sdtx_ctrl_char(ctx, c_u8); } else { - _sdtx_draw_char(ctx, c_u8); + _sdtx_render_char(ctx, c_u8); } } -static sdtx_desc_t _sdtx_desc_defaults(const sdtx_desc_t* in_desc) { - sdtx_desc_t desc = *in_desc; - desc.context_pool_size = _sdtx_def(desc.context_pool_size, _SDTX_DEFAULT_CONTEXT_POOL_SIZE); - desc.printf_buf_size = _sdtx_def(desc.printf_buf_size, _SDTX_DEFAULT_PRINTF_BUF_SIZE); +SOKOL_API_IMPL void _sdtx_draw_layer(_sdtx_context_t* ctx, int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(ctx); + if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-debugtext"); + + if (ctx->update_frame_id != ctx->frame_id) { + ctx->update_frame_id = ctx->frame_id; + const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sdtx_vertex_t) }; + sg_update_buffer(ctx->vbuf, &range); + } + + sg_apply_pipeline(ctx->pip); + sg_bindings bindings; + _sdtx_clear(&bindings, sizeof(bindings)); + bindings.vertex_buffers[0] = ctx->vbuf; + bindings.fs_images[0] = _sdtx.font_img; + sg_apply_bindings(&bindings); + for (int cmd_index = 0; cmd_index < ctx->commands.next; cmd_index++) { + const _sdtx_command_t* cmd = &ctx->commands.ptr[cmd_index]; + if (cmd->layer_id != layer_id) { + continue; + } + SOKOL_ASSERT((cmd->num_vertices % 6) == 0); + sg_draw(cmd->first_vertex, cmd->num_vertices, 1); + } + sg_pop_debug_group(); + } +} + + +static sdtx_desc_t _sdtx_desc_defaults(const sdtx_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + sdtx_desc_t res = *desc; + 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); for (int i = 0; i < SDTX_MAX_FONTS; i++) { - if (desc.fonts[i].data.ptr) { - desc.fonts[i].last_char = _sdtx_def(desc.fonts[i].last_char, 255); + if (res.fonts[i].data.ptr) { + res.fonts[i].last_char = _sdtx_def(res.fonts[i].last_char, 255); } } - desc.context = _sdtx_context_desc_defaults(&desc.context); - SOKOL_ASSERT(desc.context_pool_size > 0); - SOKOL_ASSERT(desc.printf_buf_size > 0); - SOKOL_ASSERT(desc.context.char_buf_size > 0); - return desc; + res.context = _sdtx_context_desc_defaults(&res.context); + SOKOL_ASSERT(res.context_pool_size > 0); + SOKOL_ASSERT(res.printf_buf_size > 0); + SOKOL_ASSERT(res.context.char_buf_size > 0); + return res; } -/*=== PUBLIC API FUNCTIONS ===================================================*/ - +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void sdtx_setup(const sdtx_desc_t* desc) { SOKOL_ASSERT(desc); - memset(&_sdtx, 0, sizeof(_sdtx)); + _sdtx_clear(&_sdtx, sizeof(_sdtx)); _sdtx.init_cookie = _SDTX_INIT_COOKIE; _sdtx.desc = _sdtx_desc_defaults(desc); _sdtx_setup_context_pool(&_sdtx.desc); @@ -3949,7 +4363,7 @@ SOKOL_API_IMPL sdtx_context sdtx_make_context(const sdtx_context_desc_t* desc) { _sdtx_init_context(ctx_id, desc); } else { - SOKOL_LOG("sokol_debugtext.h: context pool exhausted!"); + _SDTX_ERROR(CONTEXT_POOL_EXHAUSTED); } return ctx_id; } @@ -3957,7 +4371,7 @@ SOKOL_API_IMPL sdtx_context sdtx_make_context(const sdtx_context_desc_t* desc) { SOKOL_API_IMPL void sdtx_destroy_context(sdtx_context ctx_id) { SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); if (_sdtx_is_default_context(ctx_id)) { - SOKOL_LOG("sokol_debugtext.h: cannot destroy default context"); + _SDTX_ERROR(CANNOT_DESTROY_DEFAULT_CONTEXT); return; } _sdtx_destroy_context(ctx_id); @@ -3987,6 +4401,14 @@ SOKOL_API_IMPL sdtx_context sdtx_default_context(void) { return SDTX_DEFAULT_CONTEXT; } +SOKOL_API_IMPL void sdtx_layer(int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_set_layer(ctx, layer_id); + } +} + SOKOL_API_IMPL void sdtx_font(int font_index) { SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); SOKOL_ASSERT((font_index >= 0) && (font_index < SDTX_MAX_FONTS)); @@ -4189,27 +4611,31 @@ SOKOL_API_IMPL void sdtx_draw(void) { SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); _sdtx_context_t* ctx = _sdtx.cur_ctx; if (ctx) { - const int num_verts = (int) (ctx->cur_vertex_ptr - ctx->vertices); - if (num_verts > 0) { - SOKOL_ASSERT((num_verts % 6) == 0); - sg_push_debug_group("sokol-debugtext"); - const sg_range range = { ctx->vertices, (size_t)num_verts * sizeof(_sdtx_vertex_t) }; - int vbuf_offset = sg_append_buffer(ctx->vbuf, &range); - sg_apply_pipeline(ctx->pip); - sg_bindings bindings; - memset(&bindings, 0, sizeof(bindings)); - bindings.vertex_buffers[0] = ctx->vbuf; - bindings.vertex_buffer_offsets[0] = vbuf_offset; - bindings.fs_images[0] = _sdtx.font_img; - sg_apply_bindings(&bindings); - sg_draw(0, num_verts, 1); - sg_pop_debug_group(); - } - ctx->cur_vertex_ptr = ctx->vertices; - ctx->cur_font = 0; - ctx->pos.x = 0.0f; - ctx->pos.y = 0.0f; + _sdtx_draw_layer(ctx, 0); } } +SOKOL_API_IMPL void sdtx_context_draw(sdtx_context ctx_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + if (ctx) { + _sdtx_draw_layer(ctx, 0); + } +} + +SOKOL_API_IMPL void sdtx_draw_layer(int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_draw_layer(ctx, layer_id); + } +} + +SOKOL_API_IMPL void sdtx_context_draw_layer(sdtx_context ctx_id, int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + if (ctx) { + _sdtx_draw_layer(ctx, layer_id); + } +} #endif /* SOKOL_DEBUGTEXT_IMPL */ diff --git a/3rdparty/sokol/util/sokol_fontstash.h b/3rdparty/sokol/util/sokol_fontstash.h index 8bf9089..049d5e1 100644 --- a/3rdparty/sokol/util/sokol_fontstash.h +++ b/3rdparty/sokol/util/sokol_fontstash.h @@ -28,12 +28,9 @@ ...optionally provide the following macros to override defaults: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_FONTSTASH_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_FONTSTASH_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) Include the following headers before including sokol_fontstash.h: @@ -55,7 +52,10 @@ --- Create at least one fontstash context with sfons_create() (this replaces glfonsCreate() from fontstash.h's example GL renderer: - FONScontext* ctx = sfons_create(atlas_width, atlas_height, FONS_ZERO_TOPLEFT); + FONScontext* ctx = sfons_create(&(sfons_desc_t){ + .width = atlas_width, + .height = atlas_height, + }); Each FONScontext manages one font atlas texture which can hold rasterized glyphs for multiple fonts. @@ -88,7 +88,7 @@ --- finally on application shutdown, call: - sfons_shutdown() + sfons_destroy(FONScontext* ctx) before sgl_shutdown() and sg_shutdown() @@ -96,7 +96,7 @@ WHAT HAPPENS UNDER THE HOOD: ============================ - sfons_create(): + FONScontext* sfons_create(const sfons_desc_t* desc) - creates a sokol-gfx shader compatible with sokol-gl - creates an sgl_pipeline object with alpha-blending using this shader @@ -122,14 +122,43 @@ all calls to fonsDrawText() will be merged into a single draw call as long as all calls use the same FONScontext - sfons_flush(): + sfons_flush(FONScontext* ctx): - this will call sg_update_image() on the font atlas texture if fontstash.h has added any rasterized glyphs since the last frame - sfons_shutdown(): + sfons_destroy(FONScontext* ctx): - destroy the font atlas texture, sgl_pipeline and sg_shader objects + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + FONScontext* fons_context = sfons_create(&(sfons_desc_t){ + ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. Please + note that this doesn't affect any memory allocation performed + in fontstash.h (unfortunately those are hardwired to malloc/free). + LICENSE ======= zlib/libpng license @@ -158,6 +187,7 @@ #define SOKOL_FONTSTASH_INCLUDED (1) #include #include +#include // size_t #if !defined(SOKOL_GFX_INCLUDED) #error "Please include sokol_gfx.h before sokol_fontstash.h" @@ -179,7 +209,30 @@ extern "C" { #endif -SOKOL_FONTSTASH_API_DECL FONScontext* sfons_create(int width, int height, int flags); +/* + sfonst_allocator_t + + 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 + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). + + NOTE that this does not affect memory allocation calls inside + fontstash.h +*/ +typedef struct sfons_allocator_t { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sfons_allocator_t; + +typedef struct sfons_desc_t { + int width; // initial width of font atlas texture (default: 512, must be power of 2) + int height; // initial height of font atlas texture (default: 512, must be power of 2) + sfons_allocator_t allocator; // optional memory allocation overrides +} sfons_desc_t; + +SOKOL_FONTSTASH_API_DECL FONScontext* sfons_create(const sfons_desc_t* desc); SOKOL_FONTSTASH_API_DECL void sfons_destroy(FONScontext* ctx); SOKOL_FONTSTASH_API_DECL void sfons_flush(FONScontext* ctx); SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); @@ -192,7 +245,13 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui /*-- IMPLEMENTATION ----------------------------------------------------------*/ #ifdef SOKOL_FONTSTASH_IMPL #define SOKOL_FONTSTASH_IMPL_INCLUDED (1) -#include /* memset, memcpy */ + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfons_desc_t.allocator to override memory allocation functions" +#endif + +#include // memset, memcpy +#include // malloc, free #if !defined(SOKOL_GL_INCLUDED) #error "Please include sokol_gl.h before sokol_fontstash.h" @@ -206,26 +265,13 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif @@ -250,10 +296,12 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui in vec4 position; in vec2 texcoord0; in vec4 color0; + in float psize; out vec4 uv; out vec4 color; void main() { gl_Position = mvp * position; + gl_PointSize = psize; uv = tm * vec4(texcoord0, 0.0, 1.0); color = color0; } @@ -271,33 +319,37 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui @program sfontstash vs fs */ -static const char _sfons_vs_source_glsl330[415] = { +static const char _sfons_vs_source_glsl330[478] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, - 0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, - 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, - 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, - 0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f, - 0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, - 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, - 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, - 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, - 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, - 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, - 0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74, - 0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20, - 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, - 0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c, - 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, - 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x70,0x73, + 0x69,0x7a,0x65,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76, + 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69, + 0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a, + 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, + 0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, + 0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, + 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34, + 0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; static const char _sfons_fs_source_glsl330[195] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, @@ -315,31 +367,34 @@ static const char _sfons_fs_source_glsl330[195] = { 0x0a,0x0a,0x00, }; #elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) -static const char _sfons_vs_source_glsl100[381] = { +static const char _sfons_vs_source_glsl100[430] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75, 0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x75, - 0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63, - 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x76,0x61,0x72, - 0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, - 0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, - 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, - 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, - 0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, - 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74, - 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, - 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, - 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67, + 0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62, + 0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, + 0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a, + 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, + 0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, + 0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, + 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34, + 0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; static const char _sfons_fs_source_glsl100[233] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x70,0x72,0x65, @@ -359,241 +414,245 @@ static const char _sfons_fs_source_glsl100[233] = { 0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_METAL) -static const uint8_t _sfons_vs_bytecode_metal_macos[3360] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sfons_vs_bytecode_metal_macos[3417] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x59,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x10,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xec,0xec,0xa8,0x23,0x83,0x8d, - 0xc7,0xcf,0x6e,0x67,0x94,0x95,0x54,0x94,0xeb,0x98,0x15,0x2a,0x2c,0x54,0xa0,0x4b, - 0x24,0x6f,0x5e,0xed,0x6e,0x76,0x45,0xb7,0x5e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x0a,0x16,0x9b,0x1c,0x17,0xd6,0x38, + 0xd5,0x49,0x10,0x98,0x04,0x66,0x15,0xf9,0x7e,0x49,0x87,0x6c,0x57,0x88,0x31,0x1d, + 0xab,0xb6,0x09,0x92,0x56,0xe2,0x6f,0x4d,0x48,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, - 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x40,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, - 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, - 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xf0,0x0b,0x00,0x00, - 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf9,0x02,0x00,0x00, - 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, - 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, - 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, - 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, - 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, - 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, - 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, - 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, - 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, - 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, - 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, - 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, - 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, - 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, - 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, - 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, - 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, - 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, - 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, - 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, - 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, - 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, - 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, - 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, - 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, - 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, - 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, - 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, - 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, - 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, - 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, - 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, - 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, - 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, - 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, - 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00, - 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, - 0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, - 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, - 0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80, - 0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90,0x04,0x61,0x26,0x6a, - 0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1,0x1c,0xe8,0x21,0x1c, - 0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d,0xf0,0x01,0x05,0xe4, - 0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60,0x24,0x24,0x94,0x32, - 0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x72,0x60, - 0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00, - 0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, - 0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03, - 0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07, - 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71, - 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80, - 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, - 0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60, - 0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e, - 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71, - 0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40, - 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07, - 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a, - 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60, - 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07, - 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78, - 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10, - 0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, - 0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72, - 0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20, - 0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07, - 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d, - 0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00, - 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a, - 0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20, - 0xd4,0x46,0x00,0x88,0x8d,0x35,0x28,0x0f,0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00, - 0x19,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, - 0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93, - 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e, - 0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d, - 0x24,0x07,0x46,0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, - 0x25,0x07,0x46,0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, - 0x58,0x8e,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, - 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, - 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, - 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, - 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, - 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, - 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, - 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26, - 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, - 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, - 0x50,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, - 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, - 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72, - 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, - 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, - 0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, + 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x24,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x06,0x03,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xf6,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80, + 0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36, + 0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0, + 0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1, + 0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e, + 0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78, + 0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08, + 0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda, + 0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81, + 0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8, + 0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87, + 0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76, + 0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30, + 0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01, + 0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08, + 0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36, + 0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d, + 0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca, + 0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90, + 0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07, + 0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61, + 0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e, + 0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4, + 0xa1,0x1c,0x00,0x3c,0x00,0x08,0x7a,0x08,0x07,0x79,0x38,0x87,0x72,0xa0,0x87,0x36, + 0x30,0x87,0x72,0x08,0x07,0x7a,0xa8,0x07,0x79,0x28,0x87,0x79,0x00,0xda,0xc0,0x1c, + 0xe0,0x21,0x0e,0xec,0x00,0x20,0xea,0xc1,0x1d,0xe6,0x21,0x1c,0xcc,0xa1,0x1c,0xda, + 0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a, + 0x28,0x07,0x80,0xa8,0x87,0x79,0x28,0x87,0x36,0x98,0x87,0x77,0x30,0x07,0x7a,0x68, + 0x03,0x73,0x60,0x87,0x77,0x08,0x07,0x7a,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca, + 0x01,0xd8,0x60,0x08,0x05,0xb0,0x00,0x15,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00, + 0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x24,0x00,0x00,0x00,0x32,0x22,0x48, + 0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90, + 0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x40,0x33,0x00,0xc3,0x08,0x04, + 0x70,0x97,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x93,0xff,0x00, + 0x82,0x42,0x0c,0x98,0x08,0x21,0x80,0x61,0x04,0x01,0x48,0x82,0x30,0x13,0x35,0x0f, + 0xf4,0x20,0x0f,0xf5,0x30,0x0e,0xf4,0xe0,0x06,0xed,0x50,0x0e,0xf4,0x10,0x0e,0xec, + 0xa0,0x07,0x7a,0xd0,0x0e,0xe1,0x40,0x0f,0xf2,0x90,0x0e,0xf8,0x80,0x02,0x72,0x90, + 0x34,0x45,0x94,0x30,0xf9,0x95,0xf4,0x3f,0x40,0x04,0x30,0x12,0x12,0x4a,0x19,0x44, + 0x30,0x84,0x62,0x88,0x30,0x02,0x38,0x84,0x06,0x02,0xe6,0x08,0xc0,0x60,0x8e,0x00, + 0x14,0x06,0x11,0x02,0x61,0x18,0x81,0x58,0x46,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0xf0,0x1e,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x78,0x00,0x07,0x7a, + 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07, + 0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d, + 0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50, + 0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a, + 0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0x60, + 0x0e,0x78,0x00,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49, + 0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x09,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x5a,0x25,0x30,0x02,0x50,0x80,0x01,0x85,0x50,0x04,0x65,0x50,0x80,0x02,0x05,0x51, + 0x20,0xc4,0x46,0x00,0x00,0x79,0x18,0x00,0x00,0x15,0x01,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x95,0xbb,0x31,0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2,0x21,0xc6,0x12,0x28, + 0xc0,0x42,0x70,0x0d,0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98,0xac,0x9a,0x40,0xec, + 0xca,0xe4,0xe6,0xd2,0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62,0x40,0x50,0xda,0xca, + 0xe8,0xc2,0xd8,0xcc,0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c,0x68,0x72,0x52,0x86, + 0x08,0x8a,0x30,0xc4,0x58,0x82,0x05,0x59,0x04,0x16,0x4d,0x65,0x74,0x61,0x6c,0x43, + 0x10,0xa5,0x58,0x82,0x25,0x58,0x04,0x6e,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, + 0x69,0x6c,0x65,0x2e,0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f,0x64,0x69,0x73,0x61, + 0x62,0x6c,0x65,0x43,0x04,0xe5,0x20,0x17,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97, + 0xc6,0x56,0xe6,0x62,0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87,0xf6,0x55,0xe6,0x16, + 0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x96,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b, + 0x5c,0x1a,0x5b,0x99,0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d,0x99,0x59,0x99,0xdc, + 0x97,0x59,0x19,0xdd,0x18,0xda,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41, + 0x59,0x18,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e, + 0x61,0x74,0x69,0x76,0x65,0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x04,0xa5,0x61,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, 0x57,0x86,0xf7,0xf5,0x56,0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85, 0xed,0x6d,0xcc,0x0d,0x26,0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, 0x1e,0x5c,0xd9,0x97,0x5b,0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32, 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86, - 0x30,0x4a,0xa5,0x58,0xca,0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50, - 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a, - 0xe1,0x94,0x4b,0xc1,0x94,0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, + 0x30,0xca,0xa3,0x40,0x4a,0xa4,0x48,0xca,0xa4,0x50,0x5c,0xea,0xe6,0xca,0xe4,0x50, + 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x47, + 0xb1,0x94,0x48,0x91,0x94,0x49,0xb9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, 0xd1,0xa5,0xbd,0xb9,0x71,0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73, - 0x1b,0xa2,0x28,0x9f,0x72,0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53, - 0xc2,0x80,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, - 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, - 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, - 0x28,0x38,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29, - 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42, - 0x06,0x8b,0xb0,0x04,0x4a,0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca, - 0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4, - 0xc8,0x60,0x88,0xec,0x64,0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30, - 0xa1,0x2b,0xc3,0x1b,0x7b,0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a, - 0x19,0x2c,0xc1,0x12,0x28,0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce, - 0xca,0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60, - 0x88,0xec,0x68,0xbe,0xcc,0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96, - 0x41,0x19,0x03,0x85,0x0c,0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5, - 0x0d,0xa8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93, - 0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26, - 0xe7,0x22,0x57,0x16,0x46,0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c, - 0xee,0x8b,0x2e,0x0f,0xae,0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18, - 0x1d,0x0d,0x1e,0x0d,0x87,0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88, - 0x45,0x50,0xe6,0x40,0xa1,0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46, - 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a, - 0x65,0xbc,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2, - 0xd8,0xd2,0xce,0xdc,0xbe,0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9, - 0x85,0xb5,0xcd,0x71,0xf8,0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07, - 0x4b,0xa1,0x90,0xc1,0x22,0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1, - 0x52,0x28,0x78,0xb0,0x24,0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce, - 0x40,0x59,0x03,0xc5,0x0d,0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0, - 0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3, - 0x33,0x95,0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34, - 0x44,0x50,0xfe,0x60,0x88,0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41, - 0x09,0x85,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, - 0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14, - 0x21,0x18,0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e, - 0xee,0x40,0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5, - 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20, - 0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b, - 0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1, - 0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0, - 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07, - 0x92,0xa8,0x11,0x4a,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43, - 0x39,0xe0,0xc3,0x94,0xa0,0x0f,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00, - 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, - 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, - 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, - 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, - 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, - 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, - 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, - 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, - 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, - 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, - 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, - 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, - 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, - 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, - 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, - 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, - 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, - 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, - 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, - 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, - 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, - 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, - 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, - 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, - 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, - 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, - 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, - 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, - 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, - 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22, - 0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11, - 0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84, - 0x61,0x10,0x05,0x65,0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7, - 0x2d,0x14,0x94,0x41,0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, - 0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a, - 0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33, - 0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a, - 0xca,0x20,0x83,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0, - 0x0d,0x3c,0x0a,0xca,0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8, - 0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3, - 0x0d,0x61,0x10,0x00,0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10, - 0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11, - 0x88,0xc2,0x96,0x41,0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51, - 0xd8,0x32,0x60,0x81,0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1b,0xa2,0x28,0x99,0x12,0x29,0x92,0x32,0x29,0x1a,0x9d,0xb0,0x34,0x39,0x17,0xb8, + 0xb7,0x34,0x37,0xba,0xaf,0xb9,0x34,0xbd,0x32,0x16,0x66,0x6c,0x6f,0x61,0x74,0x64, + 0xce,0xd8,0xbe,0xa0,0xde,0xd2,0xdc,0xe8,0xa6,0xd2,0xf4,0xca,0x86,0x28,0x0a,0xa7, + 0x44,0x4a,0xa7,0x4c,0x8a,0x37,0x04,0x51,0x2a,0x05,0x53,0x36,0xe5,0x23,0x14,0x96, + 0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0x95,0xe6,0x06,0x57,0x47,0x47,0x29, + 0x2c,0x4d,0xce,0x85,0xed,0x6d,0x2c,0x8c,0x2e,0xed,0xcd,0xed,0x2b,0xcd,0x8d,0xac, + 0x0c,0x8f,0xd9,0x59,0x99,0x5b,0x99,0x5c,0x18,0x5d,0x19,0x19,0x0a,0x0e,0xdc,0xdb, + 0x5c,0x1a,0x5d,0xda,0x9b,0x1b,0x91,0x1d,0xcd,0x97,0x59,0x0a,0x11,0xb8,0xb7,0xb9, + 0x34,0xba,0xb4,0x37,0xb7,0x21,0xd4,0x22,0x28,0x61,0xa0,0x88,0xc1,0x22,0x2c,0x81, + 0x32,0x06,0x4a,0xa4,0x48,0xca,0xa4,0x90,0x01,0xb5,0xb3,0x32,0xb7,0x32,0xb9,0x30, + 0xba,0x32,0x32,0x94,0x1c,0xba,0x32,0xbc,0xb1,0xb7,0x37,0x39,0x32,0x18,0x22,0x3b, + 0x99,0x2f,0xb3,0x14,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x32,0x4c,0xe8,0xca,0xf0,0xc6, + 0xde,0xde,0xe4,0xc8,0x60,0x86,0x50,0x4b,0xa0,0x84,0x81,0x22,0x06,0x4b,0xb0,0x04, + 0x8a,0x19,0x28,0x91,0x72,0x06,0xca,0xa4,0xa0,0x01,0xaf,0xb3,0x32,0xb7,0x32,0xb9, + 0x30,0xba,0x32,0x32,0x14,0x9b,0xb1,0x37,0xb6,0x37,0x39,0x18,0x22,0x3b,0x9a,0x2f, + 0xb3,0x14,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0xa5,0x50,0xc2,0x40,0x11, + 0x83,0xa5,0x58,0x02,0x45,0x0d,0x94,0x48,0x91,0x94,0x49,0x59,0x03,0x4a,0x67,0x65, + 0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x35,0x70,0x73,0x69,0x7a,0x65,0x66,0x29, + 0x2c,0xe0,0xe6,0xd2,0xf4,0xca,0x86,0x50,0x8b,0xa1,0x84,0x81,0x22,0x06,0x8b,0xb1, + 0x04,0x4a,0x1b,0x28,0x91,0xd2,0x29,0x93,0xe2,0x06,0x54,0xc2,0xd2,0xe4,0x5c,0xc4, + 0xea,0xcc,0xcc,0xca,0xe4,0xf8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9, + 0x7d,0xcd,0xa5,0xe9,0x95,0x11,0x09,0x4b,0x93,0x73,0x91,0x2b,0x0b,0x23,0x23,0x15, + 0x96,0x26,0xe7,0x32,0x47,0x27,0x57,0x37,0x46,0xf7,0x45,0x97,0x07,0x57,0xf6,0x95, + 0xe6,0x66,0xf6,0x46,0xc4,0x8c,0xed,0x2d,0x8c,0x8e,0x06,0x8f,0x86,0x43,0x9b,0x1d, + 0x1c,0x05,0xba,0xb6,0x21,0xd4,0x22,0x2c,0xc3,0x22,0x28,0x74,0xa0,0xd4,0xc1,0x32, + 0x2c,0xc3,0x22,0x28,0x74,0xa0,0xd8,0x01,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3, + 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0xb9,0x34,0xbd,0x32,0x5e,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x69, + 0x7a,0x65,0x4c,0xec,0xe6,0xbe,0xe0,0xc2,0xe4,0xc2,0xda,0xe6,0x38,0x7c,0xc9,0xc4, + 0x0c,0x21,0x83,0x85,0x50,0xe0,0x40,0x89,0x83,0xe5,0x50,0xc4,0x60,0x11,0x96,0x40, + 0x91,0x03,0x65,0x0e,0x94,0x3b,0x50,0xf0,0x60,0x39,0x94,0x3c,0x58,0x12,0x25,0x52, + 0xf4,0x40,0x99,0x94,0x3d,0x18,0xa2,0x28,0x65,0xa0,0xa4,0x81,0xc2,0x06,0xca,0x1b, + 0x28,0x7c,0x30,0xc4,0x48,0x00,0x05,0x0c,0x94,0x3e,0xe0,0xf3,0xd6,0xe6,0x96,0x06, + 0xf7,0x46,0x57,0xe6,0x46,0x07,0x32,0x86,0x16,0x26,0xc7,0x67,0x2a,0xad,0x0d,0x8e, + 0xad,0x0c,0x64,0x68,0x65,0x05,0x84,0x4a,0x28,0x28,0x68,0x88,0xa0,0x80,0xc2,0x10, + 0x43,0xf9,0x03,0x25,0x14,0x18,0x65,0x88,0xa1,0x88,0x82,0x22,0x0a,0x8c,0x32,0x22, + 0x62,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x76,0x78,0x07,0x72,0xa8,0x07,0x76,0x28, + 0x07,0x37,0x30,0x07,0x76,0x08,0x87,0x73,0x98,0x87,0x29,0x41,0x30,0x42,0x61,0x07, + 0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x0c, + 0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78, + 0x07,0x77,0x98,0x12,0x10,0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07, + 0x77,0x38,0x87,0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79, + 0x48,0x87,0x77,0x70,0x87,0x29,0x81,0x31,0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18, + 0x87,0x77,0x68,0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87, + 0x74,0x78,0x07,0x77,0x98,0x87,0x29,0x04,0xa2,0x30,0xce,0x08,0x25,0x1c,0xd2,0x41, + 0x1e,0xdc,0xc0,0x1e,0xca,0x41,0x1e,0xe8,0xa1,0x1c,0xf0,0x61,0x4a,0xe0,0x07,0x00, + 0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00, + 0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00, + 0x00,0x3e,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00, + 0x00,0xe4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20, + 0x08,0x88,0x95,0x40,0x19,0x14,0x01,0xb9,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00, + 0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65,0x90,0x21,0x1a, + 0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70, + 0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30,0x70,0x28,0x28, + 0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0x99,0x67,0x06, + 0x66,0x40,0x51,0x50,0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6, + 0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09, + 0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca,0x20,0xc3,0x19, + 0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01, + 0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41, + 0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00, + 0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x18,0x85,0x2d,0x43,0x11,0x8c,0xc2,0x96, + 0x41,0x09,0x46,0x61,0xcb,0xf0,0x04,0xa3,0xb0,0x65,0xa0,0x82,0x51,0xd8,0x32,0x60, + 0xc1,0x28,0x6c,0x19,0xba,0x60,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const uint8_t _sfons_fs_bytecode_metal_macos[2941] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x7d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sfons_fs_bytecode_metal_macos[2893] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xa0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x70,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xfd,0x3a,0x06,0x83,0x09,0xe9,0xe7, - 0xd2,0xb5,0xf9,0x1e,0x86,0x99,0xb0,0x0c,0x8c,0xbe,0xe0,0xd3,0x2d,0x9b,0x8c,0xe9, - 0x2a,0x92,0xa3,0xeb,0x0b,0xf7,0x98,0x18,0x30,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xb6,0x8b,0xa9,0x7e,0x5a,0xee,0x89, + 0x6e,0x6c,0x22,0x2c,0x4b,0xad,0x03,0xda,0xe6,0xd7,0x2e,0x88,0x05,0x1f,0x44,0x92, + 0x88,0x08,0xa6,0x2c,0x57,0xb7,0x10,0x8a,0x6e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, - 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x84,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x9e,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x54,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x92,0x02,0x00,0x00,0x0b,0x82,0x20, 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, - 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0x00,0x51,0x18,0x00,0x00,0x92,0x00,0x00,0x00,0x1b,0xfa,0x25,0xf8,0xff,0xff,0xff, 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, @@ -621,104 +680,101 @@ static const uint8_t _sfons_fs_bytecode_metal_macos[2941] = { 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, - 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, - 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, - 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, - 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, - 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, - 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, - 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, - 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, - 0x00,0x89,0x20,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, - 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, - 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, - 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, - 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, - 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, - 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, - 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, - 0xb5,0x81,0x80,0x1c,0x58,0xc3,0x08,0xc4,0x32,0x02,0x00,0x00,0x00,0x13,0xb2,0x70, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa0,0x87,0x70,0x90,0x87, + 0x73,0x28,0x07,0x7a,0x68,0x03,0x73,0x28,0x87,0x70,0xa0,0x87,0x7a,0x90,0x87,0x72, + 0x98,0x07,0xa0,0x0d,0xcc,0x01,0x1e,0xe2,0xc0,0x0e,0x00,0xa2,0x1e,0xdc,0x61,0x1e, + 0xc2,0xc1,0x1c,0xca,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0, + 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x7a,0x98,0x87,0x72,0x68,0x83,0x79, + 0x78,0x07,0x73,0xa0,0x87,0x36,0x30,0x07,0x76,0x78,0x87,0x70,0xa0,0x07,0xc0,0x1c, + 0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x80,0x0d,0x86,0x30,0x00,0x0b,0x50,0x6d,0x30,0x06, + 0x02,0x58,0x80,0x6a,0x03,0x42,0xfc,0xff,0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54, + 0x1b,0x8c,0x22,0x00,0x16,0xa0,0xda,0x60,0x18,0x02,0xb0,0x00,0x15,0x00,0x00,0x00, + 0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44, + 0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48, + 0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90, + 0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04, + 0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2, + 0x51,0xd2,0x14,0x51,0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f, + 0x63,0x04,0xc0,0x20,0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc, + 0xb3,0x10,0xd1,0x3f,0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74, + 0x86,0x11,0x04,0x60,0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20, + 0x26,0x29,0xa6,0x00,0xb5,0x81,0x80,0x61,0x04,0x62,0x19,0x01,0x00,0x13,0xb2,0x70, 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, - 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x38,0x70,0x03,0x38,0xd8,0xf0,0x1e,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, - 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, - 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, - 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, - 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, - 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, - 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, - 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, - 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, - 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, - 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, - 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, - 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, - 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00, - 0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64, - 0x81,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, - 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50, - 0x10,0x65,0x40,0x70,0xac,0x41,0x79,0x08,0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00, - 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, - 0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, - 0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c, - 0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x24,0x07,0x46, - 0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x25,0x07,0x46, - 0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65, - 0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16, - 0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26, - 0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61, - 0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f, - 0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x61,0x19, - 0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5, - 0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5, - 0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6, - 0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56, - 0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x1e,0x92, - 0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d, - 0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda, - 0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f, - 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64, - 0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c, - 0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7, - 0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b, - 0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d, - 0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72, - 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x86,0xc7,0x7a,0xae, - 0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b, - 0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2, - 0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c, - 0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d, - 0x19,0x85,0x3a,0xbb,0x21,0xd2,0x32,0x3c,0xdc,0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73, - 0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52, - 0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x16,0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7, - 0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1, - 0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73, - 0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, - 0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d, - 0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10, - 0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x19,0x16,0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0, - 0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7, - 0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b, - 0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60,0x19,0x16,0xe1,0xb1,0x1e,0x36,0x78, - 0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c, - 0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e, - 0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3, - 0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39, - 0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8, - 0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43, - 0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39, - 0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11, - 0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43, - 0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14, - 0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e, - 0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00,0x79,0x18,0x00, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x78,0x00,0x07,0x7a, + 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07, + 0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d, + 0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50, + 0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a, + 0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0x60, + 0x0e,0x78,0x00,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41, + 0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00, + 0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, + 0x50,0x08,0x05,0x51,0x04,0x65,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xbb,0x00,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x95,0xbb,0x31,0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2, + 0x21,0xc6,0x22,0x3c,0xc0,0x42,0x70,0x0d,0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98, + 0xac,0x9a,0x40,0xec,0xca,0xe4,0xe6,0xd2,0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62, + 0x40,0x50,0xda,0xca,0xe8,0xc2,0xd8,0xcc,0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c, + 0x68,0x72,0x52,0x86,0x08,0x8f,0x30,0xc4,0x58,0x84,0xa5,0x58,0x06,0x16,0x4d,0x65, + 0x74,0x61,0x6c,0x43,0x90,0xa7,0x58,0x84,0x45,0x58,0x06,0x6e,0x61,0x69,0x72,0x2e, + 0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f, + 0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0xe7,0x20,0x17,0x96,0x26,0xe7,0x32, + 0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x62,0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87, + 0xf6,0x55,0xe6,0x16,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x96,0x41,0x58,0x9a,0x9c, + 0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d, + 0x99,0x59,0x99,0xdc,0x97,0x59,0x19,0xdd,0x18,0xda,0x17,0x59,0xda,0x5c,0x98,0x18, + 0x5b,0xd9,0x10,0xe1,0x59,0x18,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69, + 0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65, + 0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0xa7,0x61,0x14,0x96,0x26,0xe7, + 0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c, + 0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c, + 0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f, + 0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43, + 0x98,0xe7,0x59,0x86,0x07,0x7a,0xa2,0x47,0x7a,0xa6,0x21,0xc2,0x43,0x51,0x0a,0x4b, + 0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3, + 0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d, + 0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc, + 0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x32,0x3c,0xd6, + 0x73,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xa3,0x71,0xa9,0x9b,0x2b,0x93,0x43, + 0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x44,0x5a,0x84, + 0xc7,0x7a,0xb8,0x07,0x7b,0xb2,0x07,0x7a,0xa2,0x47,0x7a,0x3a,0x2e,0x61,0x69,0x72, + 0x2e,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x94,0xc2,0xd2,0xe4,0x5c,0xd8,0xde,0xc6, + 0xc2,0xe8,0xd2,0xde,0xdc,0xbe,0xd2,0xdc,0xc8,0xca,0xf0,0xa8,0x84,0xa5,0xc9,0xb9, + 0xcc,0x85,0xb5,0xc1,0xb1,0x95,0x11,0xa3,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x93,0x21, + 0xe3,0x31,0x63,0x7b,0x0b,0xa3,0x63,0x01,0x99,0x0b,0x6b,0x83,0x63,0x2b,0xf3,0xe1, + 0x40,0x57,0x86,0x37,0x84,0x5a,0x8c,0xe7,0x7b,0xc0,0x60,0x19,0x16,0xe1,0x09,0x83, + 0x07,0x7a,0xc4,0xe0,0x91,0x9e,0x31,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06, + 0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde, + 0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x32,0x78,0xc0,0x60,0x19,0x16,0xe1, + 0x81,0x1e,0x33,0x78,0xa4,0xe7,0x0c,0x86,0x20,0xcf,0xf6,0x78,0x0f,0x19,0x3c,0x68, + 0x30,0xc4,0x40,0x80,0xa7,0x7a,0xd2,0x60,0x44,0xc4,0x0e,0xec,0x60,0x0f,0xed,0xe0, + 0x06,0xed,0xf0,0x0e,0xe4,0x50,0x0f,0xec,0x50,0x0e,0x6e,0x60,0x0e,0xec,0x10,0x0e, + 0xe7,0x30,0x0f,0x53,0x82,0x60,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9, + 0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x18,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0, + 0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x20,0x46,0x50, + 0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7, + 0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02, + 0x63,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e, + 0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53, + 0x08,0x44,0x61,0x9c,0x11,0x4c,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xc8,0x43,0x38, + 0x9c,0x43,0x3b,0x94,0x83,0x3b,0xd0,0xc3,0x94,0x40,0x0d,0x00,0x00,0x79,0x18,0x00, 0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, 0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c, 0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d, @@ -750,247 +806,252 @@ static const uint8_t _sfons_fs_bytecode_metal_macos[2941] = { 0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45, 0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00, 0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, - 0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22,0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25, - 0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31,0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a, + 0x00,0x0b,0x00,0x00,0x00,0x04,0xc7,0x22,0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25, + 0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31,0x03,0x40,0x61,0x0e,0xc2,0xb2,0x2c,0x6a, 0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18,0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00, - 0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28,0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30, + 0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28,0x42,0x70,0x39,0x4d,0x80,0x2c,0xc9,0x30, 0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66,0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const uint8_t _sfons_vs_bytecode_metal_ios[3344] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x10,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sfons_vs_bytecode_metal_ios[3417] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x59,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x58,0xff,0x12,0x36,0x80,0x88,0xb5, - 0xf7,0x1c,0xc3,0xd5,0x31,0x57,0x18,0x26,0x26,0xf0,0x8c,0xd4,0x15,0xa2,0x55,0x8b, - 0xd5,0x3c,0x6b,0x11,0xbd,0x17,0x49,0x4a,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x53,0xd2,0x06,0xf6,0xae,0x63,0x48, + 0x79,0x19,0x71,0xbe,0x11,0x2a,0xc0,0x72,0x90,0x9c,0xd4,0x1c,0xf2,0xfa,0xd7,0x02, + 0x09,0x76,0xb9,0xee,0x24,0x42,0xd7,0xbd,0x99,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, - 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x40,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, - 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, - 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xec,0x0b,0x00,0x00, - 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf8,0x02,0x00,0x00, - 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, - 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, - 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, - 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, - 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, - 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, - 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, - 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, - 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, - 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, - 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, - 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, - 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, - 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, - 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, - 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, - 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, - 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, - 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, - 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, - 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, - 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, - 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, - 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, - 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, - 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, - 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, - 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, - 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, - 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, - 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, - 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, - 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, - 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, - 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, - 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54, - 0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00, - 0x89,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, - 0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c, - 0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94, - 0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90, - 0x04,0x61,0x26,0x6a,0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1, - 0x1c,0xe8,0x21,0x1c,0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d, - 0xf0,0x01,0x05,0xe4,0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60, - 0x24,0x24,0x94,0x32,0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e, - 0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08, - 0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70, - 0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80, - 0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07, - 0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9, - 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30, - 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07, - 0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72, - 0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07, - 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73, - 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, - 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07, - 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, - 0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, - 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07, - 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72, - 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20, - 0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, - 0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75, - 0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, - 0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07, - 0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00, - 0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, - 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, - 0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20,0xd4,0x46,0x00,0x88, - 0x8d,0x25,0x34,0x05,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x19,0x01,0x00,0x00, - 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, - 0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, - 0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84, - 0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46, - 0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07, - 0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8e,0x45,0x60, - 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0,0x16,0x96, - 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, - 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, - 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, - 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, - 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, - 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, - 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06, - 0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6, - 0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x1e,0x92,0x41,0x58, - 0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d, - 0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98, - 0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, - 0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f, - 0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, - 0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, + 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x20,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x05,0x03,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x8a,0x00,0x00,0x00,0x1b,0xf6,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80, + 0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36, + 0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0, + 0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1, + 0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e, + 0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78, + 0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08, + 0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda, + 0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81, + 0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8, + 0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87, + 0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76, + 0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30, + 0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01, + 0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08, + 0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36, + 0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d, + 0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca, + 0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90, + 0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07, + 0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61, + 0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e, + 0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4, + 0xa1,0x1c,0x00,0x3c,0x00,0x08,0x7a,0x08,0x07,0x79,0x38,0x87,0x72,0xa0,0x87,0x36, + 0x30,0x87,0x72,0x08,0x07,0x7a,0xa8,0x07,0x79,0x28,0x87,0x79,0x00,0xda,0xc0,0x1c, + 0xe0,0x21,0x0e,0xec,0x00,0x20,0xea,0xc1,0x1d,0xe6,0x21,0x1c,0xcc,0xa1,0x1c,0xda, + 0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a, + 0x28,0x07,0x80,0xa8,0x87,0x79,0x28,0x87,0x36,0x98,0x87,0x77,0x30,0x07,0x7a,0x68, + 0x03,0x73,0x60,0x87,0x77,0x08,0x07,0x7a,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca, + 0x01,0xd8,0x80,0x08,0x05,0x90,0x00,0x0b,0x50,0x01,0x00,0x00,0x00,0x49,0x18,0x00, + 0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x24,0x00,0x00, + 0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22, + 0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x40,0x33, + 0x00,0xc3,0x08,0x04,0x70,0x97,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef, + 0x50,0x93,0xff,0x00,0x82,0x42,0x0c,0x98,0x08,0x21,0x80,0x61,0x04,0x01,0x48,0x82, + 0x30,0x13,0x35,0x0f,0xf4,0x20,0x0f,0xf5,0x30,0x0e,0xf4,0xe0,0x06,0xed,0x50,0x0e, + 0xf4,0x10,0x0e,0xec,0xa0,0x07,0x7a,0xd0,0x0e,0xe1,0x40,0x0f,0xf2,0x90,0x0e,0xf8, + 0x80,0x02,0x72,0x90,0x34,0x45,0x94,0x30,0xf9,0x95,0xf4,0x3f,0x40,0x04,0x30,0x12, + 0x12,0x4a,0x19,0x44,0x30,0x84,0x62,0x88,0x30,0x02,0x38,0x84,0x06,0x02,0xe6,0x08, + 0xc0,0x60,0x8e,0x00,0x14,0x06,0x11,0x02,0x61,0x18,0x81,0x58,0x46,0x00,0x00,0x00, + 0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78, + 0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80, + 0x83,0x0d,0xef,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07, + 0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0, + 0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x80,0x07,0x70,0xa0,0x07,0x71,0x20,0x07, + 0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71, + 0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d, + 0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, + 0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07, + 0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xe6,0x80,0x07,0x70, + 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xee,0x80, + 0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00, + 0x00,0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x09,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, + 0x50,0x80,0x01,0x85,0x50,0x04,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xc4,0x46,0x00, + 0x00,0x79,0x18,0x00,0x00,0x15,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x95,0xbb,0x31, + 0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2,0x21,0xc6,0x12,0x28,0xc0,0x42,0x70,0x0d, + 0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98,0xac,0x9a,0x40,0xec,0xca,0xe4,0xe6,0xd2, + 0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62,0x40,0x50,0xda,0xca,0xe8,0xc2,0xd8,0xcc, + 0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c,0x6a,0x60,0x52,0x86,0x08,0x8a,0x30,0xc4, + 0x58,0x82,0x05,0x59,0x04,0x16,0x4d,0x65,0x74,0x61,0x6c,0x43,0x10,0xa5,0x58,0x82, + 0x45,0x58,0x04,0x6e,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e, + 0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x04,0xe5,0x20,0x17,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x62, + 0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87,0xf6,0x55,0xe6,0x16,0x26,0xc6,0x56,0x36, + 0x44,0x50,0x12,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99, + 0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d,0x99,0x59,0x99,0xdc,0x97,0x59,0x19,0xdd, + 0x18,0xda,0x57,0x99,0x5b,0x98,0x18,0x5b,0xd9,0x10,0x41,0x59,0x18,0x06,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65, + 0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x04,0xa5,0x61,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, 0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x0d,0x26, 0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0x97,0x5b, 0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32,0x61,0x69,0x72,0x2e,0x61, - 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0x4a,0xa5,0x58,0xca, - 0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, - 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a,0xe1,0x94,0x4b,0xc1,0x94, - 0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, - 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x9f,0x72, - 0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53,0xc2,0x80,0x50,0x58,0x9a, - 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, - 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, - 0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x38,0x70,0x6f,0x73, - 0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29,0x44,0xe0,0xde,0xe6,0xd2, - 0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42,0x06,0x8b,0xb0,0x04,0x4a, - 0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca,0xdc,0xca,0xe4,0xc2,0xe8, - 0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60,0x88,0xec,0x64, - 0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30,0xa1,0x2b,0xc3,0x1b,0x7b, - 0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a,0x19,0x2c,0xc1,0x12,0x28, - 0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce,0xca,0xdc,0xca,0xe4,0xc2, - 0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60,0x88,0xec,0x68,0xbe,0xcc, - 0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96,0x41,0x19,0x03,0x85,0x0c, - 0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5,0x0d,0xa8,0x84,0xa5,0xc9, - 0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33, - 0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26,0xe7,0x22,0x57,0x16,0x46, - 0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c,0xee,0x8b,0x2e,0x0f,0xae, - 0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x1e,0x0d,0x87, - 0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88,0x45,0x50,0xe6,0x40,0xa1, - 0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46,0x61,0x69,0x72,0x2e,0x61, - 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a,0x65,0xbc,0xc2,0xd2,0xe4, - 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2,0xd8,0xd2,0xce,0xdc,0xbe, - 0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9,0x85,0xb5,0xcd,0x71,0xf8, - 0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07,0x4b,0xa1,0x90,0xc1,0x22, - 0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1,0x52,0x28,0x78,0xb0,0x24, - 0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce,0x40,0x59,0x03,0xc5,0x0d, - 0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0,0x79,0x6b,0x73,0x4b,0x83, - 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, - 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfe,0x60,0x88, - 0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41,0x09,0x85,0x46,0x19,0x11, - 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, - 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, - 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, - 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, - 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, - 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, - 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, - 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, - 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07,0x92,0xa8,0x11,0x4a,0x38, - 0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43,0x39,0xe0,0xc3,0x94,0xa0, - 0x0f,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, - 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, - 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, - 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, - 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, - 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, - 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, - 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, - 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, - 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, - 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, - 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, - 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, - 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, - 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, - 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, - 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, - 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, - 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, - 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, - 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, - 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, - 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, - 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, - 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, - 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, - 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, - 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00, - 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, - 0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6, - 0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00, - 0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65, - 0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41, - 0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30, - 0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15, - 0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33,0x21,0x90,0x8f,0x15, - 0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0x83,0x18, - 0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca, - 0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01, - 0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00, - 0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10,0x03,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11,0x88,0xc2,0x96,0x41, - 0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51,0xd8,0x32,0x60,0x81, - 0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0xca,0xa3,0x40,0x4a, + 0xa4,0x48,0xca,0xa4,0x50,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, + 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x47,0xb1,0x94,0x48,0x91,0x94, + 0x49,0xb9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, + 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x99,0x12, + 0x29,0x92,0x32,0x29,0x1a,0x9d,0xb0,0x34,0x39,0x17,0xb8,0xb7,0x34,0x37,0xba,0xaf, + 0xb9,0x34,0xbd,0x32,0x16,0x66,0x6c,0x6f,0x61,0x74,0x64,0xce,0xd8,0xbe,0xa0,0xde, + 0xd2,0xdc,0xe8,0xa6,0xd2,0xf4,0xca,0x86,0x28,0x0a,0xa7,0x44,0x4a,0xa7,0x4c,0x8a, + 0x37,0x04,0x51,0x2a,0x05,0x53,0x36,0xe5,0x23,0x14,0x96,0x26,0xe7,0x62,0x57,0x26, + 0x47,0x57,0x86,0xf7,0x95,0xe6,0x06,0x57,0x47,0x47,0x29,0x2c,0x4d,0xce,0x85,0xed, + 0x6d,0x2c,0x8c,0x2e,0xed,0xcd,0xed,0x2b,0xcd,0x8d,0xac,0x0c,0x8f,0xd9,0x59,0x99, + 0x5b,0x99,0x5c,0x18,0x5d,0x19,0x19,0x0a,0x0e,0xdc,0xdb,0x5c,0x1a,0x5d,0xda,0x9b, + 0x1b,0x91,0x1d,0xcd,0x97,0x59,0x0a,0x11,0xb8,0xb7,0xb9,0x34,0xba,0xb4,0x37,0xb7, + 0x21,0xd4,0x22,0x28,0x61,0xa0,0x88,0xc1,0x22,0x2c,0x81,0x32,0x06,0x4a,0xa4,0x48, + 0xca,0xa4,0x90,0x01,0xb5,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0x1c, + 0xba,0x32,0xbc,0xb1,0xb7,0x37,0x39,0x32,0x18,0x22,0x3b,0x99,0x2f,0xb3,0x14,0x1a, + 0x66,0x6c,0x6f,0x61,0x74,0x32,0x4c,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60, + 0x86,0x50,0x4b,0xa0,0x84,0x81,0x22,0x06,0x4b,0xb0,0x04,0x8a,0x19,0x28,0x91,0x72, + 0x06,0xca,0xa4,0xa0,0x01,0xaf,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x14, + 0x9b,0xb1,0x37,0xb6,0x37,0x39,0x18,0x22,0x3b,0x9a,0x2f,0xb3,0x14,0x1a,0x63,0x6f, + 0x6c,0x6f,0x72,0x30,0x43,0xa8,0xa5,0x50,0xc2,0x40,0x11,0x83,0xa5,0x58,0x02,0x45, + 0x0d,0x94,0x48,0x91,0x94,0x49,0x59,0x03,0x4a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74, + 0x65,0x64,0x28,0x35,0x70,0x73,0x69,0x7a,0x65,0x66,0x29,0x2c,0xe0,0xe6,0xd2,0xf4, + 0xca,0x86,0x50,0x8b,0xa1,0x84,0x81,0x22,0x06,0x8b,0xb1,0x04,0x4a,0x1b,0x28,0x91, + 0xd2,0x29,0x93,0xe2,0x06,0x54,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4, + 0xf8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0x7d,0xcd,0xa5,0xe9,0x95, + 0x11,0x09,0x4b,0x93,0x73,0x91,0x2b,0x0b,0x23,0x23,0x15,0x96,0x26,0xe7,0x32,0x47, + 0x27,0x57,0x37,0x46,0xf7,0x45,0x97,0x07,0x57,0xf6,0x95,0xe6,0x66,0xf6,0x46,0xc4, + 0x8c,0xed,0x2d,0x8c,0x8e,0x06,0x8f,0x86,0x43,0x9b,0x1d,0x1c,0x05,0xba,0xb6,0x21, + 0xd4,0x22,0x2c,0xc3,0x22,0x28,0x74,0xa0,0xd4,0xc1,0x32,0x2c,0xc3,0x22,0x28,0x74, + 0xa0,0xd8,0x01,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2, + 0xaf,0xb9,0x34,0xbd,0x32,0x5e,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79, + 0x70,0x65,0x5f,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x69,0x7a,0x65,0x4c,0xec,0xe6, + 0xbe,0xe0,0xc2,0xe4,0xc2,0xda,0xe6,0x38,0x7c,0xc9,0xc4,0x0c,0x21,0x83,0x85,0x50, + 0xe0,0x40,0x89,0x83,0xe5,0x50,0xc4,0x60,0x11,0x96,0x40,0x91,0x03,0x65,0x0e,0x94, + 0x3b,0x50,0xf0,0x60,0x39,0x94,0x3c,0x58,0x12,0x25,0x52,0xf4,0x40,0x99,0x94,0x3d, + 0x18,0xa2,0x28,0x65,0xa0,0xa4,0x81,0xc2,0x06,0xca,0x1b,0x28,0x7c,0x30,0xc4,0x48, + 0x00,0x05,0x0c,0x94,0x3e,0xe0,0xf3,0xd6,0xe6,0x96,0x06,0xf7,0x46,0x57,0xe6,0x46, + 0x07,0x32,0x86,0x16,0x26,0xc7,0x67,0x2a,0xad,0x0d,0x8e,0xad,0x0c,0x64,0x68,0x65, + 0x05,0x84,0x4a,0x28,0x28,0x68,0x88,0xa0,0x80,0xc2,0x10,0x43,0xf9,0x03,0x25,0x14, + 0x18,0x65,0x88,0xa1,0x88,0x82,0x22,0x0a,0x8c,0x32,0x22,0x62,0x07,0x76,0xb0,0x87, + 0x76,0x70,0x83,0x76,0x78,0x07,0x72,0xa8,0x07,0x76,0x28,0x07,0x37,0x30,0x07,0x76, + 0x08,0x87,0x73,0x98,0x87,0x29,0x41,0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70, + 0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x0c,0x23,0x96,0x70,0x48,0x07, + 0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x10, + 0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07,0x77,0x38,0x87,0x7a,0x08, + 0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87, + 0x29,0x81,0x31,0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18,0x87,0x77,0x68,0x07,0x78, + 0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87,0x74,0x78,0x07,0x77,0x98, + 0x87,0x29,0x04,0xa2,0x30,0xce,0x08,0x25,0x1c,0xd2,0x41,0x1e,0xdc,0xc0,0x1e,0xca, + 0x41,0x1e,0xe8,0xa1,0x1c,0xf0,0x61,0x4a,0xe0,0x07,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, + 0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c, + 0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d, + 0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d, + 0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79, + 0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc, + 0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50, + 0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30, + 0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03, + 0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07, + 0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76, + 0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98, + 0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8, + 0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21, + 0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43, + 0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f, + 0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70, + 0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0, + 0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40, + 0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41, + 0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e, + 0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07, + 0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f, + 0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d, + 0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38, + 0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88, + 0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08, + 0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00, + 0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xe4,0xc6,0x22, + 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0x88,0x95,0x40, + 0x19,0x14,0x01,0xb9,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00, + 0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9, + 0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50, + 0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60, + 0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b, + 0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42, + 0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05, + 0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d, + 0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00, + 0x00,0x5b,0x86,0x20,0x18,0x85,0x2d,0x43,0x11,0x8c,0xc2,0x96,0x41,0x09,0x46,0x61, + 0xcb,0xf0,0x04,0xa3,0xb0,0x65,0xa0,0x82,0x51,0xd8,0x32,0x60,0xc1,0x28,0x6c,0x19, + 0xba,0x60,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const uint8_t _sfons_fs_bytecode_metal_ios[2925] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x6d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sfons_fs_bytecode_metal_ios[2877] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x90,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x60,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x1b,0x2c,0xa9,0x59,0x2d,0x7d,0xde, - 0xcd,0x5c,0x34,0x76,0x1e,0x63,0x4d,0x0c,0xdc,0xfc,0x74,0x70,0x26,0x76,0xe8,0xde, - 0xed,0xf7,0xc3,0xf7,0xe9,0x62,0x28,0xfa,0xd6,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xcb,0x23,0xc1,0x79,0xec,0x33, + 0x7a,0x1d,0x12,0x8c,0x7c,0xe5,0x11,0xa5,0xdb,0xc5,0x1c,0x52,0xd5,0x69,0x04,0xe6, + 0x72,0x3d,0xdb,0xf1,0xf1,0xf4,0x44,0xa4,0xb5,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, - 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x7c,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x9c,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x4c,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x90,0x02,0x00,0x00,0x0b,0x82,0x20, 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, - 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0x00,0x51,0x18,0x00,0x00,0x92,0x00,0x00,0x00,0x1b,0xfa,0x25,0xf8,0xff,0xff,0xff, 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, @@ -1018,25 +1079,27 @@ static const uint8_t _sfons_fs_bytecode_metal_ios[2925] = { 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, - 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, - 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, - 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, - 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, - 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, - 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, - 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, - 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, - 0x00,0x89,0x20,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, - 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, - 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, - 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, - 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, - 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, - 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, - 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, - 0xb5,0x81,0x80,0x14,0x58,0xc3,0x08,0xc4,0x32,0x02,0x00,0x00,0x00,0x13,0xa8,0x70, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa0,0x87,0x70,0x90,0x87, + 0x73,0x28,0x07,0x7a,0x68,0x03,0x73,0x28,0x87,0x70,0xa0,0x87,0x7a,0x90,0x87,0x72, + 0x98,0x07,0xa0,0x0d,0xcc,0x01,0x1e,0xe2,0xc0,0x0e,0x00,0xa2,0x1e,0xdc,0x61,0x1e, + 0xc2,0xc1,0x1c,0xca,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0, + 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x7a,0x98,0x87,0x72,0x68,0x83,0x79, + 0x78,0x07,0x73,0xa0,0x87,0x36,0x30,0x07,0x76,0x78,0x87,0x70,0xa0,0x07,0xc0,0x1c, + 0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x80,0x0d,0x86,0x30,0x00,0x0b,0x50,0x6d,0x30,0x06, + 0x02,0x58,0x80,0x6a,0x03,0x42,0xfc,0xff,0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54, + 0x1b,0x8c,0x22,0x00,0x16,0xa0,0xda,0x60,0x18,0x02,0xb0,0x00,0x15,0x00,0x00,0x00, + 0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44, + 0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48, + 0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90, + 0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04, + 0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2, + 0x51,0xd2,0x14,0x51,0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f, + 0x63,0x04,0xc0,0x20,0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc, + 0xb3,0x10,0xd1,0x3f,0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74, + 0x86,0x11,0x04,0x60,0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20, + 0x26,0x29,0xa6,0x00,0xb5,0x81,0x80,0x61,0x04,0x62,0x19,0x01,0x00,0x13,0xa8,0x70, 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, - 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xef,0x51, 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, @@ -1044,78 +1107,73 @@ static const uint8_t _sfons_fs_bytecode_metal_ios[2925] = { 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, - 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, - 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, - 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, - 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, - 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, - 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, - 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, - 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, - 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, - 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, - 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, - 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, - 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, - 0x20,0x07,0x43,0x18,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03, - 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00, - 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, - 0x5a,0x23,0x00,0x25,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xa1,0x29,0x00, - 0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, - 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50, - 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c, - 0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c, - 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad, - 0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88, - 0xf0,0x10,0x43,0x8c,0x65,0x58,0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04, - 0x79,0x8e,0x65,0x58,0x84,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97, - 0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26, - 0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69, - 0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61, - 0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1, - 0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d, - 0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86, - 0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46, - 0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26, - 0xc6,0x56,0x36,0x44,0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c, - 0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17, - 0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78, - 0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74, - 0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73, - 0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7, - 0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c, - 0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c, - 0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f, - 0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43, - 0x98,0xa7,0x5a,0x84,0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b, - 0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3, - 0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d, - 0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc, - 0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xdc, - 0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4, - 0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x96, - 0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84, - 0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61, - 0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96, - 0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae, - 0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad, - 0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x11,0x96, - 0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32, - 0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08, - 0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60, - 0x11,0x96,0xe1,0xb1,0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64, - 0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0, - 0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06, - 0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83, - 0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1, - 0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8, - 0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3, - 0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a, - 0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc, - 0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3, - 0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20, - 0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25, - 0x80,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80, + 0x74,0xd0,0x06,0xe6,0x80,0x07,0x70,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10, + 0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06, + 0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78, + 0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10, + 0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07, + 0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10, + 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06, + 0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a, + 0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xe6,0x80,0x07,0x70,0xa0,0x07,0x71,0x20, + 0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07, + 0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x18,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00, + 0x00,0x07,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x23,0x00,0x25,0x50,0x08,0x05,0x51,0x04,0x65,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0xbb,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x95,0xbb,0x31, + 0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2,0x21,0xc6,0x32,0x3c,0xc0,0x42,0x70,0x0d, + 0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98,0xac,0x9a,0x40,0xec,0xca,0xe4,0xe6,0xd2, + 0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62,0x40,0x50,0xda,0xca,0xe8,0xc2,0xd8,0xcc, + 0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c,0x6a,0x60,0x52,0x86,0x08,0x8f,0x30,0xc4, + 0x58,0x86,0xa5,0x58,0x04,0x16,0x4d,0x65,0x74,0x61,0x6c,0x43,0x90,0xa7,0x58,0x86, + 0x45,0x58,0x04,0x6e,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e, + 0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x84,0xe7,0x20,0x17,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x62, + 0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87,0xf6,0x55,0xe6,0x16,0x26,0xc6,0x56,0x36, + 0x44,0x78,0x12,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99, + 0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d,0x99,0x59,0x99,0xdc,0x97,0x59,0x19,0xdd, + 0x18,0xda,0x57,0x99,0x5b,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x59,0x18,0x06,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65, + 0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x84,0xa7,0x61,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17, + 0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f, + 0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0, + 0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xe7,0x59,0x84,0x07,0x7a,0xa2,0x47,0x7a, + 0xa6,0x21,0xc2,0x43,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73, + 0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36, + 0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f, + 0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85, + 0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xd6,0x73,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2, + 0xa3,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6, + 0xc6,0xf6,0x26,0x37,0x44,0x5a,0x86,0xc7,0x7a,0xb8,0x07,0x7b,0xb2,0x07,0x7a,0xa2, + 0x47,0x7a,0x3a,0x2e,0x61,0x69,0x72,0x2e,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x94, + 0xc2,0xd2,0xe4,0x5c,0xd8,0xde,0xc6,0xc2,0xe8,0xd2,0xde,0xdc,0xbe,0xd2,0xdc,0xc8, + 0xca,0xf0,0xa8,0x84,0xa5,0xc9,0xb9,0xcc,0x85,0xb5,0xc1,0xb1,0x95,0x11,0xa3,0x2b, + 0xc3,0xa3,0xab,0x93,0x2b,0x93,0x21,0xe3,0x31,0x63,0x7b,0x0b,0xa3,0x63,0x01,0x99, + 0x0b,0x6b,0x83,0x63,0x2b,0xf3,0xe1,0x40,0x57,0x86,0x37,0x84,0x5a,0x8c,0xe7,0x7b, + 0xc0,0x60,0x11,0x96,0xe1,0x09,0x83,0x07,0x7a,0xc4,0xe0,0x91,0x9e,0x31,0xe0,0x12, + 0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e, + 0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e, + 0x32,0x78,0xc0,0x60,0x11,0x96,0xe1,0x81,0x1e,0x33,0x78,0xa4,0xe7,0x0c,0x86,0x20, + 0xcf,0xf6,0x78,0x0f,0x19,0x3c,0x68,0x30,0xc4,0x40,0x80,0xa7,0x7a,0xd2,0x60,0x44, + 0xc4,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xed,0xf0,0x0e,0xe4,0x50,0x0f,0xec,0x50, + 0x0e,0x6e,0x60,0x0e,0xec,0x10,0x0e,0xe7,0x30,0x0f,0x53,0x82,0x60,0x84,0xc2,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x18, + 0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0, + 0x0e,0xee,0x30,0x25,0x20,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e, + 0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3, + 0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30, + 0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f, + 0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x08,0x44,0x61,0x9c,0x11,0x4c,0x38,0xa4,0x83, + 0x3c,0xb8,0x81,0x39,0xc8,0x43,0x38,0x9c,0x43,0x3b,0x94,0x83,0x3b,0xd0,0xc3,0x94, + 0x40,0x0d,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80, 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, @@ -1146,14 +1204,14 @@ static const uint8_t _sfons_fs_bytecode_metal_ios[2925] = { 0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00, 0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d, 0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00, - 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0xc7,0x22, 0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25,0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31, - 0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a,0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18, + 0x03,0x40,0x61,0x0e,0xc2,0xb2,0x2c,0x6a,0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18, 0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28, - 0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66, + 0x42,0x70,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66, 0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const char _sfons_vs_source_metal_sim[698] = { +static const char _sfons_vs_source_metal_sim[866] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1169,37 +1227,48 @@ static const char _sfons_vs_source_metal_sim[698] = { 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, - 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, - 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, - 0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74, - 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, - 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d, - 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22, - 0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, - 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, - 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, - 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66, - 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, - 0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22,0x22,0x0a, - 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20, - 0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69, - 0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, - 0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, - 0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23, - 0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f, - 0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f, - 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, - 0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a, + 0x65,0x20,0x5b,0x5b,0x70,0x6f,0x69,0x6e,0x74,0x5f,0x73,0x69,0x7a,0x65,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31, + 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, + 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67, + 0x6c,0x73,0x6c,0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f, + 0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x31,0x20,0x5b,0x5b, + 0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74, + 0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x37,0x20, + 0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20, + 0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x5f,0x32,0x31,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x69,0x6e,0x2e, + 0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, + 0x31,0x38,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b, + 0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x39,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73, + 0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75, + 0x76,0x20,0x3d,0x20,0x5f,0x32,0x31,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23,0x6c,0x69, + 0x6e,0x65,0x20,0x32,0x30,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73, + 0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x00, }; -static const char _sfons_fs_source_metal_sim[498] = { +static const char _sfons_fs_source_metal_sim[518] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1214,31 +1283,32 @@ static const char _sfons_fs_source_metal_sim[498] = { 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, - 0x31,0x31,0x20,0x22,0x22,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, - 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, - 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, - 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, - 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b, - 0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f, - 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31, - 0x31,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, - 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20, - 0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x53,0x6d, - 0x70,0x6c,0x72,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78, - 0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a, - 0x0a,0x00, + 0x31,0x31,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a, + 0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, + 0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f, + 0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20, + 0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b, + 0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x31,0x20,0x22,0x73,0x66,0x6f, + 0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x2c,0x20,0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74, + 0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x2e,0x78, + 0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74, + 0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_D3D11) -static const uint8_t _sfons_vs_bytecode_hlsl4[1008] = { - 0x44,0x58,0x42,0x43,0xf3,0xd9,0xf4,0x58,0x44,0xc2,0xb9,0x96,0x56,0x7c,0xb3,0x71, - 0x84,0xc5,0xde,0x61,0x01,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0x05,0x00,0x00,0x00, - 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0xe8,0x01,0x00,0x00, - 0x74,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +static const uint8_t _sfons_vs_bytecode_hlsl4[1032] = { + 0x44,0x58,0x42,0x43,0x09,0x96,0xbb,0xbb,0xfc,0x44,0x44,0xa8,0xa4,0x1c,0x9e,0x45, + 0x50,0x97,0xf1,0xde,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x90,0x01,0x00,0x00,0x00,0x02,0x00,0x00, + 0x8c,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -1247,58 +1317,59 @@ static const uint8_t _sfons_vs_bytecode_hlsl4[1008] = { 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x31,0x5f, 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x31,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, - 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x50,0x00,0x00,0x00, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00, - 0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, - 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, - 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, - 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, - 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08, - 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00, - 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, - 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00, - 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, - 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, - 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00, - 0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, - 0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, - 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00, - 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00, - 0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, - 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05, + 0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00, + 0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54, + 0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { 0x44,0x58,0x42,0x43,0x5e,0xbc,0x77,0xd9,0xc9,0xdf,0x7d,0x8e,0x0c,0x24,0x18,0x66, @@ -1341,7 +1412,6 @@ static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - }; #elif defined(SOKOL_WGPU) /* @@ -1355,9 +1425,10 @@ static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { { mat4 mvp; mat4 tm; - } _20; + } _21; layout(location = 0) in vec4 position; + layout(location = 3) in float psize; layout(location = 0) out vec4 uv; layout(location = 1) in vec2 texcoord0; layout(location = 1) out vec4 color; @@ -1365,8 +1436,9 @@ static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { void main() { - gl_Position = _20.mvp * position; - uv = _20.tm * vec4(texcoord0, 0.0, 1.0); + gl_Position = _21.mvp * position; + gl_PointSize = psize; + uv = _21.tm * vec4(texcoord0, 0.0, 1.0); color = color0; } @@ -1385,15 +1457,16 @@ static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { frag_color = vec4(1.0, 1.0, 1.0, texture(tex, uv.xy).x) * color; } */ -static const uint8_t _sfons_vs_bytecode_wgpu[1932] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x32,0x00,0x00,0x00, +static const uint8_t _sfons_vs_bytecode_wgpu[1968] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x35,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x31,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0f,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x33,0x00,0x00,0x00, + 0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00, 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, @@ -1409,168 +1482,170 @@ static const uint8_t _sfons_vs_bytecode_wgpu[1932] = { 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00, 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00, 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x12,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, - 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, - 0x05,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x5f,0x32,0x30,0x00,0x05,0x00,0x05,0x00, - 0x19,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, - 0x24,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, - 0x05,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, - 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, - 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x12,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, - 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, - 0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, - 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00, - 0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x91,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x29,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, - 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x08,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x1e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2c,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x13,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, + 0x05,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x5f,0x32,0x31,0x00,0x05,0x00,0x05,0x00, + 0x1a,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x70,0x73,0x69,0x7a,0x65,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x2a,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x21,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x25,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x18,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x1e,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x91,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1e,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x24,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x26,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x28,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x09,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x09,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x32,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, }; -static const uint8_t fs_bytecode_wgpu[980] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1e,0x00,0x00,0x00, +static const uint8_t fs_bytecode_wgpu[1000] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1f,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, - 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, - 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, - 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, - 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, - 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, - 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, - 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, - 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, - 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00, - 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x75,0x76,0x00,0x00, - 0x05,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, - 0x19,0x00,0x09,0x00,0x0c,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x4f,0x00,0x07,0x00, - 0x13,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x08,0x00,0x00,0x00, - 0x16,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x85,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, - 0x38,0x00,0x01,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00,0x03,0x00,0x37,0x00, + 0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f, + 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, + 0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30, + 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, + 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70, + 0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, + 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72, + 0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e, + 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, + 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e, + 0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f, + 0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e, + 0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00, + 0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00, + 0x10,0x00,0x00,0x00,0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x19,0x00,0x09,0x00,0x0d,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0e,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00, + 0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x4f,0x00,0x07,0x00,0x14,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00, + 0x09,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _sfons_vs_source_dummy = ""; @@ -1580,13 +1655,48 @@ static const char* _sfons_fs_source_dummy = ""; #endif typedef struct _sfons_t { + sfons_desc_t desc; sg_shader shd; sgl_pipeline pip; sg_image img; - int width, height; + int cur_width, cur_height; bool img_dirty; } _sfons_t; +static void _sfons_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(allocator && (size > 0)); + void* ptr; + if (allocator->alloc) { + ptr = allocator->alloc(size, allocator->user_data); + } + else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +static void* _sfons_malloc_clear(const sfons_allocator_t* allocator, size_t size) { + void* ptr = _sfons_malloc(allocator, size); + _sfons_clear(ptr, size); + return ptr; +} + +static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) { + SOKOL_ASSERT(allocator); + if (allocator->free) { + allocator->free(ptr, allocator->user_data); + } + else { + free(ptr); + } +} + static int _sfons_render_create(void* user_ptr, int width, int height) { SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8)); _sfons_t* sfons = (_sfons_t*) user_ptr; @@ -1594,16 +1704,19 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { /* sokol-gl compatible shader which treats RED channel as alpha */ if (sfons->shd.id == SG_INVALID_ID) { sg_shader_desc shd_desc; - memset(&shd_desc, 0, sizeof(shd_desc)); + _sfons_clear(&shd_desc, sizeof(shd_desc)); shd_desc.attrs[0].name = "position"; shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[3].name = "psize"; shd_desc.attrs[0].sem_name = "TEXCOORD"; shd_desc.attrs[0].sem_index = 0; shd_desc.attrs[1].sem_name = "TEXCOORD"; shd_desc.attrs[1].sem_index = 1; shd_desc.attrs[2].sem_name = "TEXCOORD"; shd_desc.attrs[2].sem_index = 2; + shd_desc.attrs[3].sem_name = "TEXCOORD"; + shd_desc.attrs[3].sem_index = 3; sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; ub->size = 128; ub->uniforms[0].name = "vs_params"; @@ -1655,7 +1768,7 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { /* sokol-gl pipeline object */ if (sfons->pip.id == SG_INVALID_ID) { sg_pipeline_desc pip_desc; - memset(&pip_desc, 0, sizeof(pip_desc)); + _sfons_clear(&pip_desc, sizeof(pip_desc)); pip_desc.shader = sfons->shd; pip_desc.colors[0].blend.enabled = true; pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; @@ -1668,14 +1781,14 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { sg_destroy_image(sfons->img); sfons->img.id = SG_INVALID_ID; } - sfons->width = width; - sfons->height = height; + sfons->cur_width = width; + sfons->cur_height = height; SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID); sg_image_desc img_desc; - memset(&img_desc, 0, sizeof(img_desc)); - img_desc.width = sfons->width; - img_desc.height = sfons->height; + _sfons_clear(&img_desc, sizeof(img_desc)); + img_desc.width = sfons->cur_width; + img_desc.height = sfons->cur_height; img_desc.min_filter = SG_FILTER_LINEAR; img_desc.mag_filter = SG_FILTER_LINEAR; img_desc.usage = SG_USAGE_DYNAMIC; @@ -1727,42 +1840,43 @@ static void _sfons_render_delete(void* user_ptr) { sg_destroy_shader(sfons->shd); sfons->shd.id = SG_INVALID_ID; } - SOKOL_FREE(sfons); } -// NOTE clang analyzer will report a potential memory leak for the call -// to SOKOL_MALLOC in the sfons_create() function, this is a false positive -// (the freeing happens in _sfons_render_delete()). The following macro -// silences the false positive when compilation happens with the analyzer active -#if __clang_analyzer__ -#define _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(x) SOKOL_FREE(x) -#else -#define _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(x) -#endif +#define _sfons_def(val, def) (((val) == 0) ? (def) : (val)) -SOKOL_API_IMPL FONScontext* sfons_create(int width, int height, int flags) { - SOKOL_ASSERT((width > 0) && (height > 0)); +static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) { + SOKOL_ASSERT(desc); + sfons_desc_t res = *desc; + res.width = _sfons_def(res.width, 512); + res.height = _sfons_def(res.height, 512); + return res; +} + +SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t)); + sfons->desc = _sfons_desc_defaults(desc); FONSparams params; - _sfons_t* sfons = (_sfons_t*) SOKOL_MALLOC(sizeof(_sfons_t)); - memset(sfons, 0, sizeof(_sfons_t)); - memset(¶ms, 0, sizeof(params)); - params.width = width; - params.height = height; - params.flags = (unsigned char) flags; + _sfons_clear(¶ms, sizeof(params)); + params.width = sfons->desc.width; + params.height = sfons->desc.height; + params.flags = FONS_ZERO_TOPLEFT; params.renderCreate = _sfons_render_create; params.renderResize = _sfons_render_resize; params.renderUpdate = _sfons_render_update; params.renderDraw = _sfons_render_draw; params.renderDelete = _sfons_render_delete; params.userPtr = sfons; - FONScontext* ctx = fonsCreateInternal(¶ms); - _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(sfons); - return ctx; + return fonsCreateInternal(¶ms); } SOKOL_API_IMPL void sfons_destroy(FONScontext* ctx) { SOKOL_ASSERT(ctx); + _sfons_t* sfons = (_sfons_t*) ctx->params.userPtr; fonsDeleteInternal(ctx); + const sfons_allocator_t allocator = sfons->desc.allocator; + _sfons_free(&allocator, sfons); } SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) { @@ -1771,9 +1885,9 @@ SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) { if (sfons->img_dirty) { sfons->img_dirty = false; sg_image_data data; - memset(&data, 0, sizeof(data)); + _sfons_clear(&data, sizeof(data)); data.subimage[0][0].ptr = ctx->texData; - data.subimage[0][0].size = (size_t) (sfons->width * sfons->height); + data.subimage[0][0].size = (size_t) (sfons->cur_width * sfons->cur_height); sg_update_image(sfons->img, &data); } } diff --git a/3rdparty/sokol/util/sokol_gfx_imgui.h b/3rdparty/sokol/util/sokol_gfx_imgui.h index 4c5da29..9b9b8d7 100644 --- a/3rdparty/sokol/util/sokol_gfx_imgui.h +++ b/3rdparty/sokol/util/sokol_gfx_imgui.h @@ -45,8 +45,6 @@ SOKOL_ASSERT(c) -- your own assert macro, default: assert(c) SOKOL_UNREACHABLE -- your own macro to annotate unreachable code, default: SOKOL_ASSERT(false) - SOKOL_MALLOC(s) -- your own memory allocation function, default: malloc(s) - SOKOL_FREE(p) -- your own memory free function, default: free(p) SOKOL_GFX_IMGUI_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GFX_IMGUI_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) @@ -64,7 +62,21 @@ --- create an sg_imgui_t struct (which must be preserved between frames) and initialize it with: - sg_imgui_init(&sg_imgui); + sg_imgui_init(&sg_imgui, &(sg_imgui_desc_t){ 0 }); + + Note that from C++ you can't inline the desc structure initialization: + + const sg_imgui_desc_t desc = { }; + sg_imgui_init(&sg_imgui, &desc); + + Provide optional memory allocator override functions (compatible with malloc/free) like this: + + sg_imgui_init(&sg_imgui, &(sg_imgui_desc_t){ + .allocator = { + .alloc = my_malloc, + .free = my_free, + } + }); --- somewhere in the per-frame code call: @@ -136,6 +148,34 @@ Finer-grained drawing functions may be moved to the public API in the future as needed. + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sg_imgui_init(&(&ctx, &(sg_imgui_desc_t){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ...; + } + }); + ... + + This only affects memory allocation calls done by sokol_gfx_imgui.h + itself though, not any allocations in OS libraries. + + LICENSE ======= zlib/libpng license @@ -164,6 +204,7 @@ #define SOKOL_GFX_IMGUI_INCLUDED (1) #include #include +#include // size_t #if !defined(SOKOL_GFX_INCLUDED) #error "Please include sokol_gfx.h before sokol_gfx_imgui.h" @@ -616,8 +657,32 @@ typedef struct sg_imgui_caps_t { bool open; } sg_imgui_caps_t; +/* + sg_imgui_allocator_t + + Used in sg_imgui_desc_t to provide custom memory-alloc and -free functions + to sokol_gfx_imgui.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sg_imgui_allocator_t { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sg_imgui_allocator_t; + +/* + sg_imgui_desc_t + + Initialization options for sg_imgui_init(). +*/ +typedef struct sg_imgui_desc_t { + sg_imgui_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) +} sg_imgui_desc_t; + typedef struct sg_imgui_t { uint32_t init_tag; + sg_imgui_desc_t desc; sg_imgui_buffers_t buffers; sg_imgui_images_t images; sg_imgui_shaders_t shaders; @@ -629,7 +694,7 @@ typedef struct sg_imgui_t { sg_trace_hooks hooks; } sg_imgui_t; -SOKOL_GFX_IMGUI_API_DECL void sg_imgui_init(sg_imgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sg_imgui_init(sg_imgui_t* ctx, const sg_imgui_desc_t* desc); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_discard(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw(sg_imgui_t* ctx); @@ -657,6 +722,11 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capabilities_window(sg_imgui_t* ctx) /*=== IMPLEMENTATION =========================================================*/ #ifdef SOKOL_GFX_IMGUI_IMPL #define SOKOL_GFX_IMGUI_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_imgui_desc_t.allocator to override memory allocation functions" +#endif + #if defined(__cplusplus) #if !defined(IMGUI_VERSION) #error "Please include imgui.h before the sokol_imgui.h implementation" @@ -673,11 +743,6 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capabilities_window(sg_imgui_t* ctx) #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -693,7 +758,8 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capabilities_window(sg_imgui_t* ctx) #endif #include -#include /* snprintf */ +#include // snprintf +#include // malloc, free #define _SG_IMGUI_SLOT_MASK (0xFFFF) #define _SG_IMGUI_LIST_WIDTH (192) @@ -718,38 +784,38 @@ _SOKOL_PRIVATE void igSeparator() { _SOKOL_PRIVATE void igSameLine(float offset_from_start_x, float spacing) { return ImGui::SameLine(offset_from_start_x,spacing); } -_SOKOL_PRIVATE void igPushIDInt(int int_id) { +_SOKOL_PRIVATE void igPushID_Int(int int_id) { return ImGui::PushID(int_id); } _SOKOL_PRIVATE void igPopID() { return ImGui::PopID(); } -_SOKOL_PRIVATE bool igSelectableBool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size) { +_SOKOL_PRIVATE bool igSelectable_Bool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size) { return ImGui::Selectable(label,selected,flags,size); } _SOKOL_PRIVATE bool igSmallButton(const char* label) { return ImGui::SmallButton(label); } -_SOKOL_PRIVATE bool igBeginChildStr(const char* str_id,const ImVec2 size,bool border,ImGuiWindowFlags flags) { +_SOKOL_PRIVATE bool igBeginChild_Str(const char* str_id,const ImVec2 size,bool border,ImGuiWindowFlags flags) { return ImGui::BeginChild(str_id,size,border,flags); } _SOKOL_PRIVATE void igEndChild() { return ImGui::EndChild(); } -_SOKOL_PRIVATE void igPushStyleColorU32(ImGuiCol idx, ImU32 col) { +_SOKOL_PRIVATE void igPushStyleColor_U32(ImGuiCol idx, ImU32 col) { return ImGui::PushStyleColor(idx,col); } _SOKOL_PRIVATE void igPopStyleColor(int count) { return ImGui::PopStyleColor(count); } -_SOKOL_PRIVATE bool igTreeNodeStrStr(const char* str_id,const char* fmt,...) { +_SOKOL_PRIVATE bool igTreeNode_StrStr(const char* str_id,const char* fmt,...) { va_list args; va_start(args, fmt); bool ret = ImGui::TreeNodeV(str_id,fmt,args); va_end(args); return ret; } -_SOKOL_PRIVATE bool igTreeNodeStr(const char* label) { +_SOKOL_PRIVATE bool igTreeNode_Str(const char* label) { return ImGui::TreeNode(label); } _SOKOL_PRIVATE void igTreePop() { @@ -785,48 +851,131 @@ _SOKOL_PRIVATE void igEnd() { #endif /*--- UTILS ------------------------------------------------------------------*/ +_SOKOL_PRIVATE void _sg_imgui_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sg_imgui_malloc(const sg_imgui_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(allocator && (size > 0)); + void* ptr; + if (allocator->alloc) { + ptr = allocator->alloc(size, allocator->user_data); + } + else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +_SOKOL_PRIVATE void* _sg_imgui_malloc_clear(const sg_imgui_allocator_t* allocator, size_t size) { + void* ptr = _sg_imgui_malloc(allocator, size); + _sg_imgui_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sg_imgui_free(const sg_imgui_allocator_t* allocator, void* ptr) { + SOKOL_ASSERT(allocator); + if (allocator->free) { + allocator->free(ptr, allocator->user_data); + } + else { + free(ptr); + } +} + + _SOKOL_PRIVATE void* _sg_imgui_realloc(const sg_imgui_allocator_t* allocator, void* old_ptr, size_t old_size, size_t new_size) { + SOKOL_ASSERT(allocator && (new_size > 0) && (new_size > old_size)); + void* new_ptr = _sg_imgui_malloc(allocator, new_size); + if (old_ptr) { + if (old_size > 0) { + memcpy(new_ptr, old_ptr, old_size); + } + _sg_imgui_free(allocator, old_ptr); + } + return new_ptr; +} + _SOKOL_PRIVATE int _sg_imgui_slot_index(uint32_t id) { int slot_index = (int) (id & _SG_IMGUI_SLOT_MASK); SOKOL_ASSERT(0 != slot_index); return slot_index; } -_SOKOL_PRIVATE int _sg_imgui_uniform_size(sg_uniform_type type, int count) { - switch (type) { - case SG_UNIFORMTYPE_INVALID: return 0; - case SG_UNIFORMTYPE_FLOAT: return 4 * count; - case SG_UNIFORMTYPE_FLOAT2: return 8 * count; - case SG_UNIFORMTYPE_FLOAT3: return 12 * count; /* FIXME: std140??? */ - case SG_UNIFORMTYPE_FLOAT4: return 16 * count; - case SG_UNIFORMTYPE_MAT4: return 64 * count; - default: - SOKOL_UNREACHABLE; - return -1; - } +_SOKOL_PRIVATE uint32_t _sg_imgui_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); } -_SOKOL_PRIVATE void* _sg_imgui_alloc(size_t size) { - SOKOL_ASSERT(size > 0); - return SOKOL_MALLOC(size); -} - -_SOKOL_PRIVATE void _sg_imgui_free(void* ptr) { - if (ptr) { - SOKOL_FREE(ptr); - } -} - -_SOKOL_PRIVATE void* _sg_imgui_realloc(void* old_ptr, size_t old_size, size_t new_size) { - SOKOL_ASSERT((new_size > 0) && (new_size > old_size)); - void* new_ptr = SOKOL_MALLOC(new_size); - SOKOL_ASSERT(new_ptr); - if (old_ptr) { - if (old_size > 0) { - memcpy(new_ptr, old_ptr, old_size); +_SOKOL_PRIVATE uint32_t _sg_imgui_std140_uniform_alignment(sg_uniform_type type, int array_count) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 16; + default: + SOKOL_UNREACHABLE; + return 1; + } + } + else { + return 16; + } +} + +_SOKOL_PRIVATE uint32_t _sg_imgui_std140_uniform_size(sg_uniform_type type, int array_count) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 64; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + else { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT: + case SG_UNIFORMTYPE_INT2: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; } - _sg_imgui_free(old_ptr); } - return new_ptr; } _SOKOL_PRIVATE void _sg_imgui_strcpy(sg_imgui_str_t* dst, const char* src) { @@ -840,7 +989,7 @@ _SOKOL_PRIVATE void _sg_imgui_strcpy(sg_imgui_str_t* dst, const char* src) { dst->buf[SG_IMGUI_STRBUF_LEN-1] = 0; } else { - memset(dst->buf, 0, SG_IMGUI_STRBUF_LEN); + _sg_imgui_clear(dst->buf, SG_IMGUI_STRBUF_LEN); } } @@ -850,17 +999,17 @@ _SOKOL_PRIVATE sg_imgui_str_t _sg_imgui_make_str(const char* str) { return res; } -_SOKOL_PRIVATE const char* _sg_imgui_str_dup(const char* src) { - SOKOL_ASSERT(src); +_SOKOL_PRIVATE const char* _sg_imgui_str_dup(const sg_imgui_allocator_t* allocator, const char* src) { + SOKOL_ASSERT(allocator && src); size_t len = strlen(src) + 1; - char* dst = (char*) _sg_imgui_alloc(len); + char* dst = (char*) _sg_imgui_malloc(allocator, len); memcpy(dst, src, len); return (const char*) dst; } -_SOKOL_PRIVATE const void* _sg_imgui_bin_dup(const void* src, size_t num_bytes) { - SOKOL_ASSERT(src && (num_bytes > 0)); - void* dst = _sg_imgui_alloc(num_bytes); +_SOKOL_PRIVATE const void* _sg_imgui_bin_dup(const sg_imgui_allocator_t* allocator, const void* src, size_t num_bytes) { + SOKOL_ASSERT(allocator && src && (num_bytes > 0)); + void* dst = _sg_imgui_malloc(allocator, num_bytes); memcpy(dst, src, num_bytes); return (const void*) dst; } @@ -966,6 +1115,7 @@ _SOKOL_PRIVATE const char* _sg_imgui_pixelformat_string(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: return "SG_PIXELFORMAT_RG16SI"; case SG_PIXELFORMAT_RG16F: return "SG_PIXELFORMAT_RG16F"; case SG_PIXELFORMAT_RGBA8: return "SG_PIXELFORMAT_RGBA8"; + case SG_PIXELFORMAT_SRGB8A8: return "SG_PIXELFORMAT_SRGB8A8"; case SG_PIXELFORMAT_RGBA8SN: return "SG_PIXELFORMAT_RGBA8SN"; case SG_PIXELFORMAT_RGBA8UI: return "SG_PIXELFORMAT_RGBA8UI"; case SG_PIXELFORMAT_RGBA8SI: return "SG_PIXELFORMAT_RGBA8SI"; @@ -1004,6 +1154,7 @@ _SOKOL_PRIVATE const char* _sg_imgui_pixelformat_string(sg_pixel_format fmt) { case SG_PIXELFORMAT_ETC2_RGBA8: return "SG_PIXELFORMAT_ETC2_RGBA8"; case SG_PIXELFORMAT_ETC2_RG11: return "SG_PIXELFORMAT_ETC2_RG11"; case SG_PIXELFORMAT_ETC2_RG11SN: return "SG_PIXELFORMAT_ETC2_RG11SN"; + case SG_PIXELFORMAT_RGB9E5: return "SG_PIXELFORMAT_RGB9E5"; default: return "???"; } } @@ -1045,6 +1196,10 @@ _SOKOL_PRIVATE const char* _sg_imgui_uniformtype_string(sg_uniform_type t) { case SG_UNIFORMTYPE_FLOAT2: return "SG_UNIFORMTYPE_FLOAT2"; case SG_UNIFORMTYPE_FLOAT3: return "SG_UNIFORMTYPE_FLOAT3"; case SG_UNIFORMTYPE_FLOAT4: return "SG_UNIFORMTYPE_FLOAT4"; + case SG_UNIFORMTYPE_INT: return "SG_UNIFORMTYPE_INT"; + case SG_UNIFORMTYPE_INT2: return "SG_UNIFORMTYPE_INT2"; + case SG_UNIFORMTYPE_INT3: return "SG_UNIFORMTYPE_INT3"; + case SG_UNIFORMTYPE_INT4: return "SG_UNIFORMTYPE_INT4"; case SG_UNIFORMTYPE_MAT4: return "SG_UNIFORMTYPE_MAT4"; default: return "???"; } @@ -1358,16 +1513,16 @@ _SOKOL_PRIVATE void _sg_imgui_shader_created(sg_imgui_t* ctx, sg_shader res_id, } } if (shd->desc.vs.source) { - shd->desc.vs.source = _sg_imgui_str_dup(shd->desc.vs.source); + shd->desc.vs.source = _sg_imgui_str_dup(&ctx->desc.allocator, shd->desc.vs.source); } if (shd->desc.vs.bytecode.ptr) { - shd->desc.vs.bytecode.ptr = _sg_imgui_bin_dup(shd->desc.vs.bytecode.ptr, shd->desc.vs.bytecode.size); + shd->desc.vs.bytecode.ptr = _sg_imgui_bin_dup(&ctx->desc.allocator, shd->desc.vs.bytecode.ptr, shd->desc.vs.bytecode.size); } if (shd->desc.fs.source) { - shd->desc.fs.source = _sg_imgui_str_dup(shd->desc.fs.source); + shd->desc.fs.source = _sg_imgui_str_dup(&ctx->desc.allocator, shd->desc.fs.source); } if (shd->desc.fs.bytecode.ptr) { - shd->desc.fs.bytecode.ptr = _sg_imgui_bin_dup(shd->desc.fs.bytecode.ptr, shd->desc.fs.bytecode.size); + shd->desc.fs.bytecode.ptr = _sg_imgui_bin_dup(&ctx->desc.allocator, shd->desc.fs.bytecode.ptr, shd->desc.fs.bytecode.size); } for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { sg_shader_attr_desc* ad = &shd->desc.attrs[i]; @@ -1387,19 +1542,19 @@ _SOKOL_PRIVATE void _sg_imgui_shader_destroyed(sg_imgui_t* ctx, int slot_index) sg_imgui_shader_t* shd = &ctx->shaders.slots[slot_index]; shd->res_id.id = SG_INVALID_ID; if (shd->desc.vs.source) { - _sg_imgui_free((void*)shd->desc.vs.source); + _sg_imgui_free(&ctx->desc.allocator, (void*)shd->desc.vs.source); shd->desc.vs.source = 0; } if (shd->desc.vs.bytecode.ptr) { - _sg_imgui_free((void*)shd->desc.vs.bytecode.ptr); + _sg_imgui_free(&ctx->desc.allocator, (void*)shd->desc.vs.bytecode.ptr); shd->desc.vs.bytecode.ptr = 0; } if (shd->desc.fs.source) { - _sg_imgui_free((void*)shd->desc.fs.source); + _sg_imgui_free(&ctx->desc.allocator, (void*)shd->desc.fs.source); shd->desc.fs.source = 0; } if (shd->desc.fs.bytecode.ptr) { - _sg_imgui_free((void*)shd->desc.fs.bytecode.ptr); + _sg_imgui_free(&ctx->desc.allocator, (void*)shd->desc.fs.bytecode.ptr); shd->desc.fs.bytecode.ptr = 0; } } @@ -1443,8 +1598,7 @@ _SOKOL_PRIVATE void _sg_imgui_capture_init(sg_imgui_t* ctx) { for (int i = 0; i < 2; i++) { sg_imgui_capture_bucket_t* bucket = &ctx->capture.bucket[i]; bucket->ubuf_size = ubuf_initial_size; - bucket->ubuf = (uint8_t*) _sg_imgui_alloc(bucket->ubuf_size); - SOKOL_ASSERT(bucket->ubuf); + bucket->ubuf = (uint8_t*) _sg_imgui_malloc(&ctx->desc.allocator, bucket->ubuf_size); } } @@ -1452,7 +1606,7 @@ _SOKOL_PRIVATE void _sg_imgui_capture_discard(sg_imgui_t* ctx) { for (int i = 0; i < 2; i++) { sg_imgui_capture_bucket_t* bucket = &ctx->capture.bucket[i]; SOKOL_ASSERT(bucket->ubuf); - _sg_imgui_free(bucket->ubuf); + _sg_imgui_free(&ctx->desc.allocator, bucket->ubuf); bucket->ubuf = 0; } } @@ -1478,7 +1632,7 @@ _SOKOL_PRIVATE void _sg_imgui_capture_grow_ubuf(sg_imgui_t* ctx, size_t required size_t old_size = bucket->ubuf_size; size_t new_size = required_size + (required_size>>1); /* allocate a bit ahead */ bucket->ubuf_size = new_size; - bucket->ubuf = (uint8_t*) _sg_imgui_realloc(bucket->ubuf, old_size, new_size); + bucket->ubuf = (uint8_t*) _sg_imgui_realloc(&ctx->desc.allocator, bucket->ubuf, old_size, new_size); } _SOKOL_PRIVATE sg_imgui_capture_item_t* _sg_imgui_capture_next_write_item(sg_imgui_t* ctx) { @@ -2834,15 +2988,15 @@ _SOKOL_PRIVATE void _sg_imgui_err_bindings_invalid(void* user_data) { /*--- IMGUI HELPERS ----------------------------------------------------------*/ _SOKOL_PRIVATE bool _sg_imgui_draw_resid_list_item(uint32_t res_id, const char* label, bool selected) { - igPushIDInt((int)res_id); + igPushID_Int((int)res_id); bool res; if (label[0]) { - res = igSelectableBool(label, selected, 0, IMVEC2(0,0)); + res = igSelectable_Bool(label, selected, 0, IMVEC2(0,0)); } else { sg_imgui_str_t str; _sg_imgui_snprintf(&str, "0x%08X", res_id); - res = igSelectableBool(str.buf, selected, 0, IMVEC2(0,0)); + res = igSelectable_Bool(str.buf, selected, 0, IMVEC2(0,0)); } igPopID(); return res; @@ -2859,7 +3013,7 @@ _SOKOL_PRIVATE bool _sg_imgui_draw_resid_link(uint32_t res_type, uint32_t res_id _sg_imgui_snprintf(&str_buf, "0x%08X", res_id); str = str_buf.buf; } - igPushIDInt((int)((res_type<<24)|res_id)); + igPushID_Int((int)((res_type<<24)|res_id)); bool res = igSmallButton(str); igPopID(); return res; @@ -2908,7 +3062,7 @@ _SOKOL_PRIVATE void _sg_imgui_show_shader(sg_imgui_t* ctx, sg_shader shd) { } _SOKOL_PRIVATE void _sg_imgui_draw_buffer_list(sg_imgui_t* ctx) { - igBeginChildStr("buffer_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); + igBeginChild_Str("buffer_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); for (int i = 0; i < ctx->buffers.num_slots; i++) { sg_buffer buf = ctx->buffers.slots[i].res_id; sg_resource_state state = sg_query_buffer_state(buf); @@ -2923,7 +3077,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_buffer_list(sg_imgui_t* ctx) { } _SOKOL_PRIVATE void _sg_imgui_draw_image_list(sg_imgui_t* ctx) { - igBeginChildStr("image_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); + igBeginChild_Str("image_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); for (int i = 0; i < ctx->images.num_slots; i++) { sg_image img = ctx->images.slots[i].res_id; sg_resource_state state = sg_query_image_state(img); @@ -2938,7 +3092,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_image_list(sg_imgui_t* ctx) { } _SOKOL_PRIVATE void _sg_imgui_draw_shader_list(sg_imgui_t* ctx) { - igBeginChildStr("shader_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); + igBeginChild_Str("shader_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); for (int i = 0; i < ctx->shaders.num_slots; i++) { sg_shader shd = ctx->shaders.slots[i].res_id; sg_resource_state state = sg_query_shader_state(shd); @@ -2953,7 +3107,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_list(sg_imgui_t* ctx) { } _SOKOL_PRIVATE void _sg_imgui_draw_pipeline_list(sg_imgui_t* ctx) { - igBeginChildStr("pipeline_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); + igBeginChild_Str("pipeline_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); for (int i = 1; i < ctx->pipelines.num_slots; i++) { sg_pipeline pip = ctx->pipelines.slots[i].res_id; sg_resource_state state = sg_query_pipeline_state(pip); @@ -2968,7 +3122,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_pipeline_list(sg_imgui_t* ctx) { } _SOKOL_PRIVATE void _sg_imgui_draw_pass_list(sg_imgui_t* ctx) { - igBeginChildStr("pass_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); + igBeginChild_Str("pass_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); for (int i = 1; i < ctx->passes.num_slots; i++) { sg_pass pass = ctx->passes.slots[i].res_id; sg_resource_state state = sg_query_pass_state(pass); @@ -2983,19 +3137,19 @@ _SOKOL_PRIVATE void _sg_imgui_draw_pass_list(sg_imgui_t* ctx) { } _SOKOL_PRIVATE void _sg_imgui_draw_capture_list(sg_imgui_t* ctx) { - igBeginChildStr("capture_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); + igBeginChild_Str("capture_list", IMVEC2(_SG_IMGUI_LIST_WIDTH,0), true, 0); const int num_items = _sg_imgui_capture_num_read_items(ctx); uint64_t group_stack = 1; /* bit set: group unfolded, cleared: folded */ for (int i = 0; i < num_items; i++) { const sg_imgui_capture_item_t* item = _sg_imgui_capture_read_item_at(ctx, i); sg_imgui_str_t item_string = _sg_imgui_capture_item_string(ctx, i, item); - igPushStyleColorU32(ImGuiCol_Text, item->color); - igPushIDInt(i); + igPushStyleColor_U32(ImGuiCol_Text, item->color); + igPushID_Int(i); if (item->cmd == SG_IMGUI_CMD_PUSH_DEBUG_GROUP) { if (group_stack & 1) { group_stack <<= 1; const char* group_name = item->args.push_debug_group.name.buf; - if (igTreeNodeStrStr(group_name, "Group: %s", group_name)) { + if (igTreeNode_StrStr(group_name, "Group: %s", group_name)) { group_stack |= 1; } } @@ -3010,7 +3164,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_capture_list(sg_imgui_t* ctx) { group_stack >>= 1; } else if (group_stack & 1) { - if (igSelectableBool(item_string.buf, ctx->capture.sel_item == i, 0, IMVEC2(0,0))) { + if (igSelectable_Bool(item_string.buf, ctx->capture.sel_item == i, 0, IMVEC2(0,0))) { ctx->capture.sel_item = i; } if (igIsItemHovered(0)) { @@ -3025,7 +3179,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_capture_list(sg_imgui_t* ctx) { _SOKOL_PRIVATE void _sg_imgui_draw_buffer_panel(sg_imgui_t* ctx, sg_buffer buf) { if (buf.id != SG_INVALID_ID) { - igBeginChildStr("buffer", IMVEC2(0,0), false, 0); + igBeginChild_Str("buffer", IMVEC2(0,0), false, 0); sg_buffer_info info = sg_query_buffer_info(buf); if (info.slot.state == SG_RESOURCESTATE_VALID) { const sg_imgui_buffer_t* buf_ui = &ctx->buffers.slots[_sg_imgui_slot_index(buf.id)]; @@ -3060,7 +3214,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_embedded_image(sg_imgui_t* ctx, sg_image img, if (sg_query_image_state(img) == SG_RESOURCESTATE_VALID) { sg_imgui_image_t* img_ui = &ctx->images.slots[_sg_imgui_slot_index(img.id)]; if (_sg_imgui_image_renderable(img_ui->desc.type, img_ui->desc.pixel_format)) { - igPushIDInt((int)img.id); + igPushID_Int((int)img.id); igSliderFloat("Scale", scale, 0.125f, 8.0f, "%.3f", ImGuiSliderFlags_Logarithmic); float w = (float)img_ui->desc.width * (*scale); float h = (float)img_ui->desc.height * (*scale); @@ -3075,7 +3229,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_embedded_image(sg_imgui_t* ctx, sg_image img, _SOKOL_PRIVATE void _sg_imgui_draw_image_panel(sg_imgui_t* ctx, sg_image img) { if (img.id != SG_INVALID_ID) { - igBeginChildStr("image", IMVEC2(0,0), false, 0); + igBeginChild_Str("image", IMVEC2(0,0), false, 0); sg_image_info info = sg_query_image_info(img); if (info.slot.state == SG_RESOURCESTATE_VALID) { sg_imgui_image_t* img_ui = &ctx->images.slots[_sg_imgui_slot_index(img.id)]; @@ -3139,14 +3293,14 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_stage(const sg_shader_stage_desc* stag } } if (num_valid_ubs > 0) { - if (igTreeNodeStr("Uniform Blocks")) { + if (igTreeNode_Str("Uniform Blocks")) { for (int i = 0; i < num_valid_ubs; i++) { igText("#%d:", i); const sg_shader_uniform_block_desc* ub = &stage->uniform_blocks[i]; for (int j = 0; j < SG_MAX_UB_MEMBERS; j++) { const sg_shader_uniform_desc* u = &ub->uniforms[j]; if (SG_UNIFORMTYPE_INVALID != u->type) { - if (u->array_count == 0) { + if (u->array_count <= 1) { igText(" %s %s", _sg_imgui_uniformtype_string(u->type), u->name ? u->name : ""); } else { @@ -3159,7 +3313,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_stage(const sg_shader_stage_desc* stag } } if (num_valid_images > 0) { - if (igTreeNodeStr("Images")) { + if (igTreeNode_Str("Images")) { for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { const sg_shader_image_desc* sid = &stage->images[i]; if (sid->image_type != _SG_IMAGETYPE_DEFAULT) { @@ -3182,13 +3336,13 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_stage(const sg_shader_stage_desc* stag igText("D3D11 Target: %s", stage->d3d11_target); } if (stage->source) { - if (igTreeNodeStr("Source")) { + if (igTreeNode_Str("Source")) { igText("%s", stage->source); igTreePop(); } } else if (stage->bytecode.ptr) { - if (igTreeNodeStr("Byte Code")) { + if (igTreeNode_Str("Byte Code")) { igText("Byte-code display currently not supported."); igTreePop(); } @@ -3197,14 +3351,14 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_stage(const sg_shader_stage_desc* stag _SOKOL_PRIVATE void _sg_imgui_draw_shader_panel(sg_imgui_t* ctx, sg_shader shd) { if (shd.id != SG_INVALID_ID) { - igBeginChildStr("shader", IMVEC2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); + igBeginChild_Str("shader", IMVEC2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); sg_shader_info info = sg_query_shader_info(shd); if (info.slot.state == SG_RESOURCESTATE_VALID) { const sg_imgui_shader_t* shd_ui = &ctx->shaders.slots[_sg_imgui_slot_index(shd.id)]; igText("Label: %s", shd_ui->label.buf[0] ? shd_ui->label.buf : "---"); _sg_imgui_draw_resource_slot(&info.slot); igSeparator(); - if (igTreeNodeStr("Attrs")) { + if (igTreeNode_Str("Attrs")) { for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { const sg_shader_attr_desc* a_desc = &shd_ui->desc.attrs[i]; if (a_desc->name || a_desc->sem_index) { @@ -3216,11 +3370,11 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_panel(sg_imgui_t* ctx, sg_shader shd) } igTreePop(); } - if (igTreeNodeStr("Vertex Shader Stage")) { + if (igTreeNode_Str("Vertex Shader Stage")) { _sg_imgui_draw_shader_stage(&shd_ui->desc.vs); igTreePop(); } - if (igTreeNodeStr("Fragment Shader Stage")) { + if (igTreeNode_Str("Fragment Shader Stage")) { _sg_imgui_draw_shader_stage(&shd_ui->desc.fs); igTreePop(); } @@ -3233,7 +3387,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_shader_panel(sg_imgui_t* ctx, sg_shader shd) } _SOKOL_PRIVATE void _sg_imgui_draw_vertex_layout(const sg_layout_desc* layout) { - if (igTreeNodeStr("Buffers")) { + if (igTreeNode_Str("Buffers")) { for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { const sg_buffer_layout_desc* l_desc = &layout->buffers[i]; if (l_desc->stride > 0) { @@ -3245,7 +3399,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_vertex_layout(const sg_layout_desc* layout) { } igTreePop(); } - if (igTreeNodeStr("Attrs")) { + if (igTreeNode_Str("Attrs")) { for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { const sg_vertex_attr_desc* a_desc = &layout->attrs[i]; if (a_desc->format != SG_VERTEXFORMAT_INVALID) { @@ -3271,11 +3425,11 @@ _SOKOL_PRIVATE void _sg_imgui_draw_stencil_state(const sg_stencil_state* ss) { igText("Read Mask: 0x%02X", ss->read_mask); igText("Write Mask: 0x%02X", ss->write_mask); igText("Ref: 0x%02X", ss->ref); - if (igTreeNodeStr("Front")) { + if (igTreeNode_Str("Front")) { _sg_imgui_draw_stencil_face_state(&ss->front); igTreePop(); } - if (igTreeNodeStr("Back")) { + if (igTreeNode_Str("Back")) { _sg_imgui_draw_stencil_face_state(&ss->back); igTreePop(); } @@ -3303,7 +3457,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_blend_state(const sg_blend_state* bs) { _SOKOL_PRIVATE void _sg_imgui_draw_color_state(const sg_color_state* cs) { igText("Pixel Format: %s", _sg_imgui_pixelformat_string(cs->pixel_format)); igText("Write Mask: %s", _sg_imgui_colormask_string(cs->write_mask)); - if (igTreeNodeStr("Blend State:")) { + if (igTreeNode_Str("Blend State:")) { _sg_imgui_draw_blend_state(&cs->blend); igTreePop(); } @@ -3311,7 +3465,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_color_state(const sg_color_state* cs) { _SOKOL_PRIVATE void _sg_imgui_draw_pipeline_panel(sg_imgui_t* ctx, sg_pipeline pip) { if (pip.id != SG_INVALID_ID) { - igBeginChildStr("pipeline", IMVEC2(0,0), false, 0); + igBeginChild_Str("pipeline", IMVEC2(0,0), false, 0); sg_pipeline_info info = sg_query_pipeline_info(pip); if (info.slot.state == SG_RESOURCESTATE_VALID) { const sg_imgui_pipeline_t* pip_ui = &ctx->pipelines.slots[_sg_imgui_slot_index(pip.id)]; @@ -3322,15 +3476,15 @@ _SOKOL_PRIVATE void _sg_imgui_draw_pipeline_panel(sg_imgui_t* ctx, sg_pipeline p if (_sg_imgui_draw_shader_link(ctx, pip_ui->desc.shader)) { _sg_imgui_show_shader(ctx, pip_ui->desc.shader); } - if (igTreeNodeStr("Vertex Layout")) { + if (igTreeNode_Str("Vertex Layout")) { _sg_imgui_draw_vertex_layout(&pip_ui->desc.layout); igTreePop(); } - if (igTreeNodeStr("Depth State")) { + if (igTreeNode_Str("Depth State")) { _sg_imgui_draw_depth_state(&pip_ui->desc.depth); igTreePop(); } - if (igTreeNodeStr("Stencil State")) { + if (igTreeNode_Str("Stencil State")) { _sg_imgui_draw_stencil_state(&pip_ui->desc.stencil); igTreePop(); } @@ -3338,7 +3492,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_pipeline_panel(sg_imgui_t* ctx, sg_pipeline p for (int i = 0; i < pip_ui->desc.color_count; i++) { sg_imgui_str_t str; _sg_imgui_snprintf(&str, "Color %d", i); - if (igTreeNodeStr(str.buf)) { + if (igTreeNode_Str(str.buf)) { _sg_imgui_draw_color_state(&pip_ui->desc.colors[i]); igTreePop(); } @@ -3371,7 +3525,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_pass_attachment(sg_imgui_t* ctx, const sg_pas _SOKOL_PRIVATE void _sg_imgui_draw_pass_panel(sg_imgui_t* ctx, sg_pass pass) { if (pass.id != SG_INVALID_ID) { - igBeginChildStr("pass", IMVEC2(0,0), false, 0); + igBeginChild_Str("pass", IMVEC2(0,0), false, 0); sg_pass_info info = sg_query_pass_info(pass); if (info.slot.state == SG_RESOURCESTATE_VALID) { sg_imgui_pass_t* pass_ui = &ctx->passes.slots[_sg_imgui_slot_index(pass.id)]; @@ -3482,7 +3636,9 @@ _SOKOL_PRIVATE void _sg_imgui_draw_uniforms_panel(sg_imgui_t* ctx, const sg_imgu sg_imgui_capture_bucket_t* bucket = _sg_imgui_capture_get_read_bucket(ctx); SOKOL_ASSERT((args->ubuf_pos + args->data_size) <= bucket->ubuf_size); const float* uptrf = (const float*) (bucket->ubuf + args->ubuf_pos); + const int32_t* uptri32 = (const int32_t*) uptrf; if (!draw_dump) { + uint32_t u_off = 0; for (int i = 0; i < SG_MAX_UB_MEMBERS; i++) { const sg_shader_uniform_desc* ud = &ub_desc->uniforms[i]; if (ud->type == SG_UNIFORMTYPE_INVALID) { @@ -3496,38 +3652,54 @@ _SOKOL_PRIVATE void _sg_imgui_draw_uniforms_panel(sg_imgui_t* ctx, const sg_imgu igText("%d: %s %s =", i, _sg_imgui_uniformtype_string(ud->type), ud->name?ud->name:""); } for (int item_index = 0; item_index < num_items; item_index++) { + const uint32_t u_size = _sg_imgui_std140_uniform_size(ud->type, ud->array_count) / 4; + const uint32_t u_align = _sg_imgui_std140_uniform_alignment(ud->type, ud->array_count) / 4; + u_off = _sg_imgui_align_u32(u_off, u_align); switch (ud->type) { case SG_UNIFORMTYPE_FLOAT: - igText(" %.3f", *uptrf); + igText(" %.3f", uptrf[u_off]); + break; + case SG_UNIFORMTYPE_INT: + igText(" %d", uptri32[u_off]); break; case SG_UNIFORMTYPE_FLOAT2: - igText(" %.3f, %.3f", uptrf[0], uptrf[1]); + igText(" %.3f, %.3f", uptrf[u_off], uptrf[u_off+1]); + break; + case SG_UNIFORMTYPE_INT2: + igText(" %d, %d", uptri32[u_off], uptri32[u_off+1]); break; case SG_UNIFORMTYPE_FLOAT3: - igText(" %.3f, %.3f, %.3f", uptrf[0], uptrf[1], uptrf[2]); + igText(" %.3f, %.3f, %.3f", uptrf[u_off], uptrf[u_off+1], uptrf[u_off+2]); + break; + case SG_UNIFORMTYPE_INT3: + igText(" %d, %d, %d", uptri32[u_off], uptri32[u_off+1], uptri32[u_off+2]); break; case SG_UNIFORMTYPE_FLOAT4: - igText(" %.3f, %.3f, %.3f, %.3f", uptrf[0], uptrf[1], uptrf[2], uptrf[3]); + igText(" %.3f, %.3f, %.3f, %.3f", uptrf[u_off], uptrf[u_off+1], uptrf[u_off+2], uptrf[u_off+3]); + break; + case SG_UNIFORMTYPE_INT4: + igText(" %d, %d, %d, %d", uptri32[u_off], uptri32[u_off+1], uptri32[u_off+2], uptri32[u_off+3]); break; case SG_UNIFORMTYPE_MAT4: igText(" %.3f, %.3f, %.3f, %.3f\n" " %.3f, %.3f, %.3f, %.3f\n" " %.3f, %.3f, %.3f, %.3f\n" " %.3f, %.3f, %.3f, %.3f", - uptrf[0], uptrf[1], uptrf[2], uptrf[3], - uptrf[4], uptrf[5], uptrf[6], uptrf[7], - uptrf[8], uptrf[9], uptrf[10], uptrf[11], - uptrf[12], uptrf[13], uptrf[14], uptrf[15]); + uptrf[u_off+0], uptrf[u_off+1], uptrf[u_off+2], uptrf[u_off+3], + uptrf[u_off+4], uptrf[u_off+5], uptrf[u_off+6], uptrf[u_off+7], + uptrf[u_off+8], uptrf[u_off+9], uptrf[u_off+10], uptrf[u_off+11], + uptrf[u_off+12], uptrf[u_off+13], uptrf[u_off+14], uptrf[u_off+15]); break; default: igText("???"); break; } - uptrf += _sg_imgui_uniform_size(ud->type, 1) / (int)sizeof(float); + u_off += u_size; } } } else { + // FIXME: float vs int const size_t num_floats = ub_desc->size / sizeof(float); for (uint32_t i = 0; i < num_floats; i++) { igText("%.3f, ", uptrf[i]); @@ -3589,8 +3761,8 @@ _SOKOL_PRIVATE void _sg_imgui_draw_capture_panel(sg_imgui_t* ctx) { return; } sg_imgui_capture_item_t* item = _sg_imgui_capture_read_item_at(ctx, sel_item_index); - igBeginChildStr("capture_item", IMVEC2(0, 0), false, 0); - igPushStyleColorU32(ImGuiCol_Text, item->color); + igBeginChild_Str("capture_item", IMVEC2(0, 0), false, 0); + igPushStyleColor_U32(ImGuiCol_Text, item->color); igText("%s", _sg_imgui_capture_item_string(ctx, sel_item_index, item).buf); igPopStyleColor(1); igSeparator(); @@ -3736,6 +3908,7 @@ _SOKOL_PRIVATE void _sg_imgui_draw_caps_panel(void) { igText(" max_image_array_layers: %d", l.max_image_array_layers); igText(" max_vertex_attrs: %d", l.max_vertex_attrs); igText(" gl_max_vertex_uniform_vectors: %d", l.gl_max_vertex_uniform_vectors); + igText(" gl_max_combined_texture_image_units: %d", l.gl_max_combined_texture_image_units); igText("\nUsable Pixelformats:"); for (int i = (int)(SG_PIXELFORMAT_NONE+1); i < (int)_SG_PIXELFORMAT_NUM; i++) { sg_pixel_format fmt = (sg_pixel_format)i; @@ -3753,16 +3926,26 @@ _SOKOL_PRIVATE void _sg_imgui_draw_caps_panel(void) { } } +#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_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + sg_imgui_desc_t res = *desc; + // FIXME: any additional default overrides would go here + return res; +} + /*--- PUBLIC FUNCTIONS -------------------------------------------------------*/ -SOKOL_API_IMPL void sg_imgui_init(sg_imgui_t* ctx) { - SOKOL_ASSERT(ctx); - memset(ctx, 0, sizeof(sg_imgui_t)); +SOKOL_API_IMPL void sg_imgui_init(sg_imgui_t* ctx, const sg_imgui_desc_t* desc) { + SOKOL_ASSERT(ctx && desc); + _sg_imgui_clear(ctx, sizeof(sg_imgui_t)); ctx->init_tag = 0xABCDABCD; + ctx->desc = _sg_imgui_desc_defaults(desc); _sg_imgui_capture_init(ctx); /* hook into sokol_gfx functions */ sg_trace_hooks hooks; - memset(&hooks, 0, sizeof(hooks)); + _sg_imgui_clear(&hooks, sizeof(hooks)); hooks.user_data = (void*) ctx; hooks.reset_state_cache = _sg_imgui_reset_state_cache; hooks.make_buffer = _sg_imgui_make_buffer; @@ -3827,37 +4010,27 @@ SOKOL_API_IMPL void sg_imgui_init(sg_imgui_t* ctx) { ctx->hooks = sg_install_trace_hooks(&hooks); /* allocate resource debug-info slots */ - sg_desc desc = sg_query_desc(); - ctx->buffers.num_slots = desc.buffer_pool_size; - ctx->images.num_slots = desc.image_pool_size; - ctx->shaders.num_slots = desc.shader_pool_size; - ctx->pipelines.num_slots = desc.pipeline_pool_size; - ctx->passes.num_slots = desc.pass_pool_size; + const sg_desc sgdesc = sg_query_desc(); + ctx->buffers.num_slots = sgdesc.buffer_pool_size; + ctx->images.num_slots = sgdesc.image_pool_size; + ctx->shaders.num_slots = sgdesc.shader_pool_size; + ctx->pipelines.num_slots = sgdesc.pipeline_pool_size; + ctx->passes.num_slots = sgdesc.pass_pool_size; const size_t buffer_pool_size = (size_t)ctx->buffers.num_slots * sizeof(sg_imgui_buffer_t); - ctx->buffers.slots = (sg_imgui_buffer_t*) _sg_imgui_alloc(buffer_pool_size); - SOKOL_ASSERT(ctx->buffers.slots); - memset(ctx->buffers.slots, 0, buffer_pool_size); + ctx->buffers.slots = (sg_imgui_buffer_t*) _sg_imgui_malloc_clear(&ctx->desc.allocator, buffer_pool_size); const size_t image_pool_size = (size_t)ctx->images.num_slots * sizeof(sg_imgui_image_t); - ctx->images.slots = (sg_imgui_image_t*) _sg_imgui_alloc(image_pool_size); - SOKOL_ASSERT(ctx->images.slots); - memset(ctx->images.slots, 0, image_pool_size); + ctx->images.slots = (sg_imgui_image_t*) _sg_imgui_malloc_clear(&ctx->desc.allocator, image_pool_size); const size_t shader_pool_size = (size_t)ctx->shaders.num_slots * sizeof(sg_imgui_shader_t); - ctx->shaders.slots = (sg_imgui_shader_t*) _sg_imgui_alloc(shader_pool_size); - SOKOL_ASSERT(ctx->shaders.slots); - memset(ctx->shaders.slots, 0, shader_pool_size); + ctx->shaders.slots = (sg_imgui_shader_t*) _sg_imgui_malloc_clear(&ctx->desc.allocator, shader_pool_size); const size_t pipeline_pool_size = (size_t)ctx->pipelines.num_slots * sizeof(sg_imgui_pipeline_t); - ctx->pipelines.slots = (sg_imgui_pipeline_t*) _sg_imgui_alloc(pipeline_pool_size); - SOKOL_ASSERT(ctx->pipelines.slots); - memset(ctx->pipelines.slots, 0, pipeline_pool_size); + ctx->pipelines.slots = (sg_imgui_pipeline_t*) _sg_imgui_malloc_clear(&ctx->desc.allocator, pipeline_pool_size); const size_t pass_pool_size = (size_t)ctx->passes.num_slots * sizeof(sg_imgui_pass_t); - ctx->passes.slots = (sg_imgui_pass_t*) _sg_imgui_alloc(pass_pool_size); - SOKOL_ASSERT(ctx->passes.slots); - memset(ctx->passes.slots, 0, pass_pool_size); + ctx->passes.slots = (sg_imgui_pass_t*) _sg_imgui_malloc_clear(&ctx->desc.allocator, pass_pool_size); } SOKOL_API_IMPL void sg_imgui_discard(sg_imgui_t* ctx) { @@ -3872,7 +4045,7 @@ SOKOL_API_IMPL void sg_imgui_discard(sg_imgui_t* ctx) { _sg_imgui_buffer_destroyed(ctx, i); } } - _sg_imgui_free((void*)ctx->buffers.slots); + _sg_imgui_free(&ctx->desc.allocator, (void*)ctx->buffers.slots); ctx->buffers.slots = 0; } if (ctx->images.slots) { @@ -3881,7 +4054,7 @@ SOKOL_API_IMPL void sg_imgui_discard(sg_imgui_t* ctx) { _sg_imgui_image_destroyed(ctx, i); } } - _sg_imgui_free((void*)ctx->images.slots); + _sg_imgui_free(&ctx->desc.allocator, (void*)ctx->images.slots); ctx->images.slots = 0; } if (ctx->shaders.slots) { @@ -3890,7 +4063,7 @@ SOKOL_API_IMPL void sg_imgui_discard(sg_imgui_t* ctx) { _sg_imgui_shader_destroyed(ctx, i); } } - _sg_imgui_free((void*)ctx->shaders.slots); + _sg_imgui_free(&ctx->desc.allocator, (void*)ctx->shaders.slots); ctx->shaders.slots = 0; } if (ctx->pipelines.slots) { @@ -3899,7 +4072,7 @@ SOKOL_API_IMPL void sg_imgui_discard(sg_imgui_t* ctx) { _sg_imgui_pipeline_destroyed(ctx, i); } } - _sg_imgui_free((void*)ctx->pipelines.slots); + _sg_imgui_free(&ctx->desc.allocator, (void*)ctx->pipelines.slots); ctx->pipelines.slots = 0; } if (ctx->passes.slots) { @@ -3908,7 +4081,7 @@ SOKOL_API_IMPL void sg_imgui_discard(sg_imgui_t* ctx) { _sg_imgui_pass_destroyed(ctx, i); } } - _sg_imgui_free((void*)ctx->passes.slots); + _sg_imgui_free(&ctx->desc.allocator, (void*)ctx->passes.slots); ctx->passes.slots = 0; } } diff --git a/3rdparty/sokol/util/sokol_gl.h b/3rdparty/sokol/util/sokol_gl.h index 578d9eb..2360a4c 100644 --- a/3rdparty/sokol/util/sokol_gl.h +++ b/3rdparty/sokol/util/sokol_gl.h @@ -27,12 +27,9 @@ ...optionally provide the following macros to override defaults: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_GL_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GL_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) If sokol_gl.h is compiled as a DLL, define the following before @@ -67,7 +64,7 @@ - triangle list and strip - line list and strip - quad list (TODO: quad strips) - - point list (TODO: point size) + - point list - one texture layer (no multi-texturing) - viewport and scissor-rect with selectable origin (top-left or bottom-left) - all GL 1.x matrix stack functions, and additionally equivalent @@ -78,7 +75,7 @@ - vertex arrays (although providing whole chunks of vertex data at once might be a useful feature for a later version) - texture coordinate generation - - point size and line width + - line width - all pixel store functions - no ALPHA_TEST - no clear functions (clearing is handled by the sokol-gfx render pass) @@ -104,14 +101,26 @@ sokol-gfx resource objects. If you're intending to render to the default pass, and also don't - want to tweak memory usage, you can just keep sgl_desc_t zero-initialized: + want to tweak memory usage, and don't want any logging output you can + just keep sgl_desc_t zero-initialized: sgl_setup(&(sgl_desc_t*){ 0 }); In this case, sokol-gl will create internal sg_pipeline objects that - are compatible with the sokol-app default framebuffer. If you want - to render into a framebuffer with different pixel-format and MSAA - attributes you need to provide the matching attributes in the + are compatible with the sokol-app default framebuffer. + + I would recommend to at least install a logging callback so that + you'll see any warnings and errors. The easiest way is through + sokol_log.h: + + #include "sokol_log.h" + + sgl_setup(&(sgl_desc_t){ + .logger.func = slog_func. + }); + + If you want to render into a framebuffer with different pixel-format + and MSAA attributes you need to provide the matching attributes in the sgl_setup() call: sgl_setup(&(sgl_desc_t*){ @@ -196,6 +205,7 @@ - current texture coordinate to u=0.0f, v=0.0f - current color to white (rgba all 1.0f) + - current point size to 1.0f - unbind the current texture and texturing will be disabled - *all* matrices will be set to identity (also the projection matrix) - the default render state will be set by loading the 'default pipeline' @@ -298,10 +308,12 @@ --- perform primitive rendering: - ...set the current texture coordinate and color 'registers' with: + ...set the current texture coordinate and color 'registers' with or + point size with: sgl_t2f(float u, float v) - set current texture coordinate sgl_c*(...) - set current color + sgl_point_size(float size) - set current point size There are several functions for setting the color (as float values, unsigned byte values, packed as unsigned 32-bit integer, with @@ -310,6 +322,9 @@ NOTE that these are the only functions that can be called both inside sgl_begin_*() / sgl_end() and outside. + Also NOTE that point size is currently hardwired to 1.0f if the D3D11 + backend is used. + ...start a primitive vertex sequence with: sgl_begin_points() @@ -376,6 +391,32 @@ ...if sokol-gl is in an error-state, sgl_draw() will skip any rendering, and reset the error code to SGL_NO_ERROR. + RENDER LAYERS + ============= + Render layers allow to split sokol-gl rendering into separate draw-command + groups which can then be rendered separately in a sokol-gfx draw pass. This + allows to mix/interleave sokol-gl rendering with other render operations. + + Layered rendering is controlled through two functions: + + sgl_layer(int layer_id) + sgl_draw_layer(int layer_id) + + (and the context-variant sgl_draw_layer(): sgl_context_draw_layer() + + The sgl_layer() function sets the 'current layer', any sokol-gl calls + which internally record draw commands will also store the current layer + in the draw command, and later in a sokol-gfx render pass, a call + to sgl_draw_layer() will only render the draw commands that have + a matching layer. + + The default layer is '0', this is active after sokol-gl setup, and + is also restored at the start of a new frame (but *not* by calling + sgl_defaults()). + + NOTE that calling sgl_draw() is equivalent with sgl_draw_layer(0) + (in general you should either use either use sgl_draw() or + sgl_draw_layer() in an application, but not both). WORKING WITH CONTEXTS: ====================== @@ -460,7 +501,7 @@ The only functions which call into sokol_gfx.h are: - sgl_setup() - sgl_shutdown() - - sgl_draw() + - sgl_draw() (and variants) sgl_setup() must be called after initializing sokol-gfx. sgl_shutdown() must be called before shutting down sokol-gfx. @@ -505,10 +546,13 @@ all pipeline objects) are destroyed - the 3 memory buffers are freed - sgl_draw(): + sgl_draw() (and variants) - copy all recorded vertex data into the dynamic sokol-gfx buffer via a call to sg_update_buffer() - for each recorded command: + - if the layer number stored in the command doesn't match + the layer that's to be rendered, skip to the next + command - if it's a viewport command, call sg_apply_viewport() - if it's a scissor-rect command, call sg_apply_scissor_rect() - if it's a draw command: @@ -530,10 +574,11 @@ A draw command will be merged with the previous command if "no relevant state has changed" since the last sgl_end(), meaning: - - no calls to sgl_apply_viewport() and sgl_apply_scissor_rect() + - no calls to sgl_viewport() and sgl_scissor_rect() - the primitive type hasn't changed - the primitive type isn't a 'strip type' (no line or triangle strip) - the pipeline state object hasn't changed + - the current layer hasn't changed - none of the matrices has changed - none of the texture state has changed @@ -541,6 +586,75 @@ to render in the previous draw command will be incremented by the number of vertices in the new draw command. + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sgl_setup(&(sgl_desc_t){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sgl_setup(&(sgl_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sgl' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SGL_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-gl like this: + + sgl_setup(&(sgl_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + LICENSE ======= zlib/libpng license @@ -569,6 +683,7 @@ #define SOKOL_GL_INCLUDED (1) #include #include +#include // size_t, offsetof #if !defined(SOKOL_GFX_INCLUDED) #error "Please include sokol_gfx.h before sokol_gl.h" @@ -591,6 +706,47 @@ extern "C" { #endif +/* + sgl_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sgl_log_item' - and in debug mode only - corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SGL_LOG_ITEMS \ + _SGL_LOGITEM_XMACRO(OK, "Ok") \ + _SGL_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SGL_LOGITEM_XMACRO(MAKE_PIPELINE_FAILED, "sg_make_pipeline() failed") \ + _SGL_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted (use sgl_desc_t.pipeline_pool_size to adjust)") \ + _SGL_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed") \ + _SGL_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (use sgl_desc_t.context_pool_size to adjust)") \ + _SGL_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context") \ + +#define _SGL_LOGITEM_XMACRO(item,msg) SGL_LOGITEM_##item, +typedef enum sgl_log_item_t { + _SGL_LOG_ITEMS +} sgl_log_item_t; +#undef _SGL_LOGITEM_XMACRO + +/* + sgl_logger_t + + Used in sgl_desc_t to provide a custom logging and error reporting + callback to sokol-gl. +*/ +typedef struct sgl_logger_t { + void (*func)( + const char* tag, // always "sgl" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SGL_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sgl_logger_t; + /* sokol_gl pipeline handle (created with sgl_make_pipeline()) */ typedef struct sgl_pipeline { uint32_t id; } sgl_pipeline; @@ -628,6 +784,20 @@ typedef struct sgl_context_desc_t { int sample_count; } sgl_context_desc_t; +/* + sgl_allocator_t + + Used in sgl_desc_t to provide custom memory-alloc and -free functions + to sokol_gl.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sgl_allocator_t { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} sgl_allocator_t; + typedef struct sgl_desc_t { int max_vertices; // default: 64k int max_commands; // default: 16k @@ -637,6 +807,8 @@ typedef struct sgl_desc_t { sg_pixel_format depth_format; int sample_count; sg_face_winding face_winding; // default: SG_FACEWINDING_CCW + sgl_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sgl_logger_t logger; // optional log function override (default: NO LOGGING) } sgl_desc_t; /* the default context handle */ @@ -657,6 +829,12 @@ SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx); SOKOL_GL_API_DECL sgl_context sgl_get_context(void); SOKOL_GL_API_DECL sgl_context sgl_default_context(void); +/* draw recorded commands (call inside a sokol-gfx render pass) */ +SOKOL_GL_API_DECL void sgl_draw(); +SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx); +SOKOL_GL_API_DECL void sgl_draw_layer(int layer_id); +SOKOL_GL_API_DECL void sgl_context_draw_layer(sgl_context ctx, int layer_id); + /* create and destroy pipeline objects */ SOKOL_GL_API_DECL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc); SOKOL_GL_API_DECL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc); @@ -671,6 +849,7 @@ SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, boo SOKOL_GL_API_DECL void sgl_enable_texture(void); SOKOL_GL_API_DECL void sgl_disable_texture(void); SOKOL_GL_API_DECL void sgl_texture(sg_image img); +SOKOL_GL_API_DECL void sgl_layer(int layer_id); /* pipeline stack functions */ SOKOL_GL_API_DECL void sgl_load_default_pipeline(void); @@ -697,13 +876,14 @@ SOKOL_GL_API_DECL void sgl_lookat(float eye_x, float eye_y, float eye_z, float c SOKOL_GL_API_DECL void sgl_push_matrix(void); SOKOL_GL_API_DECL void sgl_pop_matrix(void); -/* these functions only set the internal 'current texcoord / color' (valid inside or outside begin/end) */ +/* these functions only set the internal 'current texcoord / color / point size' (valid inside or outside begin/end) */ SOKOL_GL_API_DECL void sgl_t2f(float u, float v); SOKOL_GL_API_DECL void sgl_c3f(float r, float g, float b); SOKOL_GL_API_DECL void sgl_c4f(float r, float g, float b, float a); SOKOL_GL_API_DECL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b); SOKOL_GL_API_DECL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); SOKOL_GL_API_DECL void sgl_c1i(uint32_t rgba); +SOKOL_GL_API_DECL void sgl_point_size(float s); /* define primitives, each begin/end is one draw command */ SOKOL_GL_API_DECL void sgl_begin_points(void); @@ -738,10 +918,6 @@ SOKOL_GL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float SOKOL_GL_API_DECL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba); SOKOL_GL_API_DECL void sgl_end(void); -/* render recorded commands */ -SOKOL_GL_API_DECL void sgl_draw(); -SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx); - #ifdef __cplusplus } /* extern "C" */ @@ -753,13 +929,23 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline #endif #endif /* SOKOL_GL_INCLUDED */ -/*-- IMPLEMENTATION ----------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_GL_IMPL #define SOKOL_GL_IMPL_INCLUDED (1) -#include /* offsetof */ -#include /* memset */ -#include /* M_PI, sqrtf, sinf, cosf */ +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sgl_desc_t.allocator to override memory allocation functions" +#endif + +#include // malloc/free +#include // memset +#include // M_PI, sqrtf, sinf, cosf #ifndef M_PI #define M_PI 3.14159265358979323846264338327 @@ -770,29 +956,13 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif -#ifndef SOKOL_UNREACHABLE - #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) -#endif #define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) #define _SGL_INIT_COOKIE (0xABCDABCD) @@ -813,10 +983,12 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline in vec4 position; in vec2 texcoord0; in vec4 color0; + in float psize; out vec4 uv; out vec4 color; void main() { gl_Position = mvp * position; + gl_PointSize = psize; uv = tm * vec4(texcoord0, 0.0, 1.0); color = color0; } @@ -836,33 +1008,37 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline */ #if defined(SOKOL_GLCORE33) -static const char _sgl_vs_source_glsl330[415] = { +static const char _sgl_vs_source_glsl330[478] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, - 0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, - 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, - 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, - 0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f, - 0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, - 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, - 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, - 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, - 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, - 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, - 0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74, - 0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20, - 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, - 0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c, - 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, - 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x70,0x73, + 0x69,0x7a,0x65,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76, + 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69, + 0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a, + 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, + 0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, + 0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, + 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34, + 0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; static const char _sgl_fs_source_glsl330[172] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, @@ -878,31 +1054,34 @@ static const char _sgl_fs_source_glsl330[172] = { 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) -static const char _sgl_vs_source_glsl100[381] = { +static const char _sgl_vs_source_glsl100[430] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75, 0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x75, - 0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63, - 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x76,0x61,0x72, - 0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, - 0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, - 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, - 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, - 0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, - 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74, - 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, - 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, - 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67, + 0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62, + 0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, + 0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a, + 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, + 0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, + 0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, + 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34, + 0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; static const char _sgl_fs_source_glsl100[210] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x70,0x72,0x65, @@ -921,162 +1100,156 @@ static const char _sgl_fs_source_glsl100[210] = { 0x0a,0x00, }; #elif defined(SOKOL_METAL) -static const uint8_t _sgl_vs_bytecode_metal_macos[3360] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sgl_vs_bytecode_metal_macos[3321] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x06,0x00,0x00,0x81,0x0a,0x00,0x0b,0x00, + 0xf9,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x10,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xec,0xec,0xa8,0x23,0x83,0x8d, - 0xc7,0xcf,0x6e,0x67,0x94,0x95,0x54,0x94,0xeb,0x98,0x15,0x2a,0x2c,0x54,0xa0,0x4b, - 0x24,0x6f,0x5e,0xed,0x6e,0x76,0x45,0xb7,0x5e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x80,0x27,0x7a,0x67,0xc7,0xfc,0xfd, + 0xe3,0x47,0x44,0x1b,0xf3,0x40,0x18,0x72,0x5a,0xd3,0x6e,0xad,0xec,0x58,0x70,0xc5, + 0x20,0xfe,0x98,0x5e,0x82,0x7f,0xed,0xf9,0x89,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, - 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x40,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, - 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, - 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xf0,0x0b,0x00,0x00, - 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf9,0x02,0x00,0x00, - 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, - 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, - 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, - 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, - 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, - 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, - 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, - 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, - 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, - 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, - 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, - 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, - 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, - 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, - 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, - 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, - 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, - 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, - 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, - 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, - 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, - 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, - 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, - 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, - 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, - 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, - 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, - 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, - 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, - 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, - 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, - 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, - 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, - 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, - 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, - 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00, - 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, - 0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, - 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, - 0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80, - 0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90,0x04,0x61,0x26,0x6a, - 0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1,0x1c,0xe8,0x21,0x1c, - 0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d,0xf0,0x01,0x05,0xe4, - 0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60,0x24,0x24,0x94,0x32, - 0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x72,0x60, - 0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00, - 0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, - 0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03, - 0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07, - 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71, - 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80, - 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, - 0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60, - 0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e, - 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71, - 0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40, - 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07, - 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a, - 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60, - 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07, - 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78, - 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10, - 0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, - 0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72, - 0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20, - 0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07, - 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d, - 0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00, - 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a, - 0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20, - 0xd4,0x46,0x00,0x88,0x8d,0x35,0x28,0x0f,0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00, - 0x19,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, - 0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93, - 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e, - 0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d, - 0x24,0x07,0x46,0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, - 0x25,0x07,0x46,0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, - 0x58,0x8e,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, - 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, - 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, - 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, - 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, - 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, - 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, - 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26, - 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, - 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, - 0x50,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, - 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, - 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72, - 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, - 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, - 0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, - 0x57,0x86,0xf7,0xf5,0x56,0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85, - 0xed,0x6d,0xcc,0x0d,0x26,0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, - 0x1e,0x5c,0xd9,0x97,0x5b,0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32, - 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86, - 0x30,0x4a,0xa5,0x58,0xca,0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50, - 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a, - 0xe1,0x94,0x4b,0xc1,0x94,0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, - 0xd1,0xa5,0xbd,0xb9,0x71,0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73, - 0x1b,0xa2,0x28,0x9f,0x72,0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53, - 0xc2,0x80,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, - 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, - 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, - 0x28,0x38,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29, - 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42, - 0x06,0x8b,0xb0,0x04,0x4a,0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca, - 0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4, - 0xc8,0x60,0x88,0xec,0x64,0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30, - 0xa1,0x2b,0xc3,0x1b,0x7b,0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a, - 0x19,0x2c,0xc1,0x12,0x28,0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce, - 0xca,0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60, - 0x88,0xec,0x68,0xbe,0xcc,0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96, - 0x41,0x19,0x03,0x85,0x0c,0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5, - 0x0d,0xa8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93, - 0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26, - 0xe7,0x22,0x57,0x16,0x46,0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c, - 0xee,0x8b,0x2e,0x0f,0xae,0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18, - 0x1d,0x0d,0x1e,0x0d,0x87,0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88, - 0x45,0x50,0xe6,0x40,0xa1,0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46, - 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a, - 0x65,0xbc,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2, - 0xd8,0xd2,0xce,0xdc,0xbe,0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9, - 0x85,0xb5,0xcd,0x71,0xf8,0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07, - 0x4b,0xa1,0x90,0xc1,0x22,0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1, - 0x52,0x28,0x78,0xb0,0x24,0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce, - 0x40,0x59,0x03,0xc5,0x0d,0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, + 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0xc0,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xed,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80, + 0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36, + 0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0, + 0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1, + 0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e, + 0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78, + 0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08, + 0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda, + 0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81, + 0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8, + 0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87, + 0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76, + 0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30, + 0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01, + 0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08, + 0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36, + 0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d, + 0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca, + 0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90, + 0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07, + 0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61, + 0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e, + 0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4, + 0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e, + 0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda, + 0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72, + 0x00,0x36,0x18,0x42,0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00, + 0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48, + 0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90, + 0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04, + 0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2, + 0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0, + 0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44, + 0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28, + 0x86,0x08,0x23,0x80,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x52,0x60,0xcd,0x11,0x80, + 0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, + 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, + 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, + 0x50,0x04,0x05,0x18,0x50,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00, + 0x88,0x8d,0x25,0x30,0x00,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x01,0x01,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, + 0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x26,0x65,0x88,0xa0,0x10,0x43, + 0x8c,0x25,0x58,0x90,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25, + 0x58,0x82,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6, + 0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36, + 0x44,0x50,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e, + 0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65, + 0x43,0x04,0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95, + 0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1, + 0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58, + 0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d, + 0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72, + 0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc, + 0xd8,0xde,0xc2,0xe8,0x68,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85, + 0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42, + 0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6, + 0xf6,0x26,0x37,0x84,0x51,0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96, + 0x26,0xe7,0x02,0xf7,0x36,0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea, + 0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68, + 0x74,0xc2,0xd2,0xe4,0x5c,0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58, + 0x98,0xb1,0xbd,0x85,0xd1,0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a, + 0xd3,0x2b,0x1b,0xa2,0x28,0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14, + 0x4c,0xd9,0x94,0x8f,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57, + 0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4, + 0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32,0x3c,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74, + 0x65,0x64,0x28,0x5f,0x5f,0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f, + 0x6c,0x64,0x65,0x72,0x5f,0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc, + 0x86,0x50,0x8b,0xa0,0x84,0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22, + 0x29,0x93,0x42,0x06,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d, + 0xbd,0xbd,0xc9,0x91,0xc1,0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09, + 0x94,0x31,0x50,0x22,0xc5,0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x43,0xa8,0x65,0x50,0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48, + 0x91,0x94,0x49,0x49,0x03,0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2, + 0x40,0x11,0x83,0xc5,0x58,0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a, + 0x61,0x69,0x72,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4, + 0xea,0xcc,0xcc,0xca,0xe4,0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8, + 0x95,0x85,0x91,0x91,0x0a,0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2, + 0xcb,0x83,0x2b,0xfb,0x4a,0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83, + 0x47,0xc3,0xa1,0xcd,0x0e,0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94, + 0x38,0x50,0xe4,0x60,0x21,0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a, + 0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf, + 0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4, + 0x33,0xb7,0xaf,0xb9,0x34,0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x1c,0xbe,0x64,0x62,0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28, + 0x62,0xb0,0x08,0x4b,0xa0,0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a, + 0x1d,0x2c,0x89,0x12,0x29,0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0, + 0x40,0x51,0x03,0x85,0x0d,0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,0xf0, 0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3, 0x33,0x95,0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34, - 0x44,0x50,0xfe,0x60,0x88,0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41, - 0x09,0x85,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, + 0x44,0x50,0xfa,0x60,0x88,0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40, + 0xf9,0x83,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, 0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14, 0x21,0x18,0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e, 0xee,0x40,0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5, @@ -1084,72 +1257,76 @@ static const uint8_t _sgl_vs_bytecode_metal_macos[3360] = { 0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b, 0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1, 0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0, - 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07, - 0x92,0xa8,0x11,0x4a,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43, - 0x39,0xe0,0xc3,0x94,0xa0,0x0f,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00, - 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, - 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, - 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, - 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, - 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, - 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, - 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, - 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, - 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, - 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, - 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, - 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, - 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, - 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, - 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, - 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, - 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, - 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, - 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, - 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, - 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, - 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, - 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, - 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, - 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, - 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, - 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, - 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, - 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, - 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22, - 0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11, - 0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84, - 0x61,0x10,0x05,0x65,0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7, - 0x2d,0x14,0x94,0x41,0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, - 0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a, - 0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33, - 0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a, - 0xca,0x20,0x83,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0, - 0x0d,0x3c,0x0a,0xca,0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8, - 0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3, - 0x0d,0x61,0x10,0x00,0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10, - 0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11, - 0x88,0xc2,0x96,0x41,0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51, - 0xd8,0x32,0x60,0x81,0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0x65,0x50,0x18,0x67, + 0x84,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8, + 0x30,0x25,0xd8,0x03,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00, + 0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22, + 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40, + 0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00, + 0x00,0xe3,0x15,0x4b,0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9, + 0x8c,0x57,0x3c,0x55,0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50, + 0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68, + 0x03,0x36,0x20,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b, + 0xc4,0x00,0x0d,0xe2,0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42, + 0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05, + 0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d, + 0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00, + 0x00,0x5b,0x86,0x20,0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61, + 0xcb,0xf0,0x04,0xa0,0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19, + 0xba,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const uint8_t _sgl_fs_bytecode_metal_macos[2909] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x5d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sgl_fs_bytecode_metal_macos[2829] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x06,0x00,0x00,0x81,0x0a,0x00,0x0b,0x00, + 0x0d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x80,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x30,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xc3,0xf2,0x7e,0x47,0xcc,0xf2,0x87, - 0xc2,0x27,0x51,0x05,0xdb,0xac,0xc5,0x2d,0x03,0xfc,0xe6,0x91,0x1c,0x38,0xc9,0xd8, - 0x34,0x4c,0xa7,0xd1,0x68,0xc2,0x62,0x41,0xf6,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xeb,0xd5,0xe5,0x1e,0x0a,0x10,0x1a, + 0x32,0xd3,0xbd,0xd9,0xe3,0x31,0x39,0x1e,0x13,0x0d,0xe0,0xb8,0x26,0xfa,0x4f,0x40, + 0xeb,0xf7,0x82,0x75,0x70,0x8a,0xc9,0x08,0x0c,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, - 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x64,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x96,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x14,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x82,0x02,0x00,0x00,0x0b,0x82,0x20, 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, @@ -1191,491 +1368,88 @@ static const uint8_t _sgl_fs_bytecode_metal_macos[2909] = { 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, - 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x00,0x89,0x20,0x00,0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, - 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, - 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, - 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, - 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, - 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, - 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, - 0xb5,0x81,0x80,0x1c,0x58,0x23,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, - 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, - 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, - 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, - 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, - 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, - 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, - 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, - 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, - 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, - 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, - 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, - 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, - 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, - 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, - 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, - 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, - 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, - 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, - 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, - 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, - 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, - 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, - 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, - 0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00, - 0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, - 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70, - 0xac,0x41,0x79,0x08,0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c, - 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42, - 0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, - 0x2c,0xc2,0x23,0x2c,0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa, - 0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x24,0x07,0x46,0xc6,0x25,0x86,0x06, - 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x25,0x07,0x46,0xc6,0x25,0x86,0xc6, - 0x25,0x27,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46, - 0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32, - 0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45, - 0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63, - 0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68, - 0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9, - 0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99, - 0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1, - 0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6, - 0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6, - 0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c, - 0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17, - 0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b, - 0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c, - 0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f,0x76,0x65, - 0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0x67, - 0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77, - 0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec, - 0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39, - 0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63, - 0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x86,0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21, - 0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb, - 0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06, - 0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a, - 0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb, - 0x21,0xd2,0x32,0x3c,0xdc,0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06, - 0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1, - 0xbd,0xc9,0x0d,0x91,0x16,0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a, - 0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51, - 0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23, - 0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae, - 0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64, - 0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0x32, - 0x78,0xcc,0x60,0x19,0x16,0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0, - 0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d, - 0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39, - 0x9e,0x35,0x78,0xcc,0x60,0x19,0x16,0xe1,0xb1,0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86, - 0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7, - 0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5, - 0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86, - 0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0, - 0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3, - 0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b, - 0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94, - 0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83, - 0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b, - 0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a, - 0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5, - 0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00, - 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, - 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, - 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, - 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, - 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, - 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, - 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, - 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, - 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, - 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, - 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, - 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, - 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, - 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, - 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, - 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, - 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, - 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, - 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, - 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, - 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, - 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, - 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, - 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, - 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, - 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, - 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, - 0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, - 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, - 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, - 0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00, - 0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80,0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0, - 0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00,0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28, - 0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -static const uint8_t _sgl_vs_bytecode_metal_ios[3344] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x10,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, - 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x58,0xff,0x12,0x36,0x80,0x88,0xb5, - 0xf7,0x1c,0xc3,0xd5,0x31,0x57,0x18,0x26,0x26,0xf0,0x8c,0xd4,0x15,0xa2,0x55,0x8b, - 0xd5,0x3c,0x6b,0x11,0xbd,0x17,0x49,0x4a,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, - 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, - 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, - 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, - 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xec,0x0b,0x00,0x00, - 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf8,0x02,0x00,0x00, - 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, - 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, - 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, - 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, - 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, - 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, - 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, - 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, - 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, - 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, - 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, - 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, - 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, - 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, - 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, - 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, - 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, - 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, - 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, - 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, - 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, - 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, - 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, - 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, - 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, - 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, - 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, - 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, - 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, - 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, - 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, - 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, - 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, - 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, - 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, - 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, - 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54, - 0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00, - 0x89,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, - 0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c, - 0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94, - 0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90, - 0x04,0x61,0x26,0x6a,0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1, - 0x1c,0xe8,0x21,0x1c,0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d, - 0xf0,0x01,0x05,0xe4,0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60, - 0x24,0x24,0x94,0x32,0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e, - 0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08, - 0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70, - 0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80, - 0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07, - 0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9, - 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30, - 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07, - 0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72, - 0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07, - 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73, - 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, - 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07, - 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, - 0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, - 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07, - 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72, - 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20, - 0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, - 0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75, - 0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, - 0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07, - 0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00, - 0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, - 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, - 0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20,0xd4,0x46,0x00,0x88, - 0x8d,0x25,0x34,0x05,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x19,0x01,0x00,0x00, - 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, - 0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, - 0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84, - 0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46, - 0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07, - 0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8e,0x45,0x60, - 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0,0x16,0x96, - 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, - 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, - 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, - 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, - 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, - 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, - 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06, - 0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6, - 0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x1e,0x92,0x41,0x58, - 0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d, - 0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98, - 0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, - 0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f, - 0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, - 0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, - 0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x0d,0x26, - 0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0x97,0x5b, - 0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32,0x61,0x69,0x72,0x2e,0x61, - 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0x4a,0xa5,0x58,0xca, - 0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, - 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a,0xe1,0x94,0x4b,0xc1,0x94, - 0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, - 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x9f,0x72, - 0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53,0xc2,0x80,0x50,0x58,0x9a, - 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, - 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, - 0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x38,0x70,0x6f,0x73, - 0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29,0x44,0xe0,0xde,0xe6,0xd2, - 0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42,0x06,0x8b,0xb0,0x04,0x4a, - 0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca,0xdc,0xca,0xe4,0xc2,0xe8, - 0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60,0x88,0xec,0x64, - 0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30,0xa1,0x2b,0xc3,0x1b,0x7b, - 0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a,0x19,0x2c,0xc1,0x12,0x28, - 0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce,0xca,0xdc,0xca,0xe4,0xc2, - 0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60,0x88,0xec,0x68,0xbe,0xcc, - 0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96,0x41,0x19,0x03,0x85,0x0c, - 0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5,0x0d,0xa8,0x84,0xa5,0xc9, - 0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33, - 0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26,0xe7,0x22,0x57,0x16,0x46, - 0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c,0xee,0x8b,0x2e,0x0f,0xae, - 0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x1e,0x0d,0x87, - 0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88,0x45,0x50,0xe6,0x40,0xa1, - 0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46,0x61,0x69,0x72,0x2e,0x61, - 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a,0x65,0xbc,0xc2,0xd2,0xe4, - 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2,0xd8,0xd2,0xce,0xdc,0xbe, - 0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9,0x85,0xb5,0xcd,0x71,0xf8, - 0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07,0x4b,0xa1,0x90,0xc1,0x22, - 0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1,0x52,0x28,0x78,0xb0,0x24, - 0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce,0x40,0x59,0x03,0xc5,0x0d, - 0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0,0x79,0x6b,0x73,0x4b,0x83, - 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, - 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfe,0x60,0x88, - 0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41,0x09,0x85,0x46,0x19,0x11, - 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, - 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, - 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, - 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, - 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, - 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, - 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, - 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, - 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07,0x92,0xa8,0x11,0x4a,0x38, - 0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43,0x39,0xe0,0xc3,0x94,0xa0, - 0x0f,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, - 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, - 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, - 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, - 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, - 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, - 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, - 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, - 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, - 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, - 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, - 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, - 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, - 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, - 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, - 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, - 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, - 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, - 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, - 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, - 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, - 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, - 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, - 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, - 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, - 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, - 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, - 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00, - 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, - 0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6, - 0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00, - 0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65, - 0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41, - 0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30, - 0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15, - 0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33,0x21,0x90,0x8f,0x15, - 0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0x83,0x18, - 0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca, - 0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01, - 0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00, - 0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10,0x03,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11,0x88,0xc2,0x96,0x41, - 0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51,0xd8,0x32,0x60,0x81, - 0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -static const uint8_t _sgl_fs_bytecode_metal_ios[2893] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x4d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x70,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, - 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, - 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa8,0xe6,0x5c,0x09,0x1c,0x98,0x6d, - 0xf9,0x59,0x27,0x6f,0xf0,0xd6,0x41,0x4d,0xf9,0x93,0x2f,0x51,0x99,0xc5,0xc8,0xc7, - 0xad,0x1e,0x89,0xca,0xfe,0x25,0x89,0x6a,0x2a,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, - 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, - 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, - 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x5c,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x94,0x02,0x00,0x00,0x0b,0x82,0x20, - 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, - 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, - 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, - 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, - 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, - 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, - 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, - 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, - 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, - 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, - 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, - 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, - 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, - 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, - 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, - 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, - 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, - 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, - 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, - 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, - 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, - 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, - 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, - 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, - 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, - 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, - 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, - 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, - 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, - 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, - 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, - 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, - 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, - 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, - 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, - 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, - 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, - 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, - 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, - 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, - 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, - 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, - 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, - 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, - 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, - 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, - 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, - 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, - 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, - 0xb5,0x81,0x80,0x14,0x58,0x23,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0, - 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87, - 0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f, - 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9, - 0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0, - 0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07, - 0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, - 0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, - 0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, - 0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, - 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20, - 0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07, - 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78, - 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07, - 0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72, - 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, - 0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07, - 0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, - 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, - 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07, - 0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, - 0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x18, - 0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00, - 0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98, - 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x23,0x00,0x25, - 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xa1,0x29,0x00,0x00,0x79,0x18,0x00, - 0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, - 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b, - 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c,0x05,0xd9,0x20,0x08, - 0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd, - 0x0d,0x64,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd, - 0xac,0xac,0x65,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c, - 0x65,0x58,0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x65,0x58, - 0x84,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42, - 0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, - 0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66, - 0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43, - 0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9, - 0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d, - 0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26, - 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, - 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, - 0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, - 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, - 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72, - 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, - 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, - 0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46, - 0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c, - 0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b, - 0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32, - 0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x84, - 0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93, - 0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57, - 0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc, - 0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18, - 0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xdc,0xd3,0x3d,0xde,0xf3, - 0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6, - 0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x96,0xe1,0xe1,0x1e,0x31, - 0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0, - 0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3, - 0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17, - 0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7, - 0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d, - 0x19,0xde,0x10,0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x11,0x96,0xe1,0x39,0x83,0xc7, - 0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, - 0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54, - 0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60,0x11,0x96,0xe1,0xb1, - 0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3, - 0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e, - 0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1, - 0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, - 0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8, - 0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18, - 0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38, - 0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c, - 0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43, - 0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c, - 0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e, - 0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00, - 0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x70,0x94, + 0x34,0x45,0x94,0x30,0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18, + 0x01,0x30,0x88,0x30,0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c, + 0x44,0xf4,0x4f,0x63,0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61, + 0x04,0x01,0x98,0x23,0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49, + 0x8a,0x29,0x40,0x6d,0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, + 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, + 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64, + 0x81,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50, + 0x10,0x65,0x40,0x70,0x2c,0x81,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xb8,0x00,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, + 0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x26,0x65,0x88,0xf0,0x10,0x43, + 0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45, + 0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6, + 0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36, + 0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e, + 0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65, + 0x43,0x84,0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95, + 0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1, + 0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x51,0x58, + 0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d, + 0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0, + 0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4, + 0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd, + 0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45, + 0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d, + 0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95, + 0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70, + 0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14,0xea,0xec,0x86,0x48,0xcb, + 0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae, + 0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10, + 0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9,0xb8,0x84, + 0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61, + 0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96, + 0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae, + 0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad, + 0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0xef,0x01,0x83,0x65,0x58,0x84, + 0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c, + 0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x22,0x74, + 0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65, + 0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64, + 0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0, + 0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90, + 0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25, + 0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5, + 0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0, + 0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e, + 0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee, + 0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20, + 0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, @@ -1702,16 +1476,407 @@ static const uint8_t _sgl_fs_bytecode_metal_ios[2893] = { 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, - 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00, - 0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, - 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, - 0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41, - 0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80, - 0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0,0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00, - 0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28,0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60, - 0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00,0x00, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00, + 0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80,0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0, + 0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00,0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28, + 0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const char _sgl_vs_source_metal_sim[698] = { +static const uint8_t _sgl_vs_bytecode_metal_ios[3305] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x06,0x00,0x00,0x82,0x09,0x00,0x00,0x00, + 0xe9,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x77,0x3d,0x05,0xbd,0x9f,0x26,0xc1, + 0xae,0x87,0x1d,0xc4,0x7e,0x6d,0x86,0xc6,0x23,0xd4,0x40,0x35,0xfc,0x64,0x3f,0x25, + 0xe4,0xe3,0x19,0x09,0x7d,0xba,0xe3,0x12,0x70,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x40,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, + 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0xbc,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xec,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80, + 0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36, + 0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0, + 0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1, + 0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e, + 0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78, + 0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08, + 0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda, + 0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81, + 0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8, + 0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87, + 0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76, + 0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30, + 0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07, + 0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01, + 0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08, + 0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36, + 0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d, + 0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca, + 0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90, + 0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07, + 0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61, + 0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e, + 0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4, + 0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e, + 0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda, + 0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72, + 0x00,0x36,0x20,0x42,0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00,0x49,0x18,0x00, + 0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00, + 0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22, + 0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33, + 0x00,0xc3,0x08,0x04,0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51, + 0xf3,0x40,0x0f,0xf2,0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1, + 0xc0,0x0e,0x7a,0xa0,0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20, + 0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94, + 0x41,0x04,0x43,0x28,0x86,0x08,0x23,0x80,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x52, + 0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00, + 0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78, + 0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80, + 0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07, + 0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0, + 0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20, + 0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72, + 0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72, + 0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40, + 0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07, + 0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x05,0x18, + 0x50,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x3c, + 0x00,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x01,0x01,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32, + 0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0x81,0x22,0x2c,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0x65,0x46,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0x65,0x46,0xa6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x90, + 0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0, + 0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6, + 0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72, + 0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74, + 0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21, + 0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85, + 0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95, + 0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99, + 0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39, + 0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f, + 0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x68, + 0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3, + 0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93, + 0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x84,0x51, + 0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7,0x02,0xf7,0x36, + 0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e,0x8d,0x2e,0xed, + 0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2,0xd2,0xe4,0x5c, + 0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1,0xbd,0x85,0xd1, + 0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b,0x1b,0xa2,0x28, + 0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9,0x94,0x8f,0x50, + 0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d, + 0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37, + 0xb2,0x32,0x3c,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x5f,0x5f, + 0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72,0x5f, + 0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x84, + 0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93,0x42,0x06,0x34, + 0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd,0xc9,0x91,0xc1, + 0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31,0x50,0x22,0xc5, + 0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50, + 0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94,0x49,0x49,0x03, + 0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11,0x83,0xc5,0x58, + 0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69,0x72,0x2e,0x62, + 0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4, + 0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85,0x91,0x91,0x0a, + 0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x4a, + 0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3,0xa1,0xcd,0x0e, + 0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50,0xe4,0x60,0x21, + 0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9, + 0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34,0x39,0x97,0x30, + 0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7,0xaf,0xb9,0x34, + 0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c,0xbe,0x64,0x62, + 0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0,0x08,0x4b,0xa0, + 0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c,0x89,0x12,0x29, + 0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51,0x03,0x85,0x0d, + 0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,0xf0,0x79,0x6b,0x73,0x4b,0x83, + 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, + 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfa,0x60,0x88, + 0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83,0x46,0x19,0x11, + 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, + 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, + 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, + 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, + 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, + 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, + 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, + 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, + 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0x65,0x50,0x18,0x67,0x84,0x12,0x0e,0xe9,0x20, + 0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25,0xd8,0x03,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6, + 0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd, + 0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x4b, + 0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x55, + 0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50,0x06,0x19,0xbe,0x48, + 0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68,0x03,0x36,0x20,0x03, + 0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0x00,0x0d,0xe2, + 0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a, + 0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e, + 0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36, + 0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20, + 0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61,0xcb,0xf0,0x04,0xa0, + 0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19,0xba,0x00,0x14,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_ios[2813] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x06,0x00,0x00,0x82,0x09,0x00,0x00,0x00, + 0xfd,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa2,0xfb,0x9e,0x58,0x9a,0x22,0x99, + 0x8e,0xdc,0x41,0x5a,0x15,0xf4,0x1f,0xbc,0xfa,0xdd,0xa2,0x32,0xf3,0xff,0x3e,0xe2, + 0x2c,0x7f,0x20,0x9e,0x14,0xb3,0xef,0x8e,0x56,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x08,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x7f,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x70,0x94, + 0x34,0x45,0x94,0x30,0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18, + 0x01,0x30,0x88,0x30,0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c, + 0x44,0xf4,0x4f,0x63,0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61, + 0x04,0x01,0x98,0x23,0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49, + 0x8a,0x29,0x40,0x6d,0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x18,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03, + 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x5a,0x23,0x00,0x25,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xe1,0x01,0x00, + 0x00,0x79,0x18,0x00,0x00,0xb7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c, + 0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0x65,0x46,0xa6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0x65, + 0x46,0xa6,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x65,0x58,0x8c,0x45,0x60,0xd1,0x54, + 0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x65,0x58,0x84,0x45,0xe0,0x16,0x96,0x26,0xe7, + 0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7, + 0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e, + 0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74, + 0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9, + 0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5, + 0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1, + 0x95,0x0d,0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc, + 0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f, + 0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9, + 0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x11,0x1e,0xe8,0x89, + 0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac, + 0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a, + 0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32, + 0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76, + 0x65,0x14,0xea,0xec,0x86,0x48,0x8b,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f, + 0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85, + 0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x19,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e, + 0xe8,0x89,0x1e,0xe9,0xe9,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9, + 0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a, + 0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46, + 0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e, + 0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21, + 0x9e,0xef,0x01,0x83,0x45,0x58,0x86,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6, + 0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0, + 0x36,0x38,0xb6,0x32,0x39,0x22,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x43,0xa4, + 0xe5,0x78,0xca,0xe0,0x01,0x83,0x45,0x58,0x86,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33, + 0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49, + 0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d, + 0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61, + 0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4, + 0x30,0x25,0x28,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30, + 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06, + 0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5, + 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20, + 0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e, + 0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1, + 0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40, + 0x0f,0x53,0x02,0x35,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00, + 0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45, + 0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00, + 0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x06,0x00,0x00,0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80,0xd4,0x08,0x40,0x0d, + 0x90,0x98,0x01,0xa0,0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00,0x00,0x83,0x0c,0x8b, + 0x60,0x8c,0x18,0x28,0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9, + 0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const char _sgl_vs_source_metal_sim[856] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1727,37 +1892,47 @@ static const char _sgl_vs_source_metal_sim[698] = { 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, - 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, - 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, - 0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74, - 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, - 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d, - 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22, - 0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, - 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, - 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, - 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66, - 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, - 0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22,0x22,0x0a, - 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20, - 0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69, - 0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, - 0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, - 0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23, - 0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f, - 0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f, - 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, - 0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a, + 0x65,0x20,0x5b,0x5b,0x70,0x6f,0x69,0x6e,0x74,0x5f,0x73,0x69,0x7a,0x65,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31, + 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, + 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73, + 0x6c,0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e, + 0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x31,0x20,0x5b,0x5b,0x62,0x75, + 0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d, + 0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x73, + 0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f, + 0x32,0x31,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22, + 0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75, + 0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d, + 0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65, + 0x20,0x31,0x39,0x20,0x22,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x31,0x2e, + 0x74,0x6d,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x32,0x30,0x20,0x22,0x73, + 0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f, + 0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; -static const char _sgl_fs_source_metal_sim[473] = { +static const char _sgl_fs_source_metal_sim[489] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1772,17 +1947,18 @@ static const char _sgl_fs_source_metal_sim[473] = { 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, - 0x31,0x31,0x20,0x22,0x22,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, - 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, - 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, - 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, - 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b, - 0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f, - 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31, - 0x31,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, + 0x31,0x31,0x20,0x22,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x66,0x72, + 0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74, + 0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20, + 0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c, + 0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74, + 0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65, + 0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, + 0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b, + 0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x31,0x20,0x22,0x73,0x67,0x6c,0x2e,0x67, + 0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x2e,0x73,0x61, 0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x2c,0x20,0x69, 0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f, @@ -1790,11 +1966,11 @@ static const char _sgl_fs_source_metal_sim[473] = { 0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_D3D11) -static const uint8_t _sgl_vs_bytecode_hlsl4[1008] = { - 0x44,0x58,0x42,0x43,0xf3,0xd9,0xf4,0x58,0x44,0xc2,0xb9,0x96,0x56,0x7c,0xb3,0x71, - 0x84,0xc5,0xde,0x61,0x01,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0x05,0x00,0x00,0x00, - 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0xe8,0x01,0x00,0x00, - 0x74,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +static const uint8_t _sgl_vs_bytecode_hlsl4[1032] = { + 0x44,0x58,0x42,0x43,0x09,0x96,0xbb,0xbb,0xfc,0x44,0x44,0xa8,0xa4,0x1c,0x9e,0x45, + 0x50,0x97,0xf1,0xde,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x90,0x01,0x00,0x00,0x00,0x02,0x00,0x00, + 0x8c,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -1803,58 +1979,59 @@ static const uint8_t _sgl_vs_bytecode_hlsl4[1008] = { 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x31,0x5f, 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x31,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, - 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x50,0x00,0x00,0x00, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00, - 0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, - 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, - 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, - 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, - 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08, - 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00, - 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, - 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00, - 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, - 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, - 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00, - 0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, - 0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, - 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00, - 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00, - 0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, - 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05, + 0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00, + 0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54, + 0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; static const uint8_t _sgl_fs_bytecode_hlsl4[620] = { 0x44,0x58,0x42,0x43,0x4f,0x7a,0xe1,0xcf,0x0c,0x89,0xc0,0x09,0x50,0x5a,0xca,0xe9, @@ -1898,15 +2075,16 @@ static const uint8_t _sgl_fs_bytecode_hlsl4[620] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; #elif defined(SOKOL_WGPU) -static const uint8_t _sgl_vs_bytecode_wgpu[1932] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x32,0x00,0x00,0x00, +static const uint8_t _sgl_vs_bytecode_wgpu[1968] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x35,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x31,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0f,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x33,0x00,0x00,0x00, + 0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00,0x00,0x00, 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, @@ -1922,164 +2100,166 @@ static const uint8_t _sgl_vs_bytecode_wgpu[1932] = { 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00, 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00, 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x12,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, - 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, - 0x05,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x5f,0x32,0x30,0x00,0x05,0x00,0x05,0x00, - 0x19,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, - 0x24,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, - 0x05,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, - 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, - 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x12,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, - 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, - 0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, - 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00, - 0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x91,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x29,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, - 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x08,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x1e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2c,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x13,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, + 0x05,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x5f,0x32,0x31,0x00,0x05,0x00,0x05,0x00, + 0x1a,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x70,0x73,0x69,0x7a,0x65,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x2a,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x21,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x25,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x18,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x1e,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x91,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1e,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x24,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x26,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x28,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x2f,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x09,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x09,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x32,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, }; -static const uint8_t _sgl_fs_bytecode_wgpu[916] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x19,0x00,0x00,0x00, +static const uint8_t _sgl_fs_bytecode_wgpu[936] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1a,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x17,0x00,0x00,0x00, 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, - 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, - 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, - 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, - 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, - 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, - 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, - 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, - 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, - 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, - 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x75,0x76,0x00,0x00, - 0x05,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x0c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x4f,0x00,0x07,0x00, - 0x12,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x08,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x85,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, - 0x38,0x00,0x01,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00, + 0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f, + 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, + 0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30, + 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, + 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70, + 0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, + 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72, + 0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e, + 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, + 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e, + 0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f, + 0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e, + 0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00, + 0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00, + 0x0f,0x00,0x00,0x00,0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0d,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00, + 0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x4f,0x00,0x07,0x00,0x13,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00, + 0x09,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _sgl_vs_source_dummy = ""; @@ -2088,6 +2268,13 @@ static const char* _sgl_fs_source_dummy = ""; #error "Please define one of SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif +// ████████ ██ ██ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ +// +// >>types typedef enum { SGL_PRIMITIVETYPE_POINTS = 0, SGL_PRIMITIVETYPE_LINES, @@ -2131,6 +2318,7 @@ typedef struct { float pos[3]; float uv[2]; uint32_t rgba; + float psize; } _sgl_vertex_t; typedef struct { @@ -2174,6 +2362,7 @@ typedef union { typedef struct { _sgl_command_type_t cmd; + int layer_id; _sgl_args_t args; } _sgl_command_t; @@ -2190,24 +2379,33 @@ typedef struct { typedef struct { _sgl_slot_t slot; sgl_context_desc_t desc; - - int num_vertices; - int num_uniforms; - int num_commands; - int cur_vertex; - int cur_uniform; - int cur_command; - _sgl_vertex_t* vertices; - _sgl_uniform_t* uniforms; - _sgl_command_t* commands; + uint32_t frame_id; + uint32_t update_frame_id; + struct { + int cap; + int next; + _sgl_vertex_t* ptr; + } vertices; + struct { + int cap; + int next; + _sgl_uniform_t* ptr; + } uniforms; + struct { + int cap; + int next; + _sgl_command_t* ptr; + } commands; /* state tracking */ int base_vertex; int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ sgl_error_t error; bool in_begin; + int layer_id; float u, v; uint32_t rgba; + float point_size; _sgl_primitive_type_t cur_prim_type; sg_image cur_img; bool texturing_enabled; @@ -2246,8 +2444,94 @@ typedef struct { } _sgl_t; static _sgl_t _sgl; -/*== PRIVATE FUNCTIONS =======================================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SGL_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sgl_log_messages[] = { + _SGL_LOG_ITEMS +}; +#undef _SGL_LOGITEM_XMACRO +#endif // SOKOL_DEBUG +#define _SGL_PANIC(code) _sgl_log(SGL_LOGITEM_ ##code, 0, __LINE__) +#define _SGL_ERROR(code) _sgl_log(SGL_LOGITEM_ ##code, 1, __LINE__) +#define _SGL_WARN(code) _sgl_log(SGL_LOGITEM_ ##code, 2, __LINE__) +#define _SGL_INFO(code) _sgl_log(SGL_LOGITEM_ ##code, 3, __LINE__) + +static void _sgl_log(sgl_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sgl.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sgl_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sgl.desc.logger.func("sgl", log_level, log_item, message, line_nr, filename, _sgl.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _sgl_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _sgl_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sgl.desc.allocator.alloc) { + ptr = _sgl.desc.allocator.alloc(size, _sgl.desc.allocator.user_data); + } + else { + ptr = malloc(size); + } + if (0 == ptr) { + _SGL_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _sgl_malloc_clear(size_t size) { + void* ptr = _sgl_malloc(size); + _sgl_clear(ptr, size); + return ptr; +} + +static void _sgl_free(void* ptr) { + if (_sgl.desc.allocator.free) { + _sgl.desc.allocator.free(ptr, _sgl.desc.allocator.user_data); + } + else { + free(ptr); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool static void _sgl_init_pool(_sgl_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ @@ -2255,12 +2539,9 @@ static void _sgl_init_pool(_sgl_pool_t* pool, int num) { pool->queue_top = 0; /* generation counters indexable by pool slot index, slot 0 is reserved */ size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; - pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); - SOKOL_ASSERT(pool->gen_ctrs); - memset(pool->gen_ctrs, 0, gen_ctrs_size); + pool->gen_ctrs = (uint32_t*) _sgl_malloc_clear(gen_ctrs_size); /* it's not a bug to only reserve 'num' here */ - pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); - SOKOL_ASSERT(pool->free_queue); + pool->free_queue = (int*) _sgl_malloc_clear(sizeof(int) * (size_t)num); /* never allocate the zero-th pool item since the invalid id is 0 */ for (int i = pool->size-1; i >= 1; i--) { pool->free_queue[pool->queue_top++] = i; @@ -2270,10 +2551,10 @@ static void _sgl_init_pool(_sgl_pool_t* pool, int num) { static void _sgl_discard_pool(_sgl_pool_t* pool) { SOKOL_ASSERT(pool); SOKOL_ASSERT(pool->free_queue); - SOKOL_FREE(pool->free_queue); + _sgl_free(pool->free_queue); pool->free_queue = 0; SOKOL_ASSERT(pool->gen_ctrs); - SOKOL_FREE(pool->gen_ctrs); + _sgl_free(pool->gen_ctrs); pool->gen_ctrs = 0; pool->size = 0; pool->queue_top = 0; @@ -2308,51 +2589,6 @@ static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) { SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); } -static void _sgl_reset_context(_sgl_context_t* ctx) { - SOKOL_ASSERT(ctx); - SOKOL_ASSERT(0 == ctx->vertices); - SOKOL_ASSERT(0 == ctx->uniforms); - SOKOL_ASSERT(0 == ctx->commands); - memset(ctx, 0, sizeof(_sgl_context_t)); -} - -static void _sgl_setup_context_pool(int pool_size) { - /* note: the pools here will have an additional item, since slot 0 is reserved */ - SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); - _sgl_init_pool(&_sgl.context_pool.pool, pool_size); - size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size; - _sgl.context_pool.contexts = (_sgl_context_t*) SOKOL_MALLOC(pool_byte_size); - SOKOL_ASSERT(_sgl.context_pool.contexts); - memset(_sgl.context_pool.contexts, 0, pool_byte_size); -} - -static void _sgl_discard_context_pool(void) { - SOKOL_ASSERT(0 != _sgl.context_pool.contexts); - SOKOL_FREE(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0; - _sgl_discard_pool(&_sgl.context_pool.pool); -} - -static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { - SOKOL_ASSERT(pip); - memset(pip, 0, sizeof(_sgl_pipeline_t)); -} - -static void _sgl_setup_pipeline_pool(int pool_size) { - /* note: the pools here will have an additional item, since slot 0 is reserved */ - SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); - _sgl_init_pool(&_sgl.pip_pool.pool, pool_size); - size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size; - _sgl.pip_pool.pips = (_sgl_pipeline_t*) SOKOL_MALLOC(pool_byte_size); - SOKOL_ASSERT(_sgl.pip_pool.pips); - memset(_sgl.pip_pool.pips, 0, pool_byte_size); -} - -static void _sgl_discard_pipeline_pool(void) { - SOKOL_ASSERT(0 != _sgl.pip_pool.pips); - SOKOL_FREE(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; - _sgl_discard_pool(&_sgl.pip_pool.pool); -} - /* allocate the slot at slot_index: - bump the slot's generation counter - create a resource id from the generation counter and slot index @@ -2381,6 +2617,32 @@ static int _sgl_slot_index(uint32_t id) { return slot_index; } +// ██████ ██ ██████ ███████ ██ ██ ███ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██████ ██ ██████ █████ ██ ██ ██ ██ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ ██ ██ ████ ███████ ███████ +// +// >>pipelines +static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sgl_clear(pip, sizeof(_sgl_pipeline_t)); +} + +static void _sgl_setup_pipeline_pool(int pool_size) { + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.pip_pool.pool, pool_size); + size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size; + _sgl.pip_pool.pips = (_sgl_pipeline_t*) _sgl_malloc_clear(pool_byte_size); +} + +static void _sgl_discard_pipeline_pool(void) { + SOKOL_ASSERT(0 != _sgl.pip_pool.pips); + _sgl_free(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; + _sgl_discard_pool(&_sgl.pip_pool.pool); +} + /* get pipeline pointer without id-check */ static _sgl_pipeline_t* _sgl_pipeline_at(uint32_t pip_id) { SOKOL_ASSERT(SG_INVALID_ID != pip_id); @@ -2440,6 +2702,11 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d rgba->offset = offsetof(_sgl_vertex_t, rgba); rgba->format = SG_VERTEXFORMAT_UBYTE4N; } + { + sg_vertex_attr_desc* psize = &desc.layout.attrs[3]; + psize->offset = offsetof(_sgl_vertex_t, psize); + psize->format = SG_VERTEXFORMAT_FLOAT; + } if (in_desc->shader.id == SG_INVALID_ID) { desc.shader = _sgl.shd; } @@ -2486,7 +2753,7 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d else { pip->pip[i] = sg_make_pipeline(&desc); if (pip->pip[i].id == SG_INVALID_ID) { - SOKOL_LOG("sokol_gl.h: failed to create pipeline object"); + _SGL_ERROR(MAKE_PIPELINE_FAILED); pip->slot.state = SG_RESOURCESTATE_FAILED; } } @@ -2500,7 +2767,7 @@ static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc, const sgl_c _sgl_init_pipeline(pip_id, desc, ctx_desc); } else { - SOKOL_LOG("sokol_gl.h: pipeline pool exhausted!"); + _SGL_ERROR(PIPELINE_POOL_EXHAUSTED); } return pip_id; } @@ -2531,6 +2798,35 @@ static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t } } +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ ███████ +// +// >>contexts +static void _sgl_reset_context(_sgl_context_t* ctx) { + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(0 == ctx->vertices.ptr); + SOKOL_ASSERT(0 == ctx->uniforms.ptr); + SOKOL_ASSERT(0 == ctx->commands.ptr); + _sgl_clear(ctx, sizeof(_sgl_context_t)); +} + +static void _sgl_setup_context_pool(int pool_size) { + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.context_pool.pool, pool_size); + size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size; + _sgl.context_pool.contexts = (_sgl_context_t*) _sgl_malloc_clear(pool_byte_size); +} + +static void _sgl_discard_context_pool(void) { + SOKOL_ASSERT(0 != _sgl.context_pool.contexts); + _sgl_free(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0; + _sgl_discard_pool(&_sgl.context_pool.pool); +} + // get context pointer without id-check static _sgl_context_t* _sgl_context_at(uint32_t ctx_id) { SOKOL_ASSERT(SG_INVALID_ID != ctx_id); @@ -2578,43 +2874,48 @@ static sgl_context_desc_t _sgl_context_desc_defaults(const sgl_context_desc_t* d } static void _sgl_identity(_sgl_matrix_t*); +static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx); static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_desc) { SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc); _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); SOKOL_ASSERT(ctx); ctx->desc = _sgl_context_desc_defaults(in_desc); + // NOTE: frame_id must be non-zero, so that updates trigger in first frame + ctx->frame_id = 1; ctx->cur_img = _sgl.def_img; // allocate buffers and pools - ctx->num_vertices = ctx->desc.max_vertices; - ctx->num_commands = ctx->num_uniforms = ctx->desc.max_commands; - ctx->vertices = (_sgl_vertex_t*) SOKOL_MALLOC((size_t)ctx->num_vertices * sizeof(_sgl_vertex_t)); - SOKOL_ASSERT(ctx->vertices); - ctx->uniforms = (_sgl_uniform_t*) SOKOL_MALLOC((size_t)ctx->num_uniforms * sizeof(_sgl_uniform_t)); - SOKOL_ASSERT(ctx->uniforms); - ctx->commands = (_sgl_command_t*) SOKOL_MALLOC((size_t)ctx->num_commands * sizeof(_sgl_command_t)); - SOKOL_ASSERT(ctx->commands); + ctx->vertices.cap = ctx->desc.max_vertices; + ctx->commands.cap = ctx->uniforms.cap = ctx->desc.max_commands; + ctx->vertices.ptr = (_sgl_vertex_t*) _sgl_malloc((size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t)); + ctx->uniforms.ptr = (_sgl_uniform_t*) _sgl_malloc((size_t)ctx->uniforms.cap * sizeof(_sgl_uniform_t)); + ctx->commands.ptr = (_sgl_command_t*) _sgl_malloc((size_t)ctx->commands.cap * sizeof(_sgl_command_t)); // create sokol-gfx resource objects sg_push_debug_group("sokol-gl"); sg_buffer_desc vbuf_desc; - memset(&vbuf_desc, 0, sizeof(vbuf_desc)); - vbuf_desc.size = (size_t)ctx->num_vertices * sizeof(_sgl_vertex_t); + _sgl_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.size = (size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t); vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; vbuf_desc.usage = SG_USAGE_STREAM; vbuf_desc.label = "sgl-vertex-buffer"; ctx->vbuf = sg_make_buffer(&vbuf_desc); SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id); + ctx->bind.vertex_buffers[0] = ctx->vbuf; sg_pipeline_desc def_pip_desc; - memset(&def_pip_desc, 0, sizeof(def_pip_desc)); + _sgl_clear(&def_pip_desc, sizeof(def_pip_desc)); def_pip_desc.depth.write_enabled = true; ctx->def_pip = _sgl_make_pipeline(&def_pip_desc, &ctx->desc); + if (!sg_add_commit_listener(_sgl_make_commit_listener(ctx))) { + _SGL_ERROR(ADD_COMMIT_LISTENER_FAILED); + } sg_pop_debug_group(); // default state ctx->rgba = 0xFFFFFFFF; + ctx->point_size = 1.0f; for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) { _sgl_identity(&ctx->matrix_stack[i][0]); } @@ -2629,7 +2930,7 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) { _sgl_init_context(ctx_id, desc); } else { - SOKOL_LOG("sokol_gl.h: context pool exhausted!"); + _SGL_ERROR(CONTEXT_POOL_EXHAUSTED); } return ctx_id; } @@ -2637,21 +2938,21 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) { static void _sgl_destroy_context(sgl_context ctx_id) { _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); if (ctx) { - SOKOL_ASSERT(ctx->vertices); - SOKOL_ASSERT(ctx->uniforms); - SOKOL_ASSERT(ctx->commands); + SOKOL_ASSERT(ctx->vertices.ptr); + SOKOL_ASSERT(ctx->uniforms.ptr); + SOKOL_ASSERT(ctx->commands.ptr); - SOKOL_FREE(ctx->vertices); - SOKOL_FREE(ctx->uniforms); - SOKOL_FREE(ctx->commands); - - ctx->vertices = 0; - ctx->uniforms = 0; - ctx->commands = 0; + _sgl_free(ctx->vertices.ptr); + _sgl_free(ctx->uniforms.ptr); + _sgl_free(ctx->commands.ptr); + ctx->vertices.ptr = 0; + ctx->uniforms.ptr = 0; + ctx->commands.ptr = 0; sg_push_debug_group("sokol-gl"); sg_destroy_buffer(ctx->vbuf); _sgl_destroy_pipeline(ctx->def_pip); + sg_remove_commit_listener(_sgl_make_commit_listener(ctx)); sg_pop_debug_group(); _sgl_reset_context(ctx); @@ -2659,25 +2960,47 @@ static void _sgl_destroy_context(sgl_context ctx_id) { } } -static inline void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) { +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc +static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) { ctx->in_begin = true; - ctx->base_vertex = ctx->cur_vertex; + ctx->base_vertex = ctx->vertices.next; ctx->vtx_count = 0; ctx->cur_prim_type = mode; } static void _sgl_rewind(_sgl_context_t* ctx) { + ctx->frame_id++; + ctx->vertices.next = 0; + ctx->uniforms.next = 0; + ctx->commands.next = 0; ctx->base_vertex = 0; - ctx->cur_vertex = 0; - ctx->cur_uniform = 0; - ctx->cur_command = 0; ctx->error = SGL_NO_ERROR; + ctx->layer_id = 0; ctx->matrix_dirty = true; } -static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { - if (ctx->cur_vertex < ctx->num_vertices) { - return &ctx->vertices[ctx->cur_vertex++]; +// called from inside sokol-gfx sg_commit() +static void _sgl_commit_listener(void* userdata) { + _sgl_context_t* ctx = _sgl_lookup_context((uint32_t)(uintptr_t)userdata); + if (ctx) { + _sgl_rewind(ctx); + } +} + +static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx) { + sg_commit_listener listener = { _sgl_commit_listener, (void*)(uintptr_t)(ctx->slot.id) }; + return listener; +} + +static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { + if (ctx->vertices.next < ctx->vertices.cap) { + return &ctx->vertices.ptr[ctx->vertices.next++]; } else { ctx->error = SGL_ERROR_VERTICES_FULL; @@ -2685,9 +3008,9 @@ static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { } } -static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { - if (ctx->cur_uniform < ctx->num_uniforms) { - return &ctx->uniforms[ctx->cur_uniform++]; +static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { + if (ctx->uniforms.next < ctx->uniforms.cap) { + return &ctx->uniforms.ptr[ctx->uniforms.next++]; } else { ctx->error = SGL_ERROR_UNIFORMS_FULL; @@ -2695,18 +3018,18 @@ static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { } } -static inline _sgl_command_t* _sgl_prev_command(_sgl_context_t* ctx) { - if (ctx->cur_command > 0) { - return &ctx->commands[ctx->cur_command - 1]; +static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) { + if (ctx->commands.next > 0) { + return &ctx->commands.ptr[ctx->commands.next - 1]; } else { return 0; } } -static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { - if (ctx->cur_command < ctx->num_commands) { - return &ctx->commands[ctx->cur_command++]; +static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { + if (ctx->commands.next < ctx->commands.cap) { + return &ctx->commands.ptr[ctx->commands.next++]; } else { ctx->error = SGL_ERROR_COMMANDS_FULL; @@ -2714,17 +3037,17 @@ static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { } } -static inline uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { +static uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r); } -static inline float _sgl_clamp(float v, float lo, float hi) { +static float _sgl_clamp(float v, float lo, float hi) { if (v < lo) return lo; else if (v > hi) return hi; else return v; } -static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) { +static uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) { uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f); uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f); uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f); @@ -2732,7 +3055,7 @@ static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) { return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8); } -static inline void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) { +static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) { SOKOL_ASSERT(ctx->in_begin); _sgl_vertex_t* vtx; /* handle non-native primitive types */ @@ -2750,6 +3073,7 @@ static inline void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, floa vtx->pos[0] = x; vtx->pos[1] = y; vtx->pos[2] = z; vtx->uv[0] = u; vtx->uv[1] = v; vtx->rgba = rgba; + vtx->psize = ctx->point_size; } ctx->vtx_count++; } @@ -2945,27 +3269,28 @@ static void _sgl_lookat(_sgl_matrix_t* dst, } /* current top-of-stack projection matrix */ -static inline _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) { +static _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) { return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->matrix_tos[SGL_MATRIXMODE_PROJECTION]]; } /* get top-of-stack modelview matrix */ -static inline _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) { +static _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) { return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->matrix_tos[SGL_MATRIXMODE_MODELVIEW]]; } /* get top-of-stack texture matrix */ -static inline _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) { +static _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) { return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->matrix_tos[SGL_MATRIXMODE_TEXTURE]]; } /* get pointer to current top-of-stack of current matrix mode */ -static inline _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) { +static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) { return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]]; } // return sg_context_desc_t with patched defaults 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)); sgl_desc_t res = *desc; res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES); res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS); @@ -2984,7 +3309,7 @@ static void _sgl_setup_common(void) { pixels[i] = 0xFFFFFFFF; } sg_image_desc img_desc; - memset(&img_desc, 0, sizeof(img_desc)); + _sgl_clear(&img_desc, sizeof(img_desc)); img_desc.type = SG_IMAGETYPE_2D; img_desc.width = 8; img_desc.height = 8; @@ -2999,16 +3324,19 @@ static void _sgl_setup_common(void) { // one shader for all contexts sg_shader_desc shd_desc; - memset(&shd_desc, 0, sizeof(shd_desc)); + _sgl_clear(&shd_desc, sizeof(shd_desc)); shd_desc.attrs[0].name = "position"; shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[3].name = "psize"; shd_desc.attrs[0].sem_name = "TEXCOORD"; shd_desc.attrs[0].sem_index = 0; shd_desc.attrs[1].sem_name = "TEXCOORD"; shd_desc.attrs[1].sem_index = 1; shd_desc.attrs[2].sem_name = "TEXCOORD"; shd_desc.attrs[2].sem_index = 2; + shd_desc.attrs[3].sem_name = "TEXCOORD"; + shd_desc.attrs[3].sem_index = 3; sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; ub->size = sizeof(_sgl_uniform_t); ub->uniforms[0].name = "vs_params"; @@ -3068,18 +3396,26 @@ static bool _sgl_is_default_context(sgl_context ctx_id) { return ctx_id.id == SGL_DEFAULT_CONTEXT.id; } -static void _sgl_draw(_sgl_context_t* ctx) { +static void _sgl_draw(_sgl_context_t* ctx, int layer_id) { SOKOL_ASSERT(ctx); - if ((ctx->error == SGL_NO_ERROR) && (ctx->cur_vertex > 0) && (ctx->cur_command > 0)) { + if ((ctx->error == SGL_NO_ERROR) && (ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-gl"); + uint32_t cur_pip_id = SG_INVALID_ID; uint32_t cur_img_id = SG_INVALID_ID; int cur_uniform_index = -1; - sg_push_debug_group("sokol-gl"); - const sg_range range = { ctx->vertices, (size_t)ctx->cur_vertex * sizeof(_sgl_vertex_t) }; - sg_update_buffer(ctx->vbuf, &range); - ctx->bind.vertex_buffers[0] = ctx->vbuf; - for (int i = 0; i < ctx->cur_command; i++) { - const _sgl_command_t* cmd = &ctx->commands[i]; + + if (ctx->update_frame_id != ctx->frame_id) { + ctx->update_frame_id = ctx->frame_id; + const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sgl_vertex_t) }; + sg_update_buffer(ctx->vbuf, &range); + } + + for (int i = 0; i < ctx->commands.next; i++) { + const _sgl_command_t* cmd = &ctx->commands.ptr[i]; + if (cmd->layer_id != layer_id) { + continue; + } switch (cmd->cmd) { case SGL_COMMAND_VIEWPORT: { @@ -3109,7 +3445,7 @@ static void _sgl_draw(_sgl_context_t* ctx) { cur_img_id = args->img.id; } if (cur_uniform_index != args->uniform_index) { - const sg_range ub_range = { &ctx->uniforms[args->uniform_index], sizeof(_sgl_uniform_t) }; + const sg_range ub_range = { &ctx->uniforms.ptr[args->uniform_index], sizeof(_sgl_uniform_t) }; sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range); cur_uniform_index = args->uniform_index; } @@ -3123,12 +3459,11 @@ static void _sgl_draw(_sgl_context_t* ctx) { } sg_pop_debug_group(); } - _sgl_rewind(ctx); } static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) { sgl_context_desc_t ctx_desc; - memset(&ctx_desc, 0, sizeof(ctx_desc)); + _sgl_clear(&ctx_desc, sizeof(ctx_desc)); ctx_desc.max_vertices = desc->max_vertices; ctx_desc.max_commands = desc->max_commands; ctx_desc.color_format = desc->color_format; @@ -3137,10 +3472,16 @@ static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) { return ctx_desc; } -/*== PUBLIC FUNCTIONS ========================================================*/ +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { SOKOL_ASSERT(desc); - memset(&_sgl, 0, sizeof(_sgl)); + _sgl_clear(&_sgl, sizeof(_sgl)); _sgl.init_cookie = _SGL_INIT_COOKIE; _sgl.desc = _sgl_desc_defaults(desc); _sgl_setup_pipeline_pool(_sgl.desc.pipeline_pool_size); @@ -3205,7 +3546,7 @@ SOKOL_API_IMPL sgl_context sgl_make_context(const sgl_context_desc_t* desc) { SOKOL_API_IMPL void sgl_destroy_context(sgl_context ctx_id) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); if (_sgl_is_default_context(ctx_id)) { - SOKOL_LOG("sokol_gl.h: cannot destroy default context"); + _SGL_WARN(CANNOT_DESTROY_DEFAULT_CONTEXT); return; } _sgl_destroy_context(ctx_id); @@ -3320,6 +3661,7 @@ SOKOL_API_IMPL void sgl_defaults(void) { SOKOL_ASSERT(!ctx->in_begin); ctx->u = 0.0f; ctx->v = 0.0f; ctx->rgba = 0xFFFFFFFF; + ctx->point_size = 1.0f; ctx->texturing_enabled = false; ctx->cur_img = _sgl.def_img; sgl_load_default_pipeline(); @@ -3330,6 +3672,16 @@ SOKOL_API_IMPL void sgl_defaults(void) { ctx->matrix_dirty = true; } +SOKOL_API_IMPL void sgl_layer(int layer_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->layer_id = layer_id; +} + SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); _sgl_context_t* ctx = _sgl.cur_ctx; @@ -3340,6 +3692,7 @@ SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_lef _sgl_command_t* cmd = _sgl_next_command(ctx); if (cmd) { cmd->cmd = SGL_COMMAND_VIEWPORT; + cmd->layer_id = ctx->layer_id; cmd->args.viewport.x = x; cmd->args.viewport.y = y; cmd->args.viewport.w = w; @@ -3362,6 +3715,7 @@ SOKOL_API_IMPL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top _sgl_command_t* cmd = _sgl_next_command(ctx); if (cmd) { cmd->cmd = SGL_COMMAND_SCISSOR_RECT; + cmd->layer_id = ctx->layer_id; cmd->args.scissor_rect.x = x; cmd->args.scissor_rect.y = y; cmd->args.scissor_rect.w = w; @@ -3476,7 +3830,7 @@ SOKOL_API_IMPL void sgl_end(void) { return; } SOKOL_ASSERT(ctx->in_begin); - SOKOL_ASSERT(ctx->cur_vertex >= ctx->base_vertex); + SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex); ctx->in_begin = false; bool matrix_dirty = ctx->matrix_dirty; if (matrix_dirty) { @@ -3487,41 +3841,50 @@ SOKOL_API_IMPL void sgl_end(void) { uni->tm = *_sgl_matrix_texture(ctx); } } - /* check if command can be merged with previous command */ + /* check if command can be merged with current command */ sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img; - _sgl_command_t* prev_cmd = _sgl_prev_command(ctx); + _sgl_command_t* cur_cmd = _sgl_cur_command(ctx); bool merge_cmd = false; - if (prev_cmd) { - if ((prev_cmd->cmd == SGL_COMMAND_DRAW) && + if (cur_cmd) { + if ((cur_cmd->cmd == SGL_COMMAND_DRAW) && + (cur_cmd->layer_id == ctx->layer_id) && (ctx->cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) && (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) && !matrix_dirty && - (prev_cmd->args.draw.img.id == img.id) && - (prev_cmd->args.draw.pip.id == pip.id)) + (cur_cmd->args.draw.img.id == img.id) && + (cur_cmd->args.draw.pip.id == pip.id)) { merge_cmd = true; } } if (merge_cmd) { /* draw command can be merged with the previous command */ - prev_cmd->args.draw.num_vertices += ctx->cur_vertex - ctx->base_vertex; + cur_cmd->args.draw.num_vertices += ctx->vertices.next - ctx->base_vertex; } else { /* append a new draw command */ _sgl_command_t* cmd = _sgl_next_command(ctx); if (cmd) { - SOKOL_ASSERT(ctx->cur_uniform > 0); + SOKOL_ASSERT(ctx->uniforms.next > 0); cmd->cmd = SGL_COMMAND_DRAW; + cmd->layer_id = ctx->layer_id; cmd->args.draw.img = img; cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); cmd->args.draw.base_vertex = ctx->base_vertex; - cmd->args.draw.num_vertices = ctx->cur_vertex - ctx->base_vertex; - cmd->args.draw.uniform_index = ctx->cur_uniform - 1; + cmd->args.draw.num_vertices = ctx->vertices.next - ctx->base_vertex; + cmd->args.draw.uniform_index = ctx->uniforms.next - 1; } } } +SOKOL_API_IMPL void sgl_point_size(float s) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->point_size = s; + } +} + SOKOL_API_IMPL void sgl_t2f(float u, float v) { _sgl_context_t* ctx = _sgl.cur_ctx; if (ctx) { @@ -3919,7 +4282,15 @@ SOKOL_API_IMPL void sgl_draw(void) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); _sgl_context_t* ctx = _sgl.cur_ctx; if (ctx) { - _sgl_draw(ctx); + _sgl_draw(ctx, 0); + } +} + +SOKOL_API_IMPL void sgl_draw_layer(int layer_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_draw(ctx, layer_id); } } @@ -3927,7 +4298,15 @@ SOKOL_API_IMPL void sgl_context_draw(sgl_context ctx_id) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); if (ctx) { - _sgl_draw(ctx); + _sgl_draw(ctx, 0); + } +} + +SOKOL_API_IMPL void sgl_context_draw_layer(sgl_context ctx_id, int layer_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + _sgl_draw(ctx, layer_id); } } diff --git a/3rdparty/sokol/util/sokol_imgui.h b/3rdparty/sokol/util/sokol_imgui.h index 0bd43ac..2a6cf6d 100644 --- a/3rdparty/sokol/util/sokol_imgui.h +++ b/3rdparty/sokol/util/sokol_imgui.h @@ -42,8 +42,6 @@ to override defaults: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_IMGUI_API_DECL- public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_IMGUI_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) @@ -107,20 +105,18 @@ sg_pixel_format color_format The color pixel format of the render pass where the UI - will be rendered. The default is SG_PIXELFORMAT_RGBA8 + will be rendered. The default (0) matches sokoL_gfx.h's + default pass. sg_pixel_format depth_format The depth-buffer pixel format of the render pass where - the UI will be rendered. The default is SG_PIXELFORMAT_DEPTHSTENCIL. + the UI will be rendered. The default (0) matches + sokol_gfx.h's default pass depth format. int sample_count The MSAA sample-count of the render pass where the UI - will be rendered. The default is 1. - - float dpi_scale - DPI scaling factor. Set this to the result of sapp_dpi_scale(). - To render in high resolution on a Retina Mac this would - typically be 2.0. The default value is 1.0 + will be rendered. The default (0) matches sokol_gfx.h's + default pass sample count. const char* ini_filename Sets this path as ImGui::GetIO().IniFilename where ImGui will store @@ -138,19 +134,51 @@ font. In this case you need to initialize the font yourself after simgui_setup() is called. + bool disable_paste_override + If set to true, sokol_imgui.h will not 'emulate' a Dear Imgui + clipboard paste action on SAPP_EVENTTYPE_CLIPBOARD_PASTED event. + This is mainly a hack/workaround to allow external workarounds + for making copy/paste work on the web platform. In general, + copy/paste support isn't properly fleshed out in sokol_imgui.h yet. + + bool disable_set_mouse_cursor + If true, sokol_imgui.h will not control the mouse cursor type + by calling sapp_set_mouse_cursor(). + + bool disable_windows_resize_from_edges + If true, windows can only be resized from the bottom right corner. + The default is false, meaning windows can be resized from edges. + + bool write_alpha_channel + Set this to true if you want alpha values written to the + framebuffer. By default this behavior is disabled to prevent + undesired behavior on platforms like the web where the canvas is + always alpha-blended with the background. + + simgui_allocator_t allocator + Used to override memory allocation functions. See further below + for details. + --- At the start of a frame, call: - simgui_new_frame(int width, int height, double delta_time) + simgui_new_frame(&(simgui_frame_desc_t){.width = ..., .height = ..., .delta_time = ..., .dpi_scale = ...}); 'width' and 'height' are the dimensions of the rendering surface, passed to ImGui::GetIO().DisplaySize. 'delta_time' is the frame duration passed to ImGui::GetIO().DeltaTime. - For example, if you're using sokol_app.h and render to the - default framebuffer: + 'dpi_scale' is the current DPI scale factor, if this is left zero-initialized, + 1.0f will be used instead. Typical values for dpi_scale are >= 1.0f. - simgui_new_frame(sapp_width(), sapp_height(), delta_time); + For example, if you're using sokol_app.h and render to the default framebuffer: + + simgui_new_frame(&(simgui_frame_desc_t){ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); --- at the end of the frame, before the sg_end_pass() where you want to render the UI, call: @@ -169,10 +197,49 @@ if this is true, you might want to skip keyboard input handling in your own event handler. + If you want to use the ImGui functions for checking if a key is pressed + (e.g. ImGui::IsKeyPressed()) the following helper function to map + an sapp_keycode to an ImGuiKey value may be useful: + + int simgui_map_keycode(sapp_keycode c); + + Note that simgui_map_keycode() can be called outside simgui_setup()/simgui_shutdown(). + --- finally, on application shutdown, call simgui_shutdown() + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + simgui_setup(&(simgui_desc_t){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_imgui.h + itself though, not any allocations in Dear ImGui. + + LICENSE ======= @@ -202,6 +269,7 @@ #define SOKOL_IMGUI_INCLUDED (1) #include #include +#include // size_t #if !defined(SOKOL_GFX_INCLUDED) #error "Please include sokol_gfx.h before sokol_imgui.h" @@ -227,22 +295,47 @@ extern "C" { #endif +/* + simgui_allocator_t + + 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 + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct simgui_allocator_t { + void* (*alloc)(size_t size, void* user_data); + void (*free)(void* ptr, void* user_data); + void* user_data; +} simgui_allocator_t; + typedef struct simgui_desc_t { int max_vertices; sg_pixel_format color_format; sg_pixel_format depth_format; int sample_count; - float dpi_scale; const char* ini_filename; bool no_default_font; - bool disable_hotkeys; /* don't let ImGui handle Ctrl-A,C,V,X,Y,Z */ + bool disable_paste_override; // if true, don't send Ctrl-V on EVENTTYPE_CLIPBOARD_PASTED + bool disable_set_mouse_cursor; // if true, don't control the mouse cursor type via sapp_set_mouse_cursor() + bool disable_windows_resize_from_edges; // if true, only resize edges from the bottom right corner + bool write_alpha_channel; // if true, alpha values get written into the framebuffer + simgui_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) } simgui_desc_t; +typedef struct simgui_frame_desc_t { + int width; + int height; + double delta_time; + float dpi_scale; +} simgui_frame_desc_t; + SOKOL_IMGUI_API_DECL void simgui_setup(const simgui_desc_t* desc); -SOKOL_IMGUI_API_DECL void simgui_new_frame(int width, int height, double delta_time); +SOKOL_IMGUI_API_DECL void simgui_new_frame(const simgui_frame_desc_t* desc); SOKOL_IMGUI_API_DECL void simgui_render(void); #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) SOKOL_IMGUI_API_DECL bool simgui_handle_event(const sapp_event* ev); +SOKOL_IMGUI_API_DECL int simgui_map_keycode(sapp_keycode keycode); // returns ImGuiKey_* #endif SOKOL_IMGUI_API_DECL void simgui_shutdown(void); @@ -251,14 +344,21 @@ SOKOL_IMGUI_API_DECL void simgui_shutdown(void); /* reference-based equivalents for C++ */ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc); } +inline void simgui_new_frame(const simgui_frame_desc_t& desc) { return simgui_new_frame(&desc); } #endif #endif /* SOKOL_IMGUI_INCLUDED */ /*-- IMPLEMENTATION ----------------------------------------------------------*/ +#define SOKOL_IMGUI_IMPL + #ifdef SOKOL_IMGUI_IMPL #define SOKOL_IMGUI_IMPL_INCLUDED (1) +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use simgui_desc_t.allocator to override memory allocation functions" +#endif + #if defined(__cplusplus) #if !defined(IMGUI_VERSION) #error "Please include imgui.h before the sokol_imgui.h implementation" @@ -269,8 +369,8 @@ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc) #endif #endif -#include /* offsetof */ -#include /* memset */ +#include // memset +#include // malloc/free #if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) #include @@ -281,18 +381,13 @@ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc) #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -301,7 +396,7 @@ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc) #endif #endif -/* helper macros */ +/* helper macros and constants */ #define _simgui_def(val, def) (((val) == 0) ? (def) : (val)) typedef struct { @@ -309,26 +404,17 @@ typedef struct { uint8_t _pad_8[8]; } _simgui_vs_params_t; -#define SIMGUI_MAX_KEY_VALUE (512) // same as ImGuis IO.KeysDown array - typedef struct { simgui_desc_t desc; + float cur_dpi_scale; sg_buffer vbuf; sg_buffer ibuf; sg_image img; sg_shader shd; sg_pipeline pip; - bool is_osx; // return true if running on OSX (or HTML5 OSX), needed for copy/paste - sg_range vertices; sg_range indices; - - #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) - bool btn_down[SAPP_MAX_MOUSEBUTTONS]; - bool btn_up[SAPP_MAX_MOUSEBUTTONS]; - uint8_t keys_down[SIMGUI_MAX_KEY_VALUE]; // bits 0..3 or modifiers, != 0 is key-down - uint8_t keys_up[SIMGUI_MAX_KEY_VALUE]; // same is keys_down - #endif + bool is_osx; // return true if running on OSX (or HTML5 OSX), needed for copy/paste } _simgui_state_t; static _simgui_state_t _simgui; @@ -1605,6 +1691,33 @@ EM_JS(int, simgui_js_is_osx, (void), { }); #endif +static void _simgui_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _simgui_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_simgui.desc.allocator.alloc) { + ptr = _simgui.desc.allocator.alloc(size, _simgui.desc.allocator.user_data); + } + else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +static void _simgui_free(void* ptr) { + if (_simgui.desc.allocator.free) { + _simgui.desc.allocator.free(ptr, _simgui.desc.allocator.user_data); + } + else { + free(ptr); + } +} + static bool _simgui_is_osx(void) { #if defined(SOKOL_DUMMY_BACKEND) return false; @@ -1617,12 +1730,18 @@ static bool _simgui_is_osx(void) { #endif } +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)); + simgui_desc_t res = *desc; + res.max_vertices = _simgui_def(res.max_vertices, 65536); + return res; +} + SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { SOKOL_ASSERT(desc); - memset(&_simgui, 0, sizeof(_simgui)); - _simgui.desc = *desc; - _simgui.desc.max_vertices = _simgui_def(_simgui.desc.max_vertices, 65536); - _simgui.desc.dpi_scale = _simgui_def(_simgui.desc.dpi_scale, 1.0f); + _simgui_clear(&_simgui, sizeof(_simgui)); + _simgui.desc = _simgui_desc_defaults(desc); + _simgui.cur_dpi_scale = 1.0f; #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) _simgui.is_osx = _simgui_is_osx(); #endif @@ -1633,11 +1752,9 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { /* allocate an intermediate vertex- and index-buffer */ SOKOL_ASSERT(_simgui.desc.max_vertices > 0); _simgui.vertices.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert); - _simgui.vertices.ptr = SOKOL_MALLOC(_simgui.vertices.size); - SOKOL_ASSERT(_simgui.vertices.ptr); + _simgui.vertices.ptr = _simgui_malloc(_simgui.vertices.size); _simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx); - _simgui.indices.ptr = SOKOL_MALLOC(_simgui.indices.size); - SOKOL_ASSERT(_simgui.indices.ptr); + _simgui.indices.ptr = _simgui_malloc(_simgui.indices.size); /* initialize Dear ImGui */ #if defined(__cplusplus) @@ -1659,47 +1776,27 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { io->ConfigMacOSXBehaviors = _simgui_is_osx(); io->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) - io->KeyMap[ImGuiKey_Tab] = SAPP_KEYCODE_TAB; - io->KeyMap[ImGuiKey_LeftArrow] = SAPP_KEYCODE_LEFT; - io->KeyMap[ImGuiKey_RightArrow] = SAPP_KEYCODE_RIGHT; - io->KeyMap[ImGuiKey_UpArrow] = SAPP_KEYCODE_UP; - io->KeyMap[ImGuiKey_DownArrow] = SAPP_KEYCODE_DOWN; - io->KeyMap[ImGuiKey_PageUp] = SAPP_KEYCODE_PAGE_UP; - io->KeyMap[ImGuiKey_PageDown] = SAPP_KEYCODE_PAGE_DOWN; - io->KeyMap[ImGuiKey_Home] = SAPP_KEYCODE_HOME; - io->KeyMap[ImGuiKey_End] = SAPP_KEYCODE_END; - io->KeyMap[ImGuiKey_Delete] = SAPP_KEYCODE_DELETE; - io->KeyMap[ImGuiKey_Backspace] = SAPP_KEYCODE_BACKSPACE; - io->KeyMap[ImGuiKey_Space] = SAPP_KEYCODE_SPACE; - io->KeyMap[ImGuiKey_Enter] = SAPP_KEYCODE_ENTER; - io->KeyMap[ImGuiKey_Escape] = SAPP_KEYCODE_ESCAPE; - if (!_simgui.desc.disable_hotkeys) { - io->KeyMap[ImGuiKey_A] = SAPP_KEYCODE_A; - io->KeyMap[ImGuiKey_C] = SAPP_KEYCODE_C; - io->KeyMap[ImGuiKey_V] = SAPP_KEYCODE_V; - io->KeyMap[ImGuiKey_X] = SAPP_KEYCODE_X; - io->KeyMap[ImGuiKey_Y] = SAPP_KEYCODE_Y; - io->KeyMap[ImGuiKey_Z] = SAPP_KEYCODE_Z; + if (!_simgui.desc.disable_set_mouse_cursor) { + io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors; } - #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) io->SetClipboardTextFn = _simgui_set_clipboard; io->GetClipboardTextFn = _simgui_get_clipboard; - #endif #endif + io->ConfigWindowsResizeFromEdges = !_simgui.desc.disable_windows_resize_from_edges; /* create sokol-gfx resources */ sg_push_debug_group("sokol-imgui"); /* NOTE: since we're in C++ mode here we can't use C99 designated init */ sg_buffer_desc vb_desc; - memset(&vb_desc, 0, sizeof(vb_desc)); + _simgui_clear(&vb_desc, sizeof(vb_desc)); vb_desc.usage = SG_USAGE_STREAM; vb_desc.size = _simgui.vertices.size; vb_desc.label = "sokol-imgui-vertices"; _simgui.vbuf = sg_make_buffer(&vb_desc); sg_buffer_desc ib_desc; - memset(&ib_desc, 0, sizeof(ib_desc)); + _simgui_clear(&ib_desc, sizeof(ib_desc)); ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER; ib_desc.usage = SG_USAGE_STREAM; ib_desc.size = _simgui.indices.size; @@ -1717,7 +1814,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { ImFontAtlas_GetTexDataAsRGBA32(io->Fonts, &font_pixels, &font_width, &font_height, &bytes_per_pixel); #endif sg_image_desc img_desc; - memset(&img_desc, 0, sizeof(img_desc)); + _simgui_clear(&img_desc, sizeof(img_desc)); img_desc.width = font_width; img_desc.height = font_height; img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; @@ -1734,7 +1831,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { /* shader object for using the embedded shader source (or bytecode) */ sg_shader_desc shd_desc; - memset(&shd_desc, 0, sizeof(shd_desc)); + _simgui_clear(&shd_desc, sizeof(shd_desc)); shd_desc.attrs[0].name = "position"; shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[2].name = "color0"; @@ -1790,7 +1887,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { /* pipeline object for imgui rendering */ sg_pipeline_desc pip_desc; - memset(&pip_desc, 0, sizeof(pip_desc)); + _simgui_clear(&pip_desc, sizeof(pip_desc)); pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert); { sg_vertex_attr_desc* attr = &pip_desc.layout.attrs[0]; @@ -1812,10 +1909,14 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { pip_desc.sample_count = _simgui.desc.sample_count; pip_desc.depth.pixel_format = _simgui.desc.depth_format; pip_desc.colors[0].pixel_format = _simgui.desc.color_format; - pip_desc.colors[0].write_mask = SG_COLORMASK_RGB; + pip_desc.colors[0].write_mask = _simgui.desc.write_alpha_channel ? SG_COLORMASK_RGBA : SG_COLORMASK_RGB; pip_desc.colors[0].blend.enabled = true; pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + if (_simgui.desc.write_alpha_channel) { + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE; + } pip_desc.label = "sokol-imgui-pipeline"; _simgui.pip = sg_make_pipeline(&pip_desc); @@ -1837,58 +1938,52 @@ SOKOL_API_IMPL void simgui_shutdown(void) { sg_destroy_buffer(_simgui.vbuf); sg_pop_debug_group(); SOKOL_ASSERT(_simgui.vertices.ptr); - SOKOL_FREE((void*)_simgui.vertices.ptr); + _simgui_free((void*)_simgui.vertices.ptr); SOKOL_ASSERT(_simgui.indices.ptr); - SOKOL_FREE((void*)_simgui.indices.ptr); + _simgui_free((void*)_simgui.indices.ptr); } -#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) -_SOKOL_PRIVATE void _simgui_set_imgui_modifiers(ImGuiIO* io, uint32_t mods) { - io->KeyAlt = (mods & SAPP_MODIFIER_ALT) != 0; - io->KeyCtrl = (mods & SAPP_MODIFIER_CTRL) != 0; - io->KeyShift = (mods & SAPP_MODIFIER_SHIFT) != 0; - io->KeySuper = (mods & SAPP_MODIFIER_SUPER) != 0; -} -#endif - -SOKOL_API_IMPL void simgui_new_frame(int width, int height, double delta_time) { +SOKOL_API_IMPL void simgui_new_frame(const simgui_frame_desc_t* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->width > 0); + SOKOL_ASSERT(desc->height > 0); + _simgui.cur_dpi_scale = _simgui_def(desc->dpi_scale, 1.0f); #if defined(__cplusplus) ImGuiIO* io = &ImGui::GetIO(); #else ImGuiIO* io = igGetIO(); #endif - io->DisplaySize.x = ((float) width) / _simgui.desc.dpi_scale; - io->DisplaySize.y = ((float) height) / _simgui.desc.dpi_scale; - io->DeltaTime = (float) delta_time; + io->DisplaySize.x = ((float)desc->width) / _simgui.cur_dpi_scale; + io->DisplaySize.y = ((float)desc->height) / _simgui.cur_dpi_scale; + io->DeltaTime = (float)desc->delta_time; #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) - for (int i = 0; i < SAPP_MAX_MOUSEBUTTONS; i++) { - if (_simgui.btn_down[i]) { - _simgui.btn_down[i] = false; - io->MouseDown[i] = true; + if (io->WantTextInput && !sapp_keyboard_shown()) { + sapp_show_keyboard(true); } - else if (_simgui.btn_up[i]) { - _simgui.btn_up[i] = false; - io->MouseDown[i] = false; + if (!io->WantTextInput && sapp_keyboard_shown()) { + sapp_show_keyboard(false); } - } - for (int i = 0; i < SIMGUI_MAX_KEY_VALUE; i++) { - if (_simgui.keys_down[i]) { - io->KeysDown[i] = true; - _simgui_set_imgui_modifiers(io, _simgui.keys_down[i]); - _simgui.keys_down[i] = 0; + if (!_simgui.desc.disable_set_mouse_cursor) { + #if defined(__cplusplus) + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + #else + ImGuiMouseCursor imgui_cursor = igGetMouseCursor(); + #endif + sapp_mouse_cursor cursor = sapp_get_mouse_cursor(); + switch (imgui_cursor) { + case ImGuiMouseCursor_Arrow: cursor = SAPP_MOUSECURSOR_ARROW; break; + case ImGuiMouseCursor_TextInput: cursor = SAPP_MOUSECURSOR_IBEAM; break; + case ImGuiMouseCursor_ResizeAll: cursor = SAPP_MOUSECURSOR_RESIZE_ALL; break; + case ImGuiMouseCursor_ResizeNS: cursor = SAPP_MOUSECURSOR_RESIZE_NS; break; + case ImGuiMouseCursor_ResizeEW: cursor = SAPP_MOUSECURSOR_RESIZE_EW; break; + case ImGuiMouseCursor_ResizeNESW: cursor = SAPP_MOUSECURSOR_RESIZE_NESW; break; + case ImGuiMouseCursor_ResizeNWSE: cursor = SAPP_MOUSECURSOR_RESIZE_NWSE; break; + case ImGuiMouseCursor_Hand: cursor = SAPP_MOUSECURSOR_POINTING_HAND; break; + case ImGuiMouseCursor_NotAllowed: cursor = SAPP_MOUSECURSOR_NOT_ALLOWED; break; + default: break; + } + sapp_set_mouse_cursor(cursor); } - else if (_simgui.keys_up[i]) { - io->KeysDown[i] = false; - _simgui_set_imgui_modifiers(io, _simgui.keys_up[i]); - _simgui.keys_up[i] = 0; - } - } - if (io->WantTextInput && !sapp_keyboard_shown()) { - sapp_show_keyboard(true); - } - if (!io->WantTextInput && sapp_keyboard_shown()) { - sapp_show_keyboard(false); - } #endif #if defined(__cplusplus) ImGui::NewFrame(); @@ -1966,7 +2061,7 @@ SOKOL_API_IMPL void simgui_render(void) { } /* render the ImGui command list */ - const float dpi_scale = _simgui.desc.dpi_scale; + const float dpi_scale = _simgui.cur_dpi_scale; const int fb_width = (int) (io->DisplaySize.x * dpi_scale); const int fb_height = (int) (io->DisplaySize.y * dpi_scale); sg_apply_viewport(0, 0, fb_width, fb_height, true); @@ -1974,12 +2069,12 @@ SOKOL_API_IMPL void simgui_render(void) { sg_apply_pipeline(_simgui.pip); _simgui_vs_params_t vs_params; - memset((void*)&vs_params, 0, sizeof(vs_params)); + _simgui_clear((void*)&vs_params, sizeof(vs_params)); vs_params.disp_size.x = io->DisplaySize.x; vs_params.disp_size.y = io->DisplaySize.y; sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); sg_bindings bind; - memset((void*)&bind, 0, sizeof(bind)); + _simgui_clear((void*)&bind, sizeof(bind)); bind.vertex_buffers[0] = _simgui.vbuf; bind.index_buffer = _simgui.ibuf; ImTextureID tex_id = io->Fonts->TexID; @@ -1993,7 +2088,6 @@ SOKOL_API_IMPL void simgui_render(void) { bind.index_buffer_offset = ib_offset; sg_apply_bindings(&bind); - int base_element = 0; #if defined(__cplusplus) const int num_cmds = cl->CmdBuffer.size(); #else @@ -2023,13 +2117,12 @@ SOKOL_API_IMPL void simgui_render(void) { const int scissor_w = (int) ((pcmd->ClipRect.z - pcmd->ClipRect.x) * dpi_scale); const int scissor_h = (int) ((pcmd->ClipRect.w - pcmd->ClipRect.y) * dpi_scale); sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true); - sg_draw(base_element, (int)pcmd->ElemCount, 1); + sg_draw((int)pcmd->IdxOffset, (int)pcmd->ElemCount, 1); } - base_element += (int)pcmd->ElemCount; } #if defined(__cplusplus) - const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert); - const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx); + const size_t vtx_size = (size_t)cl->VtxBuffer.size() * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.size() * sizeof(ImDrawIdx); #else const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); @@ -2052,66 +2145,256 @@ _SOKOL_PRIVATE bool _simgui_is_ctrl(uint32_t modifiers) { } } +_SOKOL_PRIVATE ImGuiKey _simgui_map_keycode(sapp_keycode key) { + switch (key) { + case SAPP_KEYCODE_SPACE: return ImGuiKey_Space; + case SAPP_KEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case SAPP_KEYCODE_COMMA: return ImGuiKey_Comma; + case SAPP_KEYCODE_MINUS: return ImGuiKey_Minus; + case SAPP_KEYCODE_PERIOD: return ImGuiKey_Apostrophe; + case SAPP_KEYCODE_SLASH: return ImGuiKey_Slash; + case SAPP_KEYCODE_0: return ImGuiKey_0; + case SAPP_KEYCODE_1: return ImGuiKey_1; + case SAPP_KEYCODE_2: return ImGuiKey_2; + case SAPP_KEYCODE_3: return ImGuiKey_3; + case SAPP_KEYCODE_4: return ImGuiKey_4; + case SAPP_KEYCODE_5: return ImGuiKey_5; + case SAPP_KEYCODE_6: return ImGuiKey_6; + case SAPP_KEYCODE_7: return ImGuiKey_7; + case SAPP_KEYCODE_8: return ImGuiKey_8; + case SAPP_KEYCODE_9: return ImGuiKey_9; + case SAPP_KEYCODE_SEMICOLON: return ImGuiKey_Semicolon; + case SAPP_KEYCODE_EQUAL: return ImGuiKey_Equal; + case SAPP_KEYCODE_A: return ImGuiKey_A; + case SAPP_KEYCODE_B: return ImGuiKey_B; + case SAPP_KEYCODE_C: return ImGuiKey_C; + case SAPP_KEYCODE_D: return ImGuiKey_D; + case SAPP_KEYCODE_E: return ImGuiKey_E; + case SAPP_KEYCODE_F: return ImGuiKey_F; + case SAPP_KEYCODE_G: return ImGuiKey_G; + case SAPP_KEYCODE_H: return ImGuiKey_H; + case SAPP_KEYCODE_I: return ImGuiKey_I; + case SAPP_KEYCODE_J: return ImGuiKey_J; + case SAPP_KEYCODE_K: return ImGuiKey_K; + case SAPP_KEYCODE_L: return ImGuiKey_L; + case SAPP_KEYCODE_M: return ImGuiKey_M; + case SAPP_KEYCODE_N: return ImGuiKey_N; + case SAPP_KEYCODE_O: return ImGuiKey_O; + case SAPP_KEYCODE_P: return ImGuiKey_P; + case SAPP_KEYCODE_Q: return ImGuiKey_Q; + case SAPP_KEYCODE_R: return ImGuiKey_R; + case SAPP_KEYCODE_S: return ImGuiKey_S; + case SAPP_KEYCODE_T: return ImGuiKey_T; + case SAPP_KEYCODE_U: return ImGuiKey_U; + case SAPP_KEYCODE_V: return ImGuiKey_V; + case SAPP_KEYCODE_W: return ImGuiKey_W; + case SAPP_KEYCODE_X: return ImGuiKey_X; + case SAPP_KEYCODE_Y: return ImGuiKey_Y; + case SAPP_KEYCODE_Z: return ImGuiKey_Z; + case SAPP_KEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case SAPP_KEYCODE_BACKSLASH: return ImGuiKey_Backslash; + case SAPP_KEYCODE_RIGHT_BRACKET:return ImGuiKey_RightBracket; + case SAPP_KEYCODE_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case SAPP_KEYCODE_ESCAPE: return ImGuiKey_Escape; + case SAPP_KEYCODE_ENTER: return ImGuiKey_Enter; + case SAPP_KEYCODE_TAB: return ImGuiKey_Tab; + case SAPP_KEYCODE_BACKSPACE: return ImGuiKey_Backspace; + case SAPP_KEYCODE_INSERT: return ImGuiKey_Insert; + case SAPP_KEYCODE_DELETE: return ImGuiKey_Delete; + case SAPP_KEYCODE_RIGHT: return ImGuiKey_RightArrow; + case SAPP_KEYCODE_LEFT: return ImGuiKey_LeftArrow; + case SAPP_KEYCODE_DOWN: return ImGuiKey_DownArrow; + case SAPP_KEYCODE_UP: return ImGuiKey_UpArrow; + case SAPP_KEYCODE_PAGE_UP: return ImGuiKey_PageUp; + case SAPP_KEYCODE_PAGE_DOWN: return ImGuiKey_PageDown; + case SAPP_KEYCODE_HOME: return ImGuiKey_Home; + case SAPP_KEYCODE_END: return ImGuiKey_End; + case SAPP_KEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock; + case SAPP_KEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case SAPP_KEYCODE_NUM_LOCK: return ImGuiKey_NumLock; + case SAPP_KEYCODE_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case SAPP_KEYCODE_PAUSE: return ImGuiKey_Pause; + case SAPP_KEYCODE_F1: return ImGuiKey_F1; + case SAPP_KEYCODE_F2: return ImGuiKey_F2; + case SAPP_KEYCODE_F3: return ImGuiKey_F3; + case SAPP_KEYCODE_F4: return ImGuiKey_F4; + case SAPP_KEYCODE_F5: return ImGuiKey_F5; + case SAPP_KEYCODE_F6: return ImGuiKey_F6; + case SAPP_KEYCODE_F7: return ImGuiKey_F7; + case SAPP_KEYCODE_F8: return ImGuiKey_F8; + case SAPP_KEYCODE_F9: return ImGuiKey_F9; + case SAPP_KEYCODE_F10: return ImGuiKey_F10; + case SAPP_KEYCODE_F11: return ImGuiKey_F11; + case SAPP_KEYCODE_F12: return ImGuiKey_F12; + case SAPP_KEYCODE_KP_0: return ImGuiKey_Keypad0; + case SAPP_KEYCODE_KP_1: return ImGuiKey_Keypad1; + case SAPP_KEYCODE_KP_2: return ImGuiKey_Keypad2; + case SAPP_KEYCODE_KP_3: return ImGuiKey_Keypad3; + case SAPP_KEYCODE_KP_4: return ImGuiKey_Keypad4; + case SAPP_KEYCODE_KP_5: return ImGuiKey_Keypad5; + case SAPP_KEYCODE_KP_6: return ImGuiKey_Keypad6; + case SAPP_KEYCODE_KP_7: return ImGuiKey_Keypad7; + case SAPP_KEYCODE_KP_8: return ImGuiKey_Keypad8; + case SAPP_KEYCODE_KP_9: return ImGuiKey_Keypad9; + case SAPP_KEYCODE_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case SAPP_KEYCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SAPP_KEYCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SAPP_KEYCODE_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case SAPP_KEYCODE_KP_ADD: return ImGuiKey_KeypadAdd; + case SAPP_KEYCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SAPP_KEYCODE_KP_EQUAL: return ImGuiKey_KeypadEqual; + case SAPP_KEYCODE_LEFT_SHIFT: return ImGuiKey_LeftShift; + case SAPP_KEYCODE_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case SAPP_KEYCODE_LEFT_ALT: return ImGuiKey_LeftAlt; + case SAPP_KEYCODE_LEFT_SUPER: return ImGuiKey_LeftSuper; + case SAPP_KEYCODE_RIGHT_SHIFT: return ImGuiKey_RightShift; + case SAPP_KEYCODE_RIGHT_CONTROL:return ImGuiKey_RightCtrl; + case SAPP_KEYCODE_RIGHT_ALT: return ImGuiKey_RightAlt; + case SAPP_KEYCODE_RIGHT_SUPER: return ImGuiKey_RightSuper; + case SAPP_KEYCODE_MENU: return ImGuiKey_Menu; + default: return ImGuiKey_None; + } +} + +_SOKOL_PRIVATE void _simgui_add_focus_event(ImGuiIO* io, bool focus) { + #if defined(__cplusplus) + io->AddFocusEvent(focus); + #else + ImGuiIO_AddFocusEvent(io, focus); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_mouse_pos_event(ImGuiIO* io, float x, float y) { + #if defined(__cplusplus) + io->AddMousePosEvent(x, y); + #else + ImGuiIO_AddMousePosEvent(io, x, y); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_mouse_button_event(ImGuiIO* io, int mouse_button, bool down) { + #if defined(__cplusplus) + io->AddMouseButtonEvent(mouse_button, down); + #else + ImGuiIO_AddMouseButtonEvent(io, mouse_button, down); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_mouse_wheel_event(ImGuiIO* io, float wheel_x, float wheel_y) { + #if defined(__cplusplus) + io->AddMouseWheelEvent(wheel_x, wheel_y); + #else + ImGuiIO_AddMouseWheelEvent(io, wheel_x, wheel_y); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_sapp_key_event(ImGuiIO* io, sapp_keycode sapp_key, bool down) { + const ImGuiKey imgui_key = _simgui_map_keycode(sapp_key); + #if defined(__cplusplus) + io->AddKeyEvent(imgui_key, down); + io->SetKeyEventNativeData(imgui_key, (int)sapp_key, 0, -1); + #else + ImGuiIO_AddKeyEvent(io, imgui_key, down); + ImGuiIO_SetKeyEventNativeData(io, imgui_key, (int)sapp_key, 0, -1); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_imgui_key_event(ImGuiIO* io, ImGuiKey imgui_key, bool down) { + #if defined(__cplusplus) + io->AddKeyEvent(imgui_key, down); + #else + ImGuiIO_AddKeyEvent(io, imgui_key, down); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_input_character(ImGuiIO* io, uint32_t c) { + #if defined(__cplusplus) + io->AddInputCharacter(c); + #else + ImGuiIO_AddInputCharacter(io, c); + #endif +} + +_SOKOL_PRIVATE void _simgui_update_modifiers(ImGuiIO* io, uint32_t mods) { + _simgui_add_imgui_key_event(io, ImGuiMod_Ctrl, (mods & SAPP_MODIFIER_CTRL) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Shift, (mods & SAPP_MODIFIER_SHIFT) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Alt, (mods & SAPP_MODIFIER_ALT) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Super, (mods & SAPP_MODIFIER_SUPER) != 0); +} + +// returns Ctrl or Super, depending on platform +_SOKOL_PRIVATE ImGuiKey _simgui_copypaste_modifier(void) { + return _simgui.is_osx ? ImGuiMod_Super : ImGuiMod_Ctrl; +} + +SOKOL_API_IMPL int simgui_map_keycode(sapp_keycode keycode) { + return (int)_simgui_map_keycode(keycode); +} + SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { - const float dpi_scale = _simgui.desc.dpi_scale; + const float dpi_scale = _simgui.cur_dpi_scale; #if defined(__cplusplus) ImGuiIO* io = &ImGui::GetIO(); #else ImGuiIO* io = igGetIO(); #endif - _simgui_set_imgui_modifiers(io, ev->modifiers); switch (ev->type) { + case SAPP_EVENTTYPE_FOCUSED: + _simgui_add_focus_event(io, true); + break; + case SAPP_EVENTTYPE_UNFOCUSED: + _simgui_add_focus_event(io, false); + break; case SAPP_EVENTTYPE_MOUSE_DOWN: - io->MousePos.x = ev->mouse_x / dpi_scale; - io->MousePos.y = ev->mouse_y / dpi_scale; - if (ev->mouse_button < 3) { - _simgui.btn_down[ev->mouse_button] = true; - } + _simgui_add_mouse_pos_event(io, ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + _simgui_add_mouse_button_event(io, (int)ev->mouse_button, true); + _simgui_update_modifiers(io, ev->modifiers); break; case SAPP_EVENTTYPE_MOUSE_UP: - io->MousePos.x = ev->mouse_x / dpi_scale; - io->MousePos.y = ev->mouse_y / dpi_scale; - if (ev->mouse_button < 3) { - _simgui.btn_up[ev->mouse_button] = true; - } + _simgui_add_mouse_pos_event(io, ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + _simgui_add_mouse_button_event(io, (int)ev->mouse_button, false); + _simgui_update_modifiers(io, ev->modifiers); break; case SAPP_EVENTTYPE_MOUSE_MOVE: - io->MousePos.x = ev->mouse_x / dpi_scale; - io->MousePos.y = ev->mouse_y / dpi_scale; + _simgui_add_mouse_pos_event(io, ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); break; case SAPP_EVENTTYPE_MOUSE_ENTER: case SAPP_EVENTTYPE_MOUSE_LEAVE: - for (int i = 0; i < 3; i++) { - _simgui.btn_down[i] = false; - _simgui.btn_up[i] = false; - io->MouseDown[i] = false; + // FIXME: since the sokol_app.h emscripten backend doesn't support + // mouse capture, mouse buttons must be released when the mouse leaves the + // browser window, so that they don't "stick" when released outside the window. + // A cleaner solution would be a new sokol_app.h function to query + // "platform behaviour flags". + #if defined(__EMSCRIPTEN__) + for (int i = 0; i < SAPP_MAX_MOUSEBUTTONS; i++) { + _simgui_add_mouse_button_event(io, i, false); } + #endif break; case SAPP_EVENTTYPE_MOUSE_SCROLL: - io->MouseWheelH = ev->scroll_x; - io->MouseWheel = ev->scroll_y; + _simgui_add_mouse_wheel_event(io, ev->scroll_x, ev->scroll_y); break; case SAPP_EVENTTYPE_TOUCHES_BEGAN: - _simgui.btn_down[0] = true; - io->MousePos.x = ev->touches[0].pos_x / dpi_scale; - io->MousePos.y = ev->touches[0].pos_y / dpi_scale; + _simgui_add_mouse_pos_event(io, ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + _simgui_add_mouse_button_event(io, 0, true); break; case SAPP_EVENTTYPE_TOUCHES_MOVED: - io->MousePos.x = ev->touches[0].pos_x / dpi_scale; - io->MousePos.y = ev->touches[0].pos_y / dpi_scale; + _simgui_add_mouse_pos_event(io, ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); break; case SAPP_EVENTTYPE_TOUCHES_ENDED: - _simgui.btn_up[0] = true; - io->MousePos.x = ev->touches[0].pos_x / dpi_scale; - io->MousePos.y = ev->touches[0].pos_y / dpi_scale; + _simgui_add_mouse_pos_event(io, ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + _simgui_add_mouse_button_event(io, 0, false); break; case SAPP_EVENTTYPE_TOUCHES_CANCELLED: - _simgui.btn_up[0] = _simgui.btn_down[0] = false; + _simgui_add_mouse_button_event(io, 0, false); break; case SAPP_EVENTTYPE_KEY_DOWN: + _simgui_update_modifiers(io, ev->modifiers); /* intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED */ - if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { - break; + if (!_simgui.desc.disable_paste_override) { + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } } /* on web platform, don't forward Ctrl-X, Ctrl-V to the browser */ if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { @@ -2120,9 +2403,11 @@ SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { sapp_consume_event(); } - _simgui.keys_down[ev->key_code] = 0x80 | (uint8_t)ev->modifiers; + // it's ok to add ImGuiKey_None key events + _simgui_add_sapp_key_event(io, ev->key_code, true); break; case SAPP_EVENTTYPE_KEY_UP: + _simgui_update_modifiers(io, ev->modifiers); /* intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED */ if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { break; @@ -2134,7 +2419,8 @@ SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { sapp_consume_event(); } - _simgui.keys_up[ev->key_code] = 0x80 | (uint8_t)ev->modifiers; + // it's ok to add ImGuiKey_None key events + _simgui_add_sapp_key_event(io, ev->key_code, false); break; case SAPP_EVENTTYPE_CHAR: /* on some platforms, special keys may be reported as @@ -2142,27 +2428,28 @@ SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { drop those, also don't forward characters if some modifiers have been pressed */ + _simgui_update_modifiers(io, ev->modifiers); if ((ev->char_code >= 32) && (ev->char_code != 127) && (0 == (ev->modifiers & (SAPP_MODIFIER_ALT|SAPP_MODIFIER_CTRL|SAPP_MODIFIER_SUPER)))) { - #if defined(__cplusplus) - io->AddInputCharacter((ImWchar)ev->char_code); - #else - ImGuiIO_AddInputCharacter(io, (ImWchar)ev->char_code); - #endif + _simgui_add_input_character(io, ev->char_code); } break; case SAPP_EVENTTYPE_CLIPBOARD_PASTED: /* simulate a Ctrl-V key down/up */ - _simgui.keys_down[SAPP_KEYCODE_V] = _simgui.keys_up[SAPP_KEYCODE_V] = - (uint8_t) (0x80 | (_simgui.is_osx ? SAPP_MODIFIER_SUPER:SAPP_MODIFIER_CTRL)); + if (!_simgui.desc.disable_paste_override) { + _simgui_add_imgui_key_event(io, _simgui_copypaste_modifier(), true); + _simgui_add_imgui_key_event(io, ImGuiKey_V, true); + _simgui_add_imgui_key_event(io, ImGuiKey_V, false); + _simgui_add_imgui_key_event(io, _simgui_copypaste_modifier(), false); + } break; default: break; } return io->WantCaptureKeyboard || io->WantCaptureMouse; } -#endif +#endif // SOKOL_IMGUI_NO_SOKOL_APP -#endif /* SOKOL_IMPL */ +#endif // SOKOL_IMPL diff --git a/3rdparty/sokol/util/sokol_memtrack.h b/3rdparty/sokol/util/sokol_memtrack.h index c7ba20b..dc0b469 100644 --- a/3rdparty/sokol/util/sokol_memtrack.h +++ b/3rdparty/sokol/util/sokol_memtrack.h @@ -8,15 +8,6 @@ Project URL: https://github.com/floooh/sokol - Simply include this file before the sokol header implementation includes - and after the SOKOL_IMPL macro: - - #define SOKOL_IMPL - #include "sokol_memtrack.h" - #include "sokol_app.h" - #include "sokol_gfx.h" - ... - Optionally provide the following defines with your own implementations: SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern) @@ -28,24 +19,29 @@ SOKOL_DLL - The sokol_memtrack.h header will redirect the macros SOKOL_MALLOC, - SOKOL_FREE and SOKOL_CALLOC to use its own function wrappers which - keep track of number and size of allocations. The wrapper functions - then call the CRT functions malloc(), calloc() or free(). + USAGE + ===== + Just plug the malloc/free wrapper functions into the desc.allocator + struct provided by most sokol header setup functions: - Naturally, only the memory management calls in sokol header code are tracked, - not in underlying APIs such as D3D11 or OpenGL. + sg_setup(&(sg_desc){ + //... + .allocator = { + .alloc = smemtrack_alloc, + .free = smemtrack_free, + } + }); - To get the current number and overall size of allocations, call: + Then call smemtrack_info() to get information about current number + of allocations and overall allocation size: - smemtrack_info_t alloc_info = smemtrack_info(); + const smemtrack_info_t info = smemtrack_info(); + const int num_allocs = info.num_allocs; + const int num_bytes = info.num_bytes; - int num_allocations = info.num_alloc; - int allocation_size = info.num_bytes; - - The allocation wrapper functions are *not* thread-safe (which shouldn't - be a problem because the sokol headers don't allocate or deallocate - in threads). + Note the sokol_memtrack.h can only track allocations issued by + the sokol headers, not allocations that happen under the hood + in system libraries. LICENSE ======= @@ -75,6 +71,7 @@ */ #define SOKOL_MEMTRACK_INCLUDED (1) #include +#include // size_t #if defined(SOKOL_API_DECL) && !defined(SOKOL_MEMTRACK_API_DECL) #define SOKOL_MEMTRACK_API_DECL SOKOL_API_DECL @@ -99,6 +96,8 @@ typedef struct smemtrack_info_t { } smemtrack_info_t; SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void); +SOKOL_MEMTRACK_API_DECL void* smemtrack_alloc(size_t size, void* user_data); +SOKOL_MEMTRACK_API_DECL void smemtrack_free(void* ptr, void* user_data); #ifdef __cplusplus } /* extern "C" */ @@ -108,16 +107,15 @@ SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void); /*=== IMPLEMENTATION =========================================================*/ #ifdef SOKOL_MEMTRACK_IMPL #define SOKOL_MEMTRACK_IMPL_INCLUDED (1) -#include /* malloc, free, calloc */ -#include /* memset */ -#include /* size_t */ +#include // malloc, free +#include // memset #ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef _SOKOL_PRIVATE @@ -128,40 +126,38 @@ SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void); #endif #endif -#define SOKOL_MALLOC(s) _smemtrack_malloc(s) -#define SOKOL_FREE(p) _smemtrack_free(p) -#define SOKOL_CALLOC(n,s) _smemtrack_calloc(n,s) - +// per-allocation header used to keep track of the allocation size #define _SMEMTRACK_HEADER_SIZE (16) static struct { smemtrack_info_t state; } _smemtrack; -_SOKOL_PRIVATE void* _smemtrack_malloc(size_t size) { - _smemtrack.state.num_allocs++; - _smemtrack.state.num_bytes += (int) size; +SOKOL_API_IMPL void* smemtrack_alloc(size_t size, void* user_data) { + (void)user_data; uint8_t* ptr = (uint8_t*) malloc(size + _SMEMTRACK_HEADER_SIZE); - *(size_t*)ptr = size; - return ptr + _SMEMTRACK_HEADER_SIZE; + if (ptr) { + // store allocation size (for allocation size tracking) + *(size_t*)ptr = size; + _smemtrack.state.num_allocs++; + _smemtrack.state.num_bytes += (int) size; + return ptr + _SMEMTRACK_HEADER_SIZE; + } + else { + // allocation failed, return null pointer + return ptr; + } } -_SOKOL_PRIVATE void _smemtrack_free(void* ptr) { - uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE; - size_t size = *(size_t*)alloc_ptr; - _smemtrack.state.num_allocs--; - _smemtrack.state.num_bytes -= (int) size; - free(alloc_ptr); -} - -_SOKOL_PRIVATE void* _smemtrack_calloc(size_t num, size_t size) { - size_t mem_size = num * size; - _smemtrack.state.num_allocs++; - _smemtrack.state.num_bytes += (int) mem_size; - uint8_t* ptr = (uint8_t*) malloc(mem_size + _SMEMTRACK_HEADER_SIZE); - memset(ptr + _SMEMTRACK_HEADER_SIZE, 0, mem_size); - *(size_t*)ptr = size; - return ptr + _SMEMTRACK_HEADER_SIZE; +SOKOL_API_IMPL void smemtrack_free(void* ptr, void* user_data) { + (void)user_data; + if (ptr) { + uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE; + size_t size = *(size_t*)alloc_ptr; + _smemtrack.state.num_allocs--; + _smemtrack.state.num_bytes -= (int) size; + free(alloc_ptr); + } } SOKOL_API_IMPL smemtrack_info_t smemtrack_info(void) { diff --git a/3rdparty/sokol/util/sokol_nuklear.h b/3rdparty/sokol/util/sokol_nuklear.h index 1c56ed4..3d99606 100644 --- a/3rdparty/sokol/util/sokol_nuklear.h +++ b/3rdparty/sokol/util/sokol_nuklear.h @@ -267,7 +267,7 @@ inline void snk_setup(const snk_desc_t& desc) { return snk_setup(&desc); } #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG - #define SOKOL_DEBUG (1) + #define SOKOL_DEBUG #endif #endif #ifndef SOKOL_ASSERT @@ -1941,8 +1941,15 @@ SOKOL_API_IMPL void snk_render(int width, int height) { int idx_offset = 0; nk_draw_foreach(cmd, &_snuklear.ctx, &cmds) { if (cmd->elem_count > 0) { + sg_image img; + if (cmd->texture.id != 0) { + img = (sg_image){ .id = (uint32_t) cmd->texture.id }; + } + else { + img = _snuklear.img; + } sg_apply_bindings(&(sg_bindings){ - .fs_images[0] = _snuklear.img, + .fs_images[0] = img, .vertex_buffers[0] = _snuklear.vbuf, .index_buffer = _snuklear.ibuf, .vertex_buffer_offsets[0] = 0, diff --git a/3rdparty/sokol/util/sokol_shape.h b/3rdparty/sokol/util/sokol_shape.h index 195f623..419181f 100644 --- a/3rdparty/sokol/util/sokol_shape.h +++ b/3rdparty/sokol/util/sokol_shape.h @@ -556,7 +556,6 @@ SOKOL_SHAPE_API_DECL sshape_mat4_t sshape_mat4_transpose(const float m[16]); #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic ignored "-Wmissing-braces" #endif #ifndef SOKOL_API_IMPL @@ -857,7 +856,7 @@ SOKOL_API_IMPL sshape_mat4_t sshape_mat4_transpose(const float m[16]) { SOKOL_API_IMPL sshape_sizes_t sshape_plane_sizes(uint32_t tiles) { SOKOL_ASSERT(tiles >= 1); - sshape_sizes_t res = { 0 }; + sshape_sizes_t res = { {0} }; res.vertices.num = _sshape_plane_num_vertices(tiles); res.indices.num = _sshape_plane_num_indices(tiles); res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); @@ -867,7 +866,7 @@ SOKOL_API_IMPL sshape_sizes_t sshape_plane_sizes(uint32_t tiles) { SOKOL_API_IMPL sshape_sizes_t sshape_box_sizes(uint32_t tiles) { SOKOL_ASSERT(tiles >= 1); - sshape_sizes_t res = { 0 }; + sshape_sizes_t res = { {0} }; res.vertices.num = _sshape_box_num_vertices(tiles); res.indices.num = _sshape_box_num_indices(tiles); res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); @@ -877,7 +876,7 @@ SOKOL_API_IMPL sshape_sizes_t sshape_box_sizes(uint32_t tiles) { SOKOL_API_IMPL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks) { SOKOL_ASSERT((slices >= 3) && (stacks >= 2)); - sshape_sizes_t res = { 0 }; + sshape_sizes_t res = { {0} }; res.vertices.num = _sshape_sphere_num_vertices(slices, stacks); res.indices.num = _sshape_sphere_num_indices(slices, stacks); res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); @@ -887,7 +886,7 @@ SOKOL_API_IMPL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stac SOKOL_API_IMPL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks) { SOKOL_ASSERT((slices >= 3) && (stacks >= 1)); - sshape_sizes_t res = { 0 }; + sshape_sizes_t res = { {0} }; res.vertices.num = _sshape_cylinder_num_vertices(slices, stacks); res.indices.num = _sshape_cylinder_num_indices(slices, stacks); res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); @@ -897,7 +896,7 @@ SOKOL_API_IMPL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t st SOKOL_API_IMPL sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings) { SOKOL_ASSERT((sides >= 3) && (rings >= 3)); - sshape_sizes_t res = { 0 }; + sshape_sizes_t res = { {0} }; res.vertices.num = _sshape_torus_num_vertices(sides, rings); res.indices.num = _sshape_torus_num_indices(sides, rings); res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t);