commit f49ebb5474ba9308dac12cc2b2a84865c95b9dde Author: Judah Caruso Date: Wed Feb 18 18:07:51 2026 -0700 . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc1869d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.zed/ +out/ +thirdparty/blocksruntime +thirdparty/zig* +.DS_Store diff --git a/justfile b/justfile new file mode 100644 index 0000000..0d116f7 --- /dev/null +++ b/justfile @@ -0,0 +1,143 @@ +set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] +set shell := ["bash", "-cu"] + +# configuration +project := "p2601" +out_dir := "out" +thirdparty := "thirdparty" + +# platform-specific +host_os := if os() == "macos" { "macos" } else { os() } +host_arch := arch() +exe := if os() == "windows" { ".exe" } else { "" } + +# zig stuff +zig_version := "0.15.2" +zig_slug := "zig-" + host_arch + "-" + host_os + "-" + zig_version +zig_dir := thirdparty / zig_slug +zig := zig_dir / ("zig" + exe) +zig_ext := if os() == "windows" { "zip" } else { "tar.xz" } +zig_url := "https://ziglang.org/download/" + zig_version + "/" + zig_slug + "." + zig_ext + +# blocks runtime +blocks_dir := thirdparty / "blocksruntime" +blocks_repo := "https://github.com/mackyle/blocksruntime.git" +# use the real blocks runtime if we're on mac +blocks_include := if os() == "macos" { "" } else { "-I" + blocks_dir / "BlocksRuntime" + " -I" + blocks_dir } +blocks_src := if os() == "macos" { "" } else { blocks_dir / "BlocksRuntime" / "runtime.c" + " " + blocks_dir / "BlocksRuntime" / "data.c" } + +# cflags +macflags := if os() == "macos" { "-x objective-c -lobjc -framework Foundation -framework Cocoa -framework CoreFoundation -framework CoreGraphics -framework QuartzCore -framework Metal -framework MetalKit"} else { "" } + +csrc := "src/main.c" + " " + blocks_src +cinc := "-Isrc -Ithirdparty " + blocks_include +cflags := "-std=c23 -fblocks -fno-sanitize=address -fno-sanitize=undefined -g " + macflags + " " + cinc + + + +# how the hotdog is made +# ---------------------- + +# platform-specific vendoring + +[unix] +[private] +vendor-zig: + #!/usr/bin/env bash + set -euo pipefail + [[ -x "{{zig}}" ]] && exit 0 + echo ":: Downloading Zig {{zig_version}} ({{host_os}}/{{host_arch}})…" + echo {{zig_url}} + mkdir -p "{{thirdparty}}" + curl -sSfL "{{zig_url}}" | tar -xJ -C "{{thirdparty}}" + +[windows] +[private] +vendor-zig: + #!powershell.exe -NoProfile -ExecutionPolicy Bypass + if (Test-Path "{{zig}}") { exit 0 } + Write-Host ":: Downloading Zig {{zig_version}} ({{host_os}}/{{host_arch}})..." + New-Item -ItemType Directory -Force -Path "{{thirdparty}}" | Out-Null + $tmp = Join-Path $env:TEMP "zig-vendor.zip" + Invoke-WebRequest -Uri "{{zig_url}}" -OutFile $tmp -UseBasicParsing + Expand-Archive -Path $tmp -DestinationPath "{{thirdparty}}" -Force + Remove-Item $tmp + +[unix] +[private] +vendor-blocks: + #!/usr/bin/env bash + set -euo pipefail + [[ -d "{{blocks_dir}}" ]] && exit 0 + echo ":: Cloning libBlocksRuntime…" + git clone --depth 1 "{{blocks_repo}}" "{{blocks_dir}}" + +[windows] +[private] +vendor-blocks: + #!powershell.exe -NoProfile -ExecutionPolicy Bypass + if (Test-Path "{{blocks_dir}}") { exit 0 } + Write-Host ":: Cloning libBlocksRuntime..." + git clone --depth 1 "{{blocks_repo}}" "{{blocks_dir}}" + +[doc("Pull in unversioned thirdparty dependencies")] +vendor: vendor-zig vendor-blocks + +# building/running + +[unix] +[private] +_mkout: + @mkdir -p "{{out_dir}}" + +[windows] +[private] +_mkout: + @New-Item -ItemType Directory -Force -Path "{{out_dir}}" | Out-Null + +[doc("Compile natively (debug)")] +[default] +build: vendor _mkout + {{zig}} cc {{cflags}} {{csrc}} {{blocks_include}} -o {{out_dir}}/{{project}}{{exe}} + +[doc("Compile natively with optimizations")] +build-release: vendor _mkout + {{zig}} cc {{cflags}} -Ofast -Wno-everything -ffast-math -DNDEBUG {{csrc}} {{blocks_include}} -o {{out_dir}}/{{project}}{{exe}} + +[doc("Compile for wasm32-wasi")] +build-wasm: vendor _mkout + {{zig}} cc {{cflags}} -target wasm32-wasi {{csrc}} {{blocks_include}} -o {{out_dir}}/{{project}}.wasm + +[doc("Cross-compile for a Zig target triple (e.g. aarch64-linux-gnu)")] +build-cross target: vendor _mkout + {{zig}} cc {{cflags}} -target {{target}} {{csrc}} {{blocks_include}} -o {{out_dir}}/{{project}}-{{target}} + +[doc("Build and run natively")] +run *args: build + @./{{out_dir}}/{{project}}{{exe}} {{args}} + +[doc("Build for wasm32-wasi and run via Wasmer")] +run-wasm *args: build-wasm + @wasmer run {{out_dir}}/{{project}}.wasm -- {{args}} + +# extras + +[doc("List Zig cross-compilation targets")] +list-targets: vendor-zig + {{zig}} targets + +[doc("List available just commands")] +list: + @just --list + +[doc("Remove build artifacts")] +[unix] +[confirm] +@clean: + rm -rf {{out_dir}} + +[doc("Remove build artifacts")] +[windows] +[confirm] +clean: + if (Test-Path "{{out_dir}}") { Remove-Item -Recurse -Force "{{out_dir}}" } diff --git a/src/base.c b/src/base.c new file mode 100644 index 0000000..3b2b229 --- /dev/null +++ b/src/base.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#define capture __block +#define closure Block_copy + +typedef void (^__defer_block_t)(void); + +static inline void __defer_exec(__defer_block_t *blk) { (*blk)(); } + +#define __cat_2(a, b) a##b +#define __cat_1(a, b) __cat_2(a, b) + +#define scope_exit \ + __attribute__((cleanup(__defer_exec), unused)) \ + __defer_block_t __cat_1(_defer_, __LINE__) = ^ + +#define auto __auto_type + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef float f32; +typedef double f64; + +typedef intptr_t intptr; +typedef uintptr_t uintptr; +typedef size_t usize; +typedef ptrdiff_t ssize; + +// Thanks AZMR +#define cast(to_type, expr) ((to_type)(expr)) +#define chkcast(to_type, from_type, expr) _Generic(expr, from_type: (to_type)(expr)) +#define ptrcast(to_type, from_type, expr) _Generic(expr, from_type: (to_type)(uintptr_t)(expr)) +#define bitcast(to_type, from_type, expr) (((union { from_type from; to_type to; }){_Generic(expr, from_type: expr)}).to) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4c4615f --- /dev/null +++ b/src/main.c @@ -0,0 +1,84 @@ +#include +#include "base.c" + +#define SOKOL_IMPL +#define SOKOL_METAL +#define SOKOL_NO_ENTRY +#include "sokol/sokol_app.h" +#include "sokol/sokol_gfx.h" +#include "sokol/sokol_glue.h" +#include "sokol/sokol_log.h" + +sg_pass_action pass_action; + +static void +init(void) { + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + .logger.func = slog_func, + }); + + pass_action = (sg_pass_action) { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 1.0f, 0.0f, 0.0f, 1.0f } + } + }; +} + +static void +frame(void) { + sg_begin_pass(&(sg_pass){ + .action.colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.5f, 0.25f, 0.75f, 1.0f }, + }, + .swapchain = sglue_swapchain(), + }); + sg_end_pass(); + + sg_commit(); +} + +static void +cleanup(void) { + sg_shutdown(); +} + +static void +event(const sapp_event* event) { +} + +int +main(int argc, char* argv[]) { + sapp_run(&(sapp_desc) { + .init_cb = init, + .frame_cb = frame, + .cleanup_cb = cleanup, + .logger.func = slog_func, + + .width = 400, + .height = 300, + .window_title = "hello, world", + }); + + capture auto x = 0; + auto inc = closure(^{ + x += 1; + }); + + s32 i = -10243; + scope_exit { + printf("it works %d!\n", i); + }; + + f32 fi = cast(f32, i); + printf("%.2f\n", fi); + + inc(); + inc(); + inc(); + inc(); + + printf("%d\n", x); +} diff --git a/thirdparty/sokol/LICENSE b/thirdparty/sokol/LICENSE new file mode 100644 index 0000000..efdc02e --- /dev/null +++ b/thirdparty/sokol/LICENSE @@ -0,0 +1,22 @@ +zlib/libpng license + +Copyright (c) 2018 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. diff --git a/thirdparty/sokol/sokol_app.h b/thirdparty/sokol/sokol_app.h new file mode 100644 index 0000000..b6e54ed --- /dev/null +++ b/thirdparty/sokol/sokol_app.h @@ -0,0 +1,14160 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL) +#define SOKOL_APP_IMPL +#endif +#ifndef SOKOL_APP_INCLUDED +/* + sokol_app.h -- cross-platform application wrapper + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_APP_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the 3D-API + which should be initialized by sokol_app.h (this must also match + the backend selected for sokol_gfx.h if both are used in the same + project): + + #define SOKOL_GLCORE + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_VULKAN + #define SOKOL_NOAPI + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_WIN32_FORCE_MAIN - define this on Win32 to add a main() entry point + SOKOL_WIN32_FORCE_WINMAIN - define this on Win32 to add a WinMain() entry point (enabled by default unless + SOKOL_WIN32_FORCE_MAIN or SOKOL_NO_ENTRY is defined) + 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: -) + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if NDEBUG is not defined + + If sokol_app.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + if SOKOL_WIN32_FORCE_MAIN and SOKOL_WIN32_FORCE_WINMAIN are both defined, + it is up to the developer to define the desired subsystem. + + On Linux, SOKOL_GLCORE 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 + have been taken from GLFW (http://www.glfw.org/). + + iOS onscreen keyboard support 'inspired' by libgdx. + + Link with the following system libraries: + + - on macOS: + - all backends: Foundation, Cocoa, QuartzCore + - with SOKOL_METAL: Metal, MetalKit + - with SOKOL_GLCORE: OpenGL + - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) + - on iOS: + - all backends: Foundation, UIKit + - with SOKOL_METAL: Metal, MetalKit + - with SOKOL_GLES3: OpenGLES, GLKit + - on Linux: + - all backends: X11, Xi, Xcursor, dl, pthread, m + - with SOKOL_GLCORE: GL + - with SOKOL_GLES3: GLESv2 + - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) + - with SOKOL_VULKAN: vulkan + - with EGL: EGL + - on Android: GLESv3, EGL, log, android + - on Windows: + - with MSVC or Clang: library dependencies are defined via `#pragma comment` + - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) + - with SOKOL_VULKAN: + - install the Vulkan SDK + - set a header search path to $VULKAN_SDK/Include + - set a library search path to $VULKAN_SDK/Lib + - link with vulkan-1.lib + - with MINGW/MSYS2 gcc: + - compile with '-mwin32' so that _WIN32 is defined + - link with the following libs: -lkernel32 -luser32 -lshell32 + - additionally with the GL backend: -lgdi32 + - additionally with the D3D11 backend: -ld3d11 -ldxgi + + 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 + + For Linux+Vulkan install the following packages (or equivalents): + - libvulkan-dev + - vulkan-validationlayers + - vulkan-tools + + On macOS and iOS, the implementation must be compiled as Objective-C. + + On Emscripten: + - for WebGL2: add the linker option `-s USE_WEBGL2=1` + - for WebGPU: compile and link with `--use-port=emdawnwebgpu` + (for more exotic situations read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) + + FEATURE OVERVIEW + ================ + sokol_app.h provides a minimalistic cross-platform API which + implements the 'application-wrapper' parts of a 3D application: + + - a common application entry function + - creates a window and 3D-API context/device with a swapchain + surface, depth-stencil-buffer surface and optionally MSAA surface + - makes the rendered frame visible + - provides keyboard-, mouse- and low-level touch-events + - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android + - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI + + FEATURE/PLATFORM MATRIX + ======================= + | Windows | macOS | Linux | iOS | Android | HTML5 + --------------------+---------+-------+-------+-------+---------+-------- + gl 4.x | YES | YES | YES | --- | --- | --- + gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES + metal | --- | YES | --- | YES | --- | --- + d3d11 | YES | --- | --- | --- | --- | --- + webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES + noapi | YES | TODO | TODO | --- | TODO | --- + 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 | YES(3) + 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 | YES | --- | --- | 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) + (3) fullscreen in the browser not supported on iphones + (4) WebGPU on native desktop platforms should be considered experimental + and mainly useful for debugging and benchmarking + + STEP BY STEP + ============ + --- Add a sokol_main() function to your code which returns a sapp_desc structure + with initialization parameters and callback function pointers. This + function is called very early, usually at the start of the + platform's entry function (e.g. main or WinMain). You should do as + little as possible here, since the rest of your code might be called + from another thread (this depends on the platform): + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + .width = 640, + .height = 480, + .init_cb = my_init_func, + .frame_cb = my_frame_func, + .cleanup_cb = my_cleanup_func, + .event_cb = my_event_func, + ... + }; + } + + 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. + + DO NOT call any sokol-app function from inside sokol_main(), since + sokol-app will not be initialized at this point. + + The .width and .height parameters are the preferred size of the 3D + 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). 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. + + .init_cb (void (*)(void)) + This function is called once after the application window, + 3D rendering context and swap chain have been created. The + function takes no arguments and has no return value. + .frame_cb (void (*)(void)) + This is the per-frame callback, which is usually called 60 + times per second. This is where your application would update + most of its state and perform all rendering. + .cleanup_cb (void (*)(void)) + The cleanup callback is called once right before the application + quits. + .event_cb (void (*)(const sapp_event* event)) + The event callback is mainly for input handling, but is also + 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. + + As you can see, those 'standard callbacks' don't have a user_data + argument, so any data that needs to be preserved between callbacks + must live in global variables. If keeping state in global variables + is not an option, there's an alternative set of callbacks with + an additional user_data pointer argument: + + .user_data (void*) + The user-data argument for the callbacks below + .init_userdata_cb (void (*)(void* user_data)) + .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)) + + The function sapp_userdata() can be used to query the user_data + pointer provided in the sapp_desc struct. + + You can also call sapp_query_desc() to get a copy of the + original sapp_desc structure. + + NOTE that there's also an alternative compile mode where sokol_app.h + doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. + + --- Implement the initialization callback function (init_cb), this is called + once after the rendering surface, 3D API and swap chain have been + initialized by sokol_app. All sokol-app functions can be called + from inside the initialization callback, the most useful functions + at this point are: + + int sapp_width(void) + int sapp_height(void) + Returns the current width and height of the default framebuffer in pixels, + this may change from one frame to the next, and it may be different + from the initial size provided in the sapp_desc struct. + + float sapp_widthf(void) + float sapp_heightf(void) + These are alternatives to sapp_width() and sapp_height() which return + the default framebuffer size as float values instead of integer. This + 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, + as integer values which are compatible with sokol-gfx's + sg_pixel_format enum (so that they can be plugged directly in places + where sg_pixel_format is expected). Possible values are: + + 23 == SG_PIXELFORMAT_RGBA8 + 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. + + const void* sapp_metal_get_device(void) + const void* sapp_metal_get_current_drawable(void) + const void* sapp_metal_get_depth_stencil_texture(void) + const void* sapp_metal_get_msaa_color_texture(void) + If the Metal backend has been selected, these functions return pointers + to various Metal API objects required for rendering, otherwise + they return a null pointer. These void pointers are actually + Objective-C ids converted with a (ARC) __bridge cast so that + the ids can be tunneled through C code. Also note that the returned + pointers may change from one frame to the next, only the Metal device + object is guaranteed to stay the same. + + const void* sapp_macos_get_window(void) + On macOS, get the NSWindow object pointer, otherwise a null pointer. + Before being used as Objective-C object, the void* must be converted + back with a (ARC) __bridge cast. + + const void* sapp_ios_get_window(void) + On iOS, get the UIWindow object pointer, otherwise a null pointer. + Before being used as Objective-C object, the void* must be converted + back with a (ARC) __bridge cast. + + const void* sapp_d3d11_get_device(void) + const void* sapp_d3d11_get_device_context(void) + const void* sapp_d3d11_get_render_view(void) + const void* sapp_d3d11_get_resolve_view(void); + const void* sapp_d3d11_get_depth_stencil_view(void) + Similar to the sapp_metal_* functions, the sapp_d3d11_* functions + return pointers to D3D11 API objects required for rendering, + only if the D3D11 backend has been selected. Otherwise they + return a null pointer. Note that the returned pointers to the + render-target-view and depth-stencil-view may change from one + frame to the next! + + const void* sapp_win32_get_hwnd(void) + On Windows, get the window's HWND, otherwise a null pointer. The + HWND has been cast to a void pointer in order to be tunneled + through code which doesn't include Windows.h. + + const void* sapp_x11_get_window(void) + On Linux, get the X11 Window, otherwise a null pointer. The + Window has been cast to a void pointer in order to be tunneled + through code which doesn't include X11/Xlib.h. + + const void* sapp_x11_get_display(void) + On Linux, get the X11 Display, otherwise a null pointer. The + Display has been cast to a void pointer in order to be tunneled + through code which doesn't include X11/Xlib.h. + + const void* sapp_wgpu_get_device(void) + const void* sapp_wgpu_get_render_view(void) + const void* sapp_wgpu_get_resolve_view(void) + const void* sapp_wgpu_get_depth_stencil_view(void) + These are the WebGPU-specific functions to get the WebGPU + objects and values required for rendering. If sokol_app.h + is not compiled with SOKOL_WGPU, these functions return null. + + uint32_t sapp_gl_get_framebuffer(void) + This returns the 'default framebuffer' of the GL context. + Typically this will be zero. + + int sapp_gl_get_major_version(void) + int sapp_gl_get_minor_version(void) + bool sapp_gl_is_gles(void) + Returns the major and minor version of the GL context and + whether the GL context is a GLES context + + const void* sapp_android_get_native_activity(void); + On Android, get the native activity ANativeActivity pointer, otherwise + a null pointer. + + --- Implement the frame-callback function, this function will be called + on the same thread as the init callback, but might be on a different + thread than the sokol_main() function. Note that the size of + the rendering framebuffer might have changed since the frame callback + was called last. Call the functions sapp_width() and sapp_height() + each frame to get the current size. + + --- Optionally implement the event-callback to handle input events. + sokol-app provides the following type of input events: + - a 'virtual key' was pressed down or released + - a single text character was entered (provided as UTF-32 encoded + UNICODE code point) + - a mouse button was pressed down or released (left, right, middle) + - mouse-wheel or 2D scrolling events + - the mouse was moved + - the mouse has entered or left the application window boundaries + - low-level, portable multi-touch events (began, moved, ended, cancelled) + - the application window was resized, iconified or restored + - the application was suspended or restored (on mobile platforms) + - the user or application code has asked to quit the application + - a string was pasted to the system clipboard + - one or more files have been dropped onto the application window + + To explicitly 'consume' an event and prevent that the event is + forwarded for further handling to the operating system, call + sapp_consume_event() from inside the event handler (NOTE that + this behaviour is currently only implemented for some HTML5 + events, support for other platforms and event types will + be added as needed, please open a GitHub ticket and/or provide + a PR if needed). + + NOTE: Do *not* call any 3D API rendering functions in the event + callback function, since the 3D API context may not be active when the + event callback is called (it may work on some platforms and 3D APIs, + but not others, and the exact behaviour may change between + sokol-app versions). + + --- Implement the cleanup-callback function, this is called once + after the user quits the application (see the section + "APPLICATION QUIT" for detailed information on quitting + behaviour, and how to intercept a pending quit - for instance to show a + "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 + mouse leaves the windows client area or hits the screen border (whether + it's one or the other depends on the platform), and the mouse move events + (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in + framebuffer pixels in the sapp_event items mouse_x and mouse_y, and + relative movement in framebuffer pixels in the sapp_event items mouse_dx + and mouse_dy. + + To get continuous mouse movement (also when the mouse leaves the window + client area or hits the screen border), activate mouse-lock mode + by calling: + + sapp_lock_mouse(true) + + When mouse lock is activated, the mouse pointer is hidden, the + reported absolute mouse position (sapp_event.mouse_x/y) appears + frozen, and the relative mouse movement in sapp_event.mouse_dx/dy + no longer has a direct relation to framebuffer pixels but instead + uses "raw mouse input" (what "raw mouse input" exactly means also + differs by platform). + + To deactivate mouse lock and return to normal mouse mode, call + + sapp_lock_mouse(false) + + And finally, to check if mouse lock is currently active, call + + if (sapp_mouse_locked()) { ... } + + Note that mouse-lock state may not change immediately after sapp_lock_mouse(true/false) + is called, instead on some platforms the actual state switch may be delayed + to the end of the current frame or even to a later frame. + + The mouse may also be unlocked automatically without calling sapp_lock_mouse(false), + most notably when the application window becomes inactive. + + On the web platform there are further restrictions to be aware of, caused + by the limitations of the HTML5 Pointer Lock API: + + - sapp_lock_mouse(true) can be called at any time, but it will + only take effect in a 'short-lived input event handler of a specific + type', meaning when one of the following events happens: + - SAPP_EVENTTYPE_MOUSE_DOWN + - SAPP_EVENTTYPE_MOUSE_UP + - SAPP_EVENTTYPE_MOUSE_SCROLL + - 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 + the new status after calling sapp_lock_mouse(), instead the + reported status will only change when the pointer lock has actually + been activated or deactivated in the browser. + - On the web, mouse lock can be deactivated by the user at any time + by pressing the Esc key. When this happens, sokol_app.h behaves + the same as if sapp_lock_mouse(false) is called. + + For things like camera manipulation it's most straightforward to lock + and unlock the mouse right from the sokol_app.h event handler, for + instance the following code enters and leaves mouse lock when the + left mouse button is pressed and released, and then uses the relative + movement information to manipulate a camera (taken from the + cgltf-sapp.c sample in the sokol-samples repository + at https://github.com/floooh/sokol-samples): + + static void input(const sapp_event* ev) { + switch (ev->type) { + case SAPP_EVENTTYPE_MOUSE_DOWN: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(true); + } + break; + + case SAPP_EVENTTYPE_MOUSE_UP: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(false); + } + break; + + case SAPP_EVENTTYPE_MOUSE_MOVE: + if (sapp_mouse_locked()) { + cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f); + } + break; + + default: + break; + } + } + + For a 'first person shooter mouse' the following code inside the sokol-app event handler + is recommended somewhere in your frame callback: + + if (!sapp_mouse_locked()) { + sapp_lock_mouse(true); + } + + CLIPBOARD SUPPORT + ================= + Applications can send and receive UTF-8 encoded text data from and to the + system clipboard. By default, clipboard support is disabled and + must be enabled at startup via the following sapp_desc struct + members: + + sapp_desc.enable_clipboard - set to true to enable clipboard support + sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes + + Enabling the clipboard will dynamically allocate a clipboard buffer + for UTF-8 encoded text data of the requested size in bytes, the default + size is 8 KBytes. Strings that don't fit into the clipboard buffer + (including the terminating zero) will be silently clipped, so it's + important that you provide a big enough clipboard size for your + use case. + + To send data to the clipboard, call sapp_set_clipboard_string() with + a pointer to an UTF-8 encoded, null-terminated C-string. + + NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be + called from inside a 'short-lived event handler', and there are a few + other HTML5-specific caveats to workaround. You'll basically have to + tinker until it works in all browsers :/ (maybe the situation will + improve when all browsers agree on and implement the new + HTML5 navigator.clipboard API). + + To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED + event in your event handler function, and then call sapp_get_clipboard_string() + to obtain the pasted UTF-8 encoded text. + + NOTE that behaviour of sapp_get_clipboard_string() is slightly different + depending on platform: + + - on the HTML5 platform, the internal clipboard buffer will only be updated + right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent, + and sapp_get_clipboard_string() will simply return the current content + of the clipboard buffer + - on 'native' platforms, the call to sapp_get_clipboard_string() will + update the internal clipboard buffer with the most recent data + from the system clipboard + + Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event, + and then call sapp_get_clipboard_string() right in the event handler. + + The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app + as follows: + + - on macOS: when the Cmd+V key is pressed down + - on HTML5: when the browser sends a 'paste' event to the global 'window' object + - on all other platforms: when the Ctrl+V key is pressed down + + DRAG AND DROP SUPPORT + ===================== + PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5 + and on the native desktop platforms (Win32, Linux and macOS) because + of security-related restrictions in the HTML5 drag'n'drop API. The + WASM/HTML5 specifics are described at the end of this documentation + section: + + Like clipboard support, drag'n'drop support must be explicitly enabled + at startup in the sapp_desc struct. + + sapp_desc sokol_main(void) { + return (sapp_desc) { + .enable_dragndrop = true, // default is false + ... + }; + } + + You can also adjust the maximum number of files that are accepted + in a drop operation, and the maximum path length in bytes if needed: + + sapp_desc sokol_main(void) { + return (sapp_desc) { + .enable_dragndrop = true, // default is false + .max_dropped_files = 8, // default is 1 + .max_dropped_file_path_length = 8192, // in bytes, default is 2048 + ... + }; + } + + When drag'n'drop is enabled, the event callback will be invoked with an + event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on + the application window. + + After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the + number of dropped files, and their absolute paths by calling separate + functions: + + void on_event(const sapp_event* ev) { + if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) { + + // the mouse position where the drop happened + float x = ev->mouse_x; + float y = ev->mouse_y; + + // get the number of files and their paths like this: + const int num_dropped_files = sapp_get_num_dropped_files(); + for (int i = 0; i < num_dropped_files; i++) { + const char* path = sapp_get_dropped_file_path(i); + ... + } + } + } + + The returned file paths are UTF-8 encoded strings. + + You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path() + anywhere, also outside the event handler callback, but be aware that the + file path strings will be overwritten with the next drop operation. + + In any case, sapp_get_dropped_file_path() will never return a null pointer, + instead an empty string "" will be returned if the drag'n'drop feature + hasn't been enabled, the last drop-operation failed, or the file path index + is out of range. + + Drag'n'drop caveats: + + - if more files are dropped in a single drop-action + than sapp_desc.max_dropped_files, the additional + files will be silently ignored + - if any of the file paths is longer than + sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8 + encoding) the entire drop operation will be silently ignored (this + needs some sort of error feedback in the future) + - no mouse positions are reported while the drag is in + process, this may change in the future + + Drag'n'drop on HTML5/WASM: + + The HTML5 drag'n'drop API doesn't return file paths, but instead + black-box 'file objects' which must be used to load the content + of dropped files. This is the reason why sokol_app.h adds two + HTML5-specific functions to the drag'n'drop API: + + uint32_t sapp_html5_get_dropped_file_size(int index) + Returns the size in bytes of a dropped file. + + void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) + Asynchronously loads the content of a dropped file into a + provided memory buffer (which must be big enough to hold + the file content) + + To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED + event is received: + + sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){ + .dropped_file_index = 0, + .callback = fetch_cb + .buffer = { + .ptr = buf, + .size = sizeof(buf) + }, + .user_data = ... + }); + + Make sure that the memory pointed to by 'buf' stays valid until the + callback function is called! + + As result of the asynchronous loading operation (no matter if succeeded or + failed) the 'fetch_cb' function will be called: + + void fetch_cb(const sapp_html5_fetch_response* response) { + // IMPORTANT: check if the loading operation actually succeeded: + if (response->succeeded) { + // the size of the loaded file: + 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->data.ptr; + } else { + // on error check the error code: + switch (response->error_code) { + case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL: + ... + break; + case SAPP_HTML5_FETCH_ERROR_OTHER: + ... + break; + } + } + } + + Check the droptest-sapp example for a real-world example which works + both on native platforms and the web: + + https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c + + HIGH-DPI RENDERING + ================== + You can set the sapp_desc.high_dpi flag during initialization to request + a full-resolution framebuffer on HighDPI displays. The default behaviour + is sapp_desc.high_dpi=false, this means that the application will + render to a lower-resolution framebuffer on HighDPI displays and the + rendered content will be upscaled by the window system composer. + + 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. + + 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(void) { + return (sapp_desc) { + .width = 640, + .height = 480, + .high_dpi = true, + ... + }; + } + + ...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 + + 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 + + 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 + ================ + Without special quit handling, a sokol_app.h application will quit + 'gracefully' when the user clicks the window close-button unless a + platform's application model prevents this (e.g. on web or mobile). + 'Graceful exit' means that the application-provided cleanup callback will + be called before the application quits. + + On native desktop platforms sokol_app.h provides more control over the + application-quit-process. It's possible to initiate a 'programmatic quit' + from the application code, and a quit initiated by the application user can + be intercepted (for instance to show a custom dialog box). + + This 'programmatic quit protocol' is implemented through 3 functions + and 1 event: + + - sapp_quit(): This function simply quits the application without + giving the user a chance to intervene. Usually this might + be called when the user clicks the 'Ok' button in a 'Really Quit?' + dialog box + - sapp_request_quit(): Calling sapp_request_quit() will send the + event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler + callback, giving the user code a chance to intervene and cancel the + pending quit process (for instance to show a 'Really Quit?' dialog + box). If the event handler callback does nothing, the application + will be quit as usual. To prevent this, call the function + sapp_cancel_quit() from inside the event handler. + - sapp_cancel_quit(): Cancels a pending quit request, either initiated + by the user clicking the window close button, or programmatically + by calling sapp_request_quit(). The only place where calling this + function makes sense is from inside the event handler callback when + the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received. + - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user + clicks the window's close button or application code calls the + sapp_request_quit() function. The event handler callback code can handle + this event by calling sapp_cancel_quit() to cancel the quit. + If the event is ignored, the application will quit as usual. + + On the web platform, the quit behaviour differs from native platforms, + because of web-specific restrictions: + + A `programmatic quit` initiated by calling sapp_quit() or + sapp_request_quit() will work as described above: the cleanup callback is + called, platform-specific cleanup is performed (on the web + this means that JS event handlers are unregistered), and then + the request-animation-loop will be exited. However that's all. The + web page itself will continue to exist (e.g. it's not possible to + programmatically close the browser tab). + + On the web it's also not possible to run custom code when the user + 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 + deactivated) with the following function: + + sapp_html5_ask_leave_site(bool ask); + + The initial state of the associated internal flag can be provided + at startup via sapp_desc.html5.ask_leave_site. + + This feature should only be used sparingly in critical situations - for + instance when the user would loose data - since popping up modal dialog + boxes is considered quite rude in the web world. Note that there's no way + to customize the content of this dialog box or run any code as a result + of the user's decision. Also note that the user must have interacted with + the site before the dialog box will appear. These are all security measures + to prevent fishing. + + The Dear ImGui HighDPI sample contains example code of how to + implement a 'Really Quit?' dialog box with Dear ImGui (native desktop + platforms only), and for showing the hardwired "Leave Site?" dialog box + when running on the web platform: + + https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html + + FULLSCREEN + ========== + If the sapp_desc.fullscreen flag is true, sokol-app will try to create + a fullscreen window on platforms with a 'proper' window system + (mobile devices will always use fullscreen). The implementation details + depend on the target platform, in general sokol-app will use a + 'soft approach' which doesn't interfere too much with the platform's + window system (for instance borderless fullscreen window instead of + a 'real' fullscreen mode). Such details might change over time + as sokol-app is adapted for different needs. + + The most important effect of fullscreen mode to keep in mind is that + the requested canvas width and height will be ignored for the initial + window size, calling sapp_width() and sapp_height() will instead return + the resolution of the fullscreen canvas (however the provided size + might still be used for the non-fullscreen window, in case the user can + switch back from fullscreen- to windowed-mode). + + To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen(). + + To check if the application window is currently in fullscreen mode, + call sapp_is_fullscreen(). + + On the web, sapp_desc.fullscreen will have no effect, and the application + will always start in non-fullscreen mode. Call sapp_toggle_fullscreen() + from within or 'near' an input event to switch to fullscreen programatically. + Note that on the web, the fullscreen state may change back to windowed at + any time (either because the browser had rejected switching into fullscreen, + or the user leaves fullscreen via Esc), this means that the result + of sapp_is_fullscreen() may change also without calling sapp_toggle_fullscreen()! + + + WINDOW ICON SUPPORT + =================== + Some sokol_app.h backends allow to change the window icon programmatically: + + - on Win32: the small icon in the window's title bar, and the + bigger icon in the task bar + - on Linux: highly dependent on the used window manager, but usually + the window's title bar icon and/or the task bar icon + - on HTML5: the favicon shown in the page's browser tab + - on macOS: the application icon shown in the dock, but only + for currently running applications + + NOTE that it is not possible to set the actual application icon which is + displayed by the operating system on the desktop or 'home screen'. Those + icons must be provided 'traditionally' through operating-system-specific + resources which are associated with the application (sokol_app.h might + later support setting the window icon from platform specific resource data + though). + + There are two ways to set the window icon: + + - at application start in the sokol_main() function by initializing + the sapp_desc.icon nested struct + - or later by calling the function sapp_set_icon() + + As a convenient shortcut, sokol_app.h comes with a builtin default-icon + (a rainbow-colored 'S', which at least looks a bit better than the Windows + default icon for applications), which can be activated like this: + + At startup in sokol_main(): + + sapp_desc sokol_main(...) { + return (sapp_desc){ + ... + icon.sokol_default = true + }; + } + + Or later by calling: + + sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true }); + + NOTE that a completely zero-initialized sapp_icon_desc struct will not + update the window icon in any way. This is an 'escape hatch' so that you + can handle the window icon update yourself (or if you do this already, + sokol_app.h won't get in your way, in this case just leave the + sapp_desc.icon struct zero-initialized). + + Providing your own icon images works exactly like in GLFW (down to the + data format): + + You provide one or more 'candidate images' in different sizes, and the + sokol_app.h platform backends pick the best match for the specific backend + and icon type. + + For each candidate image, you need to provide: + + - the width in pixels + - the height in pixels + - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844 + on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88, red=0x44) + + For instance, if you have 3 candidate images (small, medium, big) of + sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is setup + like this: + + // the actual pixel data (RGBA8, origin top-left) + const uint32_t small[16][16] = { ... }; + const uint32_t medium[32][32] = { ... }; + const uint32_t big[64][64] = { ... }; + + const sapp_icon_desc icon_desc = { + .images = { + { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) }, + { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) }, + // ...or without the SAPP_RANGE helper macro: + { .width = 64, .height = 64, .pixels = { .ptr=big, .size=sizeof(big) } } + } + }; + + An sapp_icon_desc struct initialized like this can then either be applied + at application start in sokol_main: + + sapp_desc sokol_main(...) { + return (sapp_desc){ + ... + icon = icon_desc + }; + } + + ...or later by calling sapp_set_icon(): + + sapp_set_icon(&icon_desc); + + Some window icon caveats: + + - once the window icon has been updated, there's no way to go back to + the platform's default icon, this is because some platforms (Linux + and HTML5) don't switch the icon visual back to the default even if + the custom icon is deleted or removed + - on HTML5, if the sokol_app.h icon doesn't show up in the browser + tab, check that there's no traditional favicon 'link' element + is defined in the page's index.html, sokol_app.h will only + append a new favicon link element, but not delete any manually + defined favicon in the page + + For an example and test of the window icon feature, check out the + 'icon-sapp' sample on the sokol-samples git repository. + + ONSCREEN KEYBOARD + ================= + On some platforms which don't provide a physical keyboard, sokol-app + can display the platform's integrated onscreen keyboard for text + input. To request that the onscreen keyboard is shown, call + + sapp_show_keyboard(true); + + Likewise, to hide the keyboard call: + + sapp_show_keyboard(false); + + Note that onscreen keyboard functionality is no longer supported + on the browser platform (the previous hacks and workarounds to make browser + keyboards work for on web applications that don't use HTML UIs + never really worked across browsers). + + INPUT EVENT BUBBLING ON THE WEB PLATFORM + ======================================== + By default, input event bubbling on the web platform is configured in + a way that makes the most sense for 'full-canvas' apps that cover the + entire browser client window area: + + - mouse, touch and wheel events do not bubble up, this prevents various + ugly side events, like: + - HTML text overlays being selected on double- or triple-click into + the canvas + - 'scroll bumping' even when the canvas covers the entire client area + - key_up/down events for 'character keys' *do* bubble up (otherwise + the browser will not generate UNICODE character events) + - all other key events *do not* bubble up by default (this prevents side effects + like F1 opening help, or F7 starting 'caret browsing') + - character events do not bubble up (although I haven't noticed any side effects + otherwise) + + Event bubbling can be enabled for input event categories during initialization + in the sapp_desc struct: + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc){ + //... + .html5 = { + .bubble_mouse_events = true, + .bubble_touch_events = true, + .bubble_wheel_events = true, + .bubble_key_events = true, + .bubble_char_events = true, + } + }; + } + + This basically opens the floodgates and lets *all* input events bubble up to the browser. + + To prevent individual events from bubbling, call sapp_consume_event() from within + the sokol_app.h event callback when that specific event is reported. + + + SETTING THE CANVAS OBJECT ON THE WEB PLATFORM + ============================================= + On the web, sokol_app.h and the Emscripten SDK functions need to find + the WebGL/WebGPU canvas intended for rendering and attaching event + handlers. This can happen in four ways: + + 1. do nothing and just set the id of the canvas object to 'canvas' (preferred) + 2. via a CSS Selector string (preferred) + 3. by setting the `Module.canvas` property to the canvas object + 4. by adding the canvas object to the global variable `specialHTMLTargets[]` + (this is a special variable used by the Emscripten runtime to lookup + event target objects for which document.querySelector() cannot be used) + + The easiest way is to just name your canvas object 'canvas': + + + + This works because the default css selector string used by sokol_app.h + is '#canvas'. + + If you name your canvas differently, you need to communicate that name to + sokol_app.h via `sapp_desc.html5.canvas_selector` as a regular css selector + string that's compatible with `document.querySelector()`. E.g. if your canvas + object looks like this: + + + + The `sapp_desc.html5.canvas_selector` string must be set to '#bla': + + .html5.canvas_selector = "#bla" + + If the canvas object cannot be looked up via `document.querySelector()` you + need to use one of the alternative methods, both involve the special + Emscripten runtime `Module` object which is usually setup in the index.html + like this before the WASM blob is loaded and instantiated: + + + + The first option is to set the `Module.canvas` property to your canvas object: + + + + When sokol_app.h initializes, it will check the global Module object whether + a `Module.canvas` property exists and is an object. This method will add + a new entry to the `specialHTMLTargets[]` object + + The other option is to add the canvas under a name chosen by you to the + special `specialHTMLTargets[]` map, which is used by the Emscripten runtime + to lookup 'event target objects' which are not visible to `document.querySelector()`. + Note that `specialHTMLTargets[]` must be updated after the Emscripten runtime + has started but before the WASM code is running. A good place for this is + the special `Module.preRun` array in index.html: + + + + In that case, pass the same string to sokol_app.h which is used as key + in the specialHTMLTargets[] map: + + .html5.canvas_selector = "my_canvas" + + If sokol_app.h can't find your canvas for some reason check for warning + messages on the browser console. + + + OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) + ====================================================== + NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android. + + In its default configuration, sokol_app.h "hijacks" the platform's + standard main() function. This was done because different platforms + have different entry point conventions which are not compatible with + C's main() (for instance WinMain on Windows has completely different + arguments). However, this "main hijacking" posed a problem for + usage scenarios like integrating sokol_app.h with other languages than + C or C++, so an alternative SOKOL_NO_ENTRY mode has been added + in which the user code provides the platform's main function: + + - define SOKOL_NO_ENTRY before including the sokol_app.h implementation + - do *not* provide a sokol_main() function + - instead provide the standard main() function of the platform + - from the main function, call the function ```sapp_run()``` which + takes a pointer to an ```sapp_desc``` structure. + - from here on```sapp_run()``` takes over control and calls the provided + init-, frame-, event- and cleanup-callbacks just like in the default model. + + sapp_run() behaves differently across platforms: + + - on some platforms, sapp_run() will return when the application quits + - on other platforms, sapp_run() will never return, even when the + application quits (the operating system is free to simply terminate + the application at any time) + - on Emscripten specifically, sapp_run() will return immediately while + the frame callback keeps being called + + This different behaviour of sapp_run() essentially means that there shouldn't + be any code *after* sapp_run(), because that may either never be called, or in + case of Emscripten will be called at an unexpected time (at application start). + + An application also should not depend on the cleanup-callback being called + when cross-platform compatibility is required. + + Since sapp_run() returns immediately on Emscripten you shouldn't activate + the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling + for the browser target), since the C/C++ exit runtime would be called immediately at + application start, causing any global objects to be destroyed and global + variables to be zeroed. + + WINDOWS CONSOLE OUTPUT + ====================== + On Windows, regular windowed applications don't show any stdout/stderr text + output, which can be a bit of a hassle for printf() debugging or generally + logging text to the console. Also, console output by default uses a local + codepage setting and thus international UTF-8 encoded text is printed + as garbage. + + To help with these issues, sokol_app.h can be configured at startup + via the following Windows-specific sapp_desc flags: + + sapp_desc.win32.console_utf8 (default: false) + When set to true, the output console codepage will be switched + to UTF-8 (and restored to the original codepage on exit) + + sapp_desc.win32.console_attach (default: false) + When set to true, stdout and stderr will be attached to the + console of the parent process (if the parent process actually + has a console). This means that if the application was started + in a command line window, stdout and stderr output will be printed + to the terminal, just like a regular command line program. But if + the application is started via double-click, it will behave like + a regular UI application, and stdout/stderr will not be visible. + + sapp_desc.win32.console_create (default: false) + When set to true, a new console window will be created and + stdout/stderr will be redirected to that console window. It + doesn't matter if the application is started from the command + line or via double-click. + + NOTE: setting both win32.console_attach and win32.console_create + to true also makes sense and has the effect that output + will appear in the existing terminal when started from the cmdline, and + otherwise (when started via double-click) will open a console window. + + 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_fn = my_alloc, + .free_fn = 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 + ============== + - sapp_desc needs a bool whether to initialize depth-stencil surface + - 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 + again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 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_APP_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL) +#define SOKOL_APP_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_APP_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL) +#define SOKOL_APP_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_APP_API_DECL __declspec(dllimport) +#else +#define SOKOL_APP_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* misc constants */ +enum { + SAPP_MAX_TOUCHPOINTS = 8, + SAPP_MAX_MOUSEBUTTONS = 3, + SAPP_MAX_KEYCODES = 512, + SAPP_MAX_ICONIMAGES = 8, +}; + +/* + sapp_event_type + + The type of event that's passed to the event handler callback + in the sapp_event.type field. These are not just "traditional" + input events, but also notify the application about state changes + or other user-invoked actions. +*/ +typedef enum sapp_event_type { + SAPP_EVENTTYPE_INVALID, + SAPP_EVENTTYPE_KEY_DOWN, + SAPP_EVENTTYPE_KEY_UP, + SAPP_EVENTTYPE_CHAR, + SAPP_EVENTTYPE_MOUSE_DOWN, + SAPP_EVENTTYPE_MOUSE_UP, + SAPP_EVENTTYPE_MOUSE_SCROLL, + SAPP_EVENTTYPE_MOUSE_MOVE, + SAPP_EVENTTYPE_MOUSE_ENTER, + SAPP_EVENTTYPE_MOUSE_LEAVE, + SAPP_EVENTTYPE_TOUCHES_BEGAN, + SAPP_EVENTTYPE_TOUCHES_MOVED, + SAPP_EVENTTYPE_TOUCHES_ENDED, + SAPP_EVENTTYPE_TOUCHES_CANCELLED, + SAPP_EVENTTYPE_RESIZED, + SAPP_EVENTTYPE_ICONIFIED, + SAPP_EVENTTYPE_RESTORED, + SAPP_EVENTTYPE_FOCUSED, + SAPP_EVENTTYPE_UNFOCUSED, + SAPP_EVENTTYPE_SUSPENDED, + SAPP_EVENTTYPE_RESUMED, + SAPP_EVENTTYPE_QUIT_REQUESTED, + SAPP_EVENTTYPE_CLIPBOARD_PASTED, + SAPP_EVENTTYPE_FILES_DROPPED, + _SAPP_EVENTTYPE_NUM, + _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF +} sapp_event_type; + +/* + sapp_keycode + + The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the + struct field sapp_event.key_code. + + Note that the keycode values are identical with GLFW. +*/ +typedef enum sapp_keycode { + SAPP_KEYCODE_INVALID = 0, + SAPP_KEYCODE_SPACE = 32, + SAPP_KEYCODE_APOSTROPHE = 39, /* ' */ + SAPP_KEYCODE_COMMA = 44, /* , */ + SAPP_KEYCODE_MINUS = 45, /* - */ + SAPP_KEYCODE_PERIOD = 46, /* . */ + SAPP_KEYCODE_SLASH = 47, /* / */ + SAPP_KEYCODE_0 = 48, + SAPP_KEYCODE_1 = 49, + SAPP_KEYCODE_2 = 50, + SAPP_KEYCODE_3 = 51, + SAPP_KEYCODE_4 = 52, + SAPP_KEYCODE_5 = 53, + SAPP_KEYCODE_6 = 54, + SAPP_KEYCODE_7 = 55, + SAPP_KEYCODE_8 = 56, + SAPP_KEYCODE_9 = 57, + SAPP_KEYCODE_SEMICOLON = 59, /* ; */ + SAPP_KEYCODE_EQUAL = 61, /* = */ + SAPP_KEYCODE_A = 65, + SAPP_KEYCODE_B = 66, + SAPP_KEYCODE_C = 67, + SAPP_KEYCODE_D = 68, + SAPP_KEYCODE_E = 69, + SAPP_KEYCODE_F = 70, + SAPP_KEYCODE_G = 71, + SAPP_KEYCODE_H = 72, + SAPP_KEYCODE_I = 73, + SAPP_KEYCODE_J = 74, + SAPP_KEYCODE_K = 75, + SAPP_KEYCODE_L = 76, + SAPP_KEYCODE_M = 77, + SAPP_KEYCODE_N = 78, + SAPP_KEYCODE_O = 79, + SAPP_KEYCODE_P = 80, + SAPP_KEYCODE_Q = 81, + SAPP_KEYCODE_R = 82, + SAPP_KEYCODE_S = 83, + SAPP_KEYCODE_T = 84, + SAPP_KEYCODE_U = 85, + SAPP_KEYCODE_V = 86, + SAPP_KEYCODE_W = 87, + SAPP_KEYCODE_X = 88, + SAPP_KEYCODE_Y = 89, + SAPP_KEYCODE_Z = 90, + SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */ + SAPP_KEYCODE_BACKSLASH = 92, /* \ */ + SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */ + SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */ + SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */ + SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */ + SAPP_KEYCODE_ESCAPE = 256, + SAPP_KEYCODE_ENTER = 257, + SAPP_KEYCODE_TAB = 258, + SAPP_KEYCODE_BACKSPACE = 259, + SAPP_KEYCODE_INSERT = 260, + SAPP_KEYCODE_DELETE = 261, + SAPP_KEYCODE_RIGHT = 262, + SAPP_KEYCODE_LEFT = 263, + SAPP_KEYCODE_DOWN = 264, + SAPP_KEYCODE_UP = 265, + SAPP_KEYCODE_PAGE_UP = 266, + SAPP_KEYCODE_PAGE_DOWN = 267, + SAPP_KEYCODE_HOME = 268, + SAPP_KEYCODE_END = 269, + SAPP_KEYCODE_CAPS_LOCK = 280, + SAPP_KEYCODE_SCROLL_LOCK = 281, + SAPP_KEYCODE_NUM_LOCK = 282, + SAPP_KEYCODE_PRINT_SCREEN = 283, + SAPP_KEYCODE_PAUSE = 284, + SAPP_KEYCODE_F1 = 290, + SAPP_KEYCODE_F2 = 291, + SAPP_KEYCODE_F3 = 292, + SAPP_KEYCODE_F4 = 293, + SAPP_KEYCODE_F5 = 294, + SAPP_KEYCODE_F6 = 295, + SAPP_KEYCODE_F7 = 296, + SAPP_KEYCODE_F8 = 297, + SAPP_KEYCODE_F9 = 298, + SAPP_KEYCODE_F10 = 299, + SAPP_KEYCODE_F11 = 300, + SAPP_KEYCODE_F12 = 301, + SAPP_KEYCODE_F13 = 302, + SAPP_KEYCODE_F14 = 303, + SAPP_KEYCODE_F15 = 304, + SAPP_KEYCODE_F16 = 305, + SAPP_KEYCODE_F17 = 306, + SAPP_KEYCODE_F18 = 307, + SAPP_KEYCODE_F19 = 308, + SAPP_KEYCODE_F20 = 309, + SAPP_KEYCODE_F21 = 310, + SAPP_KEYCODE_F22 = 311, + SAPP_KEYCODE_F23 = 312, + SAPP_KEYCODE_F24 = 313, + SAPP_KEYCODE_F25 = 314, + SAPP_KEYCODE_KP_0 = 320, + SAPP_KEYCODE_KP_1 = 321, + SAPP_KEYCODE_KP_2 = 322, + SAPP_KEYCODE_KP_3 = 323, + SAPP_KEYCODE_KP_4 = 324, + SAPP_KEYCODE_KP_5 = 325, + SAPP_KEYCODE_KP_6 = 326, + SAPP_KEYCODE_KP_7 = 327, + SAPP_KEYCODE_KP_8 = 328, + SAPP_KEYCODE_KP_9 = 329, + SAPP_KEYCODE_KP_DECIMAL = 330, + SAPP_KEYCODE_KP_DIVIDE = 331, + SAPP_KEYCODE_KP_MULTIPLY = 332, + SAPP_KEYCODE_KP_SUBTRACT = 333, + SAPP_KEYCODE_KP_ADD = 334, + SAPP_KEYCODE_KP_ENTER = 335, + SAPP_KEYCODE_KP_EQUAL = 336, + SAPP_KEYCODE_LEFT_SHIFT = 340, + SAPP_KEYCODE_LEFT_CONTROL = 341, + SAPP_KEYCODE_LEFT_ALT = 342, + SAPP_KEYCODE_LEFT_SUPER = 343, + SAPP_KEYCODE_RIGHT_SHIFT = 344, + SAPP_KEYCODE_RIGHT_CONTROL = 345, + SAPP_KEYCODE_RIGHT_ALT = 346, + SAPP_KEYCODE_RIGHT_SUPER = 347, + 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 + + Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN, + TOUCHES_MOVED, TOUCHES_ENDED). + + Touch points are stored in the nested array sapp_event.touches[], + and the number of touches is stored in sapp_event.num_touches. +*/ +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; + +/* + sapp_mousebutton + + The currently pressed mouse button in the events MOUSE_DOWN + and MOUSE_UP, stored in the struct field sapp_event.mouse_button. +*/ +typedef enum sapp_mousebutton { + SAPP_MOUSEBUTTON_LEFT = 0x0, + SAPP_MOUSEBUTTON_RIGHT = 0x1, + SAPP_MOUSEBUTTON_MIDDLE = 0x2, + SAPP_MOUSEBUTTON_INVALID = 0x100, +} sapp_mousebutton; + +/* + These are currently pressed modifier keys (and mouse buttons) which are + passed in the event struct field sapp_event.modifiers. +*/ +enum { + SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key + SAPP_MODIFIER_CTRL = 0x2, // left or right control key + SAPP_MODIFIER_ALT = 0x4, // left or right alt key + SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key + SAPP_MODIFIER_LMB = 0x100, // left mouse button + SAPP_MODIFIER_RMB = 0x200, // right mouse button + SAPP_MODIFIER_MMB = 0x400, // middle mouse button +}; + +/* + sapp_event + + This is an all-in-one event struct passed to the event handler + user callback function. Note that it depends on the event + type what struct fields actually contain useful values, so you + should first check the event type before reading other struct + fields. +*/ +typedef struct sapp_event { + uint64_t frame_count; // current frame counter, always valid, useful for checking if two events were issued in the same frame + sapp_event_type type; // the event type, always valid + sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN + uint32_t char_code; // the UTF-32 character code, only valid in CHAR events + bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR + uint32_t modifiers; // current modifier keys, valid in all key-, char- and mouse-events + sapp_mousebutton mouse_button; // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP + float mouse_x; // current horizontal mouse position in pixels, always valid except during mouse lock + float mouse_y; // current vertical mouse position in pixels, always valid except during mouse lock + float mouse_dx; // relative horizontal mouse movement since last frame, always valid + float mouse_dy; // relative vertical mouse movement since last frame, always valid + float scroll_x; // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events + float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events + int num_touches; // number of valid items in the touches[] array + sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED + int window_width; // current window- and framebuffer sizes in pixels, always valid + int window_height; + int framebuffer_width; // = window_width * dpi_scale + int framebuffer_height; // = window_height * dpi_scale +} sapp_event; + +/* + sg_range + + A general pointer/size-pair struct and constructor macros for passing binary blobs + into sokol_app.h. +*/ +typedef struct sapp_range { + const void* ptr; + size_t size; +} sapp_range; +// 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 SAPP_RANGE(x) sapp_range{ &x, sizeof(x) } +#else +#define SAPP_RANGE(x) (sapp_range){ &x, sizeof(x) } +#endif + +/* + sapp_image_desc + + This is used to describe image data to sokol_app.h (window icons and cursor images). + + The pixel format is RGBA8. + + cursor_hotspot_x and _y are used only for cursors, to define which pixel + of the image should be aligned with the mouse position. +*/ +typedef struct sapp_image_desc { + int width; + int height; + int cursor_hotspot_x; + int cursor_hotspot_y; + sapp_range pixels; +} sapp_image_desc; + +/* + sapp_icon_desc + + An icon description structure for use in sapp_desc.icon and + sapp_set_icon(). + + When setting a custom image, the application can provide a number of + candidates differing in size, and sokol_app.h will pick the image(s) + closest to the size expected by the platform's window system. + + To set sokol-app's default icon, set .sokol_default to true. + + Otherwise provide candidate images of different sizes in the + images[] array. + + If both the sokol_default flag is set to true, any image candidates + will be ignored and the sokol_app.h default icon will be set. +*/ +typedef struct sapp_icon_desc { + bool sokol_default; + 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_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sapp_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(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 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_VERSION_NOT_SUPPORTED, "requested OpenGL version 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(WIN32_DESTROYICON_FOR_CURSOR_FAILED, "DestroyIcon() for a cursor image 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(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD, "X11: Failed to become owner of clipboard selection") \ + _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 successfully created") \ + _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOST, "wgpu: device lost") \ + _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOG, "wgpu: device log") \ + _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_UNCAPTURED_ERROR, "wgpu: uncaptured error") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED, "wgpu: wgpuSurfaceGetCapabilities failed") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, "wgpu: failed to create depth-stencil texture for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED, "wgpu: failed to create view object for swapchain depth-stencil texture") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED, "wgpu: failed to create msaa texture for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED, "wgpu: failed to create view object for swapchain msaa texture") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_GETCURRENTTEXTURE_FAILED, "wgpu: wgpuSurfaceGetCurrentTexture() failed") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_ERROR, "wgpu: requesting device failed with status 'error'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN, "wgpu: requesting device failed with status 'unknown'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE, "wgpu: requesting adapter failed with 'unavailable'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_ERROR, "wgpu: requesting adapter failed with status 'error'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN, "wgpu: requesting adapter failed with status 'unknown'") \ + _SAPP_LOGITEM_XMACRO(WGPU_CREATE_INSTANCE_FAILED, "wgpu: failed to create instance") \ + _SAPP_LOGITEM_XMACRO(VULKAN_REQUIRED_INSTANCE_EXTENSION_FUNCTION_MISSING, "vulkan: could not lookup a required instance extension function pointer") \ + _SAPP_LOGITEM_XMACRO(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE, "vulkan: could not find suitable memory type") \ + _SAPP_LOGITEM_XMACRO(VULKAN_ALLOCATE_MEMORY_FAILED, "vulkan: vkAllocateMemory() failed!") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_INSTANCE_FAILED, "vulkan: vkCreateInstance failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_ENUMERATE_PHYSICAL_DEVICES_FAILED, "vulkan: vkEnumeratePhysicalDevices failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_NO_PHYSICAL_DEVICES_FOUND, "vulkan: vkEnumeratePhysicalDevices return no devices") \ + _SAPP_LOGITEM_XMACRO(VULKAN_NO_SUITABLE_PHYSICAL_DEVICE_FOUND, "vulkan: no suitable physical device found") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_EXTENSION_NOT_PRESENT, "vulkan: vkCreateDevice failed (extension not present)") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_FEATURE_NOT_PRESENT, "vulkan: vkCreateDevice failed (feature not present)") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_INITIALIZATION_FAILED, "vulkan: vkCreateDevice failed (initialization failed)") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_DEVICE_FAILED_OTHER, "vulkan: vkCreateDevice failed (other)") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_SURFACE_FAILED, "vulkan: vkCreate*SurfaceKHR failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_CREATE_SWAPCHAIN_FAILED, "vulkan: vkCreateSwapchainKHR failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED, "vulkan: vkCreateImageView for swapchain image failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_CREATE_IMAGE_FAILED, "vulkan: vkCreateImage for depth-stencil image failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED, "vulkan: failed to allocate device memory for depth-stencil image") \ + _SAPP_LOGITEM_XMACRO(VULKAN_SWAPCHAIN_BIND_IMAGE_MEMORY_FAILED, "vulkan: vkBindImageMemory() for depth-stencil image failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_ACQUIRE_NEXT_IMAGE_FAILED, "vulkan: vkAcquireNextImageKHR failed") \ + _SAPP_LOGITEM_XMACRO(VULKAN_QUEUE_PRESENT_FAILED, "vulkan: vkQueuePresentKHR failed") \ + _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_pixel_format + + Defines the pixel format for swapchain surfaces. + + NOTE: when using sokol_gfx.h do not assume that the underlying + values are compatible with sg_pixel_format! + +*/ +typedef enum sapp_pixel_format { + _SAPP_PIXELFORMAT_DEFAULT, + SAPP_PIXELFORMAT_NONE, + SAPP_PIXELFORMAT_RGBA8, + SAPP_PIXELFORMAT_SRGB8A8, + SAPP_PIXELFORMAT_BGRA8, + SAPP_PIXELFORMAT_SBGRA8, + SAPP_PIXELFORMAT_DEPTH, + SAPP_PIXELFORMAT_DEPTH_STENCIL, + _SA_PPPIXELFORMAT_FORCE_U32 = 0x7FFFFFFF +} sapp_pixel_format; + +/* + sapp_environment + + Used to provide runtime environment information to the + outside world (like default pixel formats and the backend + 3D API device pointer) via a call to sapp_get_environment(). + + NOTE: when using sokol_gfx.h, don't assume that sapp_environment + is binary compatible with sg_environment! Always use a translation + function like sglue_environment() to populate sg_environment + from sapp_environment! +*/ +typedef struct sapp_environment_defaults { + sapp_pixel_format color_format; + sapp_pixel_format depth_format; + int sample_count; +} sapp_environment_defaults; + +typedef struct sapp_metal_environment { + const void* device; +} sapp_metal_environment; + +typedef struct sapp_d3d11_environment { + const void* device; + const void* device_context; +} sapp_d3d11_environment; + +typedef struct sapp_wgpu_environment { + const void* device; +} sapp_wgpu_environment; + +typedef struct sapp_vulkan_environment { + const void* instance; + const void* physical_device; + const void* device; + const void* queue; + uint32_t queue_family_index; +} sapp_vulkan_environment; + +typedef struct sapp_environment { + sapp_environment_defaults defaults; + sapp_metal_environment metal; + sapp_d3d11_environment d3d11; + sapp_wgpu_environment wgpu; + sapp_vulkan_environment vulkan; +} sapp_environment; + +/* + sapp_swapchain + + Provides swapchain information for the current frame to the outside + world via a call to sapp_get_swapchain(). + + NOTE: sapp_get_swapchain() must be called exactly once per frame since + on some backends it will also acquire the next swapchain image. + + NOTE: when using sokol_gfx.h, don't assume that the sapp_swapchain struct + has the same memory layout as sg_swapchain! Use the sokol_log.h helper + function sglue_swapchain() to translate sapp_swapchain into a + sg_swapchain instead. +*/ +typedef struct sapp_metal_swapchain { + const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!) + const void* depth_stencil_texture; // MTLTexture + const void* msaa_color_texture; // MTLTexture +} sapp_metal_swapchain; + +typedef struct sapp_d3d11_swapchain { + const void* render_view; // ID3D11RenderTargetView + const void* resolve_view; // ID3D11RenderTargetView + const void* depth_stencil_view; // ID3D11DepthStencilView +} sapp_d3d11_swapchain; + +typedef struct sapp_wgpu_swapchain { + const void* render_view; // WGPUTextureView + const void* resolve_view; // WGPUTextureView + const void* depth_stencil_view; // WGPUTextureView +} sapp_wgpu_swapchain; + +typedef struct sapp_vulkan_swapchain { + const void* render_image; // vkImage + const void* render_view; // vkImageView + const void* resolve_image; // vkImage; + const void* resolve_view; // vkImageView + const void* depth_stencil_image; // vkImage + const void* depth_stencil_view; // vkImageView + const void* render_finished_semaphore; // vkSemaphore + const void* present_complete_semaphore; // vkSemaphore +} sapp_vulkan_swapchain; + +typedef struct sapp_gl_swapchain { + uint32_t framebuffer; // GL framebuffer object +} sapp_gl_swapchain; + +typedef struct sapp_swapchain { + int width; + int height; + int sample_count; + sapp_pixel_format color_format; + sapp_pixel_format depth_format; + sapp_metal_swapchain metal; + sapp_d3d11_swapchain d3d11; + sapp_wgpu_swapchain wgpu; + sapp_vulkan_swapchain vulkan; + sapp_gl_swapchain gl; +} sapp_swapchain; + +/* + 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; + +/* + sokol-app initialization options, used as return value of sokol_main() + or sapp_run() argument. +*/ +typedef struct sapp_gl_desc { + int major_version; // override GL/GLES major and minor version (defaults: GL4.1 (macOS) or GL4.3, GLES3.1 (Android) or GLES3.0 + int minor_version; +} sapp_gl_desc; + +typedef struct sapp_win32_desc { + bool console_utf8; // if true, set the output console codepage to UTF-8 + bool console_create; // if true, attach stdout/stderr to a new console window + bool console_attach; // if true, attach stdout/stderr to parent process +} sapp_win32_desc; + +typedef struct sapp_html5_desc { + const char* canvas_selector; // css selector of the HTML5 canvas element, default is "#canvas" + bool canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked + bool preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames + bool premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention + bool ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) + bool update_document_title; // if true, update the HTML document.title with sapp_desc.window_title + bool bubble_mouse_events; // if true, mouse events will bubble up to the web page + bool bubble_touch_events; // same for touch events + bool bubble_wheel_events; // same for wheel events + bool bubble_key_events; // if true, bubble up *all* key events to browser, not just key events that represent characters + bool bubble_char_events; // if true, bubble up character events to browser + bool use_emsc_set_main_loop; // if true, use emscripten_set_main_loop() instead of emscripten_request_animation_frame_loop() + bool emsc_set_main_loop_simulate_infinite_loop; // this will be passed as the simulate_infinite_loop arg to emscripten_set_main_loop() +} sapp_html5_desc; + +typedef struct sapp_ios_desc { + bool keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas +} sapp_ios_desc; + +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* 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*); + + int width; // the preferred width of the window / canvas + int height; // the preferred height of the window / canvas + int sample_count; // MSAA sample count + int swap_interval; // the preferred swap interval (ignored on some platforms) + bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays + 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 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 + sapp_gl_desc gl; + sapp_win32_desc win32; + sapp_html5_desc html5; + sapp_ios_desc ios; +} sapp_desc; + +/* HTML5 specific: request and response structs for + asynchronously loading dropped-file content. +*/ +typedef enum sapp_html5_fetch_error { + SAPP_HTML5_FETCH_ERROR_NO_ERROR, + SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL, + SAPP_HTML5_FETCH_ERROR_OTHER, +} sapp_html5_fetch_error; + +typedef struct sapp_html5_fetch_response { + 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) + 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) + 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_CUSTOM_0, + SAPP_MOUSECURSOR_CUSTOM_1, + SAPP_MOUSECURSOR_CUSTOM_2, + SAPP_MOUSECURSOR_CUSTOM_3, + SAPP_MOUSECURSOR_CUSTOM_4, + SAPP_MOUSECURSOR_CUSTOM_5, + SAPP_MOUSECURSOR_CUSTOM_6, + SAPP_MOUSECURSOR_CUSTOM_7, + SAPP_MOUSECURSOR_CUSTOM_8, + SAPP_MOUSECURSOR_CUSTOM_9, + SAPP_MOUSECURSOR_CUSTOM_10, + SAPP_MOUSECURSOR_CUSTOM_11, + SAPP_MOUSECURSOR_CUSTOM_12, + SAPP_MOUSECURSOR_CUSTOM_13, + SAPP_MOUSECURSOR_CUSTOM_14, + SAPP_MOUSECURSOR_CUSTOM_15, + _SAPP_MOUSECURSOR_NUM, +} sapp_mouse_cursor; + +/* user-provided functions */ +extern sapp_desc sokol_main(int argc, char* argv[]); + +/* returns true after sokol-app has been initialized */ +SOKOL_APP_API_DECL bool sapp_isvalid(void); +/* returns the current framebuffer width in pixels */ +SOKOL_APP_API_DECL int sapp_width(void); +/* same as sapp_width(), but returns float */ +SOKOL_APP_API_DECL float sapp_widthf(void); +/* returns the current framebuffer height in pixels */ +SOKOL_APP_API_DECL int sapp_height(void); +/* same as sapp_height(), but returns float */ +SOKOL_APP_API_DECL float sapp_heightf(void); +/* get default framebuffer color pixel format */ +SOKOL_APP_API_DECL sapp_pixel_format sapp_color_format(void); +/* get default framebuffer depth pixel format */ +SOKOL_APP_API_DECL sapp_pixel_format sapp_depth_format(void); +/* get default framebuffer sample count */ +SOKOL_APP_API_DECL int sapp_sample_count(void); +/* returns true when high_dpi was requested and actually running in a high-dpi scenario */ +SOKOL_APP_API_DECL bool sapp_high_dpi(void); +/* returns the dpi scaling factor (window pixels to framebuffer pixels) */ +SOKOL_APP_API_DECL float sapp_dpi_scale(void); +/* show or hide the mobile device onscreen keyboard */ +SOKOL_APP_API_DECL void sapp_show_keyboard(bool show); +/* return true if the mobile device onscreen keyboard is currently shown */ +SOKOL_APP_API_DECL bool sapp_keyboard_shown(void); +/* query fullscreen mode */ +SOKOL_APP_API_DECL bool sapp_is_fullscreen(void); +/* toggle fullscreen mode */ +SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void); +/* show or hide the mouse cursor */ +SOKOL_APP_API_DECL void sapp_show_mouse(bool show); +/* show or hide the mouse cursor */ +SOKOL_APP_API_DECL bool sapp_mouse_shown(void); +/* enable/disable mouse-pointer-lock mode */ +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); +/* associate a custom mouse cursor image to a sapp_mouse_cursor enum entry */ +SOKOL_APP_API_DECL sapp_mouse_cursor sapp_bind_mouse_cursor_image(sapp_mouse_cursor cursor, const sapp_image_desc* desc); +/* restore the sapp_mouse_cursor enum entry to it's default system appearance */ +SOKOL_APP_API_DECL void sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor); +/* 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 */ +SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void); +/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */ +SOKOL_APP_API_DECL void sapp_request_quit(void); +/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ +SOKOL_APP_API_DECL void sapp_cancel_quit(void); +/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUESTED) */ +SOKOL_APP_API_DECL void sapp_quit(void); +/* call from inside event callback to consume the current event (don't forward to platform) */ +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) */ +SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void); +/* set the window title (only on desktop platforms) */ +SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); +/* set the window icon (only on Windows and Linux) */ +SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc* icon_desc); +/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */ +SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void); +/* gets the dropped file paths */ +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); + +/* get runtime environment information */ +sapp_environment sapp_get_environment(void); +/* get current frame's swapchain information (call once per frame!) */ +sapp_swapchain sapp_get_swapchain(void); + +/* 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); + +/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ +SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); +/* HTML5: get byte size of a dropped file */ +SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index); +/* HTML5: asynchronously load the content of a dropped file */ +SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request); + +/* macOS: get bridged pointer to macOS NSWindow */ +SOKOL_APP_API_DECL const void* sapp_macos_get_window(void); +/* iOS: get bridged pointer to iOS UIWindow */ +SOKOL_APP_API_DECL const void* sapp_ios_get_window(void); + +/* D3D11: get pointer to IDXGISwapChain object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_swap_chain(void); + +/* Win32: get the HWND window handle */ +SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void); + +/* GL: get major version */ +SOKOL_APP_API_DECL int sapp_gl_get_major_version(void); +/* GL: get minor version */ +SOKOL_APP_API_DECL int sapp_gl_get_minor_version(void); +/* GL: return true if the context is GLES */ +SOKOL_APP_API_DECL bool sapp_gl_is_gles(void); + +/* X11: get Window */ +SOKOL_APP_API_DECL const void* sapp_x11_get_window(void); +/* X11: get Display */ +SOKOL_APP_API_DECL const void* sapp_x11_get_display(void); + +/* Android: get native activity handle */ +SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } + +#endif + +#endif // SOKOL_APP_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>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, strncmp +#include // size_t +#include // roundf + +// helper macros +#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) + +#ifdef __cplusplus +#define _SAPP_STRUCT(TYPE, NAME) TYPE NAME = {} +#else +#define _SAPP_STRUCT(TYPE, NAME) TYPE NAME = {0} +#endif + +#define _SAPP_MAX_TITLE_LENGTH (128) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) + +// check if the config defines are alright +#if defined(__APPLE__) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #define _SAPP_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + // MacOS + #define _SAPP_MACOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL, SOKOL_GLCORE or SOKOL_WGPU") + #endif + #else + // iOS or iOS Simulator + #define _SAPP_IOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") + #endif + #if TARGET_OS_TV + #define _SAPP_TVOS (1) + #endif + #endif +#elif defined(__EMSCRIPTEN__) + // Emscripten + #define _SAPP_EMSCRIPTEN (1) + #if !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_WGPU") + #endif +#elif defined(_WIN32) + // Windows (D3D11 or GL) + #define _SAPP_WIN32 (1) + #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU) && !defined(SOKOL_VULKAN) && !defined(SOKOL_NOAPI) + #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE, SOKOL_WGPU, SOKOL_VULKAN or SOKOL_NOAPI") + #endif + #if defined(SOKOL_VULKAN) + #define VK_USE_PLATFORM_WIN32_KHR + #include + #endif +#elif defined(__ANDROID__) + // Android + #define _SAPP_ANDROID (1) + #if !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3") + #endif + #if defined(SOKOL_NO_ENTRY) + #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") + #endif +#elif defined(__linux__) || defined(__unix__) + // Linux + #define _SAPP_LINUX (1) + #if !defined(SOKOL_GLCORE) && !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) && !defined(SOKOL_VULKAN) + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3, SOKOL_WGPU or SOKOL_VULKAN") + #endif + #if defined(SOKOL_GLCORE) + #if defined(SOKOL_FORCE_EGL) + #define _SAPP_EGL (1) + #else + #define _SAPP_GLX (1) + #endif + #define GL_GLEXT_PROTOTYPES + #include + #elif defined(SOKOL_GLES3) + #define _SAPP_EGL (1) + #include + #include + #elif defined(SOKOL_VULKAN) + #define VK_USE_PLATFORM_XLIB_KHR + #include + #endif +#else +#error "sokol_app.h: Unknown platform" +#endif + +#if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) + #define _SAPP_ANY_GL (1) +#endif + +#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_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#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 + +#if defined(SOKOL_WGPU) + #include + #if !defined(__EMSCRIPTEN__) + #define _SAPP_WGPU_HAS_WAIT (1) + #endif +#endif + +#if defined(_SAPP_APPLE) + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #if defined(SOKOL_METAL) + #import + #import + #endif + #if defined(_SAPP_MACOS) + #import + #if defined(_SAPP_ANY_GL) + #include + #endif + #if defined(SOKOL_WGPU) + #import + #import + #endif + #elif defined(_SAPP_IOS) + #import + #if defined(_SAPP_ANY_GL) + #import + #include + #endif + #endif + #include + #include +#elif defined(_SAPP_EMSCRIPTEN) + #if defined(SOKOL_GLES3) + #include + #endif + #include + #include +#elif defined(_SAPP_WIN32) + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ + #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 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #include + + #if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunknown-pragmas" + #endif + + #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the application's responsibility to use the right subsystem + + #if defined(SOKOL_WIN32_FORCE_MAIN) && defined(SOKOL_WIN32_FORCE_WINMAIN) + // If both are defined, it's the application's responsibility to use the right subsystem + #elif defined(SOKOL_WIN32_FORCE_MAIN) + #pragma comment (linker, "/subsystem:console") + #else + #pragma comment (linker, "/subsystem:windows") + #endif + #endif + #include /* freopen_s() */ + #include /* wcslen() */ + + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */ + #pragma comment (lib, "gdi32") + #if defined(SOKOL_D3D11) + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #endif + + #if defined(__GNUC__) + #pragma GCC diagnostic pop + #endif + + #if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #include + #include + // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it + #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4) + #endif + #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ + #define WM_MOUSEHWHEEL (0x020E) + #endif + #ifndef WM_DPICHANGED + #define WM_DPICHANGED (0x02E0) + #endif +#elif defined(_SAPP_ANDROID) + #include + #include + #include + #include + #include + #include + #include +#elif defined(_SAPP_LINUX) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include + #include + #include + #include + #include + #include + #include /* XC_* font cursors */ + #include /* CARD32 */ + #if defined(_SAPP_EGL) + #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 + #include +#endif + +#if defined(_SAPP_APPLE) + // this is ARC compatible + #if defined(__cplusplus) + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); } + #else + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); } +#endif + + +// ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ +// █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ██████ +// +// >>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(SOKOL_WGPU) +typedef struct { + WGPUInstance instance; + WGPUAdapter adapter; + WGPUDevice device; + WGPUSurface surface; + WGPUTextureFormat render_format; + WGPUTexture msaa_tex; + WGPUTextureView msaa_view; + WGPUTexture depth_stencil_tex; + WGPUTextureView depth_stencil_view; + WGPUTextureView swapchain_view; + bool init_done; +} _sapp_wgpu_t; +#endif + +#if defined(SOKOL_VULKAN) +#define _SAPP_VK_MAX_SWAPCHAIN_IMAGES (8) + +typedef struct { + VkImage img; + VkDeviceMemory mem; + VkImageView view; +} _sapp_vk_swapchain_surface_t; + +typedef struct { + VkInstance instance; + VkSurfaceKHR surface; + VkSurfaceFormatKHR surface_format; + VkPhysicalDevice physical_device; + uint32_t queue_family_index; + VkDevice device; + VkQueue queue; + VkSwapchainKHR swapchain; + uint32_t num_swapchain_images; + uint32_t cur_swapchain_image_index; + VkImage swapchain_images[_SAPP_VK_MAX_SWAPCHAIN_IMAGES]; + VkImageView swapchain_views[_SAPP_VK_MAX_SWAPCHAIN_IMAGES]; + _sapp_vk_swapchain_surface_t msaa; + _sapp_vk_swapchain_surface_t depth; + uint32_t sync_slot; + struct { + VkSemaphore render_finished_sem; + VkSemaphore present_complete_sem; + } sync[_SAPP_VK_MAX_SWAPCHAIN_IMAGES]; + struct { + PFN_vkSetDebugUtilsObjectNameEXT set_debug_utils_object_name_ext; + } ext; +} _sapp_vk_t; +#endif + +#if defined(_SAPP_MACOS) +@interface _sapp_macos_app_delegate : NSObject +@end +@interface _sapp_macos_window : NSWindow +@end +@interface _sapp_macos_window_delegate : NSObject +@end +#if defined(SOKOL_METAL) + @interface _sapp_macos_view : MTKView + @end +#elif defined(SOKOL_GLCORE) + @interface _sapp_macos_view : NSOpenGLView + - (void)timerFired:(id)sender; + @end +#elif defined(SOKOL_WGPU) + @interface _sapp_macos_view : NSView + - (void)displayLinkFired:(id)sender; + @end +#endif // SOKOL_GLCORE + +typedef struct { + uint32_t flags_changed_store; + 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* standard_cursors[_SAPP_MOUSECURSOR_NUM]; + NSCursor* custom_cursors[_SAPP_MOUSECURSOR_NUM]; + #if defined(SOKOL_METAL) + id mtl_device; + #endif + #if defined(SOKOL_WGPU) + struct { + CAMetalLayer* mtl_layer; + CADisplayLink* display_link; + } wgpu; + #endif +} _sapp_macos_t; + +#endif // _SAPP_MACOS + +#if defined(_SAPP_IOS) + +@interface _sapp_app_delegate : NSObject +@end +@interface _sapp_textfield_dlg : NSObject +- (void)keyboardWasShown:(NSNotification*)notif; +- (void)keyboardWillBeHidden:(NSNotification*)notif; +- (void)keyboardDidChangeFrame:(NSNotification*)notif; +@end +#if defined(SOKOL_METAL) + @interface _sapp_ios_view : MTKView; + @end +#else + @interface _sapp_ios_view : GLKView + @end +#endif + +typedef struct { + UIWindow* window; + _sapp_ios_view* view; + UITextField* textfield; + _sapp_textfield_dlg* textfield_dlg; + #if defined(SOKOL_METAL) + UIViewController* view_ctrl; + id mtl_device; + #else + GLKViewController* view_ctrl; + EAGLContext* eagl_ctx; + #endif + bool suspended; +} _sapp_ios_t; + +#endif // _SAPP_IOS + +#if defined(_SAPP_EMSCRIPTEN) + +typedef struct { + bool mouse_lock_requested; + uint16_t mouse_buttons; +} _sapp_emsc_t; +#endif // _SAPP_EMSCRIPTEN + +#if defined(SOKOL_D3D11) && defined(_SAPP_WIN32) +typedef struct { + ID3D11Device* device; + ID3D11DeviceContext* device_context; + ID3D11Texture2D* rt; + ID3D11RenderTargetView* rtv; + ID3D11Texture2D* msaa_rt; + ID3D11RenderTargetView* msaa_rtv; + ID3D11Texture2D* ds; + 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 + +#if defined(_SAPP_WIN32) + +#ifndef DPI_ENUMS_DECLARED +typedef enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; +#endif // DPI_ENUMS_DECLARED + +typedef struct { + bool aware; + float content_scale; + float window_scale; + float mouse_scale; +} _sapp_win32_dpi_t; + +typedef struct { + HWND hwnd; + HMONITOR hmonitor; + HDC dc; + HICON big_icon; + HICON small_icon; + HCURSOR standard_cursors[_SAPP_MOUSECURSOR_NUM]; + HCURSOR custom_cursors[_SAPP_MOUSECURSOR_NUM]; + UINT orig_codepage; + WCHAR surrogate; + 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; + _sapp_win32_dpi_t dpi; + struct { + struct { + LONG pos_x, pos_y; + bool pos_valid; + } lock; + struct { + LONG pos_x, pos_y; + bool pos_valid; + } raw_input; + bool requested_lock; + bool tracked; + uint8_t capture_mask; + } mouse; + struct { + size_t size; + void* ptr; + } raw_input_data; +} _sapp_win32_t; + +#if defined(SOKOL_GLCORE) +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); + +typedef struct { + HINSTANCE opengl32; + HGLRC gl_ctx; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglMakeCurrent MakeCurrent; + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + // special case glGetIntegerv + void (WINAPI *GetIntegerv)(uint32_t pname, int32_t* data); + bool ext_swap_control; + bool arb_multisample; + bool arb_pixel_format; + bool arb_create_context; + bool arb_create_context_profile; + HWND msg_hwnd; + HDC msg_dc; +} _sapp_wgl_t; +#endif // SOKOL_GLCORE + +#endif // _SAPP_WIN32 + +#if defined(_SAPP_ANDROID) +typedef enum { + _SOKOL_ANDROID_MSG_CREATE, + _SOKOL_ANDROID_MSG_RESUME, + _SOKOL_ANDROID_MSG_PAUSE, + _SOKOL_ANDROID_MSG_FOCUS, + _SOKOL_ANDROID_MSG_NO_FOCUS, + _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, + _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, + _SOKOL_ANDROID_MSG_DESTROY, +} _sapp_android_msg_t; + +typedef struct { + pthread_t thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + int read_from_main_fd; + int write_from_main_fd; +} _sapp_android_pt_t; + +typedef struct { + ANativeWindow* window; + AInputQueue* input; +} _sapp_android_resources_t; + +typedef struct { + ANativeActivity* activity; + _sapp_android_pt_t pt; + _sapp_android_resources_t pending; + _sapp_android_resources_t current; + ALooper* looper; + bool is_thread_started; + bool is_thread_stopping; + bool is_thread_stopped; + bool has_created; + bool has_resumed; + bool has_focus; + EGLConfig config; + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_android_t; + +#endif // _SAPP_ANDROID + +#if defined(_SAPP_LINUX) + +#define _SAPP_X11_XDND_VERSION (5) +#define _SAPP_X11_MAX_X11_KEYCODES (256) + +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_SAMPLES 0x186a1 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +typedef struct { + bool available; + int major_opcode; + int event_base; + int error_base; + int major; + int minor; +} _sapp_xi_t; + +typedef struct { + int version; + Window source; + Atom format; + Atom XdndAware; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; +} _sapp_xdnd_t; + +typedef struct { + uint8_t mouse_buttons; + Display* display; + int screen; + Window root; + Colormap colormap; + Window window; + Cursor hidden_cursor; + Cursor standard_cursors[_SAPP_MOUSECURSOR_NUM]; + Cursor custom_cursors[_SAPP_MOUSECURSOR_NUM]; + int window_state; + float dpi; + unsigned char error_code; + Atom UTF8_STRING; + Atom CLIPBOARD; + Atom TARGETS; + Atom WM_PROTOCOLS; + Atom WM_DELETE_WINDOW; + Atom WM_STATE; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_ICON; + Atom NET_WM_STATE; + Atom NET_WM_STATE_FULLSCREEN; + _sapp_xi_t xi; + _sapp_xdnd_t xdnd; + // XLib manual says keycodes are in the range [8, 255] inclusive. + // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html + bool key_repeat[_SAPP_X11_MAX_X11_KEYCODES]; +} _sapp_x11_t; + +#if defined(_SAPP_GLX) + +typedef struct { + void* libgl; + int major; + int minor; + int event_base; + int error_base; + GLXContext ctx; + GLXWindow window; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + + // special case glGetIntegerv + void (*GetIntegerv)(uint32_t pname, int32_t* data); + + // extension availability + bool EXT_swap_control; + bool MESA_swap_control; + bool ARB_multisample; + bool ARB_create_context; + bool ARB_create_context_profile; +} _sapp_glx_t; +#endif // _SAPP_GLX + +#if defined(_SAPP_EGL) +typedef struct { + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_egl_t; +#endif // _SAPP_EGL +#endif // _SAPP_LINUX + +#if defined(_SAPP_ANY_GL) +typedef struct { + uint32_t framebuffer; +} _sapp_gl_t; +#endif + +typedef struct { + bool enabled; + int buf_size; + char* buffer; +} _sapp_clipboard_t; + +typedef struct { + bool enabled; + int max_files; + int max_path_length; + int num_files; + int buf_size; + char* buffer; +} _sapp_drop_t; + +typedef struct { + float x, y; + float dx, dy; + bool shown; + bool locked; + bool pos_valid; + sapp_mouse_cursor current_cursor; +} _sapp_mouse_t; + +typedef struct { + sapp_desc desc; + bool valid; + bool fullscreen; + bool first_frame; + bool init_called; + bool cleanup_called; + bool quit_requested; + bool quit_ordered; + bool event_consumed; + bool html5_ask_leave_site; + bool onscreen_keyboard_shown; + int window_width; + int window_height; + int framebuffer_width; + int framebuffer_height; + int sample_count; + 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; + _sapp_drop_t drop; + sapp_icon_desc default_icon_desc; + uint32_t* default_icon_pixels; + #if defined(SOKOL_WGPU) + _sapp_wgpu_t wgpu; + #endif + #if defined(SOKOL_VULKAN) + _sapp_vk_t vk; + #endif + #if defined(_SAPP_MACOS) + _sapp_macos_t macos; + #elif defined(_SAPP_IOS) + _sapp_ios_t ios; + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_t emsc; + #elif defined(_SAPP_WIN32) + _sapp_win32_t win32; + #if defined(SOKOL_D3D11) + _sapp_d3d11_t d3d11; + #elif defined(SOKOL_GLCORE) + _sapp_wgl_t wgl; + #endif + #elif defined(_SAPP_ANDROID) + _sapp_android_t android; + #elif defined(_SAPP_LINUX) + _sapp_x11_t x11; + #if defined(_SAPP_GLX) + _sapp_glx_t glx; + #elif defined(_SAPP_EGL) + _sapp_egl_t egl; + #endif + #endif + #if defined(_SAPP_ANY_GL) + _sapp_gl_t gl; + #endif + char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; + char window_title[_SAPP_MAX_TITLE_LENGTH]; // UTF-8 + wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; // UTF-32 or UCS-2 */ + sapp_keycode keycodes[SAPP_MAX_KEYCODES]; + bool custom_cursor_bound[_SAPP_MOUSECURSOR_NUM]; // true if a custom mouse cursor is bound on that slot +} _sapp_t; +static _sapp_t _sapp; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>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__) +#define _SAPP_PANIC_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 0, msg, __LINE__) +#define _SAPP_ERROR_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 1, msg, __LINE__) +#define _SAPP_WARN_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 2, msg, __LINE__) +#define _SAPP_INFO_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 3, msg, __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, (uint32_t)log_item, msg, line_nr, filename, _sapp.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 _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_fn) { + ptr = _sapp.desc.allocator.alloc_fn(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_fn) { + _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers + +// round float to int and at least 1 +_SOKOL_PRIVATE int _sapp_roundf_gzero(float f) { + int val = (int)roundf(f); + if (val <= 0) { + val = 1; + } + return val; +} + +_SOKOL_PRIVATE void _sapp_call_init(void) { + if (_sapp.desc.init_cb) { + _sapp.desc.init_cb(); + } else if (_sapp.desc.init_userdata_cb) { + _sapp.desc.init_userdata_cb(_sapp.desc.user_data); + } + _sapp.init_called = true; +} + +_SOKOL_PRIVATE void _sapp_call_frame(void) { + if (_sapp.init_called && !_sapp.cleanup_called) { + if (_sapp.desc.frame_cb) { + _sapp.desc.frame_cb(); + } else if (_sapp.desc.frame_userdata_cb) { + _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); + } + } +} + +_SOKOL_PRIVATE void _sapp_call_cleanup(void) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.cleanup_cb) { + _sapp.desc.cleanup_cb(); + } else if (_sapp.desc.cleanup_userdata_cb) { + _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); + } + _sapp.cleanup_called = true; + } +} + +_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.event_cb) { + _sapp.desc.event_cb(e); + } else if (_sapp.desc.event_userdata_cb) { + _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); + } + } + if (_sapp.event_consumed) { + _sapp.event_consumed = false; + return true; + } else { + return false; + } +} + +_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { + SOKOL_ASSERT(_sapp.drop.buffer); + SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files)); + int offset = index * _sapp.drop.max_path_length; + SOKOL_ASSERT(offset < _sapp.drop.buf_size); + return &_sapp.drop.buffer[offset]; +} + +/* Copy a string (either zero-terminated or with explicit length) + into a fixed size buffer with guaranteed zero-termination. + + Return false if the string didn't fit into the buffer and had to be clamped. + + FIXME: Currently UTF-8 strings might become invalid if the string + is clamped, because the last zero-byte might be written into + the middle of a multi-byte sequence. +*/ +_SOKOL_PRIVATE bool _sapp_strcpy_range(const char* src, size_t src_len, char* dst, size_t dst_buf_len) { + SOKOL_ASSERT(src && dst && (dst_buf_len > 0)); + if (0 == src_len) { + src_len = dst_buf_len; + } + char* const end = &(dst[dst_buf_len-1]); + char c = 0; + for (size_t i = 0; i < dst_buf_len; i++) { + c = *src; + if (i >= src_len) { + c = 0; + } + if (c != 0) { + src++; + } + *dst++ = c; + } + // truncated? + if (c != 0) { + *end = 0; + return false; + } else { + return true; + } +} + +_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, size_t dst_buf_len) { + return _sapp_strcpy_range(src, 0, dst, dst_buf_len); +} + +_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sapp_desc res = *desc; + res.sample_count = _sapp_def(res.sample_count, 1); + res.swap_interval = _sapp_def(res.swap_interval, 1); + if (0 == res.gl.major_version) { + #if defined(SOKOL_GLCORE) + res.gl.major_version = 4; + #if defined(_SAPP_APPLE) + res.gl.minor_version = 1; + #else + res.gl.minor_version = 3; + #endif + #elif defined(SOKOL_GLES3) + res.gl.major_version = 3; + #if defined(_SAPP_ANDROID) || defined(_SAPP_LINUX) + res.gl.minor_version = 1; + #else + res.gl.minor_version = 0; + #endif + #endif + } + res.html5.canvas_selector = _sapp_def(res.html5.canvas_selector, "#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"); + return res; +} + +_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { + 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; + _sapp.framebuffer_height = _sapp.window_height; + _sapp.sample_count = _sapp.desc.sample_count; + _sapp.swap_interval = _sapp.desc.swap_interval; + _sapp_strcpy(_sapp.desc.html5.canvas_selector, _sapp.html5_canvas_selector, sizeof(_sapp.html5_canvas_selector)); + _sapp.desc.html5.canvas_selector = _sapp.html5_canvas_selector; + _sapp.html5_ask_leave_site = _sapp.desc.html5.ask_leave_site; + _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; + if (_sapp.clipboard.enabled) { + _sapp.clipboard.buf_size = _sapp.desc.clipboard_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*) _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); + _sapp_free((void*)_sapp.clipboard.buffer); + } + if (_sapp.drop.enabled) { + SOKOL_ASSERT(_sapp.drop.buffer); + _sapp_free((void*)_sapp.drop.buffer); + } + if (_sapp.default_icon_pixels) { + _sapp_free((void*)_sapp.default_icon_pixels); + } + for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { + sapp_unbind_mouse_cursor_image((sapp_mouse_cursor) i); + } + _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); +} + +_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { + _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; + _sapp.event.window_width = _sapp.window_width; + _sapp.event.window_height = _sapp.window_height; + _sapp.event.framebuffer_width = _sapp.framebuffer_width; + _sapp.event.framebuffer_height = _sapp.framebuffer_height; + _sapp.event.mouse_x = _sapp.mouse.x; + _sapp.event.mouse_y = _sapp.mouse.y; + _sapp.event.mouse_dx = _sapp.mouse.dx; + _sapp.event.mouse_dy = _sapp.mouse.dy; +} + +_SOKOL_PRIVATE bool _sapp_events_enabled(void) { + /* only send events when an event callback is set, and the init function was called */ + return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; +} + +_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { + if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { + return _sapp.keycodes[scan_code]; + } else { + return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { + if (_sapp.drop.enabled) { + SOKOL_ASSERT(_sapp.drop.buffer); + _sapp_clear(_sapp.drop.buffer, (size_t)_sapp.drop.buf_size); + } +} + +_SOKOL_PRIVATE void _sapp_frame(void) { + if (_sapp.first_frame) { + _sapp.first_frame = false; + _sapp_call_init(); + } + _sapp_call_frame(); + _sapp.frame_count++; +} + +_SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { + SOKOL_ASSERT(desc->width > 0); + SOKOL_ASSERT(desc->height > 0); + SOKOL_ASSERT(desc->pixels.ptr != 0); + 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) { + _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH); + return false; + } + return true; +} + +_SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[], int num_images, int width, int height) { + int least_diff = 0x7FFFFFFF; + int least_index = 0; + for (int i = 0; i < num_images; i++) { + int diff = (image_descs[i].width * image_descs[i].height) - (width * height); + if (diff < 0) { + diff = -diff; + } + if (diff < least_diff) { + least_diff = diff; + least_index = i; + } + } + return least_index; +} + +_SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc* desc) { + int index = 0; + for (; index < SAPP_MAX_ICONIMAGES; index++) { + if (0 == desc->images[index].pixels.ptr) { + break; + } + } + return index; +} + +_SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc* desc, int num_images) { + SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES); + for (int i = 0; i < num_images; i++) { + const sapp_image_desc* img_desc = &desc->images[i]; + if (!_sapp_image_validate(img_desc)) { + return false; + } + } + return true; +} + +_SOKOL_PRIVATE void _sapp_setup_default_icon(void) { + SOKOL_ASSERT(0 == _sapp.default_icon_pixels); + + const int num_icons = 3; + const int icon_sizes[3] = { 16, 32, 64 }; // must be multiple of 8! + + // allocate a pixel buffer for all icon pixels + int all_num_pixels = 0; + for (int i = 0; i < num_icons; i++) { + all_num_pixels += icon_sizes[i] * icon_sizes[i]; + } + _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; + const uint32_t* dst_end = dst + all_num_pixels; + (void)dst_end; // silence unused warning in release mode + for (int i = 0; i < num_icons; i++) { + const int dim = (int) icon_sizes[i]; + const int num_pixels = dim * dim; + sapp_image_desc* img_desc = &_sapp.default_icon_desc.images[i]; + img_desc->width = dim; + img_desc->height = dim; + img_desc->pixels.ptr = dst; + img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t); + dst += num_pixels; + } + SOKOL_ASSERT(dst == dst_end); + + // Amstrad CPC font 'S' + const uint8_t tile[8] = { + 0x3C, + 0x66, + 0x60, + 0x3C, + 0x06, + 0x66, + 0x3C, + 0x00, + }; + // rainbow colors + const uint32_t colors[8] = { + 0xFF4370FF, + 0xFF26A7FF, + 0xFF58EEFF, + 0xFF57E1D4, + 0xFF65CC9C, + 0xFF6ABB66, + 0xFFF5A542, + 0xFFC2577E, + }; + dst = _sapp.default_icon_pixels; + const uint32_t blank = 0x00FFFFFF; + const uint32_t shadow = 0xFF000000; + for (int i = 0; i < num_icons; i++) { + const int dim = icon_sizes[i]; + SOKOL_ASSERT((dim % 8) == 0); + const int scale = dim / 8; + for (int ty = 0, y = 0; ty < 8; ty++) { + const uint32_t color = colors[ty]; + for (int sy = 0; sy < scale; sy++, y++) { + uint8_t bits = tile[ty]; + for (int tx = 0, x = 0; tx < 8; tx++, bits<<=1) { + uint32_t pixel = (0 == (bits & 0x80)) ? blank : color; + for (int sx = 0; sx < scale; sx++, x++) { + SOKOL_ASSERT(dst < dst_end); + *dst++ = pixel; + } + } + } + } + } + SOKOL_ASSERT(dst == dst_end); + + // right shadow + dst = _sapp.default_icon_pixels; + for (int i = 0; i < num_icons; i++) { + const int dim = icon_sizes[i]; + for (int y = 0; y < dim; y++) { + uint32_t prev_color = blank; + for (int x = 0; x < dim; x++) { + const int dst_index = y * dim + x; + const uint32_t cur_color = dst[dst_index]; + if ((cur_color == blank) && (prev_color != blank)) { + dst[dst_index] = shadow; + } + prev_color = cur_color; + } + } + dst += dim * dim; + } + SOKOL_ASSERT(dst == dst_end); + + // bottom shadow + dst = _sapp.default_icon_pixels; + for (int i = 0; i < num_icons; i++) { + const int dim = icon_sizes[i]; + for (int x = 0; x < dim; x++) { + uint32_t prev_color = blank; + for (int y = 0; y < dim; y++) { + const int dst_index = y * dim + x; + const uint32_t cur_color = dst[dst_index]; + if ((cur_color == blank) && (prev_color != blank)) { + dst[dst_index] = shadow; + } + prev_color = cur_color; + } + } + dst += dim * dim; + } + SOKOL_ASSERT(dst == dst_end); +} + +// ██ ██ ██████ ██████ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ███ ██████ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ +// ███ ███ ██████ ██ ██████ +// +// >>wgpu +#if defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUStringView _sapp_wgpu_stringview(const char* str) { + WGPUStringView res; + if (str) { + res.data = str; + res.length = strlen(str); + } else { + res.data = 0; + res.length = 0; + } + return res; +} + +_SOKOL_PRIVATE WGPUCallbackMode _sapp_wgpu_callbackmode(void) { + #if defined(_SAPP_WGPU_HAS_WAIT) + return WGPUCallbackMode_WaitAnyOnly; + #else + return WGPUCallbackMode_AllowProcessEvents; + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_await(WGPUFuture future) { + #if defined(_SAPP_WGPU_HAS_WAIT) + SOKOL_ASSERT(_sapp.wgpu.instance); + _SAPP_STRUCT(WGPUFutureWaitInfo, wait_info); + wait_info.future = future; + WGPUWaitStatus res = wgpuInstanceWaitAny(_sapp.wgpu.instance, 1, &wait_info, UINT64_MAX); + SOKOL_ASSERT(res == WGPUWaitStatus_Success); _SOKOL_UNUSED(res); + #else + // this code path should never be called + _SOKOL_UNUSED(future); + SOKOL_ASSERT(false); + #endif +} + +_SOKOL_PRIVATE WGPUTextureFormat _sapp_wgpu_pick_render_format(size_t count, const WGPUTextureFormat* formats) { + // NOTE: only accept non-SRGB formats until sokol_app.h gets proper SRGB support + SOKOL_ASSERT((count > 0) && formats); + for (size_t i = 0; i < count; i++) { + const WGPUTextureFormat fmt = formats[i]; + switch (fmt) { + case WGPUTextureFormat_RGBA8Unorm: + case WGPUTextureFormat_BGRA8Unorm: + return fmt; + default: break; + } + } + // FIXME: fallback might still return an SRGB format + return formats[0]; +} + +_SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) { + SOKOL_ASSERT(_sapp.wgpu.instance); + SOKOL_ASSERT(_sapp.wgpu.device); + SOKOL_ASSERT(0 == _sapp.wgpu.msaa_tex); + SOKOL_ASSERT(0 == _sapp.wgpu.msaa_view); + SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_tex); + SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_view); + + if (!called_from_resize) { + SOKOL_ASSERT(0 == _sapp.wgpu.surface); + _SAPP_STRUCT(WGPUSurfaceDescriptor, surf_desc); + #if defined (_SAPP_EMSCRIPTEN) + _SAPP_STRUCT(WGPUEmscriptenSurfaceSourceCanvasHTMLSelector, html_canvas_desc); + html_canvas_desc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector; + html_canvas_desc.selector = _sapp_wgpu_stringview(_sapp.html5_canvas_selector); + surf_desc.nextInChain = &html_canvas_desc.chain; + #elif defined(_SAPP_MACOS) + _SAPP_STRUCT(WGPUSurfaceSourceMetalLayer, from_metal_layer); + from_metal_layer.chain.sType = WGPUSType_SurfaceSourceMetalLayer; + from_metal_layer.layer = _sapp.macos.view.layer; + surf_desc.nextInChain = &from_metal_layer.chain; + #elif defined(_SAPP_WIN32) + _SAPP_STRUCT(WGPUSurfaceSourceWindowsHWND, from_hwnd); + from_hwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + from_hwnd.hinstance = GetModuleHandleW(NULL); + from_hwnd.hwnd = _sapp.win32.hwnd; + surf_desc.nextInChain = &from_hwnd.chain; + #elif defined(_SAPP_LINUX) + _SAPP_STRUCT(WGPUSurfaceSourceXlibWindow, from_xlib); + from_xlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + from_xlib.display = _sapp.x11.display; + from_xlib.window = _sapp.x11.window; + surf_desc.nextInChain = &from_xlib.chain; + #else + #error "sokol_app.h: unsupported WebGPU platform" + #endif + _sapp.wgpu.surface = wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc); + if (0 == _sapp.wgpu.surface) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED); + } + _SAPP_STRUCT(WGPUSurfaceCapabilities, surf_caps); + WGPUStatus caps_status = wgpuSurfaceGetCapabilities(_sapp.wgpu.surface, _sapp.wgpu.adapter, &surf_caps); + if (caps_status != WGPUStatus_Success) { + _SAPP_PANIC(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED); + } + _sapp.wgpu.render_format = _sapp_wgpu_pick_render_format(surf_caps.formatCount, surf_caps.formats); + } + + SOKOL_ASSERT(_sapp.wgpu.surface); + _SAPP_STRUCT(WGPUSurfaceConfiguration, surf_conf); + surf_conf.device = _sapp.wgpu.device; + surf_conf.format = _sapp.wgpu.render_format; + surf_conf.usage = WGPUTextureUsage_RenderAttachment; + surf_conf.width = (uint32_t)_sapp.framebuffer_width; + surf_conf.height = (uint32_t)_sapp.framebuffer_height; + surf_conf.alphaMode = WGPUCompositeAlphaMode_Opaque; + #if defined(_SAPP_EMSCRIPTEN) + // FIXME: make this further configurable? + if (_sapp.desc.html5.premultiplied_alpha) { + surf_conf.alphaMode = WGPUCompositeAlphaMode_Premultiplied; + } + #endif + surf_conf.presentMode = WGPUPresentMode_Fifo; + wgpuSurfaceConfigure(_sapp.wgpu.surface, &surf_conf); + + _SAPP_STRUCT(WGPUTextureDescriptor, ds_desc); + ds_desc.usage = WGPUTextureUsage_RenderAttachment; + ds_desc.dimension = WGPUTextureDimension_2D; + ds_desc.size.width = (uint32_t)_sapp.framebuffer_width; + ds_desc.size.height = (uint32_t)_sapp.framebuffer_height; + ds_desc.size.depthOrArrayLayers = 1; + ds_desc.format = WGPUTextureFormat_Depth32FloatStencil8; + ds_desc.mipLevelCount = 1; + ds_desc.sampleCount = (uint32_t)_sapp.sample_count; + _sapp.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &ds_desc); + if (0 == _sapp.wgpu.depth_stencil_tex) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED); + } + _sapp.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.wgpu.depth_stencil_tex, 0); + if (0 == _sapp.wgpu.depth_stencil_view) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED); + } + + if (_sapp.sample_count > 1) { + _SAPP_STRUCT(WGPUTextureDescriptor, msaa_desc); + msaa_desc.usage = WGPUTextureUsage_RenderAttachment; + msaa_desc.dimension = WGPUTextureDimension_2D; + msaa_desc.size.width = (uint32_t)_sapp.framebuffer_width; + msaa_desc.size.height = (uint32_t)_sapp.framebuffer_height; + msaa_desc.size.depthOrArrayLayers = 1; + msaa_desc.format = _sapp.wgpu.render_format; + msaa_desc.mipLevelCount = 1; + msaa_desc.sampleCount = (uint32_t)_sapp.sample_count; + _sapp.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &msaa_desc); + if (0 == _sapp.wgpu.msaa_tex) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED); + } + _sapp.wgpu.msaa_view = wgpuTextureCreateView(_sapp.wgpu.msaa_tex, 0); + if (0 == _sapp.wgpu.msaa_view) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED); + } + } +} + +_SOKOL_PRIVATE void _sapp_wgpu_discard_swapchain(bool called_from_resize) { + if (_sapp.wgpu.msaa_view) { + wgpuTextureViewRelease(_sapp.wgpu.msaa_view); + _sapp.wgpu.msaa_view = 0; + } + if (_sapp.wgpu.msaa_tex) { + wgpuTextureRelease(_sapp.wgpu.msaa_tex); + _sapp.wgpu.msaa_tex = 0; + } + if (_sapp.wgpu.depth_stencil_view) { + wgpuTextureViewRelease(_sapp.wgpu.depth_stencil_view); + _sapp.wgpu.depth_stencil_view = 0; + } + if (_sapp.wgpu.depth_stencil_tex) { + wgpuTextureRelease(_sapp.wgpu.depth_stencil_tex); + _sapp.wgpu.depth_stencil_tex = 0; + } + if (!called_from_resize) { + if (_sapp.wgpu.surface) { + wgpuSurfaceRelease(_sapp.wgpu.surface); + _sapp.wgpu.surface = 0; + } + } +} + +_SOKOL_PRIVATE void _sapp_wgpu_swapchain_next(void) { + SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view); + _SAPP_STRUCT(WGPUSurfaceTexture, surf_tex); + wgpuSurfaceGetCurrentTexture(_sapp.wgpu.surface, &surf_tex); + switch (surf_tex.status) { + case WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal: + case WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal: + // all ok + break; + case WGPUSurfaceGetCurrentTextureStatus_Timeout: + case WGPUSurfaceGetCurrentTextureStatus_Outdated: + case WGPUSurfaceGetCurrentTextureStatus_Lost: + if (surf_tex.texture) { + wgpuTextureRelease(surf_tex.texture); + } + _sapp_wgpu_discard_swapchain(false); + _sapp_wgpu_create_swapchain(false); + // FIXME: currently this will assert in the caller + return; + case WGPUSurfaceGetCurrentTextureStatus_Error: + default: + _SAPP_PANIC(WGPU_SWAPCHAIN_GETCURRENTTEXTURE_FAILED); + break; + } + _sapp.wgpu.swapchain_view = wgpuTextureCreateView(surf_tex.texture, 0); + SOKOL_ASSERT(_sapp.wgpu.swapchain_view); +} + +_SOKOL_PRIVATE void _sapp_wgpu_swapchain_size_changed(void) { + if (_sapp.wgpu.surface) { + _sapp_wgpu_discard_swapchain(true); + _sapp_wgpu_create_swapchain(true); + } +} + +_SOKOL_PRIVATE void _sapp_wgpu_device_lost_cb(const WGPUDevice* dev, WGPUDeviceLostReason reason, WGPUStringView msg, void* ud1, void* ud2) { + _SOKOL_UNUSED(dev); _SOKOL_UNUSED(reason); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2); + // NOTE: on wgpuInstanceRelease(), the device lost callback is always called with + // WGPUDeviceLostReason_CallbackCancelled (even though no device should exist at that point) + if (reason != WGPUDeviceLostReason_CallbackCancelled) { + SOKOL_ASSERT(msg.data && (msg.length > 0)); + char buf[1024]; + _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf)); + _SAPP_ERROR_MSG(WGPU_DEVICE_LOST, buf); + } +} + +// NOTE: emdawnwebgpu doesn't seem to have a device logging callback +#if !defined(_SAPP_EMSCRIPTEN) +_SOKOL_PRIVATE void _sapp_wgpu_device_logging_cb(WGPULoggingType log_type, WGPUStringView msg, void* ud1, void* ud2) { + _SOKOL_UNUSED(log_type); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2); + SOKOL_ASSERT(msg.data && (msg.length > 0)); + char buf[1024]; + _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf)); + switch (log_type) { + case WGPULoggingType_Warning: + _SAPP_WARN_MSG(WGPU_DEVICE_LOG, buf); + break; + case WGPULoggingType_Error: + _SAPP_ERROR_MSG(WGPU_DEVICE_LOG, buf); + break; + default: + _SAPP_INFO_MSG(WGPU_DEVICE_LOG, buf); + break; + } +} +#endif + +_SOKOL_PRIVATE void _sapp_wgpu_uncaptured_error_cb(const WGPUDevice* dev, WGPUErrorType err_type, WGPUStringView msg, void* ud1, void* ud2) { + _SOKOL_UNUSED(dev); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2); + if (err_type != WGPUErrorType_NoError) { + SOKOL_ASSERT(msg.data && (msg.length > 0)); + char buf[1024]; + _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf)); + _SAPP_ERROR_MSG(WGPU_DEVICE_UNCAPTURED_ERROR, buf); + } +} + +_SOKOL_PRIVATE void _sapp_wgpu_request_device_cb(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView msg, void* userdata1, void* userdata2) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata1); + _SOKOL_UNUSED(userdata2); + SOKOL_ASSERT(!_sapp.wgpu.init_done); + if (status != WGPURequestDeviceStatus_Success) { + if (status == WGPURequestDeviceStatus_Error) { + _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_ERROR); + } else { + _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN); + } + } + SOKOL_ASSERT(device); + _sapp.wgpu.device = device; + #if !defined(_SAPP_EMSCRIPTEN) + _SAPP_STRUCT(WGPULoggingCallbackInfo, cb_info); + cb_info.callback = _sapp_wgpu_device_logging_cb; + wgpuDeviceSetLoggingCallback(_sapp.wgpu.device, cb_info); + #endif + _sapp_wgpu_create_swapchain(false); + _sapp.wgpu.init_done = true; +} + +_SOKOL_PRIVATE void _sapp_wgpu_create_device_and_swapchain(void) { + SOKOL_ASSERT(_sapp.wgpu.adapter); + size_t cur_feature_index = 1; + #define _SAPP_WGPU_MAX_REQUESTED_FEATURES (16) + WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = { + WGPUFeatureName_Depth32FloatStencil8, + }; + // check for optional features we're interested in + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionBC)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC; + } + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionETC2)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2; + } + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionASTC)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionASTC; + } + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_Float32Filterable)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable; + } + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_DualSourceBlending)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_DualSourceBlending; + } + #undef _SAPP_WGPU_MAX_REQUESTED_FEATURES + + WGPULimits adapterLimits = WGPU_LIMITS_INIT; + wgpuAdapterGetLimits(_sapp.wgpu.adapter, &adapterLimits); + + WGPULimits requiredLimits = WGPU_LIMITS_INIT; + requiredLimits.maxColorAttachments = adapterLimits.maxColorAttachments; + requiredLimits.maxSampledTexturesPerShaderStage = adapterLimits.maxSampledTexturesPerShaderStage; + requiredLimits.maxStorageBuffersPerShaderStage = adapterLimits.maxStorageBuffersPerShaderStage; + requiredLimits.maxStorageTexturesPerShaderStage = adapterLimits.maxStorageTexturesPerShaderStage; + + _SAPP_STRUCT(WGPURequestDeviceCallbackInfo, cb_info); + cb_info.mode = _sapp_wgpu_callbackmode(); + cb_info.callback = _sapp_wgpu_request_device_cb; + + _SAPP_STRUCT(WGPUDeviceDescriptor, dev_desc); + dev_desc.requiredFeatureCount = cur_feature_index; + dev_desc.requiredFeatures = requiredFeatures; + dev_desc.requiredLimits = &requiredLimits; + dev_desc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowProcessEvents; + dev_desc.deviceLostCallbackInfo.callback = _sapp_wgpu_device_lost_cb; + dev_desc.uncapturedErrorCallbackInfo.callback = _sapp_wgpu_uncaptured_error_cb; + WGPUFuture future = wgpuAdapterRequestDevice(_sapp.wgpu.adapter, &dev_desc, cb_info); + #if defined(_SAPP_WGPU_HAS_WAIT) + _sapp_wgpu_await(future); + #else + _SOKOL_UNUSED(future); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView msg, void* userdata1, void* userdata2) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata1); + _SOKOL_UNUSED(userdata2); + if (status != WGPURequestAdapterStatus_Success) { + switch (status) { + case WGPURequestAdapterStatus_Unavailable: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE); break; + case WGPURequestAdapterStatus_Error: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR); break; + default: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN); break; + } + } + SOKOL_ASSERT(adapter); + _sapp.wgpu.adapter = adapter; + #if !defined(_SAPP_WGPU_HAS_WAIT) + // chain device creation + _sapp_wgpu_create_device_and_swapchain(); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_create_adapter(void) { + SOKOL_ASSERT(_sapp.wgpu.instance); + // FIXME: power preference? + _SAPP_STRUCT(WGPURequestAdapterCallbackInfo, cb_info); + cb_info.mode = _sapp_wgpu_callbackmode(); + cb_info.callback = _sapp_wgpu_request_adapter_cb; + WGPUFuture future = wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, cb_info); + #if defined(_SAPP_WGPU_HAS_WAIT) + _sapp_wgpu_await(future); + #else + _SOKOL_UNUSED(future); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_init(void) { + SOKOL_ASSERT(0 == _sapp.wgpu.instance); + SOKOL_ASSERT(!_sapp.wgpu.init_done); + + _SAPP_STRUCT(WGPUInstanceDescriptor, desc); + #if defined(_SAPP_WGPU_HAS_WAIT) + WGPUInstanceFeatureName inst_features[1] = { + WGPUInstanceFeatureName_TimedWaitAny, + }; + desc.requiredFeatureCount = 1; + desc.requiredFeatures = inst_features; + #endif + _sapp.wgpu.instance = wgpuCreateInstance(&desc); + if (0 == _sapp.wgpu.instance) { + _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED); + } + // NOTE: on Emscripten, device and swapchain creation are chained in the callacks + _sapp_wgpu_create_adapter(); + #if defined(_SAPP_WGPU_HAS_WAIT) + _sapp_wgpu_create_device_and_swapchain(); + SOKOL_ASSERT(_sapp.wgpu.init_done); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_discard(void) { + _sapp_wgpu_discard_swapchain(false); + if (_sapp.wgpu.device) { + wgpuDeviceRelease(_sapp.wgpu.device); + _sapp.wgpu.device = 0; + } + if (_sapp.wgpu.adapter) { + wgpuAdapterRelease(_sapp.wgpu.adapter); + _sapp.wgpu.adapter = 0; + } + if (_sapp.wgpu.instance) { + wgpuInstanceRelease(_sapp.wgpu.instance); + _sapp.wgpu.instance = 0; + } +} + +_SOKOL_PRIVATE void _sapp_wgpu_frame(void) { + wgpuInstanceProcessEvents(_sapp.wgpu.instance); + if (_sapp.wgpu.init_done) { + _sapp_frame(); + if (_sapp.wgpu.swapchain_view) { + wgpuTextureViewRelease(_sapp.wgpu.swapchain_view); + _sapp.wgpu.swapchain_view = 0; + } + #if !defined(_SAPP_EMSCRIPTEN) + wgpuSurfacePresent(_sapp.wgpu.surface); + #endif + } +} +#endif // SOKOL_WGPU + +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ██ ██ ██ █████ ███████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██████ ███████ ██ ██ ██ ██ ██ ████ +// +// >>vulkan +// >>vk +#if defined(SOKOL_VULKAN) + +#if defined(__cplusplus) +#define _SAPP_VK_ZERO_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = 0; type array_name[num] = {} +#define _SAPP_VK_MAX_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = num; type array_name[num] = {} +#else +#define _SAPP_VK_ZERO_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = 0; type array_name[num] = {0} +#define _SAPP_VK_MAX_COUNT_AND_ARRAY(num, type, count_name, array_name) uint32_t count_name = num; type array_name[num] = {0} +#endif + +_SOKOL_PRIVATE void _sapp_vk_load_instance_ext_funcs(void) { + SOKOL_ASSERT(_sapp.vk.instance); + #if defined(SOKOL_DEBUG) + _sapp.vk.ext.set_debug_utils_object_name_ext = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(_sapp.vk.instance, "vkSetDebugUtilsObjectNameEXT"); + if (0 == _sapp.vk.ext.set_debug_utils_object_name_ext) { + _SAPP_PANIC(VULKAN_REQUIRED_INSTANCE_EXTENSION_FUNCTION_MISSING); + } + #endif +} + +_SOKOL_PRIVATE void _sapp_vk_set_object_label(VkObjectType obj_type, uint64_t obj_handle, const char* label) { + #if defined(SOKOL_DEBUG) + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(_sapp.vk.ext.set_debug_utils_object_name_ext); + SOKOL_ASSERT(obj_handle); + if (label) { + _SAPP_STRUCT(VkDebugUtilsObjectNameInfoEXT, name_info); + name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + name_info.objectType = obj_type; + name_info.objectHandle = obj_handle, + name_info.pObjectName = label; + VkResult res = _sapp.vk.ext.set_debug_utils_object_name_ext(_sapp.vk.device, &name_info); + SOKOL_ASSERT(res == VK_SUCCESS); + } + #else + _SOKOL_UNUSED(obj_type); + _SOKOL_UNUSED(obj_handle); + _SOKOL_UNUSED(label); + #endif +} + +_SOKOL_PRIVATE int _sapp_vk_mem_find_memory_type_index(uint32_t type_filter, VkMemoryPropertyFlags props) { + SOKOL_ASSERT(_sapp.vk.physical_device); + _SAPP_STRUCT(VkPhysicalDeviceMemoryProperties, mem_props); + vkGetPhysicalDeviceMemoryProperties(_sapp.vk.physical_device, &mem_props); + for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { + if ((type_filter & (1 << i)) && ((mem_props.memoryTypes[i].propertyFlags & props) == props)) { + return (int)i; + } + } + return -1; +} + +_SOKOL_PRIVATE void _sapp_vk_create_instance(void) { + SOKOL_ASSERT(0 == _sapp.vk.instance); + + _SAPP_STRUCT(VkApplicationInfo, app_info); + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "sokol-app"; // FIXME: override via sapp_desc? + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "sokol"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_3; + + _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, layer_count, layer_names); + #if defined(SOKOL_DEBUG) + layer_names[layer_count++] = "VK_LAYER_KHRONOS_validation"; + SOKOL_ASSERT(layer_count <= 32); + #endif + + _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, ext_count, ext_names); + ext_names[ext_count++] = VK_KHR_SURFACE_EXTENSION_NAME; + #if defined(SOKOL_DEBUG) + ext_names[ext_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + #endif + #if defined(VK_USE_PLATFORM_XLIB_KHR) + ext_names[ext_count++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME; + #elif defined(VK_USE_PLATFORM_WIN32_KHR) + ext_names[ext_count++] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; + #endif + SOKOL_ASSERT(ext_count <= 32); + + _SAPP_STRUCT(VkInstanceCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.flags = 0; + create_info.pApplicationInfo = &app_info; + create_info.enabledLayerCount = layer_count; + create_info.ppEnabledLayerNames = layer_names; + create_info.enabledExtensionCount = ext_count; + create_info.ppEnabledExtensionNames = ext_names; + VkResult res = vkCreateInstance(&create_info, 0, &_sapp.vk.instance); + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_CREATE_INSTANCE_FAILED); + } + SOKOL_ASSERT(_sapp.vk.instance); +} + +_SOKOL_PRIVATE void _sapp_vk_destroy_instance(void) { + SOKOL_ASSERT(_sapp.vk.instance); + vkDestroyInstance(_sapp.vk.instance, 0); + _sapp.vk.instance = 0; +} + +_SOKOL_PRIVATE uint32_t _sapp_vk_required_device_extensions(const char** out_names, uint32_t max_count) { + SOKOL_ASSERT(out_names && (max_count > 0)); + uint32_t count = 0; + out_names[count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + out_names[count++] = VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME; + SOKOL_ASSERT(count <= max_count); _SOKOL_UNUSED(max_count); + return count; +} + +_SOKOL_PRIVATE bool _sapp_vk_check_device_extensions(VkPhysicalDevice pdev, const char** required_exts, uint32_t num_required_exts) { + SOKOL_ASSERT(pdev && required_exts && num_required_exts > 0); + uint32_t ext_count = 0; + VkResult res = vkEnumerateDeviceExtensionProperties(pdev, 0, &ext_count, 0); + SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res); + if (ext_count == 0) { + return false; + } + VkExtensionProperties* ext_props = (VkExtensionProperties*) _sapp_malloc(sizeof(VkExtensionProperties) * ext_count); + SOKOL_ASSERT(ext_props); + res = vkEnumerateDeviceExtensionProperties(pdev, 0, &ext_count, ext_props); + bool all_supported = true; + for (uint32_t req_ext_idx = 0; req_ext_idx < num_required_exts; req_ext_idx++) { + const char* req_ext_name = required_exts[req_ext_idx]; + bool required_ext_supported = false; + for (uint32_t ext_idx = 0; ext_idx < ext_count; ext_idx++) { + const VkExtensionProperties* ext_prop = &ext_props[ext_idx]; + if (0 == strcmp(req_ext_name, ext_prop->extensionName)) { + required_ext_supported = true; + break; + } + } + if (!required_ext_supported) { + all_supported = false; + } + } + _sapp_free(ext_props); + return all_supported; +} + +_SOKOL_PRIVATE void _sapp_vk_pick_physical_device(void) { + SOKOL_ASSERT(_sapp.vk.instance); + SOKOL_ASSERT(_sapp.vk.surface); + SOKOL_ASSERT(0 == _sapp.vk.physical_device); + VkResult res = VK_SUCCESS; + + _SAPP_VK_MAX_COUNT_AND_ARRAY(8, VkPhysicalDevice, physical_device_count, physical_devices); + res = vkEnumeratePhysicalDevices(_sapp.vk.instance, &physical_device_count, physical_devices); + if ((res != VK_SUCCESS) && (res != VK_INCOMPLETE)) { + _SAPP_PANIC(VULKAN_ENUMERATE_PHYSICAL_DEVICES_FAILED); + } + if (physical_device_count == 0) { + _SAPP_PANIC(VULKAN_NO_PHYSICAL_DEVICES_FOUND); + } + _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, ext_count, ext_names); + ext_count = _sapp_vk_required_device_extensions(ext_names, 32); + + VkPhysicalDevice picked_pdev = 0; + for (uint32_t pdev_idx = 0; pdev_idx < physical_device_count; pdev_idx++) { + const VkPhysicalDevice pdev = physical_devices[pdev_idx]; + _SAPP_STRUCT(VkPhysicalDeviceProperties, dev_props); + vkGetPhysicalDeviceProperties(pdev, &dev_props); + if (dev_props.apiVersion < VK_API_VERSION_1_3) { + continue; + } + if (!_sapp_vk_check_device_extensions(pdev, ext_names, ext_count)) { + continue; + } + // FIXME: handle theoretical case where graphics and present aren't supported by the same queue family index + _SAPP_VK_MAX_COUNT_AND_ARRAY(8, VkQueueFamilyProperties, queue_family_props_count, queue_family_props); + vkGetPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_props_count, queue_family_props); + bool has_required_queues = false; + const VkQueueFlags required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT; + for (uint32_t qfp_idx = 0; qfp_idx < queue_family_props_count; qfp_idx++) { + const VkQueueFlags queue_flags = queue_family_props[qfp_idx].queueFlags; + if ((queue_flags & required_flags) == required_flags) { + _sapp.vk.queue_family_index = qfp_idx; + has_required_queues = true; + break; + } + } + if (!has_required_queues) { + continue; + } + + VkBool32 presentation_supported = false; + res = vkGetPhysicalDeviceSurfaceSupportKHR(pdev, _sapp.vk.queue_family_index, _sapp.vk.surface, &presentation_supported); + SOKOL_ASSERT(VK_SUCCESS == res); + if (!presentation_supported) { + continue; + } + + // if we arrive here, found a suitable device + picked_pdev = pdev; + break; + } + if (0 == picked_pdev) { + _SAPP_PANIC(VULKAN_NO_SUITABLE_PHYSICAL_DEVICE_FOUND); + } + _sapp.vk.physical_device = picked_pdev; + SOKOL_ASSERT(_sapp.vk.physical_device); +} + +_SOKOL_PRIVATE void _sapp_vk_create_device(void) { + SOKOL_ASSERT(_sapp.vk.physical_device); + SOKOL_ASSERT(0 == _sapp.vk.device); + + const float queue_priority = 0.0f; + _SAPP_STRUCT(VkDeviceQueueCreateInfo, queue_create_info); + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.queueFamilyIndex = _sapp.vk.queue_family_index; + queue_create_info.queueCount = 1; + queue_create_info.pQueuePriorities = &queue_priority; + + _SAPP_VK_ZERO_COUNT_AND_ARRAY(32, const char*, ext_count, ext_names); + ext_count = _sapp_vk_required_device_extensions(ext_names, 32); + + _SAPP_STRUCT(VkPhysicalDeviceFeatures2, supports); + supports.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + vkGetPhysicalDeviceFeatures2(_sapp.vk.physical_device, &supports); + + _SAPP_STRUCT(VkPhysicalDeviceDescriptorBufferFeaturesEXT, descriptor_buffer_features); + descriptor_buffer_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT; + descriptor_buffer_features.descriptorBuffer = VK_TRUE; + + _SAPP_STRUCT(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, xds_features); + xds_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; + xds_features.pNext = &descriptor_buffer_features; + xds_features.extendedDynamicState = VK_TRUE; + + _SAPP_STRUCT(VkPhysicalDeviceVulkan12Features, vk12_features); + vk12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + vk12_features.pNext = &xds_features; + vk12_features.bufferDeviceAddress = VK_TRUE; + + _SAPP_STRUCT(VkPhysicalDeviceVulkan13Features, vk13_features); + vk13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + vk13_features.pNext = &vk12_features; + vk13_features.dynamicRendering = VK_TRUE; + vk13_features.synchronization2 = VK_TRUE; + + _SAPP_STRUCT(VkPhysicalDeviceFeatures2, required); + required.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + required.pNext = &vk13_features; + required.features.samplerAnisotropy = VK_TRUE; + required.features.dualSrcBlend = VK_TRUE; + if (supports.features.textureCompressionBC) { + required.features.textureCompressionBC = VK_TRUE; + } + if (supports.features.textureCompressionETC2) { + required.features.textureCompressionETC2 = VK_TRUE; + } + if (supports.features.textureCompressionASTC_LDR) { + required.features.textureCompressionASTC_LDR = VK_TRUE; + } + _SAPP_STRUCT(VkDeviceCreateInfo, dev_create_info); + dev_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + dev_create_info.pNext = &required; + dev_create_info.queueCreateInfoCount = 1; + dev_create_info.pQueueCreateInfos = &queue_create_info; + dev_create_info.enabledExtensionCount = ext_count; + dev_create_info.ppEnabledExtensionNames = ext_names; + + VkResult res = vkCreateDevice(_sapp.vk.physical_device, &dev_create_info, 0, &_sapp.vk.device); + if (res != VK_SUCCESS) { + switch (res) { + case VK_ERROR_EXTENSION_NOT_PRESENT: + _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_EXTENSION_NOT_PRESENT); + break; + case VK_ERROR_FEATURE_NOT_PRESENT: + _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_FEATURE_NOT_PRESENT); + break; + case VK_ERROR_INITIALIZATION_FAILED: + _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_INITIALIZATION_FAILED); + break; + default: + _SAPP_PANIC(VULKAN_CREATE_DEVICE_FAILED_OTHER); + break; + } + } + SOKOL_ASSERT(_sapp.vk.device); + + SOKOL_ASSERT(0 == _sapp.vk.queue); + vkGetDeviceQueue(_sapp.vk.device, _sapp.vk.queue_family_index, 0, &_sapp.vk.queue); + SOKOL_ASSERT(_sapp.vk.queue); +} + +_SOKOL_PRIVATE void _sapp_vk_destroy_device(void) { + SOKOL_ASSERT(_sapp.vk.device); + vkDestroyDevice(_sapp.vk.device, 0); + _sapp.vk.device = 0; + _sapp.vk.queue = 0; +} + +_SOKOL_PRIVATE void _sapp_vk_create_surface(void) { + SOKOL_ASSERT(_sapp.vk.instance); + SOKOL_ASSERT(0 == _sapp.vk.surface); + VkResult res = VK_SUCCESS; + + #if defined(_SAPP_LINUX) + _SAPP_STRUCT(VkXlibSurfaceCreateInfoKHR, xlib_info); + xlib_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + xlib_info.dpy = _sapp.x11.display; + xlib_info.window = _sapp.x11.window; + res = vkCreateXlibSurfaceKHR(_sapp.vk.instance, &xlib_info, 0, &_sapp.vk.surface); + #elif defined(_SAPP_WIN32) + _SAPP_STRUCT(VkWin32SurfaceCreateInfoKHR, win32_info); + win32_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + win32_info.hinstance = GetModuleHandleW(NULL); + win32_info.hwnd = _sapp.win32.hwnd; + res = vkCreateWin32SurfaceKHR(_sapp.vk.instance, &win32_info, 0, &_sapp.vk.surface); + #else + #error "sokol_app.h: unsupported Vulkan platform" + #endif + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_CREATE_SURFACE_FAILED); + } + SOKOL_ASSERT(_sapp.vk.surface); +} + +_SOKOL_PRIVATE void _sapp_vk_destroy_surface(void) { + SOKOL_ASSERT(_sapp.vk.instance); + SOKOL_ASSERT(_sapp.vk.surface); + vkDestroySurfaceKHR(_sapp.vk.instance, _sapp.vk.surface, 0); + _sapp.vk.surface = 0; +} + +_SOKOL_PRIVATE VkSurfaceFormatKHR _sapp_vk_pick_surface_format(void) { + SOKOL_ASSERT(_sapp.vk.instance); + SOKOL_ASSERT(_sapp.vk.surface); + _SAPP_VK_MAX_COUNT_AND_ARRAY(64, VkSurfaceFormatKHR, fmt_count, formats); + VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(_sapp.vk.physical_device, _sapp.vk.surface, &fmt_count, formats); + SOKOL_ASSERT((res == VK_SUCCESS) || (res == VK_INCOMPLETE)); _SOKOL_UNUSED(res); + SOKOL_ASSERT(fmt_count > 0); + // FIXME: only accept non-SRGB formats until sokol_app.h gets proper SRGB support + for (uint32_t i = 0; i < fmt_count; i++) { + switch (formats[i].format) { + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_R8G8B8A8_UNORM: + return formats[i]; + default: break; + } + } + // FIXME: fallback might still return an SRGB format + return formats[0]; +} + +_SOKOL_PRIVATE void _sapp_vk_create_sync_objects(void) { + SOKOL_ASSERT(_sapp.vk.device); + _SAPP_STRUCT(VkSemaphoreCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + VkResult res; + _SOKOL_UNUSED(res); + for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) { + SOKOL_ASSERT(0 == _sapp.vk.sync[i].present_complete_sem); + SOKOL_ASSERT(0 == _sapp.vk.sync[i].render_finished_sem); + res = vkCreateSemaphore(_sapp.vk.device, &create_info, 0, &_sapp.vk.sync[i].present_complete_sem); + SOKOL_ASSERT((res == VK_SUCCESS) && (_sapp.vk.sync[i].present_complete_sem)); + _sapp_vk_set_object_label(VK_OBJECT_TYPE_SEMAPHORE, (uint64_t)_sapp.vk.sync[i].present_complete_sem, "present_complete_sem"); + res = vkCreateSemaphore(_sapp.vk.device, &create_info, 0, &_sapp.vk.sync[i].render_finished_sem); + SOKOL_ASSERT((res == VK_SUCCESS) && (_sapp.vk.sync[i].render_finished_sem)); + _sapp_vk_set_object_label(VK_OBJECT_TYPE_SEMAPHORE, (uint64_t)_sapp.vk.sync[i].render_finished_sem, "render_finished_sem"); + } +} + +_SOKOL_PRIVATE void _sapp_vk_destroy_sync_objects(void) { + SOKOL_ASSERT(_sapp.vk.device); + for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) { + SOKOL_ASSERT(_sapp.vk.sync[i].present_complete_sem); + SOKOL_ASSERT(_sapp.vk.sync[i].render_finished_sem); + vkDestroySemaphore(_sapp.vk.device, _sapp.vk.sync[i].present_complete_sem, 0); + vkDestroySemaphore(_sapp.vk.device, _sapp.vk.sync[i].render_finished_sem, 0); + _sapp.vk.sync[i].present_complete_sem = 0; + _sapp.vk.sync[i].render_finished_sem = 0; + } +} + +_SOKOL_PRIVATE VkDeviceMemory _sapp_vk_mem_alloc_image_memory(const VkMemoryRequirements* mem_reqs) { + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(mem_reqs); + int mem_type_index = _sapp_vk_mem_find_memory_type_index(mem_reqs->memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (-1 == mem_type_index) { + _SAPP_ERROR(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE); + return 0; + } + _SAPP_STRUCT(VkMemoryAllocateInfo, alloc_info); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_reqs->size; + alloc_info.memoryTypeIndex = (uint32_t) mem_type_index; + VkDeviceMemory vk_dev_mem = 0; + VkResult res = vkAllocateMemory(_sapp.vk.device, &alloc_info, 0, &vk_dev_mem); + if (res != VK_SUCCESS) { + _SAPP_ERROR(VULKAN_ALLOCATE_MEMORY_FAILED); + return 0; + } + SOKOL_ASSERT(vk_dev_mem); + return vk_dev_mem; +} + +_SOKOL_PRIVATE void _sapp_vk_mem_free_image_memory(VkDeviceMemory vk_dev_mem) { + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(vk_dev_mem); + vkFreeMemory(_sapp.vk.device, vk_dev_mem, 0); +} + +_SOKOL_PRIVATE void _sapp_vk_swapchain_destroy_surface(_sapp_vk_swapchain_surface_t* surf) { + SOKOL_ASSERT(surf); + SOKOL_ASSERT(surf->img); + SOKOL_ASSERT(surf->mem); + SOKOL_ASSERT(surf->view); + vkDestroyImageView(_sapp.vk.device, surf->view, 0); + surf->view = 0; + _sapp_vk_mem_free_image_memory(surf->mem); + surf->mem = 0; + vkDestroyImage(_sapp.vk.device, surf->img, 0); + surf->img = 0; +} + +_SOKOL_PRIVATE void _sapp_vk_swapchain_create_surface( + _sapp_vk_swapchain_surface_t* surf, + bool recreate, + VkFormat format, + uint32_t width, + uint32_t height, + VkSampleCountFlagBits sample_count_flags, + VkImageUsageFlags usage, + VkImageAspectFlags aspect_mask, + const char* image_debug_label, + const char* view_debug_label) +{ + SOKOL_ASSERT(_sapp.vk.physical_device); + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(surf); + if (recreate) { + _sapp_vk_swapchain_destroy_surface(surf); + } + SOKOL_ASSERT(0 == surf->img); + SOKOL_ASSERT(0 == surf->mem); + SOKOL_ASSERT(0 == surf->view); + VkResult res; + + _SAPP_STRUCT(VkImageCreateInfo, img_create_info); + img_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + img_create_info.imageType = VK_IMAGE_TYPE_2D; + img_create_info.format = format; + img_create_info.extent.width = width; + img_create_info.extent.height = height; + img_create_info.extent.depth = 1; + img_create_info.mipLevels = 1; + img_create_info.arrayLayers = 1; + img_create_info.samples = sample_count_flags; + img_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + img_create_info.usage = usage; + img_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + img_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + res = vkCreateImage(_sapp.vk.device, &img_create_info, 0, &surf->img); + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_FAILED); + } + SOKOL_ASSERT(surf->img); + _sapp_vk_set_object_label(VK_OBJECT_TYPE_IMAGE, (uint64_t)surf->img, image_debug_label); + + _SAPP_STRUCT(VkMemoryRequirements, mem_reqs); + vkGetImageMemoryRequirements(_sapp.vk.device, surf->img, &mem_reqs); + surf->mem = _sapp_vk_mem_alloc_image_memory(&mem_reqs); + if (0 == surf->mem) { + _SAPP_PANIC(VULKAN_SWAPCHAIN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED); + } + res = vkBindImageMemory(_sapp.vk.device, surf->img, surf->mem, 0); + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_SWAPCHAIN_BIND_IMAGE_MEMORY_FAILED); + } + SOKOL_ASSERT(surf->mem); + + _SAPP_STRUCT(VkImageViewCreateInfo, view_create_info); + view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_create_info.image = surf->img; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = format; + view_create_info.subresourceRange.aspectMask = aspect_mask; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.layerCount = 1; + res = vkCreateImageView(_sapp.vk.device, &view_create_info, 0, &surf->view); + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED); + } + SOKOL_ASSERT(surf->view); + _sapp_vk_set_object_label(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)surf->view, view_debug_label); +} + +_SOKOL_PRIVATE uint32_t _sapp_vk_swapchain_min_image_count(const VkSurfaceCapabilitiesKHR* surf_caps) { + // FIXME: figure out why at least 3 swapchain images are required to appease the validation layer + // (on the Linux Intel driver, present-mode-fifo has a surf_caps.minImageCount == 3, while + // on Windows surf_caps.minImageCount == 2, and using this directly causes validation layer + // errors about the present-complete semaphore (to reproduce simply change the '= 3' below to '= 2') + SOKOL_ASSERT(surf_caps); + const uint32_t required_image_count = 3; + uint32_t min_image_count = surf_caps->minImageCount; + if (min_image_count < required_image_count) { + min_image_count = required_image_count; + } + return min_image_count; +} + +_SOKOL_PRIVATE void _sapp_vk_create_swapchain_image_view(uint32_t image_index) { + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(image_index < _sapp.vk.num_swapchain_images); + SOKOL_ASSERT(_sapp.vk.swapchain_images[image_index]); + SOKOL_ASSERT(0 == _sapp.vk.swapchain_views[image_index]); + + _SAPP_STRUCT(VkImageViewCreateInfo, view_create_info); + view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = _sapp.vk.surface_format.format; + view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.layerCount = 1; + view_create_info.image = _sapp.vk.swapchain_images[image_index]; + VkResult res = vkCreateImageView(_sapp.vk.device, &view_create_info, 0, &_sapp.vk.swapchain_views[image_index]); + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_SWAPCHAIN_CREATE_IMAGE_VIEW_FAILED); + } + SOKOL_ASSERT(_sapp.vk.swapchain_views[image_index]); + _sapp_vk_set_object_label(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)_sapp.vk.swapchain_views[image_index], "swapchain_view"); +} + +_SOKOL_PRIVATE void _sapp_vk_destroy_swapchain_image_view(uint32_t image_index) { + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(image_index < _sapp.vk.num_swapchain_images); + SOKOL_ASSERT(_sapp.vk.swapchain_views[image_index]); + vkDestroyImageView(_sapp.vk.device, _sapp.vk.swapchain_views[image_index], 0); + _sapp.vk.swapchain_views[image_index] = 0; +} + +_SOKOL_PRIVATE void _sapp_vk_create_swapchain(bool recreate) { + SOKOL_ASSERT(_sapp.vk.physical_device); + SOKOL_ASSERT(_sapp.vk.surface); + SOKOL_ASSERT(_sapp.vk.device); + if (!recreate) { + SOKOL_ASSERT(0 == _sapp.vk.swapchain); + SOKOL_ASSERT(0 == _sapp.vk.num_swapchain_images); + SOKOL_ASSERT(0 == _sapp.vk.swapchain_images[0]); + SOKOL_ASSERT(0 == _sapp.vk.swapchain_views[0]); + } else { + SOKOL_ASSERT(_sapp.vk.swapchain); + SOKOL_ASSERT(_sapp.vk.num_swapchain_images > 0); + SOKOL_ASSERT(_sapp.vk.swapchain_images[0]); + SOKOL_ASSERT(_sapp.vk.swapchain_views[0]); + } + + VkSwapchainKHR old_swapchain = _sapp.vk.swapchain; + + _SAPP_STRUCT(VkSurfaceCapabilitiesKHR, surf_caps); + VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(_sapp.vk.physical_device, _sapp.vk.surface, &surf_caps); + SOKOL_ASSERT(res == VK_SUCCESS); + const uint32_t fb_width = surf_caps.currentExtent.width; + const uint32_t fb_height = surf_caps.currentExtent.height; + + _sapp.vk.surface_format = _sapp_vk_pick_surface_format(); + const VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; + + _SAPP_STRUCT(VkSwapchainCreateInfoKHR, create_info); + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + create_info.flags = 0; + create_info.surface = _sapp.vk.surface; + create_info.minImageCount = _sapp_vk_swapchain_min_image_count(&surf_caps); + create_info.imageFormat = _sapp.vk.surface_format.format; + create_info.imageColorSpace = _sapp.vk.surface_format.colorSpace; + create_info.imageExtent.width = fb_width; + create_info.imageExtent.height = fb_height; + create_info.imageArrayLayers = 1; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.preTransform = surf_caps.currentTransform; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + create_info.presentMode = present_mode; + create_info.clipped = true; + create_info.oldSwapchain = old_swapchain; + res = vkCreateSwapchainKHR(_sapp.vk.device, &create_info, 0, &_sapp.vk.swapchain); + if (res != VK_SUCCESS) { + _SAPP_PANIC(VULKAN_CREATE_SWAPCHAIN_FAILED); + } + SOKOL_ASSERT(_sapp.vk.swapchain); + + if (old_swapchain) { + // NOTE: destroying the depth- and msaa-surfaces happens + // down in the respective _sapp_vk_swapchain_create_surface() calls! + for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) { + _sapp_vk_destroy_swapchain_image_view(i); + } + vkDestroySwapchainKHR(_sapp.vk.device, old_swapchain, 0); + } + + _sapp.vk.num_swapchain_images = _SAPP_VK_MAX_SWAPCHAIN_IMAGES; + res = vkGetSwapchainImagesKHR(_sapp.vk.device, + _sapp.vk.swapchain, + &_sapp.vk.num_swapchain_images, + _sapp.vk.swapchain_images); + SOKOL_ASSERT(res == VK_SUCCESS); + SOKOL_ASSERT(_sapp.vk.num_swapchain_images >= surf_caps.minImageCount); + + for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) { + _sapp_vk_create_swapchain_image_view(i); + } + + // create depth-stencil buffer + _sapp_vk_swapchain_create_surface(&_sapp.vk.depth, + recreate, + VK_FORMAT_D32_SFLOAT_S8_UINT, + fb_width, + fb_height, + (VkSampleCountFlagBits)_sapp.sample_count, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + "swapchain_depthstencil_image", + "swapchain_depthstencil_view"); + + // optionally create MSAA surface + if (_sapp.sample_count > 1) { + _sapp_vk_swapchain_create_surface(&_sapp.vk.msaa, + recreate, + _sapp.vk.surface_format.format, + fb_width, + fb_height, + (VkSampleCountFlagBits)_sapp.sample_count, + VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + "swapchain_msaa_image", + "swapchain_msaa_view"); + } + + // this is the only place in the Vulkan code path which updates + // _sapp.framebuffer_width/height + _sapp.framebuffer_width = (int)fb_width; + _sapp.framebuffer_height = (int)fb_height; +} + +_SOKOL_PRIVATE void _sapp_vk_destroy_swapchain(void) { + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(_sapp.vk.swapchain); + SOKOL_ASSERT(_sapp.vk.num_swapchain_images > 0); + if (_sapp.vk.msaa.img) { + _sapp_vk_swapchain_destroy_surface(&_sapp.vk.msaa); + } + _sapp_vk_swapchain_destroy_surface(&_sapp.vk.depth); + for (uint32_t i = 0; i < _sapp.vk.num_swapchain_images; i++) { + _sapp_vk_destroy_swapchain_image_view(i); + _sapp.vk.swapchain_images[i] = 0; + } + vkDestroySwapchainKHR(_sapp.vk.device, _sapp.vk.swapchain, 0); + _sapp.vk.swapchain = 0; + _sapp.vk.num_swapchain_images = 0; +} + +#if defined(_SAPP_LINUX) +_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type); +#endif +#if defined(_SAPP_WIN32) +_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type); +#endif + +_SOKOL_PRIVATE void _sapp_vk_recreate_swapchain(void) { + SOKOL_ASSERT(_sapp.vk.device); + vkDeviceWaitIdle(_sapp.vk.device); + int fb_width = _sapp.framebuffer_width; + int fb_height = _sapp.framebuffer_height; + _sapp_vk_create_swapchain(true); + if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { + if (!_sapp.first_frame) { + #if defined(_SAPP_LINUX) + _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); + #endif + #if defined(_SAPP_WIN32) + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); + #endif + } + } +} + +_SOKOL_PRIVATE void _sapp_vk_init(void) { + _sapp_vk_create_instance(); + _sapp_vk_load_instance_ext_funcs(); + _sapp_vk_create_surface(); + _sapp_vk_pick_physical_device(); + _sapp_vk_create_device(); + _sapp_vk_create_swapchain(false); + _sapp_vk_create_sync_objects(); +} + +_SOKOL_PRIVATE void _sapp_vk_discard(void) { + SOKOL_ASSERT(_sapp.vk.device); + vkDeviceWaitIdle(_sapp.vk.device); + _sapp_vk_destroy_sync_objects(); + _sapp_vk_destroy_swapchain(); + _sapp_vk_destroy_device(); + _sapp_vk_destroy_surface(); + _sapp_vk_destroy_instance(); +} + +_SOKOL_PRIVATE void _sapp_vk_swapchain_next(void) { + SOKOL_ASSERT(_sapp.vk.device); + SOKOL_ASSERT(_sapp.vk.swapchain); + VkResult res = vkAcquireNextImageKHR( + _sapp.vk.device, + _sapp.vk.swapchain, + UINT64_MAX, // timeout + _sapp.vk.sync[_sapp.vk.sync_slot].present_complete_sem, // semaphore to signal + 0, // fence to signal + &_sapp.vk.cur_swapchain_image_index); + if ((res != VK_NOT_READY) && (res != VK_SUBOPTIMAL_KHR) && (res != VK_SUCCESS) && (res != VK_TIMEOUT)) { + _SAPP_WARN(VULKAN_ACQUIRE_NEXT_IMAGE_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_vk_present(void) { + SOKOL_ASSERT(_sapp.vk.queue); + _SAPP_STRUCT(VkPresentInfoKHR, present_info); + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + // NOTE: using the current swapchain image index here instead of `sync_slot` is *NOT* a bug! The render_finished_semaphore *must* + // be associated with the current swapchain image in case the swapchain implementation doesn't return swapchain images in order + present_info.pWaitSemaphores = &_sapp.vk.sync[_sapp.vk.cur_swapchain_image_index].render_finished_sem; + present_info.swapchainCount = 1; + present_info.pSwapchains = &_sapp.vk.swapchain; + present_info.pImageIndices = &_sapp.vk.cur_swapchain_image_index; + VkResult res = vkQueuePresentKHR(_sapp.vk.queue, &present_info); + if ((res == VK_ERROR_OUT_OF_DATE_KHR) || (res == VK_SUBOPTIMAL_KHR)) { + _sapp_vk_recreate_swapchain(); + } else if (res != VK_SUCCESS) { + _SAPP_WARN(VULKAN_QUEUE_PRESENT_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_vk_frame(void) { + _sapp_frame(); + _sapp_vk_present(); + _sapp.vk.sync_slot = (_sapp.vk.sync_slot + 1) % _sapp.vk.num_swapchain_images; +} + +#endif // SOKOL_VULKAN + +// █████ ██████ ██████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██ █████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ +// +// >>apple +#if defined(_SAPP_APPLE) + +#if __has_feature(objc_arc) +#define _SAPP_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +// ███ ███ █████ ██████ ██████ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ███████ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>macos +#if defined(_SAPP_MACOS) + +NSInteger _sapp_macos_max_fps(void) { + NSInteger max_fps = 60; + #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) + if (@available(macOS 12.0, *)) { + max_fps = [NSScreen.mainScreen maximumFramesPerSecond]; + } + #endif + return max_fps; +} + +#if defined(SOKOL_METAL) +_SOKOL_PRIVATE void _sapp_macos_mtl_init(void) { + NSInteger max_fps = _sapp_macos_max_fps(); + // NOTE: when eventually switching to CAMetalLayer, use the specialized + // CAMetalDisplayLink instead of CADisplayLink! + _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.macos.view = [[_sapp_macos_view alloc] init]; + [_sapp.macos.view updateTrackingAreas]; + _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; + _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count; + _sapp.macos.view.autoResizeDrawable = false; + _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; +} + +_SOKOL_PRIVATE void _sapp_macos_mtl_discard_state(void) { + _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); +} + +_SOKOL_PRIVATE bool _sapp_macos_mtl_update_framebuffer_dimensions(NSRect view_bounds) { + _sapp.framebuffer_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale); + const CGSize cur_fb_size = _sapp.macos.view.drawableSize; + int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width); + int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height); + bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.view.drawableSize = drawable_size; + } + return dim_changed; +} +#endif + +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sapp_macos_gl_init(NSRect window_rect) { + NSOpenGLPixelFormatAttribute attrs[32]; + int i = 0; + attrs[i++] = NSOpenGLPFAAccelerated; + attrs[i++] = NSOpenGLPFADoubleBuffer; + 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; + attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; + if (_sapp.sample_count > 1) { + attrs[i++] = NSOpenGLPFAMultisample; + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; + attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count; + } else { + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; + } + attrs[i++] = 0; + NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + SOKOL_ASSERT(glpixelformat_obj != nil); + + _sapp.macos.view = [[_sapp_macos_view alloc] + initWithFrame:window_rect + pixelFormat:glpixelformat_obj]; + _SAPP_OBJC_RELEASE(glpixelformat_obj); + [_sapp.macos.view updateTrackingAreas]; + if (_sapp.desc.high_dpi) { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; + } else { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; + } + + NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 + target:_sapp.macos.view + selector:@selector(timerFired:) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; + timer_obj = nil; +} + +_SOKOL_PRIVATE void _sapp_macos_gl_discard_state(void) { + // nothing to do here +} + +_SOKOL_PRIVATE bool _sapp_macos_gl_update_framebuffer_dimensions(NSRect view_bounds) { + const int cur_fb_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale); + const int cur_fb_height = _sapp_roundf_gzero(view_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; + return dim_changed; +} +#endif + +#if defined(SOKOL_WGPU) +_SOKOL_PRIVATE void _sapp_macos_wgpu_init(void) { + NSInteger max_fps = _sapp_macos_max_fps(); + _sapp.macos.wgpu.mtl_layer = [CAMetalLayer layer]; + _sapp.macos.wgpu.mtl_layer.magnificationFilter = kCAFilterNearest; + _sapp.macos.wgpu.mtl_layer.opaque = true; + // NOTE: might experiment with this, valid values are 2 or 3 (default: 3), I don't see any difference tbh + // _sapp.macos.wgpu.mtl_layer.maximumDrawableCount = 2; + _sapp.macos.view = [[_sapp_macos_view alloc] init]; + [_sapp.macos.view updateTrackingAreas]; + _sapp.macos.view.wantsLayer = YES; + _sapp.macos.view.layer = _sapp.macos.wgpu.mtl_layer; + _sapp.macos.wgpu.display_link = [_sapp.macos.view displayLinkWithTarget:_sapp.macos.view selector:@selector(displayLinkFired:)]; + float preferred_fps = max_fps / _sapp.swap_interval; + CAFrameRateRange frame_rate_range = { preferred_fps, preferred_fps, preferred_fps }; + _sapp.macos.wgpu.display_link.preferredFrameRateRange = frame_rate_range; + [_sapp.macos.wgpu.display_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + _sapp_wgpu_init(); +} + +_SOKOL_PRIVATE void _sapp_macos_wgpu_discard_state(void) { + _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.display_link); + _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.mtl_layer); + _sapp_wgpu_discard(); +} + +_SOKOL_PRIVATE bool _sapp_macos_wgpu_update_framebuffer_dimensions(NSRect view_bounds) { + _sapp.framebuffer_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale); + const CGSize cur_fb_size = _sapp.macos.wgpu.mtl_layer.drawableSize; + int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width); + int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height); + bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.wgpu.mtl_layer.drawableSize = drawable_size; + _sapp_wgpu_swapchain_size_changed(); + } + return dim_changed; +} +#endif + +_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { + _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; + _sapp.keycodes[0x12] = SAPP_KEYCODE_1; + _sapp.keycodes[0x13] = SAPP_KEYCODE_2; + _sapp.keycodes[0x14] = SAPP_KEYCODE_3; + _sapp.keycodes[0x15] = SAPP_KEYCODE_4; + _sapp.keycodes[0x17] = SAPP_KEYCODE_5; + _sapp.keycodes[0x16] = SAPP_KEYCODE_6; + _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; + _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; + _sapp.keycodes[0x19] = SAPP_KEYCODE_9; + _sapp.keycodes[0x00] = SAPP_KEYCODE_A; + _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; + _sapp.keycodes[0x08] = SAPP_KEYCODE_C; + _sapp.keycodes[0x02] = SAPP_KEYCODE_D; + _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; + _sapp.keycodes[0x03] = SAPP_KEYCODE_F; + _sapp.keycodes[0x05] = SAPP_KEYCODE_G; + _sapp.keycodes[0x04] = SAPP_KEYCODE_H; + _sapp.keycodes[0x22] = SAPP_KEYCODE_I; + _sapp.keycodes[0x26] = SAPP_KEYCODE_J; + _sapp.keycodes[0x28] = SAPP_KEYCODE_K; + _sapp.keycodes[0x25] = SAPP_KEYCODE_L; + _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; + _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; + _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; + _sapp.keycodes[0x23] = SAPP_KEYCODE_P; + _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01] = SAPP_KEYCODE_S; + _sapp.keycodes[0x11] = SAPP_KEYCODE_T; + _sapp.keycodes[0x20] = SAPP_KEYCODE_U; + _sapp.keycodes[0x09] = SAPP_KEYCODE_V; + _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; + _sapp.keycodes[0x07] = SAPP_KEYCODE_X; + _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; + _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x77] = SAPP_KEYCODE_END; + _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; + _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; +} + +_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); + _SAPP_OBJC_RELEASE(_sapp.macos.view); + #if defined(SOKOL_METAL) + _sapp_macos_mtl_discard_state(); + #elif defined(SOKOL_GLCORE) + _sapp_macos_gl_discard_state(); + #elif defined(SOKOL_WGPU) + _sapp_macos_wgpu_discard_state(); + #endif + _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) { + for (size_t i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { + _sapp.macos.standard_cursors[i] = nil; + _sapp.macos.custom_cursors[i] = nil; + } + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_ARROW] = [NSCursor arrowCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_IBEAM] = [NSCursor IBeamCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_CROSSHAIR] = [NSCursor crosshairCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_POINTING_HAND] = [NSCursor pointingHandCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_EW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_NESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; + _sapp.macos.standard_cursors[SAPP_MOUSECURSOR_RESIZE_ALL] = [NSCursor closedHandCursor]; + _sapp.macos.standard_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 +} + +/* MacOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_macos_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) { + const NSEventModifierFlags f = (ev == nil) ? NSEvent.modifierFlags : ev.modifierFlags; + const NSUInteger b = NSEvent.pressedMouseButtons; + uint32_t m = 0; + if (f & NSEventModifierFlagShift) { + m |= SAPP_MODIFIER_SHIFT; + } + if (f & NSEventModifierFlagControl) { + m |= SAPP_MODIFIER_CTRL; + } + if (f & NSEventModifierFlagOption) { + m |= SAPP_MODIFIER_ALT; + } + if (f & NSEventModifierFlagCommand) { + m |= SAPP_MODIFIER_SUPER; + } + if (0 != (b & (1<<0))) { + m |= SAPP_MODIFIER_LMB; + } + if (0 != (b & (1<<1))) { + m |= SAPP_MODIFIER_RMB; + } + if (0 != (b & (1<<2))) { + m |= SAPP_MODIFIER_MMB; + } + return m; +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +// called in applicationDidFinishedLaunching when no window size was provided +_SOKOL_PRIVATE void _sapp_macos_init_default_dimensions(void) { + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = NSScreen.mainScreen.backingScaleFactor; + } else { + _sapp.dpi_scale = 1.0f; + } + NSRect screen_rect = NSScreen.mainScreen.frame; + // use 4/5 of screen size as default size + const float default_widthf = (screen_rect.size.width * 4.0f) / 5.0f; + const float default_heightf = (screen_rect.size.height * 4.0f) / 5.0f; + if (_sapp.window_width == 0) { + _sapp.window_width = _sapp_roundf_gzero(default_widthf); + } + if (_sapp.window_height == 0) { + _sapp.window_height = _sapp_roundf_gzero(default_heightf); + } + _sapp.framebuffer_width = _sapp_roundf_gzero(default_widthf * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(default_heightf * _sapp.dpi_scale); +} + +/* NOTE: unlike the iOS version of this function, the macOS version + can dynamically update the DPI scaling factor when a window is moved + between HighDPI / LowDPI screens. +*/ +_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = [_sapp.macos.window screen].backingScaleFactor; + } else { + _sapp.dpi_scale = 1.0f; + } + // NOTE: needed because we set layerContentsPlacement to a non-scaling value in windowWillStartLiveResize. + _sapp.macos.view.layer.contentsScale = _sapp.dpi_scale; + const NSRect bounds = [_sapp.macos.view bounds]; + _sapp.window_width = _sapp_roundf_gzero(bounds.size.width); + _sapp.window_height = _sapp_roundf_gzero(bounds.size.height); + #if defined(SOKOL_METAL) + bool dim_changed = _sapp_macos_mtl_update_framebuffer_dimensions(bounds); + #elif defined(SOKOL_GLCORE) + bool dim_changed = _sapp_macos_gl_update_framebuffer_dimensions(bounds); + #elif defined(SOKOL_WGPU) + bool dim_changed = _sapp_macos_wgpu_update_framebuffer_dimensions(bounds); + #endif + if (dim_changed && !_sapp.first_frame) { + _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); + } +} + +_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { + /* NOTE: the _sapp.fullscreen flag is also notified by the + windowDidEnterFullscreen / windowDidExitFullscreen + event handlers + */ + _sapp.fullscreen = !_sapp.fullscreen; + [_sapp.macos.window toggleFullScreen:nil]; +} + +_SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { + @autoreleasepool { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; + [pasteboard setString:@(str) forType:NSPasteboardTypeString]; + } +} + +_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + @autoreleasepool { + _sapp.clipboard.buffer[0] = 0; + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { + return _sapp.clipboard.buffer; + } + NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; + if (!str) { + return _sapp.clipboard.buffer; + } + _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); + } + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { + [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nspoint(NSPoint mouse_pos, bool clear_dxdy) { + if (!_sapp.mouse.locked) { + float new_x = mouse_pos.x * _sapp.dpi_scale; + float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } else 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; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nsevent(NSEvent* event, bool clear_dxdy) { + _sapp_macos_mouse_update_from_nspoint(event.locationInWindow, clear_dxdy); +} + +_SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { + /* NOTE: this function is only called when the mouse visibility actually changes */ + if (visible) { + CGDisplayShowCursor(kCGDirectMainDisplay); + } else { + CGDisplayHideCursor(kCGDirectMainDisplay); + } +} + +_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + /* + NOTE that this code doesn't warp the mouse cursor to the window + center as everybody else does it. This lead to a spike in the + *second* mouse-moved event after the warp happened. The + mouse centering doesn't seem to be required (mouse-moved events + are reported correctly even when the cursor is at an edge of the screen). + + NOTE also that the hide/show of the mouse cursor should properly + stack with calls to sapp_show_mouse() + */ + if (_sapp.mouse.locked) { + CGAssociateMouseAndMouseCursorPosition(NO); + [NSCursor hide]; + } else { + [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 + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + NSCursor* ns_cursor = 0; + if (_sapp.custom_cursor_bound[cursor]) { + SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor]); + ns_cursor = _sapp.macos.custom_cursors[cursor]; + } else if (_sapp.macos.standard_cursors[cursor]) { + ns_cursor = _sapp.macos.standard_cursors[cursor]; + } else { + ns_cursor = [NSCursor arrowCursor]; + } + [ns_cursor set]; +} + +_SOKOL_PRIVATE bool _sapp_macos_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor] == nil); + + // NOTE: see glfw for reference https://github.com/glfw/glfw/blob/ac10768495837eb98da27d01fe706073d6d251c2/src/cocoa_window.m#L1712 + NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:desc->width + pixelsHigh:desc->height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bitmapFormat:NSBitmapFormatAlphaNonpremultiplied + bytesPerRow:desc->width * 4 + bitsPerPixel:32]; + if (rep != nil) { + memcpy([rep bitmapData], desc->pixels.ptr, (size_t) (desc->width * desc->height * 4)); + + NSImage* native = [[NSImage alloc] initWithSize:NSMakeSize(desc->width, desc->height)]; + SOKOL_ASSERT(native); + [native addRepresentation:rep]; + + _sapp.macos.custom_cursors[cursor] = [[NSCursor alloc] + initWithImage:native + hotSpot:NSMakePoint(desc->cursor_hotspot_x, desc->cursor_hotspot_y)]; + SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor] != nil); + + _SAPP_OBJC_RELEASE(native); + _SAPP_OBJC_RELEASE(rep); + return true; + } + return false; +} + +_SOKOL_PRIVATE void _sapp_macos_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(_sapp.macos.custom_cursors[cursor] != nil); + _SAPP_OBJC_RELEASE(_sapp.macos.custom_cursors[cursor]); +} + +_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; + const int wanted_height = (int) dock_tile.size.height; + const int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, wanted_width, wanted_height); + const sapp_image_desc* img_desc = &icon_desc->images[img_index]; + + CGColorSpaceRef cg_color_space = CGColorSpaceCreateDeviceRGB(); + CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)img_desc->pixels.ptr, (CFIndex)img_desc->pixels.size); + CGDataProviderRef cg_data_provider = CGDataProviderCreateWithCFData(cf_data); + CGImageRef cg_img = CGImageCreate( + (size_t)img_desc->width, // width + (size_t)img_desc->height, // height + 8, // bitsPerComponent + 32, // bitsPerPixel + (size_t)img_desc->width * 4,// bytesPerRow + cg_color_space, // space + kCGImageAlphaLast | kCGImageByteOrderDefault, // bitmapInfo + cg_data_provider, // provider + NULL, // decode + false, // shouldInterpolate + kCGRenderingIntentDefault); + CFRelease(cf_data); + CGDataProviderRelease(cg_data_provider); + CGColorSpaceRelease(cg_color_space); + + NSImage* ns_image = [[NSImage alloc] initWithCGImage:cg_img size:dock_tile.size]; + dock_tile.contentView = [NSImageView imageViewWithImage:ns_image]; + [dock_tile display]; + _SAPP_OBJC_RELEASE(ns_image); + CGImageRelease(cg_img); +} + +_SOKOL_PRIVATE void _sapp_macos_frame(void) { + // NOTE: DO NOT call _sapp_macos_update_dimensions() function from within the + // frame callback (at least when called from MTKView's drawRect function). + // This will trigger a chicken-egg situation that triggers a + // Metal validation layer error about different render target sizes. + _sapp_timing_measure(&_sapp.timing); + #if defined(_SAPP_ANY_GL) + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + #endif + @autoreleasepool { + #if defined(SOKOL_WGPU) + _sapp_wgpu_frame(); + #else + _sapp_frame(); + #endif + } + #if defined(_SAPP_ANY_GL) + [[_sapp.macos.view openGLContext] flushBuffer]; + #endif + if (_sapp.quit_requested || _sapp.quit_ordered) { + [_sapp.macos.window performClose:nil]; + } +} + +@implementation _sapp_macos_app_delegate +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { + _SOKOL_UNUSED(aNotification); + _sapp_macos_init_cursors(); + if ((_sapp.window_width == 0) || (_sapp.window_height == 0)) { + _sapp_macos_init_default_dimensions(); + } + const NSUInteger style = + NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable | + NSWindowStyleMaskResizable; + NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); + _sapp.macos.window = [[_sapp_macos_window alloc] + initWithContentRect:window_rect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO]; + _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate + _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; + _sapp.macos.window.acceptsMouseMovedEvents = YES; + _sapp.macos.window.restorable = YES; + + _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; + _sapp.macos.window.delegate = _sapp.macos.win_dlg; + #if defined(SOKOL_METAL) + _sapp_macos_mtl_init(); + #elif defined(SOKOL_GLCORE) + _sapp_macos_gl_init(window_rect); + #elif defined(SOKOL_WGPU) + _sapp_macos_wgpu_init(); + #endif + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + [_sapp.macos.window center]; + _sapp.valid = true; + NSApp.activationPolicy = NSApplicationActivationPolicyRegular; + if (_sapp.fullscreen) { + /* ^^^ on GL, this already toggles a rendered frame, so set the valid flag before */ + [_sapp.macos.window toggleFullScreen:self]; + } + [NSApp activateIgnoringOtherApps:YES]; + [_sapp.macos.window makeKeyAndOrderFront:nil]; + _sapp_macos_update_dimensions(); + [NSEvent setMouseCoalescingEnabled:NO]; + + // workaround for window not being focused during a long init callback + // for details see: https://github.com/floooh/sokol/pull/982 + // also see: https://gitlab.gnome.org/GNOME/gtk/-/issues/2342 + NSEvent *focusevent = [NSEvent otherEventWithType:NSEventTypeAppKitDefined + location:NSZeroPoint + modifierFlags:0x40 + timestamp:0 + windowNumber:0 + context:nil + subtype:NSEventSubtypeApplicationActivated + data1:0 + data2:0]; + [NSApp postEvent:focusevent atStart:YES]; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { + _SOKOL_UNUSED(sender); + return YES; +} + +- (void)applicationWillTerminate:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_call_cleanup(); + _sapp_macos_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_macos_window_delegate +- (BOOL)windowShouldClose:(id)sender { + _SOKOL_UNUSED(sender); + // only give user-code a chance to intervene when sapp_quit() wasn't already called + if (!_sapp.quit_ordered) { + // if window should be closed and event handling is enabled, give user code + // a chance to intervene via sapp_cancel_quit() + _sapp.quit_requested = true; + _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + return YES; + } else { + return NO; + } +} + +- (void)windowWillStartLiveResize:(NSNotification *)notification { + #if defined(SOKOL_METAL) || defined(SOKOL_WGPU) + // Work around the MTKView/CAMetalLayer resizing glitch by "anchoring" the layer to the window corner opposite + // to the currently manipulated corner (or edge). This prevents the content stretching back and + // forth during resizing. This is a workaround for this issue: https://github.com/floooh/sokol/issues/700 + // Can be removed if/when migrating to CAMetalLayer: https://github.com/floooh/sokol/issues/727 + bool resizing_from_left = _sapp.mouse.x < _sapp.window_width/2; + bool resizing_from_top = _sapp.mouse.y < _sapp.window_height/2; + NSViewLayerContentsPlacement placement; + if (resizing_from_left) { + placement = resizing_from_top ? NSViewLayerContentsPlacementBottomRight : NSViewLayerContentsPlacementTopRight; + } else { + placement = resizing_from_top ? NSViewLayerContentsPlacementBottomLeft : NSViewLayerContentsPlacementTopLeft; + } + _sapp.macos.view.layerContentsPlacement = placement; + #endif +} + +- (void)windowDidResize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_update_dimensions(); +} + +- (void)windowDidChangeScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_timing_reset(&_sapp.timing); + _sapp_macos_update_dimensions(); +} + +- (void)windowDidMiniaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); +} + +- (void)windowDidDeminiaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); +} + +- (void)windowDidBecomeKey:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_FOCUSED); +} + +- (void)windowDidResignKey:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_UNFOCUSED); +} + +- (void)windowDidEnterFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = true; +} + +- (void)windowDidExitFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = false; +} +@end + +@implementation _sapp_macos_window +- (instancetype)initWithContentRect:(NSRect)contentRect + styleMask:(NSWindowStyleMask)style + backing:(NSBackingStoreType)backingStoreType + defer:(BOOL)flag { + if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; + #endif + } + return self; +} + +- (NSDragOperation)draggingEntered:(id)sender { + return NSDragOperationCopy; +} + +- (NSDragOperation)draggingUpdated:(id)sender { + return NSDragOperationCopy; +} + +- (BOOL)performDragOperation:(id)sender { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + 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 : (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), (size_t)_sapp.drop.max_path_length)) { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + drop_failed = true; + break; + } + } + if (!drop_failed) { + if (_sapp_events_enabled()) { + _sapp_macos_mouse_update_from_nspoint(sender.draggingLocation, true); + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_macos_mods(nil); + _sapp_call_event(&_sapp.event); + } + } else { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + } + return YES; + } + #endif + return NO; +} +@end + +@implementation _sapp_macos_view +#if defined(SOKOL_GLCORE) +- (void)timerFired:(id)sender { + _SOKOL_UNUSED(sender); + [self setNeedsDisplay:YES]; +} +- (void)prepareOpenGL { + [super prepareOpenGL]; + GLint swapInt = 1; + NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; + [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; + [ctx makeCurrentContext]; +} +#endif + +#if defined(SOKOL_WGPU) +- (void)displayLinkFired:(id)sender { + _SOKOL_UNUSED(sender); + _sapp_macos_frame(); +} +#else +- (void)drawRect:(NSRect)rect { + _SOKOL_UNUSED(rect); + _sapp_macos_frame(); +} +#endif + +- (BOOL)isOpaque { + return YES; +} +- (BOOL)canBecomeKeyView { + return YES; +} +- (BOOL)acceptsFirstResponder { + return YES; +} +- (void)updateTrackingAreas { + if (_sapp.macos.tracking_area != nil) { + [self removeTrackingArea:_sapp.macos.tracking_area]; + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + } + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; + [self addTrackingArea:_sapp.macos.tracking_area]; + [super updateTrackingAreas]; +} + +// helper function to make GL context active +static void _sapp_gl_make_current(void) { + #if defined(SOKOL_GLCORE) + [[_sapp.macos.view openGLContext] makeCurrentContext]; + #endif +} + +- (void)mouseEntered:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); + /* don't send mouse enter/leave while dragging (so that it behaves the same as + on Windows while SetCapture is active + */ + if (0 == _sapp.macos.mouse_buttons) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); + } +} +- (void)mouseExited:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); + 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_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, false); + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); + _sapp.macos.mouse_buttons |= (1< 0.0f) || (_sapp_absf(dy) > 0.0f)) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_macos_mods(event); + _sapp.event.scroll_x = dx; + _sapp.event.scroll_y = dy; + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyDown:(NSEvent*)event { + if (_sapp_events_enabled()) { + _sapp_gl_make_current(); + const uint32_t mods = _sapp_macos_mods(event); + const sapp_keycode key_code = _sapp_translate_key(event.keyCode); + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); + const NSString* chars = event.characters; + const NSUInteger len = chars.length; + if (len > 0) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = mods; + for (NSUInteger i = 0; i < len; i++) { + const unichar codepoint = [chars characterAtIndex:i]; + if ((codepoint & 0xFF00) == 0xF700) { + continue; + } + _sapp.event.char_code = codepoint; + _sapp.event.key_repeat = event.isARepeat; + _sapp_call_event(&_sapp.event); + } + } + /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ + if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +- (BOOL)performKeyEquivalent:(NSEvent*)event { + // fixes Ctrl-Tab keydown not triggering a keyDown event + // + // NOTE: it seems that Ctrl-F1 cannot be intercepted the same way, but since + // this enabled critical accessibility features that's probably a good thing. + switch (_sapp_translate_key(event.keyCode)) { + case SAPP_KEYCODE_TAB: + [_sapp.macos.view keyDown:event]; + return YES; + default: + return NO; + } +} + +- (void)keyUp:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, + _sapp_translate_key(event.keyCode), + event.isARepeat, + _sapp_macos_mods(event)); +} + +- (void)flagsChanged:(NSEvent*)event { + const uint32_t old_f = _sapp.macos.flags_changed_store; + 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; + if ((new_f ^ old_f) & NSEventModifierFlagShift) { + key_code = SAPP_KEYCODE_LEFT_SHIFT; + down = 0 != (new_f & NSEventModifierFlagShift); + } + if ((new_f ^ old_f) & NSEventModifierFlagControl) { + key_code = SAPP_KEYCODE_LEFT_CONTROL; + down = 0 != (new_f & NSEventModifierFlagControl); + } + if ((new_f ^ old_f) & NSEventModifierFlagOption) { + key_code = SAPP_KEYCODE_LEFT_ALT; + down = 0 != (new_f & NSEventModifierFlagOption); + } + if ((new_f ^ old_f) & NSEventModifierFlagCommand) { + key_code = SAPP_KEYCODE_LEFT_SUPER; + down = 0 != (new_f & NSEventModifierFlagCommand); + } + if (key_code != SAPP_KEYCODE_INVALID) { + _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, + key_code, + false, + _sapp_macos_mods(event)); + } +} +- (void)cursorUpdate:(NSEvent *)event { + _sapp_macos_update_cursor(_sapp.mouse.current_cursor, _sapp.mouse.shown); +} +@end + +#endif // macOS + +// ██ ██████ ███████ +// ██ ██ ██ ██ +// ██ ██ ██ ███████ +// ██ ██ ██ ██ +// ██ ██████ ███████ +// +// >>ios +#if defined(_SAPP_IOS) + +#if defined(SOKOL_METAL) +_SOKOL_PRIVATE void _sapp_ios_mtl_init(void) { + const NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond; + _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.ios.view = [[_sapp_ios_view alloc] init]; + _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; + _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count; + /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor + and automatically renders at Retina resolution. We'll disable + autoResize and instead do the resizing in _sapp_ios_update_dimensions() + */ + _sapp.ios.view.autoResizeDrawable = false; + _sapp.ios.view.userInteractionEnabled = YES; +#if !defined(_SAPP_TVOS) + _sapp.ios.view.multipleTouchEnabled = YES; +#endif + _sapp.ios.view_ctrl = [[UIViewController alloc] init]; + _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; +} + +_SOKOL_PRIVATE void _sapp_ios_mtl_discard_state(void) { + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device); +} + +_SOKOL_PRIVATE bool _sapp_ios_mtl_update_framebuffer_dimensions(CGRect screen_rect) { + // get current screen size and if it changed, update the MTKView drawable size + const int screen_width = _sapp_roundf_gzero(screen_rect.size.width * _sapp.dpi_scale); + const int screen_height = _sapp_roundf_gzero(screen_rect.size.height * _sapp.dpi_scale); + const CGSize view_size = _sapp.ios.view.drawableSize; + const int view_width = _sapp_roundf_gzero(view_size.width); + const int view_height = _sapp_roundf_gzero(view_size.height); + const bool needs_update = (screen_width != view_width) || (screen_height != view_height); + if (needs_update) { + const CGSize drawable_size = { (CGFloat) screen_width, (CGFloat) screen_height }; + _sapp.ios.view.drawableSize = drawable_size; + } + // now separately, get the current drawable's size + const int cur_fb_width = _sapp_roundf_gzero(_sapp.ios.view.currentDrawable.texture.width); + const int cur_fb_height = _sapp_roundf_gzero(_sapp.ios.view.currentDrawable.texture.height); + const bool dim_changed = (cur_fb_width != _sapp.framebuffer_width) || (cur_fb_height != _sapp.framebuffer_height); + _sapp.framebuffer_width = cur_fb_width; + _sapp.framebuffer_height = cur_fb_height; + return dim_changed; +} +#endif + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE void _sapp_ios_gles3_init(CGRect screen_rect) { + const NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond; + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; + _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; + _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone; + _sapp.ios.view.drawableMultisample = msaa; + _sapp.ios.view.context = _sapp.ios.eagl_ctx; + _sapp.ios.view.enableSetNeedsDisplay = NO; + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + // on GLKView, contentScaleFactor appears to work just fine! + if (_sapp.desc.high_dpi) { + _sapp.ios.view.contentScaleFactor = _sapp.dpi_scale; + } else { + _sapp.ios.view.contentScaleFactor = 1.0; + } + _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.view_ctrl.preferredFramesPerSecond = max_fps / _sapp.swap_interval; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; +} + +_SOKOL_PRIVATE void _sapp_ios_gles3_discard_state(void) { + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx); +} + +_SOKOL_PRIVATE bool _sapp_ios_gles3_update_framebuffer_dimensions(CGRect screen_rect) { + _sapp.framebuffer_width = _sapp_roundf_gzero(screen_rect.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(screen_rect.size.height * _sapp.dpi_scale); + int cur_fb_width = _sapp_roundf_gzero(_sapp.ios.view.drawableWidth); + int cur_fb_height = _sapp_roundf_gzero(_sapp.ios.view.drawableHeight); + return (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); +} +#endif + +_SOKOL_PRIVATE void _sapp_ios_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg); + _SAPP_OBJC_RELEASE(_sapp.ios.textfield); + #if defined(SOKOL_METAL) + _sapp_ios_mtl_discard_state(); + #else + _sapp_ios_gles3_discard_state(); + #endif + _SAPP_OBJC_RELEASE(_sapp.ios.view); + _SAPP_OBJC_RELEASE(_sapp.ios.window); +} + +_SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { + _sapp_init_state(desc); + static int argc = 1; + static char* argv[] = { (char*)"sokol_app" }; + UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); +} + +/* iOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_ios_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_tvos_press_event(sapp_event_type type, NSSet* presses) { + if (_sapp_events_enabled()) { + for (UIPress *press in presses) { + sapp_keycode key = SAPP_KEYCODE_INVALID; + switch (press.type) { + case UIPressTypeUpArrow: key = SAPP_KEYCODE_UP; break; + case UIPressTypeDownArrow: key = SAPP_KEYCODE_DOWN; break; + case UIPressTypeLeftArrow: key = SAPP_KEYCODE_LEFT; break; + case UIPressTypeRightArrow: key = SAPP_KEYCODE_RIGHT; break; + case UIPressTypeSelect: key = SAPP_KEYCODE_ENTER; break; + case UIPressTypeMenu: key = SAPP_KEYCODE_MENU; break; + case UIPressTypePlayPause: key = SAPP_KEYCODE_PAUSE; break; + default: break; + } + if (key != SAPP_KEYCODE_INVALID) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = false; + _sapp.event.modifiers = 0; + _sapp_call_event(&_sapp.event); + } + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + NSEnumerator* enumerator = event.allTouches.objectEnumerator; + UITouch* ios_touch; + while ((ios_touch = [enumerator nextObject])) { + if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { + CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view]; + sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; + cur_point->identifier = (uintptr_t) ios_touch; + cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; + cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; + cur_point->changed = [touches containsObject:ios_touch]; + } + } + if (_sapp.event.num_touches > 0) { + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.window_width = _sapp_roundf_gzero(screen_rect.size.width); + _sapp.window_height = _sapp_roundf_gzero(screen_rect.size.height); + #if defined(SOKOL_METAL) + bool dim_changed = _sapp_ios_mtl_update_framebuffer_dimensions(screen_rect); + #else + bool dim_changed = _sapp_ios_gles3_update_framebuffer_dimensions(screen_rect); + #endif + if (dim_changed && !_sapp.first_frame) { + _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); + } +} + +_SOKOL_PRIVATE void _sapp_ios_frame(void) { + // NOTE: it's not great to call _sapp_ios_update_dimensions() so early in + // the frame, since this calls MTKView.currentDrawable, which should be + // delayed until the latest possible moment (e.g. sapp_get_swapchain()). + // MTKView is on the chopping block anyway though, so don't bother too much + // getting it right until MTKView is replaced with CAMetalLayer. + _sapp_ios_update_dimensions(); + _sapp_frame(); +} + +_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { + /* if not happened yet, create an invisible text field */ + if (nil == _sapp.ios.textfield) { + _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init]; + _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; + _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault; + _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault; + _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone; + _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo; + _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo; + _sapp.ios.textfield.hidden = YES; + _sapp.ios.textfield.text = @"x"; + _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg; + [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield]; + +#if !defined(_SAPP_TVOS) + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWasShown:) + name:UIKeyboardDidShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWillBeHidden:) + name:UIKeyboardWillHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardDidChangeFrame:) + name:UIKeyboardDidChangeFrameNotification object:nil]; +#endif + } + if (shown) { + // setting the text field as first responder brings up the onscreen keyboard + [_sapp.ios.textfield becomeFirstResponder]; + } else { + [_sapp.ios.textfield resignFirstResponder]; + } +} + +@implementation _sapp_app_delegate +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; + _sapp.window_width = _sapp_roundf_gzero(screen_rect.size.width); + _sapp.window_height = _sapp_roundf_gzero(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_roundf_gzero(_sapp.window_width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(_sapp.window_height * _sapp.dpi_scale); + #if defined(SOKOL_METAL) + _sapp_ios_mtl_init(); + #else + _sapp_ios_gles3_init(screen_rect); + #endif + [_sapp.ios.window makeKeyAndVisible]; + _sapp.valid = true; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + if (!_sapp.ios.suspended) { + _sapp.ios.suspended = true; + _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); + } +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + if (_sapp.ios.suspended) { + _sapp.ios.suspended = false; + _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); + } +} + +/* NOTE: this method will rarely ever be called, iOS application + which are terminated by the user are usually killed via signal 9 + by the operating system. +*/ +- (void)applicationWillTerminate:(UIApplication *)application { + _SOKOL_UNUSED(application); + _sapp_call_cleanup(); + _sapp_ios_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_textfield_dlg +- (void)keyboardWasShown:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = true; + /* query the keyboard's size, and modify the content view's size */ +#if !defined(_SAPP_TVOS) + if (_sapp.desc.ios.keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +#endif +} +- (void)keyboardWillBeHidden:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = false; + if (_sapp.desc.ios.keyboard_resizes_canvas) { + _sapp.ios.view.frame = UIScreen.mainScreen.bounds; + } +} +- (void)keyboardDidChangeFrame:(NSNotification*)notif { + /* this is for the case when the screen rotation changes while the keyboard is open */ +#if !defined(_SAPP_TVOS) + if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios.keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +#endif +} +- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { + if (_sapp_events_enabled()) { + const NSUInteger len = string.length; + if (len > 0) { + for (NSUInteger i = 0; i < len; i++) { + unichar c = [string characterAtIndex:i]; + if (c >= 32) { + /* ignore surrogates for now */ + if ((c < 0xD800) || (c > 0xDFFF)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = c; + _sapp_call_event(&_sapp.event); + } + } + if (c <= 32) { + sapp_keycode k = SAPP_KEYCODE_INVALID; + switch (c) { + case 10: k = SAPP_KEYCODE_ENTER; break; + case 32: k = SAPP_KEYCODE_SPACE; break; + default: break; + } + if (k != SAPP_KEYCODE_INVALID) { + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + } + } + } + } else { + // this was a backspace + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + } + } + return NO; +} +@end + +@implementation _sapp_ios_view +- (void)drawRect:(CGRect)rect { + _SOKOL_UNUSED(rect); + #if defined(_SAPP_ANY_GL) + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + #endif + _sapp_timing_measure(&_sapp.timing); + @autoreleasepool { + _sapp_ios_frame(); + } +} +- (BOOL)isOpaque { + return YES; +} +- (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)event { + _sapp_tvos_press_event(SAPP_EVENTTYPE_KEY_DOWN, presses); +} +- (void)pressesChanged:(NSSet *)presses withEvent:(UIPressesEvent *)event { +} +- (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)event { + _sapp_tvos_press_event(SAPP_EVENTTYPE_KEY_UP, presses); +} +- (void)pressesCancelled:(NSSet *)presses withEvent:(UIPressesEvent *)event { + _sapp_tvos_press_event(SAPP_EVENTTYPE_KEY_UP, presses); +} +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); +} +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); +} +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); +} +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); +} +@end +#endif /* TARGET_OS_IPHONE */ + +#endif /* _SAPP_APPLE */ + +// ███████ ███ ███ ███████ ██████ ██████ ██ ██████ ████████ ███████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// █████ ██ ████ ██ ███████ ██ ██████ ██ ██████ ██ █████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ███████ ██ ████ +// +// >>emscripten +#if defined(_SAPP_EMSCRIPTEN) + +#if defined(EM_JS_DEPS) +EM_JS_DEPS(sokol_app, "$withStackSave,$stringToUTF8OnStack,$findCanvasEventTarget") +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { + if (_sapp.clipboard.enabled) { + _sapp_strcpy(str, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ +EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { + return _sapp.html5_ask_leave_site ? 1 : 0; +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) { + if (!_sapp.drop.enabled) { + return; + } + if (num < 0) { + num = 0; + } + if (num > _sapp.drop.max_files) { + num = _sapp.drop.max_files; + } + _sapp.drop.num_files = num; + _sapp_clear_drop_buffer(); +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { + /* NOTE: name is only the filename part, not a path */ + if (!_sapp.drop.enabled) { + return; + } + if (0 == name) { + return; + } + SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files); + if ((i < 0) || (i >= _sapp.drop.num_files)) { + return; + } + if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), (size_t)_sapp.drop.max_path_length)) { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + _sapp.drop.num_files = 0; + } +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y, int mods) { + if (!_sapp.drop.enabled) { + return; + } + if (0 == _sapp.drop.num_files) { + /* there was an error copying the filenames */ + _sapp_clear_drop_buffer(); + return; + + } + if (_sapp_events_enabled()) { + _sapp.mouse.x = (float)x * _sapp.dpi_scale; + _sapp.mouse.y = (float)y * _sapp.dpi_scale; + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + // see sapp_js_add_dragndrop_listeners for mods constants + if (mods & 1) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } + if (mods & 2) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } + if (mods & 4) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } + if (mods & 8) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } + _sapp_call_event(&_sapp.event); + } +} + +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_STRUCT(sapp_html5_fetch_response, response); + response.succeeded = (0 != success); + response.error_code = (sapp_html5_fetch_error) error_code; + response.file_index = index; + 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); +} + +// will be called after the request/exitFullscreen promise rejects +// to restore the _sapp.fullscreen flag to the actual fullscreen state +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_set_fullscreen_flag(int f) { + _sapp.fullscreen = (bool)f; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +EM_JS(void, sapp_js_add_beforeunload_listener, (void), { + Module.sokol_beforeunload = (event) => { + if (__sapp_html5_get_ask_leave_site() != 0) { + event.preventDefault(); + event.returnValue = ' '; + } + }; + window.addEventListener('beforeunload', Module.sokol_beforeunload); +}) + +EM_JS(void, sapp_js_remove_beforeunload_listener, (void), { + window.removeEventListener('beforeunload', Module.sokol_beforeunload); +}) + +EM_JS(void, sapp_js_add_clipboard_listener, (void), { + Module.sokol_paste = (event) => { + const pasted_str = event.clipboardData.getData('text'); + withStackSave(() => { + const cstr = stringToUTF8OnStack(pasted_str); + __sapp_emsc_onpaste(cstr); + }); + }; + window.addEventListener('paste', Module.sokol_paste); +}) + +EM_JS(void, sapp_js_remove_clipboard_listener, (void), { + window.removeEventListener('paste', Module.sokol_paste); +}) + +EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { + const str = UTF8ToString(c_str); + const ta = document.createElement('textarea'); + ta.setAttribute('autocomplete', 'off'); + ta.setAttribute('autocorrect', 'off'); + ta.setAttribute('autocapitalize', 'off'); + ta.setAttribute('spellcheck', 'false'); + ta.style.left = -100 + 'px'; + ta.style.top = -100 + 'px'; + ta.style.height = 1; + ta.style.width = 1; + ta.value = str; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); +}) + +_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { + sapp_js_write_clipboard(str); +} + +EM_JS(void, sapp_js_add_dragndrop_listeners, (void), { + Module.sokol_drop_files = []; + Module.sokol_dragenter = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_dragleave = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_dragover = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_drop = (event) => { + event.stopPropagation(); + event.preventDefault(); + const files = event.dataTransfer.files; + Module.sokol_dropped_files = files; + __sapp_emsc_begin_drop(files.length); + for (let i = 0; i < files.length; i++) { + withStackSave(() => { + const cstr = stringToUTF8OnStack(files[i].name); + __sapp_emsc_drop(i, cstr); + }); + } + let mods = 0; + if (event.shiftKey) { mods |= 1; } + if (event.ctrlKey) { mods |= 2; } + if (event.altKey) { mods |= 4; } + if (event.metaKey) { mods |= 8; } + // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect + __sapp_emsc_end_drop(event.clientX, event.clientY, mods); + }; + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const canvas = Module.sapp_emsc_target; + canvas.addEventListener('dragenter', Module.sokol_dragenter, false); + canvas.addEventListener('dragleave', Module.sokol_dragleave, false); + canvas.addEventListener('dragover', Module.sokol_dragover, false); + canvas.addEventListener('drop', Module.sokol_drop, false); +}) + +EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const files = Module.sokol_dropped_files; + if ((index < 0) || (index >= files.length)) { + return 0; + } else { + 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), { + 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); + } else { + HEAPU8.set(new Uint8Array(content), buf_ptr); + __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data); + } + }; + reader.onerror = () => { + // SAPP_HTML5_FETCH_ERROR_OTHER + __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data); + }; + \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, (void), { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const canvas = Module.sapp_emsc_target; + canvas.removeEventListener('dragenter', Module.sokol_dragenter); + canvas.removeEventListener('dragleave', Module.sokol_dragleave); + canvas.removeEventListener('dragover', Module.sokol_dragover); + canvas.removeEventListener('drop', Module.sokol_drop); +}) + +EM_JS(void, sapp_js_init, (const char* c_str_target_selector, const char* c_str_document_title), { + if (c_str_document_title !== 0) { + document.title = UTF8ToString(c_str_document_title); + } + const target_selector_str = UTF8ToString(c_str_target_selector); + if (Module['canvas'] !== undefined) { + if (typeof Module['canvas'] === 'object') { + specialHTMLTargets[target_selector_str] = Module['canvas']; + } else { + console.warn("sokol_app.h: Module['canvas'] is set but is not an object"); + } + } + Module.sapp_emsc_target = findCanvasEventTarget(target_selector_str); + if (!Module.sapp_emsc_target) { + console.warn("sokol_app.h: can't find html5_canvas_selector ", target_selector_str); + } + if (!Module.sapp_emsc_target.requestPointerLock) { + console.warn("sokol_app.h: target doesn't support requestPointerLock: ", target_selector_str); + } +}) + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = emsc_event->isActive; + return EM_TRUE; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = false; + _sapp.emsc.mouse_lock_requested = false; + return true; +} + +EM_JS(void, sapp_js_request_pointerlock, (void), { + if (Module.sapp_emsc_target) { + if (Module.sapp_emsc_target.requestPointerLock) { + Module.sapp_emsc_target.requestPointerLock(); + } + } +}) + +EM_JS(void, sapp_js_exit_pointerlock, (void), { + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}) + +_SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) { + if (lock) { + /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */ + _sapp.emsc.mouse_lock_requested = true; + } else { + /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */ + _sapp.emsc.mouse_lock_requested = false; + sapp_js_exit_pointerlock(); + } +} + +/* called from inside event handlers to check if mouse lock had been requested, + and if yes, actually enter mouse lock. +*/ +_SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { + if (_sapp.emsc.mouse_lock_requested) { + _sapp.emsc.mouse_lock_requested = false; + sapp_js_request_pointerlock(); + } +} + +// set mouse cursor type +EM_JS(void, sapp_js_set_cursor, (int cursor_type, int shown, int use_custom_cursor_image), { + if (Module.sapp_emsc_target) { + let cursor; + if (shown === 0) { + cursor = "none"; + } else if (use_custom_cursor_image != 0) { + cursor = Module.__sapp_custom_cursors[cursor_type].css_property; + } 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)); + bool custom_cursor = _sapp.custom_cursor_bound[cursor]; + sapp_js_set_cursor((int)cursor, shown ? 1 : 0, custom_cursor ? 1 : 0); +} + +EM_JS(void, sapp_js_make_custom_mouse_cursor, (int cursor_slot_idx, int width, int height, const void* pixels_ptr, int hotspot_x, int hotspot_y), { + // encode the cursor pixels into a BMP which then is encoded into an 'object url' + const bmp_hdr_size = 14; + const dib_hdr_size = 124; // common values are 56, I saw 124 for the rgba32-1.bmp file of the test suite included in firefox, and 108 from wikipedia example 2 (transparent) + const pixels_size = width * height * 4; + const bmp_size = bmp_hdr_size + dib_hdr_size + pixels_size; + const bmp = new Uint8Array(bmp_size); + let idx = 0; + const w8 = (val) => { + bmp[idx++] = val & 255; + }; + const w16 = (val) => { + bmp[idx++] = val & 255; + bmp[idx++] = (val >> 8) & 255; + }; + const w32 = (val) => { + bmp[idx++] = val & 255; + bmp[idx++] = (val >> 8) & 255; + bmp[idx++] = (val >> 16) & 255; + bmp[idx++] = (val >> 24) & 255; + }; + + // bmp file header + w8(66); // 'B' + w8(77); // 'M' + w32(bmp_size); + w32(0); // reserved + w32(bmp_hdr_size + dib_hdr_size); // offset to pixel data + assert(idx == bmp_hdr_size); + + // DIB header + w32(dib_hdr_size); // header size + w32(width); + w32(height); + w16(1); // planes + w16(32); // bits per pixel + w32(3); // compression method. 3 = BI_BITFIELDS + w32(pixels_size); // image size + w32(2835); // pixel per metre horizontal + w32(2835); // pixel per metre vertical + w32(0); // colors number + w32(0); // important colors + w32(0x000000ff); // red channel bit mask (big endian) + w32(0x0000ff00); // green channel bit mask (big endian) + w32(0x00ff0000); // blue channel bit mask (big endian) + w32(0xff000000); // alpha channel bit mask (big endian) + w8(66); w8(71); w8(82); w8(115); // color space type: 'sRGB' + idx += 64; // color space stuff, unused for 'Win ' or 'sRGB' + assert(idx == bmp_hdr_size + dib_hdr_size); + const row_pitch = width * 4; + for (let y = 0; y < height; y++) { + const src_idx = pixels_ptr + y * row_pitch; + const dst_idx = idx + (height - y - 1) * row_pitch; + const row_data = HEAPU8.slice(src_idx, src_idx + row_pitch); + bmp.set(row_data, dst_idx); + } + const blob = new Blob([bmp.buffer], { type: 'image/bmp' }); + const url = URL.createObjectURL(blob); + + const cursor_slot = { + css_property: `url('${url}') ${hotspot_x} ${hotspot_y}, auto`, + blob_url: url // so we can release it later + }; + + // Store a reference to the js cursor object in a global table, indexed by its sapp_mouse_cursor + if (!Module.__sapp_custom_cursors) { + Module.__sapp_custom_cursors = Array().fill(null); + } + Module.__sapp_custom_cursors[cursor_slot_idx] = cursor_slot; +}) + +EM_JS(void, sapp_js_destroy_custom_mouse_cursor, (int cursor_slot_idx), { + if (Module.__sapp_custom_cursors) { + const cursor = Module.__sapp_custom_cursors[cursor_slot_idx]; + URL.revokeObjectURL(cursor.blob_url); // release the url, which should allow the blob to be garbage collected. + Module.__sapp_custom_cursors[cursor_slot_idx] = null; // clear this array entry + } +}) + +_SOKOL_PRIVATE bool _sapp_emsc_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) { + sapp_js_make_custom_mouse_cursor((int)cursor, desc->width, desc->height, desc->pixels.ptr, desc->cursor_hotspot_x, desc->cursor_hotspot_y); + return true; +} + +_SOKOL_PRIVATE void _sapp_emsc_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) { + sapp_js_destroy_custom_mouse_cursor((int) cursor); +} + +// NOTE: this callback is needed to react to the user actively leaving fullscreen mode via Esc +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_fullscreenchange_cb(int emsc_type, const EmscriptenFullscreenChangeEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + _sapp.fullscreen = emsc_event->isFullscreen; + return true; +} + +EM_JS(void, sapp_js_toggle_fullscreen, (void), { + const canvas = Module.sapp_emsc_target; + if (canvas) { + // NOTE: Safari had the prefix until 2023, Firefox until 2018 + const fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement; + let p = undefined; + if (!fullscreenElement) { + if (canvas.requestFullscreen) { + p = canvas.requestFullscreen(); + } else if (canvas.webkitRequestFullscreen) { + p = canvas.webkitRequestFullscreen(); + } else if (canvas.mozRequestFullScreen) { + p = canvas.mozRequestFullScreen(); + } + if (p) { + p.catch((err) => { + console.warn('sapp_js_toggle_fullscreen(): failed to enter fullscreen mode with', err); + __sapp_emsc_set_fullscreen_flag(0); + }); + } else { + console.warn('sapp_js_toogle_fullscreen(): browser has no [webkit|moz]requestFullscreen function'); + __sapp_emsc_set_fullscreen_flag(0); + } + } else { + if (document.exitFullscreen) { + p = document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + p = document.webkitExitFullscreen(); + } else if (document.mozCancelFullScreen) { + p = document.mozCancelFullScreen(); + } + if (p) { + p.catch((err) => { + console.warn('sapp_js_toggle_fullscreen(): failed to exit fullscreen mode with', err); + __sapp_emsc_set_fullscreen_flag(1); + }); + } else { + console.warn('sapp_js_toggle_fullscreen(): browser has no [wekbit|moz]exitFullscreen'); + // NOTE: don't need to explicitly set the fullscreen flag here + } + } + } +}) + +_SOKOL_PRIVATE void _sapp_emsc_toggle_fullscreen(void) { + // toggle the fullscreen flag preliminary, this may be undone + // when requesting/exiting fullscreen mode actually fails + _sapp.fullscreen = !_sapp.fullscreen; + sapp_js_toggle_fullscreen(); +} + +/* JS helper functions to update browser tab favicon */ +EM_JS(void, sapp_js_clear_favicon, (void), { + 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), { + const canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = 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); + const new_link = document.createElement('link'); + new_link.id = 'sokol-app-favicon'; + new_link.rel = 'shortcut icon'; + new_link.href = canvas.toDataURL(); + document.head.appendChild(new_link); +}) + +_SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + sapp_js_clear_favicon(); + // find the best matching image candidate for 16x16 pixels + int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, 16, 16); + const sapp_image_desc* img_desc = &icon_desc->images[img_index]; + sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr); +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) { + uint32_t m = 0; + if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; } + if (0 != (buttons & (1<<1))) { m |= SAPP_MODIFIER_RMB; } // not a bug + if (0 != (buttons & (1<<2))) { m |= SAPP_MODIFIER_MMB; } // not a bug + return m; +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_event_mods(const EmscriptenMouseEvent* ev) { + uint32_t m = 0; + if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } + if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } + if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } + if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } + m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); + return m; +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_key_event_mods(const EmscriptenKeyboardEvent* ev) { + uint32_t m = 0; + if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } + if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } + if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } + if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } + m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); + return m; +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* ev) { + uint32_t m = 0; + if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } + if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } + if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } + if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } + m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); + return m; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { + _SOKOL_UNUSED(event_type); + _SOKOL_UNUSED(user_data); + double w, h; + emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); + /* The above method might report zero when toggling HTML5 fullscreen, + in that case use the window's inner width reported by the + emscripten event. This works ok when toggling *into* fullscreen + but doesn't properly restore the previous canvas size when switching + back from fullscreen. + + In general, due to the HTML5's fullscreen API's flaky nature it is + recommended to use 'soft fullscreen' (stretching the WebGL canvas + over the browser windows client rect) with a CSS definition like this: + + position: absolute; + top: 0px; + left: 0px; + margin: 0px; + border: 0; + width: 100%; + height: 100%; + overflow: hidden; + display: block; + */ + if (w < 1.0) { + w = ui_event->windowInnerWidth; + } else { + _sapp.window_width = _sapp_roundf_gzero(w); + } + if (h < 1.0) { + h = ui_event->windowInnerHeight; + } else { + _sapp.window_height = _sapp_roundf_gzero(h); + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.framebuffer_width = _sapp_roundf_gzero(w * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(h * _sapp.dpi_scale); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_WGPU) + // on WebGPU: recreate size-dependent rendering surfaces + _sapp_wgpu_swapchain_size_changed(); + #endif + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_RESIZED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5.bubble_mouse_events; + _sapp.emsc.mouse_buttons = emsc_event->buttons; + if (_sapp.mouse.locked) { + _sapp.mouse.dx = (float) emsc_event->movementX; + _sapp.mouse.dy = (float) emsc_event->movementY; + } else { + float new_x = emsc_event->targetX * _sapp.dpi_scale; + float new_y = emsc_event->targetY * _sapp.dpi_scale; + 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_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { + sapp_event_type type; + bool is_button_event = false; + bool clear_dxdy = false; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + type = SAPP_EVENTTYPE_MOUSE_DOWN; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + type = SAPP_EVENTTYPE_MOUSE_UP; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + type = SAPP_EVENTTYPE_MOUSE_MOVE; + break; + case EMSCRIPTEN_EVENT_MOUSEENTER: + type = SAPP_EVENTTYPE_MOUSE_ENTER; + clear_dxdy = true; + break; + case EMSCRIPTEN_EVENT_MOUSELEAVE: + type = SAPP_EVENTTYPE_MOUSE_LEAVE; + clear_dxdy = true; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event); + if (is_button_event) { + switch (emsc_event->button) { + case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; + case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; + case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; + default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; + } + } else { + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + } + consume_event |= _sapp_call_event(&_sapp.event); + } + // mouse lock can only be activated in mouse button events (not in move, enter or leave) + if (is_button_event) { + _sapp_emsc_update_mouse_lock_state(); + } + } + return consume_event; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5.bubble_wheel_events; + _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons; + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(&emsc_event->mouse); + /* see https://github.com/floooh/sokol/issues/339 */ + float scale; + switch (emsc_event->deltaMode) { + case DOM_DELTA_PIXEL: scale = -0.01f; break; + case DOM_DELTA_LINE: scale = -1.33f; break; + case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess + default: scale = -0.1f; break; // shouldn't happen + } + _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; + _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; + consume_event |= _sapp_call_event(&_sapp.event); + } + _sapp_emsc_update_mouse_lock_state(); + return consume_event; +} + +static struct { + const char* str; + sapp_keycode code; +} _sapp_emsc_keymap[] = { + { "Backspace", SAPP_KEYCODE_BACKSPACE }, + { "Tab", SAPP_KEYCODE_TAB }, + { "Enter", SAPP_KEYCODE_ENTER }, + { "ShiftLeft", SAPP_KEYCODE_LEFT_SHIFT }, + { "ShiftRight", SAPP_KEYCODE_RIGHT_SHIFT }, + { "ControlLeft", SAPP_KEYCODE_LEFT_CONTROL }, + { "ControlRight", SAPP_KEYCODE_RIGHT_CONTROL }, + { "AltLeft", SAPP_KEYCODE_LEFT_ALT }, + { "AltRight", SAPP_KEYCODE_RIGHT_ALT }, + { "Pause", SAPP_KEYCODE_PAUSE }, + { "CapsLock", SAPP_KEYCODE_CAPS_LOCK }, + { "Escape", SAPP_KEYCODE_ESCAPE }, + { "Space", SAPP_KEYCODE_SPACE }, + { "PageUp", SAPP_KEYCODE_PAGE_UP }, + { "PageDown", SAPP_KEYCODE_PAGE_DOWN }, + { "End", SAPP_KEYCODE_END }, + { "Home", SAPP_KEYCODE_HOME }, + { "ArrowLeft", SAPP_KEYCODE_LEFT }, + { "ArrowUp", SAPP_KEYCODE_UP }, + { "ArrowRight", SAPP_KEYCODE_RIGHT }, + { "ArrowDown", SAPP_KEYCODE_DOWN }, + { "PrintScreen", SAPP_KEYCODE_PRINT_SCREEN }, + { "Insert", SAPP_KEYCODE_INSERT }, + { "Delete", SAPP_KEYCODE_DELETE }, + { "Digit0", SAPP_KEYCODE_0 }, + { "Digit1", SAPP_KEYCODE_1 }, + { "Digit2", SAPP_KEYCODE_2 }, + { "Digit3", SAPP_KEYCODE_3 }, + { "Digit4", SAPP_KEYCODE_4 }, + { "Digit5", SAPP_KEYCODE_5 }, + { "Digit6", SAPP_KEYCODE_6 }, + { "Digit7", SAPP_KEYCODE_7 }, + { "Digit8", SAPP_KEYCODE_8 }, + { "Digit9", SAPP_KEYCODE_9 }, + { "KeyA", SAPP_KEYCODE_A }, + { "KeyB", SAPP_KEYCODE_B }, + { "KeyC", SAPP_KEYCODE_C }, + { "KeyD", SAPP_KEYCODE_D }, + { "KeyE", SAPP_KEYCODE_E }, + { "KeyF", SAPP_KEYCODE_F }, + { "KeyG", SAPP_KEYCODE_G }, + { "KeyH", SAPP_KEYCODE_H }, + { "KeyI", SAPP_KEYCODE_I }, + { "KeyJ", SAPP_KEYCODE_J }, + { "KeyK", SAPP_KEYCODE_K }, + { "KeyL", SAPP_KEYCODE_L }, + { "KeyM", SAPP_KEYCODE_M }, + { "KeyN", SAPP_KEYCODE_N }, + { "KeyO", SAPP_KEYCODE_O }, + { "KeyP", SAPP_KEYCODE_P }, + { "KeyQ", SAPP_KEYCODE_Q }, + { "KeyR", SAPP_KEYCODE_R }, + { "KeyS", SAPP_KEYCODE_S }, + { "KeyT", SAPP_KEYCODE_T }, + { "KeyU", SAPP_KEYCODE_U }, + { "KeyV", SAPP_KEYCODE_V }, + { "KeyW", SAPP_KEYCODE_W }, + { "KeyX", SAPP_KEYCODE_X }, + { "KeyY", SAPP_KEYCODE_Y }, + { "KeyZ", SAPP_KEYCODE_Z }, + { "MetaLeft", SAPP_KEYCODE_LEFT_SUPER }, + { "MetaRight", SAPP_KEYCODE_RIGHT_SUPER }, + { "Numpad0", SAPP_KEYCODE_KP_0 }, + { "Numpad1", SAPP_KEYCODE_KP_1 }, + { "Numpad2", SAPP_KEYCODE_KP_2 }, + { "Numpad3", SAPP_KEYCODE_KP_3 }, + { "Numpad4", SAPP_KEYCODE_KP_4 }, + { "Numpad5", SAPP_KEYCODE_KP_5 }, + { "Numpad6", SAPP_KEYCODE_KP_6 }, + { "Numpad7", SAPP_KEYCODE_KP_7 }, + { "Numpad8", SAPP_KEYCODE_KP_8 }, + { "Numpad9", SAPP_KEYCODE_KP_9 }, + { "NumpadMultiply", SAPP_KEYCODE_KP_MULTIPLY }, + { "NumpadAdd", SAPP_KEYCODE_KP_ADD }, + { "NumpadSubtract", SAPP_KEYCODE_KP_SUBTRACT }, + { "NumpadDecimal", SAPP_KEYCODE_KP_DECIMAL }, + { "NumpadDivide", SAPP_KEYCODE_KP_DIVIDE }, + { "F1", SAPP_KEYCODE_F1 }, + { "F2", SAPP_KEYCODE_F2 }, + { "F3", SAPP_KEYCODE_F3 }, + { "F4", SAPP_KEYCODE_F4 }, + { "F5", SAPP_KEYCODE_F5 }, + { "F6", SAPP_KEYCODE_F6 }, + { "F7", SAPP_KEYCODE_F7 }, + { "F8", SAPP_KEYCODE_F8 }, + { "F9", SAPP_KEYCODE_F9 }, + { "F10", SAPP_KEYCODE_F10 }, + { "F11", SAPP_KEYCODE_F11 }, + { "F12", SAPP_KEYCODE_F12 }, + { "NumLock", SAPP_KEYCODE_NUM_LOCK }, + { "ScrollLock", SAPP_KEYCODE_SCROLL_LOCK }, + { "Semicolon", SAPP_KEYCODE_SEMICOLON }, + { "Equal", SAPP_KEYCODE_EQUAL }, + { "Comma", SAPP_KEYCODE_COMMA }, + { "Minus", SAPP_KEYCODE_MINUS }, + { "Period", SAPP_KEYCODE_PERIOD }, + { "Slash", SAPP_KEYCODE_SLASH }, + { "Backquote", SAPP_KEYCODE_GRAVE_ACCENT }, + { "BracketLeft", SAPP_KEYCODE_LEFT_BRACKET }, + { "Backslash", SAPP_KEYCODE_BACKSLASH }, + { "BracketRight", SAPP_KEYCODE_RIGHT_BRACKET }, + { "Quote", SAPP_KEYCODE_GRAVE_ACCENT }, // FIXME: ??? + { 0, SAPP_KEYCODE_INVALID }, +}; + +_SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) { + int i = 0; + const char* keystr; + while (( keystr = _sapp_emsc_keymap[i].str )) { + if (0 == strcmp(str, keystr)) { + return _sapp_emsc_keymap[i].code; + } + i += 1; + } + return SAPP_KEYCODE_INVALID; +} + +// returns true if the key code is a 'character key', this is used to decide +// if a key event needs to bubble up to create a char event +_SOKOL_PRIVATE bool _sapp_emsc_is_char_key(sapp_keycode key_code) { + return key_code < SAPP_KEYCODE_WORLD_1; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool consume_event = false; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_KEYDOWN: + type = SAPP_EVENTTYPE_KEY_DOWN; + break; + case EMSCRIPTEN_EVENT_KEYUP: + type = SAPP_EVENTTYPE_KEY_UP; + break; + case EMSCRIPTEN_EVENT_KEYPRESS: + type = SAPP_EVENTTYPE_CHAR; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + bool send_keyup_followup = false; + _sapp_init_event(type); + _sapp.event.key_repeat = emsc_event->repeat; + _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); + if (type == SAPP_EVENTTYPE_CHAR) { + // NOTE: charCode doesn't appear to be supported on Android Chrome + _sapp.event.char_code = emsc_event->charCode; + consume_event |= !_sapp.desc.html5.bubble_char_events; + } else { + 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 + // when the SUPER key is pressed + if ((type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) && + (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) && + (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) + { + send_keyup_followup = true; + } + + // 'character key events' will always need to bubble up, otherwise the browser + // wouldn't be able to generate character events. + if (!_sapp_emsc_is_char_key(_sapp.event.key_code)) { + consume_event |= !_sapp.desc.html5.bubble_key_events; + } + } + consume_event |= _sapp_call_event(&_sapp.event); + if (send_keyup_followup) { + _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; + consume_event |= _sapp_call_event(&_sapp.event); + } + } + } + _sapp_emsc_update_mouse_lock_state(); + return consume_event; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5.bubble_touch_events; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_TOUCHSTART: + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case EMSCRIPTEN_EVENT_TOUCHMOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case EMSCRIPTEN_EVENT_TOUCHEND: + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_emsc_touch_event_mods(emsc_event); + _sapp.event.num_touches = emsc_event->numTouches; + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int i = 0; i < _sapp.event.num_touches; i++) { + const EmscriptenTouchPoint* src = &emsc_event->touches[i]; + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = (uintptr_t)src->identifier; + dst->pos_x = src->targetX * _sapp.dpi_scale; + dst->pos_y = src->targetY * _sapp.dpi_scale; + dst->changed = src->isChanged; + } + consume_event |= _sapp_call_event(&_sapp.event); + } + } + return consume_event; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_focus_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(emsc_event); + _SOKOL_UNUSED(user_data); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FOCUSED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(emsc_event); + _SOKOL_UNUSED(user_data); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_UNFOCUSED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; + case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; + default: type = SAPP_EVENTTYPE_INVALID; break; + } + if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { + EmscriptenWebGLContextAttributes attrs; + emscripten_webgl_init_context_attributes(&attrs); + attrs.alpha = _sapp.desc.alpha; + attrs.depth = true; + attrs.stencil = true; + attrs.antialias = _sapp.sample_count > 1; + attrs.premultipliedAlpha = _sapp.desc.html5.premultiplied_alpha; + attrs.preserveDrawingBuffer = _sapp.desc.html5.preserve_drawing_buffer; + attrs.enableExtensionsByDefault = true; + attrs.majorVersion = 2; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); + // FIXME: error message? + emscripten_webgl_make_context_current(ctx); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); +} +#endif + +_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { + // NOTE: HTML canvas doesn't receive input focus, this is why key event handlers are added + // to the window object (this could be worked around by adding a "tab index" to the + // canvas) + emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); + emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_focus_cb); + emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_blur_cb); + emscripten_set_fullscreenchange_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_fullscreenchange_cb); + sapp_js_add_beforeunload_listener(); + if (_sapp.clipboard.enabled) { + sapp_js_add_clipboard_listener(); + } + if (_sapp.drop.enabled) { + sapp_js_add_dragndrop_listeners(); + } + #if defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); + #endif +} + +_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers(void) { + emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_fullscreenchange_callback(_sapp.html5_canvas_selector, 0, true, 0); + if (!_sapp.desc.html5.canvas_resize) { + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + } + sapp_js_remove_beforeunload_listener(); + if (_sapp.clipboard.enabled) { + sapp_js_remove_clipboard_listener(); + } + if (_sapp.drop.enabled) { + sapp_js_remove_dragndrop_listeners(); + } + #if defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); + #endif +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame_animation_loop(double time, void* userData) { + _SOKOL_UNUSED(userData); + _sapp_timing_external(&_sapp.timing, time / 1000.0); + + #if defined(SOKOL_WGPU) + _sapp_wgpu_frame(); + #else + _sapp_frame(); + #endif + + // quit-handling + if (_sapp.quit_requested) { + _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_call_event(&_sapp.event); + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + _sapp_emsc_unregister_eventhandlers(); + #if defined(SOKOL_WGPU) + _sapp_wgpu_discard(); + #endif + _sapp_call_cleanup(); + _sapp_discard_state(); + return EM_FALSE; + } + return EM_TRUE; +} + +_SOKOL_PRIVATE void _sapp_emsc_frame_main_loop(void) { + const double time = emscripten_performance_now(); + if (!_sapp_emsc_frame_animation_loop(time, 0)) { + emscripten_cancel_main_loop(); + } +} + +_SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp.fullscreen = false; // override user provided fullscreen state: can't start in fullscreen on the web! + const char* document_title = desc->html5.update_document_title ? _sapp.window_title : 0; + sapp_js_init(_sapp.html5_canvas_selector, document_title); + double w, h; + if (_sapp.desc.html5.canvas_resize) { + 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); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.window_width = _sapp_roundf_gzero(w); + _sapp.window_height = _sapp_roundf_gzero(h); + _sapp.framebuffer_width = _sapp_roundf_gzero(w * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(h * _sapp.dpi_scale); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_GLES3) + _sapp_emsc_webgl_init(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_init(); + #endif + _sapp.valid = true; + _sapp_emsc_register_eventhandlers(); + sapp_set_icon(&desc->icon); + + // start the frame loop + if (_sapp.desc.html5.use_emsc_set_main_loop) { + emscripten_set_main_loop(_sapp_emsc_frame_main_loop, 0, _sapp.desc.html5.emsc_set_main_loop_simulate_infinite_loop); + } else { + emscripten_request_animation_frame_loop(_sapp_emsc_frame_animation_loop, 0); + } + // NOT A BUG: do not call _sapp_discard_state() here, instead this is + // called in _sapp_emsc_frame() when the application is ordered to quit +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_emsc_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_EMSCRIPTEN */ + +// ██████ ██ ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>gl helpers +#if defined(SOKOL_GLCORE) +typedef struct { + int red_bits; + int green_bits; + int blue_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; + int samples; + bool doublebuffer; + uintptr_t handle; +} _sapp_gl_fbconfig; + +_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { + _sapp_clear(fbconfig, sizeof(_sapp_gl_fbconfig)); + /* -1 means "don't care" */ + fbconfig->red_bits = -1; + fbconfig->green_bits = -1; + fbconfig->blue_bits = -1; + fbconfig->alpha_bits = -1; + fbconfig->depth_bits = -1; + fbconfig->stencil_bits = -1; + fbconfig->samples = -1; +} + +typedef struct { + int least_missing; + int least_color_diff; + int least_extra_diff; + bool best_match; +} _sapp_gl_fbselect; + +_SOKOL_PRIVATE void _sapp_gl_init_fbselect(_sapp_gl_fbselect* fbselect) { + _sapp_clear(fbselect, sizeof(_sapp_gl_fbselect)); + fbselect->least_missing = 1000000; + fbselect->least_color_diff = 10000000; + fbselect->least_extra_diff = 10000000; + fbselect->best_match = false; +} + +// NOTE: this is used only in the WGL code path +_SOKOL_PRIVATE bool _sapp_gl_select_fbconfig(_sapp_gl_fbselect* fbselect, const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* current) { + int missing = 0; + if (desired->doublebuffer != current->doublebuffer) { + return false; + } + + if ((desired->alpha_bits > 0) && (current->alpha_bits == 0)) { + missing++; + } + if ((desired->depth_bits > 0) && (current->depth_bits == 0)) { + missing++; + } + if ((desired->stencil_bits > 0) && (current->stencil_bits == 0)) { + missing++; + } + if ((desired->samples > 0) && (current->samples == 0)) { + /* Technically, several multisampling buffers could be + involved, but that's a lower level implementation detail and + not important to us here, so we count them as one + */ + missing++; + } + + /* These polynomials make many small channel size differences matter + less than one large channel size difference + Calculate color channel size difference value + */ + int color_diff = 0; + if (desired->red_bits != -1) { + color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); + } + if (desired->green_bits != -1) { + color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); + } + if (desired->blue_bits != -1) { + color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); + } + + /* Calculate non-color channel size difference value */ + int extra_diff = 0; + if (desired->alpha_bits != -1) { + extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); + } + if (desired->depth_bits != -1) { + extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); + } + if (desired->stencil_bits != -1) { + extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); + } + if (desired->samples != -1) { + extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); + } + + /* Figure out if the current one is better than the best one found so far + Least number of missing buffers is the most important heuristic, + then color buffer size match and lastly size match for other buffers + */ + bool new_closest = false; + if (missing < fbselect->least_missing) { + new_closest = true; + } else if (missing == fbselect->least_missing) { + if ((color_diff < fbselect->least_color_diff) || + ((color_diff == fbselect->least_color_diff) && (extra_diff < fbselect->least_extra_diff))) + { + new_closest = true; + } + } + if (new_closest) { + fbselect->least_missing = missing; + fbselect->least_color_diff = color_diff; + fbselect->least_extra_diff = extra_diff; + fbselect->best_match = (missing | color_diff | extra_diff) == 0; + } + return new_closest; +} + +// NOTE: this is used only in the GLX code path +_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) { + int missing, least_missing = 1000000; + int color_diff, least_color_diff = 10000000; + int extra_diff, least_extra_diff = 10000000; + const _sapp_gl_fbconfig* current; + const _sapp_gl_fbconfig* closest = 0; + for (int i = 0; i < count; i++) { + current = alternatives + i; + if (desired->doublebuffer != current->doublebuffer) { + continue; + } + missing = 0; + if (desired->alpha_bits > 0 && current->alpha_bits == 0) { + missing++; + } + if (desired->depth_bits > 0 && current->depth_bits == 0) { + missing++; + } + if (desired->stencil_bits > 0 && current->stencil_bits == 0) { + missing++; + } + if (desired->samples > 0 && current->samples == 0) { + /* Technically, several multisampling buffers could be + involved, but that's a lower level implementation detail and + not important to us here, so we count them as one + */ + missing++; + } + + /* These polynomials make many small channel size differences matter + less than one large channel size difference + Calculate color channel size difference value + */ + color_diff = 0; + if (desired->red_bits != -1) { + color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); + } + if (desired->green_bits != -1) { + color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); + } + if (desired->blue_bits != -1) { + color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); + } + + /* Calculate non-color channel size difference value */ + extra_diff = 0; + if (desired->alpha_bits != -1) { + extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); + } + if (desired->depth_bits != -1) { + extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); + } + if (desired->stencil_bits != -1) { + extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); + } + if (desired->samples != -1) { + extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); + } + + /* Figure out if the current one is better than the best one found so far + Least number of missing buffers is the most important heuristic, + then color buffer size match and lastly size match for other buffers + */ + if (missing < least_missing) { + closest = current; + } else if (missing == least_missing) { + if ((color_diff < least_color_diff) || + (color_diff == least_color_diff && extra_diff < least_extra_diff)) + { + closest = current; + } + } + if (current == closest) { + least_missing = missing; + least_color_diff = color_diff; + least_extra_diff = extra_diff; + } + } + return closest; +} +#endif + +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>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)); + _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)) { + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); + return true; + } else { + // input string doesn't fit into destination buffer + return false; + } +} + +_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_init_keytable(void) { + /* same as GLFW */ + _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; + _sapp.keycodes[0x002] = SAPP_KEYCODE_1; + _sapp.keycodes[0x003] = SAPP_KEYCODE_2; + _sapp.keycodes[0x004] = SAPP_KEYCODE_3; + _sapp.keycodes[0x005] = SAPP_KEYCODE_4; + _sapp.keycodes[0x006] = SAPP_KEYCODE_5; + _sapp.keycodes[0x007] = SAPP_KEYCODE_6; + _sapp.keycodes[0x008] = SAPP_KEYCODE_7; + _sapp.keycodes[0x009] = SAPP_KEYCODE_8; + _sapp.keycodes[0x00A] = SAPP_KEYCODE_9; + _sapp.keycodes[0x01E] = SAPP_KEYCODE_A; + _sapp.keycodes[0x030] = SAPP_KEYCODE_B; + _sapp.keycodes[0x02E] = SAPP_KEYCODE_C; + _sapp.keycodes[0x020] = SAPP_KEYCODE_D; + _sapp.keycodes[0x012] = SAPP_KEYCODE_E; + _sapp.keycodes[0x021] = SAPP_KEYCODE_F; + _sapp.keycodes[0x022] = SAPP_KEYCODE_G; + _sapp.keycodes[0x023] = SAPP_KEYCODE_H; + _sapp.keycodes[0x017] = SAPP_KEYCODE_I; + _sapp.keycodes[0x024] = SAPP_KEYCODE_J; + _sapp.keycodes[0x025] = SAPP_KEYCODE_K; + _sapp.keycodes[0x026] = SAPP_KEYCODE_L; + _sapp.keycodes[0x032] = SAPP_KEYCODE_M; + _sapp.keycodes[0x031] = SAPP_KEYCODE_N; + _sapp.keycodes[0x018] = SAPP_KEYCODE_O; + _sapp.keycodes[0x019] = SAPP_KEYCODE_P; + _sapp.keycodes[0x010] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x013] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01F] = SAPP_KEYCODE_S; + _sapp.keycodes[0x014] = SAPP_KEYCODE_T; + _sapp.keycodes[0x016] = SAPP_KEYCODE_U; + _sapp.keycodes[0x02F] = SAPP_KEYCODE_V; + _sapp.keycodes[0x011] = SAPP_KEYCODE_W; + _sapp.keycodes[0x02D] = SAPP_KEYCODE_X; + _sapp.keycodes[0x015] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2; + _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x14F] = SAPP_KEYCODE_END; + _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK; + _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x040] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x041] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x042] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x043] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x044] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x057] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x058] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x064] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x065] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x066] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x067] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x068] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x069] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21; + _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22; + _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23; + _sapp.keycodes[0x076] = SAPP_KEYCODE_F24; + _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN; + _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x136] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x148] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; +} +#endif // _SAPP_WIN32 + +#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_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) + return self->GetBuffer(Buffer, riid, ppSurface); + #else + return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface); + #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); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) { + #if defined(__cplusplus) + return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags); + #else + return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags); + #endif +} + +static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) { + #if defined(__cplusplus) + return self->Present(SyncInterval, Flags); + #else + return self->lpVtbl->Present(self, SyncInterval, Flags); + #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; + sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height; + sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + sc_desc->BufferDesc.RefreshRate.Numerator = 60; + sc_desc->BufferDesc.RefreshRate.Denominator = 1; + sc_desc->OutputWindow = _sapp.win32.hwnd; + sc_desc->Windowed = true; + 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; + sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT; + #if defined(SOKOL_DEBUG) + create_flags |= D3D11_CREATE_DEVICE_DEBUG; + #endif + D3D_FEATURE_LEVEL requested_feature_levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 }; + D3D_FEATURE_LEVEL result_feature_level; + HRESULT hr = D3D11CreateDeviceAndSwapChain( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + requested_feature_levels, /* pFeatureLevels */ + 2, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + sc_desc, /* pSwapChainDesc */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ + &result_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 &= ~(UINT)D3D11_CREATE_DEVICE_DEBUG; + hr = D3D11CreateDeviceAndSwapChain( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + requested_feature_levels, /* pFeatureLevels */ + 2, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + sc_desc, /* pSwapChainDesc */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ + &result_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); +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { + SOKOL_ASSERT(0 == _sapp.d3d11.rt); + SOKOL_ASSERT(0 == _sapp.d3d11.rtv); + SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt); + SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv); + SOKOL_ASSERT(0 == _sapp.d3d11.ds); + SOKOL_ASSERT(0 == _sapp.d3d11.dsv); + + HRESULT hr; _SOKOL_UNUSED(hr); + + /* view for the swapchain-created framebuffer */ + 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 */ + _SAPP_STRUCT(D3D11_TEXTURE2D_DESC, tex_desc); + tex_desc.Width = (UINT)_sapp.framebuffer_width; + tex_desc.Height = (UINT)_sapp.framebuffer_height; + tex_desc.MipLevels = 1; + tex_desc.ArraySize = 1; + tex_desc.Usage = D3D11_USAGE_DEFAULT; + tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count; + tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + + /* create MSAA texture and view if antialiasing requested */ + if (_sapp.sample_count > 1) { + tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv); + } + + /* texture and view for the depth-stencil-surface */ + tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); + hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); +} + +_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { + _SAPP_SAFE_RELEASE(_sapp.d3d11.rt); + _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv); + _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt); + _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv); + _SAPP_SAFE_RELEASE(_sapp.d3d11.ds); + _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv); +} + +_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { + if (_sapp.d3d11.swap_chain) { + _sapp_d3d11_destroy_default_render_target(); + _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, (UINT)_sapp.framebuffer_width, (UINT)_sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + _sapp_d3d11_create_default_render_target(); + } +} + +_SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { + 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 */ + +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sapp_wgl_init(void) { + _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); + if (!_sapp.wgl.opengl32) { + _SAPP_PANIC(WIN32_LOAD_OPENGL32_DLL_FAILED); + } + SOKOL_ASSERT(_sapp.wgl.opengl32); + _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); + SOKOL_ASSERT(_sapp.wgl.CreateContext); + _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext"); + SOKOL_ASSERT(_sapp.wgl.DeleteContext); + _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress"); + SOKOL_ASSERT(_sapp.wgl.GetProcAddress); + _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC"); + SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); + _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); + SOKOL_ASSERT(_sapp.wgl.MakeCurrent); + _sapp.wgl.GetIntegerv = (void(WINAPI*)(uint32_t, int32_t*)) GetProcAddress(_sapp.wgl.opengl32, "glGetIntegerv"); + SOKOL_ASSERT(_sapp.wgl.GetIntegerv); + + _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + L"SOKOLAPP", + L"sokol-app message window", + WS_CLIPSIBLINGS|WS_CLIPCHILDREN, + 0, 0, 1, 1, + NULL, NULL, + GetModuleHandleW(NULL), + NULL); + if (!_sapp.wgl.msg_hwnd) { + _SAPP_PANIC(WIN32_CREATE_HELPER_WINDOW_FAILED); + } + SOKOL_ASSERT(_sapp.wgl.msg_hwnd); + ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); + MSG msg; + while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); + if (!_sapp.wgl.msg_dc) { + _SAPP_PANIC(WIN32_HELPER_WINDOW_GETDC_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { + SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd); + DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0; + FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0; +} + +_SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { + SOKOL_ASSERT(ext && extensions); + const char* start = extensions; + while (true) { + const char* where = strstr(start, ext); + if (!where) { + return false; + } + const char* terminator = where + strlen(ext); + if ((where == start) || (*(where - 1) == ' ')) { + if (*terminator == ' ' || *terminator == '\0') { + break; + } + } + start = terminator; + } + return true; +} + +_SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { + SOKOL_ASSERT(ext); + if (_sapp.wgl.GetExtensionsStringEXT) { + const char* extensions = _sapp.wgl.GetExtensionsStringEXT(); + if (extensions) { + if (_sapp_wgl_has_ext(ext, extensions)) { + return true; + } + } + } + if (_sapp.wgl.GetExtensionsStringARB) { + const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC()); + if (extensions) { + if (_sapp_wgl_has_ext(ext, extensions)) { + return true; + } + } + } + return false; +} + +_SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { + SOKOL_ASSERT(_sapp.wgl.msg_dc); + _SAPP_STRUCT(PIXELFORMATDESCRIPTOR, 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_PANIC(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED); + } + HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); + if (!rc) { + _SAPP_PANIC(WIN32_CREATE_DUMMY_CONTEXT_FAILED); + } + if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { + _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"); + _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB"); + _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT"); + _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB"); + _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); + _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); + _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); + _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); + _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); + _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); + _sapp.wgl.DeleteContext(rc); +} + +_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_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); + } + return value; +} + +_SOKOL_PRIVATE void _sapp_wgl_attribiv(int pixel_format, int num_attribs, const int* attribs, int* results) { + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, num_attribs, attribs, results)) { + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); + } +} + +_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { + SOKOL_ASSERT(_sapp.win32.dc); + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + + #define _sapp_wgl_num_query_tags (12) + const int query_tags[_sapp_wgl_num_query_tags] = { + WGL_SUPPORT_OPENGL_ARB, + WGL_DRAW_TO_WINDOW_ARB, + WGL_PIXEL_TYPE_ARB, + WGL_ACCELERATION_ARB, + WGL_DOUBLE_BUFFER_ARB, + WGL_RED_BITS_ARB, + WGL_GREEN_BITS_ARB, + WGL_BLUE_BITS_ARB, + WGL_ALPHA_BITS_ARB, + WGL_DEPTH_BITS_ARB, + WGL_STENCIL_BITS_ARB, + WGL_SAMPLES_ARB, + }; + const int result_support_opengl_index = 0; + const int result_draw_to_window_index = 1; + const int result_pixel_type_index = 2; + const int result_acceleration_index = 3; + const int result_double_buffer_index = 4; + const int result_red_bits_index = 5; + const int result_green_bits_index = 6; + const int result_blue_bits_index = 7; + const int result_alpha_bits_index = 8; + const int result_depth_bits_index = 9; + const int result_stencil_bits_index = 10; + const int result_samples_index = 11; + + int query_results[_sapp_wgl_num_query_tags] = {0}; + // Drop the last item if multisample extension is not supported. + // If in future querying with multiple extensions, will have to shuffle index values to have active extensions on the end. + int query_count = _sapp_wgl_num_query_tags; + if (!_sapp.wgl.arb_multisample) { + query_count = _sapp_wgl_num_query_tags - 1; + } + + int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); + + _sapp_gl_fbconfig desired; + _sapp_gl_init_fbconfig(&desired); + desired.red_bits = 8; + desired.green_bits = 8; + desired.blue_bits = 8; + desired.alpha_bits = 8; + desired.depth_bits = 24; + desired.stencil_bits = 8; + desired.doublebuffer = true; + desired.samples = (_sapp.sample_count > 1) ? _sapp.sample_count : 0; + + int pixel_format = 0; + + _sapp_gl_fbselect fbselect; + _sapp_gl_init_fbselect(&fbselect); + for (int i = 0; i < native_count; i++) { + const int n = i + 1; + _sapp_wgl_attribiv(n, query_count, query_tags, query_results); + + if (query_results[result_support_opengl_index] == 0 + || query_results[result_draw_to_window_index] == 0 + || query_results[result_pixel_type_index] != WGL_TYPE_RGBA_ARB + || query_results[result_acceleration_index] == WGL_NO_ACCELERATION_ARB) + { + continue; + } + + _SAPP_STRUCT(_sapp_gl_fbconfig, u); + u.red_bits = query_results[result_red_bits_index]; + u.green_bits = query_results[result_green_bits_index]; + u.blue_bits = query_results[result_blue_bits_index]; + u.alpha_bits = query_results[result_alpha_bits_index]; + u.depth_bits = query_results[result_depth_bits_index]; + u.stencil_bits = query_results[result_stencil_bits_index]; + u.doublebuffer = 0 != query_results[result_double_buffer_index]; + u.samples = query_results[result_samples_index]; // NOTE: If arb_multisample is not supported - just takes the default 0 + + // Test if this pixel format is better than the previous one + if (_sapp_gl_select_fbconfig(&fbselect, &desired, &u)) { + pixel_format = (uintptr_t)n; + + // Early exit if matching as good as possible + if (fbselect.best_match) { + break; + } + } + } + + return pixel_format; +} + +_SOKOL_PRIVATE void _sapp_wgl_create_context(void) { + int pixel_format = _sapp_wgl_find_pixel_format(); + if (0 == pixel_format) { + _SAPP_PANIC(WIN32_WGL_FIND_PIXELFORMAT_FAILED); + } + PIXELFORMATDESCRIPTOR pfd; + if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { + _SAPP_PANIC(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED); + } + if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { + _SAPP_PANIC(WIN32_WGL_SET_PIXELFORMAT_FAILED); + } + if (!_sapp.wgl.arb_create_context) { + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED); + } + if (!_sapp.wgl.arb_create_context_profile) { + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED); + } + const int attrs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl.major_version, + WGL_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl.minor_version, +#if defined(SOKOL_DEBUG) + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, +#else + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, +#endif + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, 0 + }; + _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); + if (!_sapp.wgl.gl_ctx) { + const DWORD err = GetLastError(); + if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { + _SAPP_PANIC(WIN32_WGL_OPENGL_VERSION_NOT_SUPPORTED); + } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { + _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED); + } else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { + _SAPP_PANIC(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT); + } else { + _SAPP_PANIC(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER); + } + } + _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); + if (_sapp.wgl.ext_swap_control) { + /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ + _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); + } + const uint32_t gl_framebuffer_binding = 0x8CA6; + _sapp.wgl.GetIntegerv(gl_framebuffer_binding, (int32_t*)&_sapp.gl.framebuffer); +} + +_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { + SOKOL_ASSERT(_sapp.wgl.gl_ctx); + _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); + _sapp.wgl.gl_ctx = 0; +} + +_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { + SOKOL_ASSERT(_sapp.win32.dc); + /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ + SwapBuffers(_sapp.win32.dc); +} +#endif /* SOKOL_GLCORE */ + +_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)); + _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); + return true; + } else { + return false; + } +} + +/* 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 = _sapp_roundf_gzero(window_width); + _sapp.window_height = _sapp_roundf_gzero(window_height); + // NOTE: on Vulkan, updating the framebuffer dimensions and firing the resize-event + // is handled entirely by the swapchain management code + #if !defined(SOKOL_VULKAN) + int fb_width = _sapp_roundf_gzero(window_width * _sapp.win32.dpi.content_scale); + int fb_height = _sapp_roundf_gzero(window_height * _sapp.win32.dpi.content_scale); + if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { + _sapp.framebuffer_width = fb_width; + _sapp.framebuffer_height = fb_height; + return true; + } + #endif + } else { + _sapp.window_width = _sapp.window_height = 1; + #if !defined(SOKOL_VULKAN) + _sapp.framebuffer_width = _sapp.framebuffer_height = 1; + #endif + } + return false; +} + +_SOKOL_PRIVATE void _sapp_win32_set_fullscreen(bool fullscreen, UINT swp_flags) { + HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); + _SAPP_STRUCT(MONITORINFO, minfo); + minfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(monitor, &minfo); + const RECT mr = minfo.rcMonitor; + const int monitor_w = mr.right - mr.left; + const int monitor_h = mr.bottom - mr.top; + + const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD win_style; + RECT rect = { 0, 0, 0, 0 }; + + _sapp.fullscreen = fullscreen; + if (!_sapp.fullscreen) { + win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + 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.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); + } + 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, win_x, win_y, win_w, win_h, swp_flags | SWP_FRAMECHANGED); +} + +_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.standard_cursors[cursor] = (HCURSOR)LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED); + } + // fallback: default cursor + if (0 == _sapp.win32.standard_cursors[cursor]) { + // 32512 => IDC_ARROW + _sapp.win32.standard_cursors[cursor] = LoadCursorW(NULL, MAKEINTRESOURCEW(32512)); + } + SOKOL_ASSERT(0 != _sapp.win32.standard_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) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + + // NOTE: when called from WM_SETCURSOR, the area test would be redundant + if (!skip_area_test) { + if (!_sapp_win32_cursor_in_content_area()) { + return; + } + } + HCURSOR cursor_handle = NULL; + if (shown) { + if (_sapp.custom_cursor_bound[cursor]) { + SOKOL_ASSERT(_sapp.win32.custom_cursors[cursor]); + cursor_handle = _sapp.win32.custom_cursors[cursor]; + SOKOL_ASSERT(0 != cursor_handle); + } else { + cursor_handle = _sapp.win32.standard_cursors[cursor]; + SOKOL_ASSERT(0 != cursor_handle); + } + } + SetCursor(cursor_handle); +} + +_SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { + if (0 == _sapp.win32.mouse.capture_mask) { + SetCapture(_sapp.win32.hwnd); + } + _sapp.win32.mouse.capture_mask |= btn_mask; +} + +_SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) { + if (0 != _sapp.win32.mouse.capture_mask) { + _sapp.win32.mouse.capture_mask &= ~btn_mask; + if (0 == _sapp.win32.mouse.capture_mask) { + ReleaseCapture(); + } + } +} + +_SOKOL_PRIVATE bool _sapp_win32_is_foreground_window(void) { + return _sapp.win32.hwnd == GetForegroundWindow(); +} + +_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { + _sapp.win32.mouse.requested_lock = lock; +} + +_SOKOL_PRIVATE void _sapp_win32_free_raw_input_data(void) { + if (_sapp.win32.raw_input_data.ptr) { + _sapp_free(_sapp.win32.raw_input_data.ptr); + _sapp.win32.raw_input_data.ptr = 0; + _sapp.win32.raw_input_data.size = 0; + } +} + +_SOKOL_PRIVATE void _sapp_win32_alloc_raw_input_data(size_t size) { + SOKOL_ASSERT(!_sapp.win32.raw_input_data.ptr); + SOKOL_ASSERT(size > 0); + _sapp.win32.raw_input_data.ptr = _sapp_malloc(size); + _sapp.win32.raw_input_data.size = size; + SOKOL_ASSERT(_sapp.win32.raw_input_data.ptr); +} + +_SOKOL_PRIVATE void* _sapp_win32_ensure_raw_input_data(size_t required_size) { + if (required_size > _sapp.win32.raw_input_data.size) { + _sapp_win32_free_raw_input_data(); + _sapp_win32_alloc_raw_input_data(required_size); + } + // we expect that malloc() returns at least 8-byte aligned memory + SOKOL_ASSERT((((uintptr_t)_sapp.win32.raw_input_data.ptr) & 7) == 0); + return _sapp.win32.raw_input_data.ptr; +} + +_SOKOL_PRIVATE void _sapp_win32_do_lock_mouse(void) { + _sapp.mouse.locked = true; + + // hide mouse cursor (NOTE: this maintains a hidden counter, but since + // only mouse-lock uses ShowCursor this doesn't matter) + ShowCursor(FALSE); + + // reset dx/dy and release any active mouse capture + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp_win32_release_mouse(0xFF); + + // store current mouse position so that it can be restored when unlocked + POINT pos; + if (GetCursorPos(&pos)) { + _sapp.win32.mouse.lock.pos_valid = true; + _sapp.win32.mouse.lock.pos_x = pos.x; + _sapp.win32.mouse.lock.pos_y = pos.y; + } else { + _sapp.win32.mouse.lock.pos_valid = false; + } + + // while mouse is locked, restrict cursor movement to the client + // rectangle so that we don't loose any mouse movement events + RECT client_rect; + GetClientRect(_sapp.win32.hwnd, &client_rect); + POINT mid_point; + mid_point.x = (client_rect.right - client_rect.left) / 2; + mid_point.y = (client_rect.bottom - client_rect.top) / 2; + ClientToScreen(_sapp.win32.hwnd, &mid_point); + RECT clip_rect; + clip_rect.left = clip_rect.right = mid_point.x; + clip_rect.top = clip_rect.bottom = mid_point.y; + ClipCursor(&clip_rect); + + // enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) + const RAWINPUTDEVICE rid = { + 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC + 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE + 0, // dwFlags + _sapp.win32.hwnd // hwndTarget + }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + _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 + _sapp.win32.mouse.raw_input.pos_valid = false; +} + +_SOKOL_PRIVATE void _sapp_win32_do_unlock_mouse(void) { + _sapp.mouse.locked = false; + + // make mouse cursor visible + ShowCursor(TRUE); + + // reset dx/dy and release any active mouse capture + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp_win32_release_mouse(0xFF); + + // disable raw input for mouse + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK); + } + + // unrestrict mouse movement + ClipCursor(NULL); + + // restore the 'pre-locked' mouse position + if (_sapp.win32.mouse.lock.pos_valid) { + SetCursorPos(_sapp.win32.mouse.lock.pos_x, _sapp.win32.mouse.lock.pos_y); + _sapp.win32.mouse.lock.pos_valid = false; + } +} + +_SOKOL_PRIVATE void _sapp_win32_update_mouse_lock(void) { + // mouse lock can only be active when we're the active window + if (!_sapp_win32_is_foreground_window()) { + // unlock mouse if currently locked + if (_sapp.mouse.locked) { + _sapp_win32_do_unlock_mouse(); + } + return; + } + + // nothing to do if requested lock state matches current lock state + const bool lock = _sapp.win32.mouse.requested_lock; + if (lock == _sapp.mouse.locked) { + return; + } + + // otherwise change into desired state + if (lock) { + _sapp_win32_do_lock_mouse(); + } else { + _sapp_win32_do_unlock_mouse(); + } +} + +_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 { + return false; + } +} + +_SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { + uint32_t mods = 0; + if (GetKeyState(VK_SHIFT) & (1<<15)) { + mods |= SAPP_MODIFIER_SHIFT; + } + if (GetKeyState(VK_CONTROL) & (1<<15)) { + mods |= SAPP_MODIFIER_CTRL; + } + if (GetKeyState(VK_MENU) & (1<<15)) { + mods |= SAPP_MODIFIER_ALT; + } + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) { + mods |= SAPP_MODIFIER_SUPER; + } + const bool swapped = (TRUE == GetSystemMetrics(SM_SWAPBUTTON)); + if (GetAsyncKeyState(VK_LBUTTON)) { + mods |= swapped ? SAPP_MODIFIER_RMB : SAPP_MODIFIER_LMB; + } + if (GetAsyncKeyState(VK_RBUTTON)) { + mods |= swapped ? SAPP_MODIFIER_LMB : SAPP_MODIFIER_RMB; + } + if (GetAsyncKeyState(VK_MBUTTON)) { + mods |= SAPP_MODIFIER_MMB; + } + 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); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.mouse_button = btn; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.scroll_x = x; + _sapp.event.scroll_y = y; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { + if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.key_code = _sapp.keycodes[vk]; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { + if (_sapp_events_enabled() && (c >= 32)) { + if (c >= 0xD800 && c <= 0xDBFF) { + _sapp.win32.surrogate = (WCHAR)c - 0xD800; + } else { + if (c > 0xDC00 && c <= 0xDFFF) { + c = (uint32_t)(_sapp.win32.surrogate) << 10 | (c - 0xDC00); + c += 0x10000; + _sapp.win32.surrogate = 0; + } + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.char_code = c; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + } + } +} + +_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; + } + _sapp_clear_drop_buffer(); + bool drop_failed = false; + const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0); + _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*) _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)) { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + drop_failed = true; + } + _sapp_free(buffer); + } + DragFinish(hdrop); + if (!drop_failed) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp_call_event(&_sapp.event); + } + } else { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + } +} + +_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) { + _SAPP_STRUCT(DXGI_FRAME_STATISTICS, 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) { + _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); + #else + _sapp_timing_measure(&_sapp.timing); + #endif +} + +_SOKOL_PRIVATE void _sapp_win32_frame(bool from_winproc) { + #if defined(SOKOL_WGPU) + _sapp_wgpu_frame(); + #elif defined(SOKOL_VULKAN) + _sapp_vk_frame(); + #else + _sapp_frame(); + #endif + #if defined(SOKOL_D3D11) + bool do_not_wait = from_winproc; + _sapp_d3d11_present(do_not_wait); + #endif + #if defined(SOKOL_GLCORE) + _sapp_wgl_swap_buffers(); + #endif + if (!from_winproc) { + if (IsIconic(_sapp.win32.hwnd)) { + Sleep((DWORD)(16 * _sapp.swap_interval)); + } + } +} + +_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (!_sapp.win32.in_create_window) { + switch (uMsg) { + case WM_CLOSE: + /* only give user a chance to intervene when sapp_quit() wasn't already called */ + if (!_sapp.quit_ordered) { + /* if window should be closed and event handling is enabled, give user code + a change to intervene via sapp_cancel_quit() + */ + _sapp.quit_requested = true; + _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; + } + } + if (_sapp.quit_ordered) { + PostQuitMessage(0); + } + return 0; + case WM_SYSCOMMAND: + switch (wParam & 0xFFF0) { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (_sapp.fullscreen) { + /* disable screen saver and blanking in fullscreen mode */ + return 0; + } + break; + case SC_KEYMENU: + /* user trying to access menu via ALT */ + return 0; + } + break; + case WM_ERASEBKGND: + return 1; + case WM_SIZE: + { + const bool iconified = wParam == SIZE_MINIMIZED; + if (iconified != _sapp.win32.iconified) { + _sapp.win32.iconified = iconified; + if (iconified) { + _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); + } else { + _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + break; + case WM_SETFOCUS: + _sapp_win32_app_event(SAPP_EVENTTYPE_FOCUSED); + break; + case WM_KILLFOCUS: + _sapp_win32_app_event(SAPP_EVENTTYPE_UNFOCUSED); + break; + case WM_SETCURSOR: + 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 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.mouse.raw_input.pos_valid) { + _sapp.mouse.dx = (float) (new_x - _sapp.win32.mouse.raw_input.pos_x); + _sapp.mouse.dy = (float) (new_y - _sapp.win32.mouse.raw_input.pos_y); + } + _sapp.win32.mouse.raw_input.pos_x = new_x; + _sapp.win32.mouse.raw_input.pos_y = new_y; + _sapp.win32.mouse.raw_input.pos_valid = true; + } else { + /* mouse reports movement delta (this seems to be the common case) */ + _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; + _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; + } + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + + case WM_MOUSELEAVE: + if (!_sapp.mouse.locked) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.win32.mouse.tracked = false; + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + case WM_MOUSEWHEEL: + _sapp_win32_scroll_event(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); + break; + case WM_MOUSEHWHEEL: + _sapp_win32_scroll_event(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); + break; + case WM_CHAR: + _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000)); + break; + case WM_KEYUP: + case WM_SYSKEYUP: + _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); + break; + case WM_ENTERSIZEMOVE: + SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); + break; + case WM_EXITSIZEMOVE: + KillTimer(_sapp.win32.hwnd, 1); + break; + case WM_TIMER: + _sapp_win32_timing_measure(); + _sapp_win32_frame(true); + /* NOTE: resizing the swap-chain during resize leads to a substantial + memory spike (hundreds of megabytes for a few seconds). + + if (_sapp_win32_update_dimensions()) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(); + #endif + _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 = { 0, 0 }; + if (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; + } + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +_SOKOL_PRIVATE void _sapp_win32_create_window(void) { + _SAPP_STRUCT(WNDCLASSW, wndclassw); + wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; + wndclassw.hInstance = GetModuleHandleW(NULL); + wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wndclassw.lpszClassName = L"SOKOLAPP"; + RegisterClassW(&wndclassw); + + /* 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 }; + 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.surrogate = 0; + _sapp.win32.hwnd = CreateWindowExW( + 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); + + /* 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); +} + +_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { + DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; + UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); +} + +_SOKOL_PRIVATE void _sapp_win32_destroy_icons(void) { + if (_sapp.win32.big_icon) { + DestroyIcon(_sapp.win32.big_icon); + _sapp.win32.big_icon = 0; + } + if (_sapp.win32.small_icon) { + DestroyIcon(_sapp.win32.small_icon); + _sapp.win32.small_icon = 0; + } +} + +_SOKOL_PRIVATE void _sapp_win32_init_console(void) { + if (_sapp.desc.win32.console_create || _sapp.desc.win32.console_attach) { + BOOL con_valid = FALSE; + if (_sapp.desc.win32.console_attach) { + con_valid = AttachConsole(ATTACH_PARENT_PROCESS); + } + if (!con_valid && _sapp.desc.win32.console_create) { + con_valid = AllocConsole(); + } + if (con_valid) { + 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; + } + } + if (_sapp.desc.win32.console_utf8) { + _sapp.win32.orig_codepage = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + } +} + +_SOKOL_PRIVATE void _sapp_win32_restore_console(void) { + if (_sapp.desc.win32.console_utf8) { + SetConsoleOutputCP(_sapp.win32.orig_codepage); + } +} + +_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 (_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); + } + } else if (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) { + POINT pt = { 1, 1 }; + HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + UINT dpix, dpiy; + HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + /* clamp window scale to an integer factor */ + _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; + } else { + _sapp.win32.dpi.window_scale = 1.0f; + } + if (_sapp.desc.high_dpi) { + _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; + _sapp.win32.dpi.mouse_scale = 1.0f; + } else { + _sapp.win32.dpi.content_scale = 1.0f; + _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale; + } + _sapp.dpi_scale = _sapp.win32.dpi.content_scale; + if (user32) { + FreeLibrary(user32); + } + if (shcore) { + FreeLibrary(shcore); + } +} + +_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { + SOKOL_ASSERT(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); + object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); + if (NULL == object) { + goto error; + } + wchar_buf = (wchar_t*) GlobalLock(object); + if (NULL == wchar_buf) { + goto error; + } + if (!_sapp_win32_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { + goto error; + } + GlobalUnlock(object); + wchar_buf = 0; + EmptyClipboard(); + // NOTE: when successful, SetClipboardData() takes ownership of memory object! + if (NULL == SetClipboardData(CF_UNICODETEXT, object)) { + goto error; + } + CloseClipboard(); + return true; + +error: + if (wchar_buf) { + GlobalUnlock(object); + } + if (object) { + GlobalFree(object); + } + CloseClipboard(); + return false; +} + +_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); + SOKOL_ASSERT(_sapp.win32.hwnd); + if (!OpenClipboard(_sapp.win32.hwnd)) { + /* silently ignore any errors and just return the current + content of the local clipboard buffer + */ + return _sapp.clipboard.buffer; + } + HANDLE object = GetClipboardData(CF_UNICODETEXT); + if (!object) { + CloseClipboard(); + return _sapp.clipboard.buffer; + } + const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); + if (!wchar_buf) { + CloseClipboard(); + return _sapp.clipboard.buffer; + } + if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); + } + GlobalUnlock(object); + CloseClipboard(); + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { + _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, bool is_cursor) { + _SAPP_STRUCT(BITMAPV5HEADER, bi); + bi.bV5Size = sizeof(bi); + bi.bV5Width = desc->width; + bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + bi.bV5AlphaMask = 0xFF000000; + + uint8_t* target = 0; + const uint8_t* source = (const uint8_t*)desc->pixels.ptr; + + HDC dc = GetDC(NULL); + HBITMAP color = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&target, NULL, (DWORD)0); + ReleaseDC(NULL, dc); + if (0 == color) { + return NULL; + } + SOKOL_ASSERT(target); + + HBITMAP mask = CreateBitmap(desc->width, desc->height, 1, 1, NULL); + if (0 == mask) { + DeleteObject(color); + return NULL; + } + + for (int i = 0; i < (desc->width*desc->height); i++) { + target[0] = source[2]; + target[1] = source[1]; + target[2] = source[0]; + target[3] = source[3]; + target += 4; + source += 4; + } + + _SAPP_STRUCT(ICONINFO, icon_info); + icon_info.fIcon = !is_cursor; + icon_info.xHotspot = (DWORD) (is_cursor ? desc->cursor_hotspot_x : 0); + icon_info.yHotspot = (DWORD) (is_cursor ? desc->cursor_hotspot_y : 0); + icon_info.hbmMask = mask; + icon_info.hbmColor = color; + HICON icon_handle = CreateIconIndirect(&icon_info); + DeleteObject(color); + DeleteObject(mask); + + return icon_handle; +} + +_SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + + int big_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); + int sml_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); + HICON big_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[big_img_index], false); + HICON sml_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[sml_img_index], false); + + // if icon creation or lookup has failed for some reason, leave the currently set icon untouched + if (0 != big_icon) { + SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_BIG, (LPARAM) big_icon); + if (0 != _sapp.win32.big_icon) { + DestroyIcon(_sapp.win32.big_icon); + } + _sapp.win32.big_icon = big_icon; + } + if (0 != sml_icon) { + SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_SMALL, (LPARAM) sml_icon); + if (0 != _sapp.win32.small_icon) { + DestroyIcon(_sapp.win32.small_icon); + } + _sapp.win32.small_icon = sml_icon; + } +} + +/* don't laugh, but this seems to be the easiest and most robust + way to check if we're running on Win10 + + From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263 +*/ +_SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) { + HMODULE h = GetModuleHandleW(L"kernel32.dll"); + if (NULL != h) { + return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation")); + } else { + return false; + } +} + +_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_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) + _sapp_d3d11_create_device_and_swapchain(); + _sapp_d3d11_create_default_render_target(); + #elif defined(SOKOL_GLCORE) + _sapp_wgl_init(); + _sapp_wgl_load_extensions(); + _sapp_wgl_create_context(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_init(); + #elif defined(SOKOL_VULKAN) + _sapp_vk_init(); + #endif + _sapp.valid = true; + + 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) { + done = true; + continue; + } else { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + _sapp_win32_frame(false); + // check for window resized, this cannot happen in WM_SIZE as it explodes memory usage + // NOTE: when Vulkan is active, _sapp_win32_update_dimensions() will never return true, + // instead the resize-event is fixed by the swapchain management code + if (_sapp_win32_update_dimensions()) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_swapchain_size_changed(); + #endif + _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); + } + // update mouse-lock state + _sapp_win32_update_mouse_lock(); + } + _sapp_call_cleanup(); + + #if defined(SOKOL_D3D11) + _sapp_d3d11_destroy_default_render_target(); + _sapp_d3d11_destroy_device_and_swapchain(); + #elif defined(SOKOL_GLCORE) + _sapp_wgl_destroy_context(); + _sapp_wgl_shutdown(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_discard(); + #elif defined(SOKOL_VULKAN) + _sapp_vk_discard(); + #endif + _sapp_win32_destroy_window(); + _sapp_win32_destroy_icons(); + _sapp_win32_restore_console(); + _sapp_win32_free_raw_input_data(); + _sapp_discard_state(); +} + +_SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { + int argc = 0; + char** argv = 0; + char* args; + + LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); + if (w_argv == NULL) { + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! + } else { + size_t size = wcslen(w_command_line) * 4; + 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) { + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! + break; + } + argv[i] = args; + size -= (size_t)n; + args += n; + } + LocalFree(w_argv); + } + *o_argc = argc; + return argv; +} + +_SOKOL_PRIVATE bool _sapp_win32_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(0 == _sapp.win32.custom_cursors[cursor]); + const HCURSOR win32_cursor = _sapp_win32_create_icon_from_image(desc, true); + _sapp.win32.custom_cursors[cursor] = win32_cursor; + return win32_cursor != 0; +} + +SOKOL_API_IMPL void _sapp_win32_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + HCURSOR win32_cursor = _sapp.win32.custom_cursors[cursor]; + SOKOL_ASSERT(win32_cursor); + _sapp.win32.custom_cursors[cursor] = 0; + // NOTE: DestroyIcon() may return zero (failure) if the cursor is currently in + // use. Normally that shouldn't happen since when attempting to unbind the + // current cursor it will be hidden first, but since there might be other edge + // cases we just log a warning but don't fail hard + BOOL res = DestroyIcon(win32_cursor); + if (!res) { + _SAPP_WARN(WIN32_DESTROYICON_FOR_CURSOR_FAILED); + } +} + +#if !defined(SOKOL_NO_ENTRY) +#if defined(SOKOL_WIN32_FORCE_MAIN) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_win32_run(&desc); + return 0; +} +#endif /* SOKOL_WIN32_FORCE_MAIN */ +#if defined(SOKOL_WIN32_FORCE_WINMAIN) || !defined(SOKOL_WIN32_FORCE_MAIN) +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { + _SOKOL_UNUSED(hInstance); + _SOKOL_UNUSED(hPrevInstance); + _SOKOL_UNUSED(lpCmdLine); + _SOKOL_UNUSED(nCmdShow); + int argc_utf8 = 0; + 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); + _sapp_free(argv_utf8); + return 0; +} +#endif /* SOKOL_WIN32_FORCE_WINMAIN */ +#endif /* SOKOL_NO_ENTRY */ + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif /* _SAPP_WIN32 */ + +// █████ ███ ██ ██████ ██████ ██████ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██████ +// +// >>android +#if defined(_SAPP_ANDROID) + +/* android loop thread */ +_SOKOL_PRIVATE bool _sapp_android_init_egl(void) { + SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT); + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + return false; + } + if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { + return false; + } + EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; + const EGLint cfg_attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, alpha_size, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 0, + EGL_NONE, + }; + EGLConfig available_cfgs[32]; + EGLint cfg_count; + eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count); + SOKOL_ASSERT(cfg_count > 0); + SOKOL_ASSERT(cfg_count <= 32); + + /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */ + EGLConfig config; + bool exact_cfg_found = false; + for (int i = 0; i < cfg_count; ++i) { + EGLConfig c = available_cfgs[i]; + EGLint r, g, b, a, d; + if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE && + r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) { + exact_cfg_found = true; + config = c; + break; + } + } + if (!exact_cfg_found) { + config = available_cfgs[0]; + } + + EGLint ctx_attributes[] = { + EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl.major_version, + EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl.minor_version, + EGL_NONE, + }; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); + if (context == EGL_NO_CONTEXT) { + return false; + } + + _sapp.android.config = config; + _sapp.android.display = display; + _sapp.android.context = context; + return true; +} + +_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) { + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; + } + if (_sapp.android.context != EGL_NO_CONTEXT) { + eglDestroyContext(_sapp.android.display, _sapp.android.context); + _sapp.android.context = EGL_NO_CONTEXT; + } + eglTerminate(_sapp.android.display); + _sapp.android.display = EGL_NO_DISPLAY; + } +} + +_SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE); + SOKOL_ASSERT(window); + + /* TODO: set window flags */ + /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ + + /* create egl surface and make it current */ + EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL); + if (surface == EGL_NO_SURFACE) { + return false; + } + if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) { + return false; + } + _sapp.android.surface = surface; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + return true; +} + +_SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { + if (_sapp.android.display == EGL_NO_DISPLAY) { + return; + } + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; + } +} + +_SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + SOKOL_ASSERT(window); + + const int32_t win_w = ANativeWindow_getWidth(window); + const int32_t win_h = ANativeWindow_getHeight(window); + SOKOL_ASSERT(win_w >= 0 && win_h >= 0); + const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height); + _sapp.window_width = win_w; + _sapp.window_height = win_h; + if (win_changed || force_update) { + if (!_sapp.desc.high_dpi) { + const int32_t buf_w = win_w / 2; + 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_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_UNUSED(result); + } + } + + /* query surface size */ + 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_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) { + _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_android_cleanup(void) { + if (_sapp.android.surface != EGL_NO_SURFACE) { + /* egl context is bound, cleanup gracefully */ + if (_sapp.init_called && !_sapp.cleanup_called) { + _sapp_call_cleanup(); + } + } + /* always try to cleanup by destroying egl context */ + _sapp_android_cleanup_egl(); +} + +_SOKOL_PRIVATE void _sapp_android_shutdown(void) { + /* try to cleanup while we still have a surface and can call cleanup_cb() */ + _sapp_android_cleanup(); + /* request exit */ + ANativeActivity_finish(_sapp.android.activity); +} + +_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); +} + +_SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { + if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) { + return false; + } + if (!_sapp_events_enabled()) { + return false; + } + int32_t action_idx = AMotionEvent_getAction(e); + int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK; + sapp_event_type type = SAPP_EVENTTYPE_INVALID; + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case AMOTION_EVENT_ACTION_MOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_UP: + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case AMOTION_EVENT_ACTION_CANCEL: + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + break; + } + if (type == SAPP_EVENTTYPE_INVALID) { + return false; + } + int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + _sapp_init_event(type); + _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e); + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int32_t i = 0; i < _sapp.event.num_touches; i++) { + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); + dst->pos_x = (AMotionEvent_getX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; + dst->pos_y = (AMotionEvent_getY(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); + } else { + dst->changed = true; + } + } + _sapp_call_event(&_sapp.event); + return true; +} + +_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { + if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) { + return false; + } + if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) { + /* FIXME: this should be hooked into a "really quit?" mechanism + so the app can ask the user for confirmation, this is currently + generally missing in sokol_app.h + */ + _sapp_android_shutdown(); + return true; + } + return false; +} + +_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) { + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB); + return 1; + } + SOKOL_ASSERT(_sapp.android.current.input); + AInputEvent* event = NULL; + while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) { + if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) { + continue; + } + int32_t handled = 0; + if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { + handled = 1; + } + AInputQueue_finishEvent(_sapp.android.current.input, event, handled); + } + return 1; +} + +_SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { + _SOKOL_UNUSED(data); + if ((events & ALOOPER_EVENT_INPUT) == 0) { + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB); + return 1; + } + + _sapp_android_msg_t msg; + if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { + _SAPP_ERROR(ANDROID_READ_MSG_FAILED); + return 1; + } + + pthread_mutex_lock(&_sapp.android.pt.mutex); + switch (msg) { + case _SOKOL_ANDROID_MSG_CREATE: + { + _SAPP_INFO(ANDROID_MSG_CREATE); + SOKOL_ASSERT(!_sapp.valid); + bool result = _sapp_android_init_egl(); + SOKOL_ASSERT(result); _SOKOL_UNUSED(result); + _sapp.valid = true; + _sapp.android.has_created = true; + } + break; + case _SOKOL_ANDROID_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: + _SAPP_INFO(ANDROID_MSG_PAUSE); + _sapp.android.has_resumed = false; + _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); + break; + case _SOKOL_ANDROID_MSG_FOCUS: + _SAPP_INFO(ANDROID_MSG_FOCUS); + _sapp.android.has_focus = true; + break; + case _SOKOL_ANDROID_MSG_NO_FOCUS: + _SAPP_INFO(ANDROID_MSG_NO_FOCUS); + _sapp.android.has_focus = false; + break; + case _SOKOL_ANDROID_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) { + if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { + _sapp_android_update_dimensions(_sapp.android.pending.window, true); + } else { + _sapp_android_shutdown(); + } + } + } + _sapp.android.current.window = _sapp.android.pending.window; + break; + case _SOKOL_ANDROID_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); + } + if (_sapp.android.pending.input != NULL) { + AInputQueue_attachLooper( + _sapp.android.pending.input, + _sapp.android.looper, + ALOOPER_POLL_CALLBACK, + _sapp_android_input_cb, + NULL); /* data */ + } + } + _sapp.android.current.input = _sapp.android.pending.input; + break; + case _SOKOL_ANDROID_MSG_DESTROY: + _SAPP_INFO(ANDROID_MSG_DESTROY); + _sapp_android_cleanup(); + _sapp.valid = false; + _sapp.android.is_thread_stopping = true; + break; + default: + _SAPP_WARN(ANDROID_UNKNOWN_MSG); + break; + } + pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ + pthread_mutex_unlock(&_sapp.android.pt.mutex); + return 1; +} + +_SOKOL_PRIVATE bool _sapp_android_should_update(void) { + bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus; + bool has_surface = _sapp.android.surface != EGL_NO_SURFACE; + return is_in_front && has_surface; +} + +_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) { + ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); + } else { + ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); + } +} + +_SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { + _SOKOL_UNUSED(arg); + _SAPP_INFO(ANDROID_LOOP_THREAD_STARTED); + + _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); + ALooper_addFd(_sapp.android.looper, + _sapp.android.pt.read_from_main_fd, + ALOOPER_POLL_CALLBACK, + ALOOPER_EVENT_INPUT, + _sapp_android_main_cb, + NULL); /* data */ + + /* signal start to main thread */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_started = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* main loop */ + while (!_sapp.android.is_thread_stopping) { + /* sokol frame */ + if (_sapp_android_should_update()) { + _sapp_android_frame(); + } + + /* process all events (or stop early if app is requested to quit) */ + bool process_events = true; + while (process_events && !_sapp.android.is_thread_stopping) { + bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update(); + process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; + } + } + + /* cleanup thread */ + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); + } + + /* the following causes heap corruption on exit, why?? + ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd); + ALooper_release(_sapp.android.looper);*/ + + /* signal "destroyed" */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_stopped = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + _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)) { + _SAPP_ERROR(ANDROID_WRITE_MSG_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTART); +} + +_SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { + _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_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_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED); + if (has_focus) { + _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); + } else { + _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS); + } +} + +_SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { + _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_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTOP); +} + +_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.window = window; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); + while (_sapp.android.current.window != window) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); +} + +_SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { + _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_UNUSED(activity); + _SOKOL_UNUSED(window); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED); + _sapp_android_msg_set_native_window(NULL); +} + +_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.input = input; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); + while (_sapp.android.current.input != input) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); +} + +_SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { + _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_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_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED); + /* see android:configChanges in manifest */ +} + +_SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY); +} + +_SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { + /* + * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH) + * on my device (Moto X 2nd gen) when the app is removed from the task view + * (TaskStackView: onTaskViewDismissed). + * + * However, if ANativeActivity_finish() is explicitly called from for example + * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? + */ + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONDESTROY); + + /* send destroy msg */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY); + while (!_sapp.android.is_thread_stopped) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* clean up main thread */ + pthread_cond_destroy(&_sapp.android.pt.cond); + pthread_mutex_destroy(&_sapp.android.pt.mutex); + + close(_sapp.android.pt.read_from_main_fd); + close(_sapp.android.pt.write_from_main_fd); + + _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); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { + _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); + _sapp.android.activity = activity; + + int pipe_fd[2]; + if (pipe(pipe_fd) != 0) { + _SAPP_ERROR(ANDROID_CREATE_THREAD_PIPE_FAILED); + return; + } + _sapp.android.pt.read_from_main_fd = pipe_fd[0]; + _sapp.android.pt.write_from_main_fd = pipe_fd[1]; + + pthread_mutex_init(&_sapp.android.pt.mutex, NULL); + pthread_cond_init(&_sapp.android.pt.cond, NULL); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0); + pthread_attr_destroy(&attr); + + /* wait until main loop has started */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + while (!_sapp.android.is_thread_started) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* send create msg */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE); + while (!_sapp.android.has_created) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* register for callbacks */ + activity->callbacks->onStart = _sapp_android_on_start; + activity->callbacks->onResume = _sapp_android_on_resume; + activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; + activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed; + activity->callbacks->onPause = _sapp_android_on_pause; + activity->callbacks->onStop = _sapp_android_on_stop; + activity->callbacks->onDestroy = _sapp_android_on_destroy; + activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created; + /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */ + /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */ + activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed; + activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; + activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; + /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ + /* activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; */ + activity->callbacks->onLowMemory = _sapp_android_on_low_memory; + + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS); + + /* NOT A BUG: do NOT call sapp_discard_state() */ +} + +#endif /* _SAPP_ANDROID */ + +// ██ ██ ███ ██ ██ ██ ██ ██ +// ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ████ ██████ ██ ██ +// +// >>linux +#if defined(_SAPP_LINUX) + +/* see GLFW's xkb_unicode.c */ +static const struct _sapp_x11_codepair { + uint16_t keysym; + uint16_t ucs; +} _sapp_x11_keysymtab[] = { + { 0x01a1, 0x0104 }, + { 0x01a2, 0x02d8 }, + { 0x01a3, 0x0141 }, + { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, + { 0x01a9, 0x0160 }, + { 0x01aa, 0x015e }, + { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, + { 0x01ae, 0x017d }, + { 0x01af, 0x017b }, + { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, + { 0x01b3, 0x0142 }, + { 0x01b5, 0x013e }, + { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, + { 0x01b9, 0x0161 }, + { 0x01ba, 0x015f }, + { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, + { 0x01bd, 0x02dd }, + { 0x01be, 0x017e }, + { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, + { 0x01c3, 0x0102 }, + { 0x01c5, 0x0139 }, + { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, + { 0x01ca, 0x0118 }, + { 0x01cc, 0x011a }, + { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, + { 0x01d1, 0x0143 }, + { 0x01d2, 0x0147 }, + { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, + { 0x01d9, 0x016e }, + { 0x01db, 0x0170 }, + { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, + { 0x01e3, 0x0103 }, + { 0x01e5, 0x013a }, + { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, + { 0x01ea, 0x0119 }, + { 0x01ec, 0x011b }, + { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, + { 0x01f1, 0x0144 }, + { 0x01f2, 0x0148 }, + { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, + { 0x01f9, 0x016f }, + { 0x01fb, 0x0171 }, + { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, + { 0x02a1, 0x0126 }, + { 0x02a6, 0x0124 }, + { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, + { 0x02ac, 0x0134 }, + { 0x02b1, 0x0127 }, + { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, + { 0x02bb, 0x011f }, + { 0x02bc, 0x0135 }, + { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, + { 0x02d5, 0x0120 }, + { 0x02d8, 0x011c }, + { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, + { 0x02e5, 0x010b }, + { 0x02e6, 0x0109 }, + { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, + { 0x02fd, 0x016d }, + { 0x02fe, 0x015d }, + { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, + { 0x03a5, 0x0128 }, + { 0x03a6, 0x013b }, + { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, + { 0x03ac, 0x0166 }, + { 0x03b3, 0x0157 }, + { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, + { 0x03ba, 0x0113 }, + { 0x03bb, 0x0123 }, + { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03c0, 0x0100 }, + { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, + { 0x03cf, 0x012a }, + { 0x03d1, 0x0145 }, + { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, + { 0x03d9, 0x0172 }, + { 0x03dd, 0x0168 }, + { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, + { 0x03e7, 0x012f }, + { 0x03ec, 0x0117 }, + { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, + { 0x03f2, 0x014d }, + { 0x03f3, 0x0137 }, + { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, + { 0x03fe, 0x016b }, + { 0x047e, 0x203e }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04a4, 0x3001 }, + { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, + { 0x04a7, 0x30a1 }, + { 0x04a8, 0x30a3 }, + { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, + { 0x04ab, 0x30a9 }, + { 0x04ac, 0x30e3 }, + { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, + { 0x04af, 0x30c3 }, + { 0x04b0, 0x30fc }, + { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, + { 0x04b3, 0x30a6 }, + { 0x04b4, 0x30a8 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04d4, 0x30e4 }, + { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06c3, 0x0446 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06c6, 0x0444 }, + { 0x06c7, 0x0433 }, + { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06d6, 0x0436 }, + { 0x06d7, 0x0432 }, + { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, + { 0x06da, 0x0437 }, + { 0x06db, 0x0448 }, + { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, + { 0x06de, 0x0447 }, + { 0x06df, 0x044a }, + { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06e3, 0x0426 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06e6, 0x0424 }, + { 0x06e7, 0x0413 }, + { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06f6, 0x0416 }, + { 0x06f7, 0x0412 }, + { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, + { 0x06fa, 0x0417 }, + { 0x06fb, 0x0428 }, + { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, + { 0x06fe, 0x0427 }, + { 0x06ff, 0x042a }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07a9, 0x03ab }, + { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, + { 0x07af, 0x2015 }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07b5, 0x03ca }, + { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07b9, 0x03cb }, + { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f2, 0x03c3 }, + { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x08a1, 0x23b7 }, + { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08bc, 0x2264 }, + { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08c5, 0x2207 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, + { 0x08ce, 0x21d2 }, + { 0x08cf, 0x2261 }, + { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08ef, 0x2202 }, + { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, + { 0x09e1, 0x2592 }, + { 0x09e2, 0x2409 }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e5, 0x240a }, + { 0x09e8, 0x2424 }, + { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, + { 0x09eb, 0x2510 }, + { 0x09ec, 0x250c }, + { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, + { 0x09f7, 0x252c }, + { 0x09f8, 0x2502 }, + { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, + { 0x0aae, 0x2026 }, + { 0x0aaf, 0x2025 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, + { 0x0acc, 0x25c1 }, + { 0x0acd, 0x25b7 }, + { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0ad4, 0x211e }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, + { 0x0adb, 0x25ac }, + { 0x0adc, 0x25c0 }, + { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, + { 0x0adf, 0x25ae }, + { 0x0ae0, 0x25e6 }, + { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, + { 0x0ae3, 0x25b3 }, + { 0x0ae4, 0x25bd }, + { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, + { 0x0ae7, 0x25aa }, + { 0x0ae8, 0x25b2 }, + { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0aec, 0x2663 }, + { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, + { 0x0af0, 0x2720 }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0af5, 0x266f }, + { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, + { 0x0af8, 0x2640 }, + { 0x0af9, 0x260e }, + { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, + { 0x0afc, 0x2038 }, + { 0x0afd, 0x201a }, + { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, + { 0x0ba6, 0x003e }, + { 0x0ba8, 0x2228 }, + { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, + { 0x0bc2, 0x22a5 }, + { 0x0bc3, 0x2229 }, + { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, + { 0x0bca, 0x2218 }, + { 0x0bcc, 0x2395 }, + { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, + { 0x0bd3, 0x2308 }, + { 0x0bd6, 0x222a }, + { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, + { 0x0bdc, 0x22a2 }, + { 0x0bfc, 0x22a3 }, + { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, + { 0x13a4, 0x20ac }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, + { 0x20ac, 0x20ac }, + { 0xfe50, '`' }, + { 0xfe51, 0x00b4 }, + { 0xfe52, '^' }, + { 0xfe53, '~' }, + { 0xfe54, 0x00af }, + { 0xfe55, 0x02d8 }, + { 0xfe56, 0x02d9 }, + { 0xfe57, 0x00a8 }, + { 0xfe58, 0x02da }, + { 0xfe59, 0x02dd }, + { 0xfe5a, 0x02c7 }, + { 0xfe5b, 0x00b8 }, + { 0xfe5c, 0x02db }, + { 0xfe5d, 0x037a }, + { 0xfe5e, 0x309b }, + { 0xfe5f, 0x309c }, + { 0xfe63, '/' }, + { 0xfe64, 0x02bc }, + { 0xfe65, 0x02bd }, + { 0xfe66, 0x02f5 }, + { 0xfe67, 0x02f3 }, + { 0xfe68, 0x02cd }, + { 0xfe69, 0xa788 }, + { 0xfe6a, 0x02f7 }, + { 0xfe6e, ',' }, + { 0xfe6f, 0x00a4 }, + { 0xfe80, 'a' }, /* XK_dead_a */ + { 0xfe81, 'A' }, /* XK_dead_A */ + { 0xfe82, 'e' }, /* XK_dead_e */ + { 0xfe83, 'E' }, /* XK_dead_E */ + { 0xfe84, 'i' }, /* XK_dead_i */ + { 0xfe85, 'I' }, /* XK_dead_I */ + { 0xfe86, 'o' }, /* XK_dead_o */ + { 0xfe87, 'O' }, /* XK_dead_O */ + { 0xfe88, 'u' }, /* XK_dead_u */ + { 0xfe89, 'U' }, /* XK_dead_U */ + { 0xfe8a, 0x0259 }, + { 0xfe8b, 0x018f }, + { 0xfe8c, 0x00b5 }, + { 0xfe90, '_' }, + { 0xfe91, 0x02c8 }, + { 0xfe92, 0x02cc }, + { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, + { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, + { 0xffab /*XKB_KEY_KP_Add*/, '+' }, + { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, + { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, + { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, + { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, + { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } +}; + +_SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { + _SOKOL_UNUSED(display); + _sapp.x11.error_code = event->error_code; + return 0; +} + +_SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { + _sapp.x11.error_code = Success; + XSetErrorHandler(_sapp_x11_error_handler); +} + +_SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { + XSync(_sapp.x11.display, False); + XSetErrorHandler(NULL); +} + +_SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { + _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False); + _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False); + _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False); + _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False); + _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False); + _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); + _sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False); + _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); + _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); + _sapp.x11.CLIPBOARD = XInternAtom(_sapp.x11.display, "CLIPBOARD", False); + _sapp.x11.TARGETS = XInternAtom(_sapp.x11.display, "TARGETS", False); + if (_sapp.drop.enabled) { + _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); + _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); + _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); + _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); + _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); + _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); + _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); + _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); + _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); + _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); + } + + /* check Xi extension for raw mouse input */ + if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { + _sapp.x11.xi.major = 2; + _sapp.x11.xi.minor = 0; + if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) { + _sapp.x11.xi.available = true; + } + } +} + +// translate the X11 KeySyms for a key to sokol-app key code +// NOTE: this is only used as a fallback, in case the XBK method fails +// it is layout-dependent and will fail partially on most non-US layouts. +// +_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_keysyms(const KeySym* keysyms, int width) { + if (width > 1) { + switch (keysyms[1]) { + case XK_KP_0: return SAPP_KEYCODE_KP_0; + case XK_KP_1: return SAPP_KEYCODE_KP_1; + case XK_KP_2: return SAPP_KEYCODE_KP_2; + case XK_KP_3: return SAPP_KEYCODE_KP_3; + case XK_KP_4: return SAPP_KEYCODE_KP_4; + case XK_KP_5: return SAPP_KEYCODE_KP_5; + case XK_KP_6: return SAPP_KEYCODE_KP_6; + case XK_KP_7: return SAPP_KEYCODE_KP_7; + case XK_KP_8: return SAPP_KEYCODE_KP_8; + case XK_KP_9: return SAPP_KEYCODE_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return SAPP_KEYCODE_KP_DECIMAL; + case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; + case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; + default: break; + } + } + + switch (keysyms[0]) { + case XK_Escape: return SAPP_KEYCODE_ESCAPE; + case XK_Tab: return SAPP_KEYCODE_TAB; + case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; + case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; + case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; + case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Meta_R: + case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; + case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; + case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; + case XK_Menu: return SAPP_KEYCODE_MENU; + case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; + case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; + case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; + case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; + case XK_Pause: return SAPP_KEYCODE_PAUSE; + case XK_Delete: return SAPP_KEYCODE_DELETE; + case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; + case XK_Return: return SAPP_KEYCODE_ENTER; + case XK_Home: return SAPP_KEYCODE_HOME; + case XK_End: return SAPP_KEYCODE_END; + case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; + case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; + case XK_Insert: return SAPP_KEYCODE_INSERT; + case XK_Left: return SAPP_KEYCODE_LEFT; + case XK_Right: return SAPP_KEYCODE_RIGHT; + case XK_Down: return SAPP_KEYCODE_DOWN; + case XK_Up: return SAPP_KEYCODE_UP; + case XK_F1: return SAPP_KEYCODE_F1; + case XK_F2: return SAPP_KEYCODE_F2; + case XK_F3: return SAPP_KEYCODE_F3; + case XK_F4: return SAPP_KEYCODE_F4; + case XK_F5: return SAPP_KEYCODE_F5; + case XK_F6: return SAPP_KEYCODE_F6; + case XK_F7: return SAPP_KEYCODE_F7; + case XK_F8: return SAPP_KEYCODE_F8; + case XK_F9: return SAPP_KEYCODE_F9; + case XK_F10: return SAPP_KEYCODE_F10; + case XK_F11: return SAPP_KEYCODE_F11; + case XK_F12: return SAPP_KEYCODE_F12; + case XK_F13: return SAPP_KEYCODE_F13; + case XK_F14: return SAPP_KEYCODE_F14; + case XK_F15: return SAPP_KEYCODE_F15; + case XK_F16: return SAPP_KEYCODE_F16; + case XK_F17: return SAPP_KEYCODE_F17; + case XK_F18: return SAPP_KEYCODE_F18; + case XK_F19: return SAPP_KEYCODE_F19; + case XK_F20: return SAPP_KEYCODE_F20; + case XK_F21: return SAPP_KEYCODE_F21; + case XK_F22: return SAPP_KEYCODE_F22; + case XK_F23: return SAPP_KEYCODE_F23; + case XK_F24: return SAPP_KEYCODE_F24; + case XK_F25: return SAPP_KEYCODE_F25; + + // numeric keypad + case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; + case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; + case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; + case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; + + // these should have been detected in secondary keysym test above! + case XK_KP_Insert: return SAPP_KEYCODE_KP_0; + case XK_KP_End: return SAPP_KEYCODE_KP_1; + case XK_KP_Down: return SAPP_KEYCODE_KP_2; + case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; + case XK_KP_Left: return SAPP_KEYCODE_KP_4; + case XK_KP_Right: return SAPP_KEYCODE_KP_6; + case XK_KP_Home: return SAPP_KEYCODE_KP_7; + case XK_KP_Up: return SAPP_KEYCODE_KP_8; + case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; + case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; + case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; + case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; + + // last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: return SAPP_KEYCODE_A; + case XK_b: return SAPP_KEYCODE_B; + case XK_c: return SAPP_KEYCODE_C; + case XK_d: return SAPP_KEYCODE_D; + case XK_e: return SAPP_KEYCODE_E; + case XK_f: return SAPP_KEYCODE_F; + case XK_g: return SAPP_KEYCODE_G; + case XK_h: return SAPP_KEYCODE_H; + case XK_i: return SAPP_KEYCODE_I; + case XK_j: return SAPP_KEYCODE_J; + case XK_k: return SAPP_KEYCODE_K; + case XK_l: return SAPP_KEYCODE_L; + case XK_m: return SAPP_KEYCODE_M; + case XK_n: return SAPP_KEYCODE_N; + case XK_o: return SAPP_KEYCODE_O; + case XK_p: return SAPP_KEYCODE_P; + case XK_q: return SAPP_KEYCODE_Q; + case XK_r: return SAPP_KEYCODE_R; + case XK_s: return SAPP_KEYCODE_S; + case XK_t: return SAPP_KEYCODE_T; + case XK_u: return SAPP_KEYCODE_U; + case XK_v: return SAPP_KEYCODE_V; + case XK_w: return SAPP_KEYCODE_W; + case XK_x: return SAPP_KEYCODE_X; + case XK_y: return SAPP_KEYCODE_Y; + case XK_z: return SAPP_KEYCODE_Z; + case XK_1: return SAPP_KEYCODE_1; + case XK_2: return SAPP_KEYCODE_2; + case XK_3: return SAPP_KEYCODE_3; + case XK_4: return SAPP_KEYCODE_4; + case XK_5: return SAPP_KEYCODE_5; + case XK_6: return SAPP_KEYCODE_6; + case XK_7: return SAPP_KEYCODE_7; + case XK_8: return SAPP_KEYCODE_8; + case XK_9: return SAPP_KEYCODE_9; + case XK_0: return SAPP_KEYCODE_0; + case XK_space: return SAPP_KEYCODE_SPACE; + case XK_minus: return SAPP_KEYCODE_MINUS; + case XK_equal: return SAPP_KEYCODE_EQUAL; + case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; + case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; + case XK_backslash: return SAPP_KEYCODE_BACKSLASH; + case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; + case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; + case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; + case XK_comma: return SAPP_KEYCODE_COMMA; + case XK_period: return SAPP_KEYCODE_PERIOD; + case XK_slash: return SAPP_KEYCODE_SLASH; + case XK_less: return SAPP_KEYCODE_WORLD_1; // At least in some layouts... + default: break; + } + + // no matching translation was found + return SAPP_KEYCODE_INVALID; +} + + +// setup dynamic keycode/scancode mapping tables, this is required +// for getting layout-independent keycodes on X11. +// +// see GLFW x11_init.c/createKeyTables() +_SOKOL_PRIVATE void _sapp_x11_init_keytable(void) { + for (int i = 0; i < SAPP_MAX_KEYCODES; i++) { + _sapp.keycodes[i] = SAPP_KEYCODE_INVALID; + } + // use XKB to determine physical key locations independently of the current keyboard layout + XkbDescPtr desc = XkbGetMap(_sapp.x11.display, 0, XkbUseCoreKbd); + SOKOL_ASSERT(desc); + XkbGetNames(_sapp.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); + + const int scancode_min = desc->min_key_code; + const int scancode_max = desc->max_key_code; + + const struct { sapp_keycode key; const char* name; } keymap[] = { + { SAPP_KEYCODE_GRAVE_ACCENT, "TLDE" }, + { SAPP_KEYCODE_1, "AE01" }, + { SAPP_KEYCODE_2, "AE02" }, + { SAPP_KEYCODE_3, "AE03" }, + { SAPP_KEYCODE_4, "AE04" }, + { SAPP_KEYCODE_5, "AE05" }, + { SAPP_KEYCODE_6, "AE06" }, + { SAPP_KEYCODE_7, "AE07" }, + { SAPP_KEYCODE_8, "AE08" }, + { SAPP_KEYCODE_9, "AE09" }, + { SAPP_KEYCODE_0, "AE10" }, + { SAPP_KEYCODE_MINUS, "AE11" }, + { SAPP_KEYCODE_EQUAL, "AE12" }, + { SAPP_KEYCODE_Q, "AD01" }, + { SAPP_KEYCODE_W, "AD02" }, + { SAPP_KEYCODE_E, "AD03" }, + { SAPP_KEYCODE_R, "AD04" }, + { SAPP_KEYCODE_T, "AD05" }, + { SAPP_KEYCODE_Y, "AD06" }, + { SAPP_KEYCODE_U, "AD07" }, + { SAPP_KEYCODE_I, "AD08" }, + { SAPP_KEYCODE_O, "AD09" }, + { SAPP_KEYCODE_P, "AD10" }, + { SAPP_KEYCODE_LEFT_BRACKET, "AD11" }, + { SAPP_KEYCODE_RIGHT_BRACKET, "AD12" }, + { SAPP_KEYCODE_A, "AC01" }, + { SAPP_KEYCODE_S, "AC02" }, + { SAPP_KEYCODE_D, "AC03" }, + { SAPP_KEYCODE_F, "AC04" }, + { SAPP_KEYCODE_G, "AC05" }, + { SAPP_KEYCODE_H, "AC06" }, + { SAPP_KEYCODE_J, "AC07" }, + { SAPP_KEYCODE_K, "AC08" }, + { SAPP_KEYCODE_L, "AC09" }, + { SAPP_KEYCODE_SEMICOLON, "AC10" }, + { SAPP_KEYCODE_APOSTROPHE, "AC11" }, + { SAPP_KEYCODE_Z, "AB01" }, + { SAPP_KEYCODE_X, "AB02" }, + { SAPP_KEYCODE_C, "AB03" }, + { SAPP_KEYCODE_V, "AB04" }, + { SAPP_KEYCODE_B, "AB05" }, + { SAPP_KEYCODE_N, "AB06" }, + { SAPP_KEYCODE_M, "AB07" }, + { SAPP_KEYCODE_COMMA, "AB08" }, + { SAPP_KEYCODE_PERIOD, "AB09" }, + { SAPP_KEYCODE_SLASH, "AB10" }, + { SAPP_KEYCODE_BACKSLASH, "BKSL" }, + { SAPP_KEYCODE_WORLD_1, "LSGT" }, + { SAPP_KEYCODE_SPACE, "SPCE" }, + { SAPP_KEYCODE_ESCAPE, "ESC" }, + { SAPP_KEYCODE_ENTER, "RTRN" }, + { SAPP_KEYCODE_TAB, "TAB" }, + { SAPP_KEYCODE_BACKSPACE, "BKSP" }, + { SAPP_KEYCODE_INSERT, "INS" }, + { SAPP_KEYCODE_DELETE, "DELE" }, + { SAPP_KEYCODE_RIGHT, "RGHT" }, + { SAPP_KEYCODE_LEFT, "LEFT" }, + { SAPP_KEYCODE_DOWN, "DOWN" }, + { SAPP_KEYCODE_UP, "UP" }, + { SAPP_KEYCODE_PAGE_UP, "PGUP" }, + { SAPP_KEYCODE_PAGE_DOWN, "PGDN" }, + { SAPP_KEYCODE_HOME, "HOME" }, + { SAPP_KEYCODE_END, "END" }, + { SAPP_KEYCODE_CAPS_LOCK, "CAPS" }, + { SAPP_KEYCODE_SCROLL_LOCK, "SCLK" }, + { SAPP_KEYCODE_NUM_LOCK, "NMLK" }, + { SAPP_KEYCODE_PRINT_SCREEN, "PRSC" }, + { SAPP_KEYCODE_PAUSE, "PAUS" }, + { SAPP_KEYCODE_F1, "FK01" }, + { SAPP_KEYCODE_F2, "FK02" }, + { SAPP_KEYCODE_F3, "FK03" }, + { SAPP_KEYCODE_F4, "FK04" }, + { SAPP_KEYCODE_F5, "FK05" }, + { SAPP_KEYCODE_F6, "FK06" }, + { SAPP_KEYCODE_F7, "FK07" }, + { SAPP_KEYCODE_F8, "FK08" }, + { SAPP_KEYCODE_F9, "FK09" }, + { SAPP_KEYCODE_F10, "FK10" }, + { SAPP_KEYCODE_F11, "FK11" }, + { SAPP_KEYCODE_F12, "FK12" }, + { SAPP_KEYCODE_F13, "FK13" }, + { SAPP_KEYCODE_F14, "FK14" }, + { SAPP_KEYCODE_F15, "FK15" }, + { SAPP_KEYCODE_F16, "FK16" }, + { SAPP_KEYCODE_F17, "FK17" }, + { SAPP_KEYCODE_F18, "FK18" }, + { SAPP_KEYCODE_F19, "FK19" }, + { SAPP_KEYCODE_F20, "FK20" }, + { SAPP_KEYCODE_F21, "FK21" }, + { SAPP_KEYCODE_F22, "FK22" }, + { SAPP_KEYCODE_F23, "FK23" }, + { SAPP_KEYCODE_F24, "FK24" }, + { SAPP_KEYCODE_F25, "FK25" }, + { SAPP_KEYCODE_KP_0, "KP0" }, + { SAPP_KEYCODE_KP_1, "KP1" }, + { SAPP_KEYCODE_KP_2, "KP2" }, + { SAPP_KEYCODE_KP_3, "KP3" }, + { SAPP_KEYCODE_KP_4, "KP4" }, + { SAPP_KEYCODE_KP_5, "KP5" }, + { SAPP_KEYCODE_KP_6, "KP6" }, + { SAPP_KEYCODE_KP_7, "KP7" }, + { SAPP_KEYCODE_KP_8, "KP8" }, + { SAPP_KEYCODE_KP_9, "KP9" }, + { SAPP_KEYCODE_KP_DECIMAL, "KPDL" }, + { SAPP_KEYCODE_KP_DIVIDE, "KPDV" }, + { SAPP_KEYCODE_KP_MULTIPLY, "KPMU" }, + { SAPP_KEYCODE_KP_SUBTRACT, "KPSU" }, + { SAPP_KEYCODE_KP_ADD, "KPAD" }, + { SAPP_KEYCODE_KP_ENTER, "KPEN" }, + { SAPP_KEYCODE_KP_EQUAL, "KPEQ" }, + { SAPP_KEYCODE_LEFT_SHIFT, "LFSH" }, + { SAPP_KEYCODE_LEFT_CONTROL, "LCTL" }, + { SAPP_KEYCODE_LEFT_ALT, "LALT" }, + { SAPP_KEYCODE_LEFT_SUPER, "LWIN" }, + { SAPP_KEYCODE_RIGHT_SHIFT, "RTSH" }, + { SAPP_KEYCODE_RIGHT_CONTROL, "RCTL" }, + { SAPP_KEYCODE_RIGHT_ALT, "RALT" }, + { SAPP_KEYCODE_RIGHT_ALT, "LVL3" }, + { SAPP_KEYCODE_RIGHT_ALT, "MDSW" }, + { SAPP_KEYCODE_RIGHT_SUPER, "RWIN" }, + { SAPP_KEYCODE_MENU, "MENU" } + }; + const int num_keymap_items = (int)(sizeof(keymap) / sizeof(keymap[0])); + + // find X11 keycode to sokol-app key code mapping + for (int scancode = scancode_min; scancode <= scancode_max; scancode++) { + sapp_keycode key = SAPP_KEYCODE_INVALID; + for (int i = 0; i < num_keymap_items; i++) { + if (strncmp(desc->names->keys[scancode].name, keymap[i].name, XkbKeyNameLength) == 0) { + key = keymap[i].key; + break; + } + } + + // fall back to key aliases in case the key name did not match + for (int i = 0; i < desc->names->num_key_aliases; i++) { + if (key != SAPP_KEYCODE_INVALID) { + break; + } + if (strncmp(desc->names->key_aliases[i].real, desc->names->keys[scancode].name, XkbKeyNameLength) != 0) { + continue; + } + for (int j = 0; j < num_keymap_items; j++) { + if (strncmp(desc->names->key_aliases[i].alias, keymap[i].name, XkbKeyNameLength) == 0) { + key = keymap[i].key; + break; + } + } + } + _sapp.keycodes[scancode] = key; + } + XkbFreeNames(desc, XkbKeyNamesMask, True); + XkbFreeKeyboard(desc, 0, True); + + int width = 0; + KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode_min, scancode_max - scancode_min + 1, &width); + for (int scancode = scancode_min; scancode <= scancode_max; scancode++) { + // translate untranslated key codes using the traditional X11 KeySym lookups + if (_sapp.keycodes[scancode] == SAPP_KEYCODE_INVALID) { + const size_t base = (size_t)((scancode - scancode_min) * width); + _sapp.keycodes[scancode] = _sapp_x11_translate_keysyms(&keysyms[base], width); + } + } + XFree(keysyms); +} + +_SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { + /* from GLFW: + + NOTE: Default to the display-wide DPI as we don't currently have a policy + for which monitor a window is considered to be on + + _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) * + 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen); + + NOTE: Basing the scale on Xft.dpi where available should provide the most + 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); + if (db) { + XrmValue value; + char* type = NULL; + 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; + while (true) { + const char* where = strstr(start, ext); + if (!where) { + return false; + } + const char* terminator = where + strlen(ext); + if ((where == start) || (*(where - 1) == ' ')) { + if (*terminator == ' ' || *terminator == '\0') { + break; + } + } + start = terminator; + } + return true; +} + +_SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) { + if (extensions) { + return _sapp_glx_has_ext(ext, extensions); + } else { + return false; + } +} + +_SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) +{ + if (_sapp.glx.GetProcAddress) { + return (void*) _sapp.glx.GetProcAddress(procname); + } else if (_sapp.glx.GetProcAddressARB) { + return (void*) _sapp.glx.GetProcAddressARB(procname); + } else { + return dlsym(_sapp.glx.libgl, procname); + } +} + +_SOKOL_PRIVATE void _sapp_glx_init(void) { + const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; + for (int i = 0; sonames[i]; i++) { + _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); + if (_sapp.glx.libgl) { + break; + } + } + if (!_sapp.glx.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"); + _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString"); + _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension"); + _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion"); + _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext"); + _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent"); + _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers"); + _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString"); + _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow"); + _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow"); + _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress"); + _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB"); + _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig"); + if (!_sapp.glx.GetFBConfigs || + !_sapp.glx.GetFBConfigAttrib || + !_sapp.glx.GetClientString || + !_sapp.glx.QueryExtension || + !_sapp.glx.QueryVersion || + !_sapp.glx.DestroyContext || + !_sapp.glx.MakeCurrent || + !_sapp.glx.SwapBuffers || + !_sapp.glx.QueryExtensionsString || + !_sapp.glx.CreateWindow || + !_sapp.glx.DestroyWindow || + !_sapp.glx.GetProcAddress || + !_sapp.glx.GetProcAddressARB || + !_sapp.glx.GetVisualFromFBConfig) + { + _SAPP_PANIC(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED); + } + + if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { + _SAPP_PANIC(LINUX_GLX_EXTENSION_NOT_FOUND); + } + if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { + _SAPP_PANIC(LINUX_GLX_QUERY_VERSION_FAILED); + } + if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { + _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)) { + _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); + _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT; + } + if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { + _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); + _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA; + } + _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); + if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { + _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); + _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; + } + _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); +} + +_SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { + int value; + _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value); + return value; +} + +_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig(void) { + GLXFBConfig* native_configs; + _sapp_gl_fbconfig* usable_configs; + const _sapp_gl_fbconfig* closest; + int i, native_count, usable_count; + const char* vendor; + bool trust_window_bit = true; + + /* HACK: This is a (hopefully temporary) workaround for Chromium + (VirtualBox GL) not setting the window bit on any GLXFBConfigs + */ + vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR); + if (vendor && strcmp(vendor, "Chromium") == 0) { + trust_window_bit = false; + } + + native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); + if (!native_configs || !native_count) { + _SAPP_PANIC(LINUX_GLX_NO_GLXFBCONFIGS); + } + + 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]; + _sapp_gl_fbconfig* u = usable_configs + usable_count; + _sapp_gl_init_fbconfig(u); + + /* Only consider RGBA GLXFBConfigs */ + if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) { + continue; + } + /* Only consider window GLXFBConfigs */ + if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { + if (trust_window_bit) { + continue; + } + } + u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE); + u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE); + u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE); + u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE); + u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE); + u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE); + if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { + u->doublebuffer = true; + } + if (_sapp.glx.ARB_multisample) { + u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); + } + u->handle = (uintptr_t) n; + usable_count++; + } + _sapp_gl_fbconfig desired; + _sapp_gl_init_fbconfig(&desired); + desired.red_bits = 8; + desired.green_bits = 8; + desired.blue_bits = 8; + desired.alpha_bits = 8; + desired.depth_bits = 24; + desired.stencil_bits = 8; + desired.doublebuffer = true; + desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; + closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); + GLXFBConfig result = 0; + if (closest) { + result = (GLXFBConfig) closest->handle; + } + XFree(native_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_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); + } + XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); + if (!result) { + _SAPP_PANIC(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED); + } + *visual = result->visual; + *depth = result->depth; + XFree(result); +} + +_SOKOL_PRIVATE void _sapp_glx_make_current(void) { + _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); +} + +_SOKOL_PRIVATE void _sapp_glx_create_context(void) { + GLXFBConfig native = _sapp_glx_choosefbconfig(); + if (0 == native){ + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); + } + if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { + _SAPP_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING); + } + _sapp_x11_grab_error_handler(); + const int attribs[] = { + 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_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_PANIC(LINUX_GLX_CREATE_WINDOW_FAILED); + } + _sapp_glx_make_current(); +} + +_SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { + if (_sapp.glx.window) { + _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window); + _sapp.glx.window = 0; + } + if (_sapp.glx.ctx) { + _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); + _sapp.glx.ctx = 0; + } +} + +_SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { + _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); +} + +_SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { + if (_sapp.glx.EXT_swap_control) { + _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); + } else if (_sapp.glx.MESA_swap_control) { + _sapp.glx.SwapIntervalMESA(interval); + } +} + +#endif // _SAPP_GLX + +_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { + _SAPP_STRUCT(XEvent, event); + event.type = ClientMessage; + event.xclient.window = _sapp.x11.window; + event.xclient.format = 32; + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_sapp.x11.display, _sapp.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + +_SOKOL_PRIVATE bool _sapp_x11_wait_for_event(int event_type, double timeout_sec, XEvent* out_event) { + _sapp_timestamp_t ts; + _sapp_timestamp_init(&ts); + while (!XCheckTypedWindowEvent(_sapp.x11.display, _sapp.x11.window, event_type, out_event)) { + struct pollfd fd = { ConnectionNumber(_sapp.x11.display), POLLIN, 0 }; + poll(&fd, 1, timeout_sec * 1000); + if (_sapp_timestamp_now(&ts) > timeout_sec) { + return false; + } + } + return true; +} + +_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_update_dimensions(int x11_window_width, int x11_window_height) { + // NOTE: do *NOT* use _sapp.dpi_scale for the window scale + const float window_scale = _sapp.x11.dpi / 96.0f; + _sapp.window_width = _sapp_roundf_gzero(x11_window_width / window_scale); + _sapp.window_height = _sapp_roundf_gzero(x11_window_height / window_scale); + // NOTE: on Vulkan, updating the framebuffer dimensions is entirely handled + // by the swapchain management code + #if !defined(SOKOL_VULKAN) + int cur_fb_width = _sapp.framebuffer_width; + int cur_fb_height = _sapp.framebuffer_height; + _sapp.framebuffer_width = _sapp_roundf_gzero(_sapp.window_width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(_sapp.window_height * _sapp.dpi_scale); + bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + #if defined(SOKOL_WGPU) + _sapp_wgpu_swapchain_size_changed(); + #endif + if (!_sapp.first_frame) { + _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); + } + } + #endif +} + +_SOKOL_PRIVATE void _sapp_x11_update_dimensions_from_window_size(void) { + XWindowAttributes attribs; + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); + _sapp_x11_update_dimensions(attribs.width, attribs.height); +} + +_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { + /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ + if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { + if (enable) { + const int _NET_WM_STATE_ADD = 1; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } else { + const int _NET_WM_STATE_REMOVE = 0; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { + SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor); + const int w = 16; + const int h = 16; + XcursorImage* img = XcursorImageCreate(w, h); + SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels); + img->xhot = 0; + img->yhot = 0; + const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel); + _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.standard_cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); + } + } + if (0 == _sapp.x11.standard_cursors[cursor]) { + _sapp.x11.standard_cursors[cursor] = XCreateFontCursor(_sapp.x11.display, fallback_native); + } +} + +_SOKOL_PRIVATE void _sapp_x11_create_standard_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_standard_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.standard_cursors[i]) { + XFreeCursor(_sapp.x11.display, _sapp.x11.standard_cursors[i]); + _sapp.x11.standard_cursors[i] = 0; + } + } +} + +_SOKOL_PRIVATE bool _sapp_x11_make_custom_mouse_cursor(sapp_mouse_cursor cursor, const sapp_image_desc* desc) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(0 == _sapp.x11.custom_cursors[cursor]); + XcursorImage* img = XcursorImageCreate(desc->width, desc->height); + SOKOL_ASSERT(img && ((int) img->width == desc->width) && ((int) img->height == desc->height) && img->pixels); + img->xhot = (XcursorDim) desc->cursor_hotspot_x; + img->yhot = (XcursorDim) desc->cursor_hotspot_y; + const size_t dest_num_bytes = (size_t)(img->width * img->height) * sizeof(XcursorPixel); + SOKOL_ASSERT(dest_num_bytes == desc->pixels.size); + // Copy RGBA -> BGRA + for (size_t i = 0; i < dest_num_bytes; i += 4) { + ((uint8_t*) img->pixels)[i+0] = ((uint8_t*) desc->pixels.ptr)[i+2]; + ((uint8_t*) img->pixels)[i+1] = ((uint8_t*) desc->pixels.ptr)[i+1]; + ((uint8_t*) img->pixels)[i+2] = ((uint8_t*) desc->pixels.ptr)[i+0]; + ((uint8_t*) img->pixels)[i+3] = ((uint8_t*) desc->pixels.ptr)[i+3]; + } + _sapp.x11.custom_cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); + return 0 != _sapp.x11.custom_cursors[cursor]; +} + +_SOKOL_PRIVATE void _sapp_x11_destroy_custom_mouse_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + Cursor xcursor = _sapp.x11.custom_cursors[cursor]; + _sapp.x11.custom_cursors[cursor] = 0; + SOKOL_ASSERT(xcursor); + XFreeCursor(_sapp.x11.display, xcursor); +} + +_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { + _sapp.fullscreen = !_sapp.fullscreen; + _sapp_x11_set_fullscreen(_sapp.fullscreen); + _sapp_x11_update_dimensions_from_window_size(); +} + +_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.custom_cursor_bound[cursor]) { + Cursor xcursor = _sapp.x11.custom_cursors[cursor]; + SOKOL_ASSERT(0 != xcursor); + XDefineCursor(_sapp.x11.display, _sapp.x11.window, xcursor); + } else if (_sapp.x11.standard_cursors[cursor]) { + XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.standard_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) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + if (_sapp.mouse.locked) { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XGrabPointer(_sapp.x11.display, // display + _sapp.x11.window, // grab_window + True, // owner_events + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask + GrabModeAsync, // pointer_mode + GrabModeAsync, // keyboard_mode + _sapp.x11.window, // confine_to + _sapp.x11.hidden_cursor, // cursor + CurrentTime); // time + } else { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[] = { 0 }; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); + XUngrabPointer(_sapp.x11.display, CurrentTime); + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_set_clipboard_string(const char* str) { + SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); + if (strlen(str) >= (size_t)_sapp.clipboard.buf_size) { + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); + } + XSetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD, _sapp.x11.window, CurrentTime); + if (XGetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD) != _sapp.x11.window) { + _SAPP_ERROR(LINUX_X11_FAILED_TO_BECOME_OWNER_OF_CLIPBOARD); + } +} + +_SOKOL_PRIVATE const char* _sapp_x11_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); + Atom none = XInternAtom(_sapp.x11.display, "SAPP_SELECTION", False); + Atom incremental = XInternAtom(_sapp.x11.display, "INCR", False); + if (XGetSelectionOwner(_sapp.x11.display, _sapp.x11.CLIPBOARD) == _sapp.x11.window) { + // Instead of doing a large number of X round-trips just to put this + // string into a window property and then read it back, just return it + return _sapp.clipboard.buffer; + } + XConvertSelection(_sapp.x11.display, + _sapp.x11.CLIPBOARD, + _sapp.x11.UTF8_STRING, + none, + _sapp.x11.window, + CurrentTime); + XEvent event; + if (!_sapp_x11_wait_for_event(SelectionNotify, 0.1, &event)) { + return NULL; + } + if (event.xselection.property == None) { + return NULL; + } + char* data = NULL; + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + const bool ret = XGetWindowProperty(_sapp.x11.display, + event.xselection.requestor, + event.xselection.property, + 0, + LONG_MAX, + True, + _sapp.x11.UTF8_STRING, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + if (ret != Success || data == NULL) { + if (data != NULL) { + XFree(data); + } + return NULL; + } + if ((actualType == incremental) || (itemCount >= (size_t)_sapp.clipboard.buf_size)) { + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); + XFree(data); + return NULL; + } + _sapp_strcpy(data, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); + XFree(data); + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { + Xutf8SetWMProperties(_sapp.x11.display, + _sapp.x11.window, + _sapp.window_title, _sapp.window_title, + NULL, 0, NULL, NULL, NULL); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + int long_count = 0; + for (int i = 0; i < num_images; i++) { + const sapp_image_desc* img_desc = &icon_desc->images[i]; + long_count += 2 + (img_desc->width * img_desc->height); + } + 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++) { + const sapp_image_desc* img_desc = &icon_desc->images[img_index]; + const uint8_t* src = (const uint8_t*) img_desc->pixels.ptr; + *dst++ = img_desc->width; + *dst++ = img_desc->height; + const int num_pixels = img_desc->width * img_desc->height; + for (int pixel_index = 0; pixel_index < num_pixels; pixel_index++) { + *dst++ = ((long)(src[pixel_index * 4 + 0]) << 16) | + ((long)(src[pixel_index * 4 + 1]) << 8) | + ((long)(src[pixel_index * 4 + 2]) << 0) | + ((long)(src[pixel_index * 4 + 3]) << 24); + } + } + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_ICON, + XA_CARDINAL, 32, + PropModeReplace, + (unsigned char*)icon_data, + long_count); + _sapp_free(icon_data); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual_or_null, int depth) { + Visual* visual = visual_or_null; + if (0 == visual_or_null) { + visual = DefaultVisual(_sapp.x11.display, _sapp.x11.screen); + depth = DefaultDepth(_sapp.x11.display, _sapp.x11.screen); + } + _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); + _SAPP_STRUCT(XSetWindowAttributes, wa); + const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; + wa.colormap = _sapp.x11.colormap; + wa.border_pixel = 0; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + 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); + // NOTE: do *NOT* use _sapp.dpi_scale for the size multiplicator! + const float window_scale = _sapp.x11.dpi / 96.0f; + int x11_window_width = _sapp_roundf_gzero(_sapp.window_width * window_scale); + int x11_window_height = _sapp_roundf_gzero(_sapp.window_height * window_scale); + if (0 == _sapp.window_width) { + x11_window_width = (display_width * 4) / 5; + } + if (0 == _sapp.window_height) { + x11_window_height = (display_height * 4) / 5; + } + _sapp_x11_grab_error_handler(); + _sapp.x11.window = XCreateWindow(_sapp.x11.display, + _sapp.x11.root, + 0, 0, + (uint32_t)x11_window_width, + (uint32_t)x11_window_height, + 0, /* border width */ + depth, /* color depth */ + InputOutput, + visual, + wamask, + &wa); + _sapp_x11_release_error_handler(); + if (!_sapp.x11.window) { + _SAPP_PANIC(LINUX_X11_CREATE_WINDOW_FAILED); + } + Atom protocols[] = { + _sapp.x11.WM_DELETE_WINDOW + }; + XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); + + // NOTE: PPosition and PSize are obsolete and ignored + XSizeHints* hints = XAllocSizeHints(); + hints->flags = PWinGravity; + hints->win_gravity = CenterGravity; + XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); + XFree(hints); + + // announce support for drag'n'drop + if (_sapp.drop.enabled) { + 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_update_dimensions_from_window_size(); +} + +_SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { + if (_sapp.x11.window) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XDestroyWindow(_sapp.x11.display, _sapp.x11.window); + _sapp.x11.window = 0; + } + if (_sapp.x11.colormap) { + XFreeColormap(_sapp.x11.display, _sapp.x11.colormap); + _sapp.x11.colormap = 0; + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { + XWindowAttributes wa; + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa); + return wa.map_state == IsViewable; +} + +_SOKOL_PRIVATE void _sapp_x11_show_window(void) { + if (!_sapp_x11_window_visible()) { + XMapWindow(_sapp.x11.display, _sapp.x11.window); + XEvent dummy; + _sapp_x11_wait_for_event(VisibilityNotify, 0.1, &dummy); + XRaiseWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); + } +} + +_SOKOL_PRIVATE void _sapp_x11_hide_window(void) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) { + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + XGetWindowProperty(_sapp.x11.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + return itemCount; +} + +_SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { + int result = WithdrawnState; + struct { + CARD32 state; + Window icon; + } *state = NULL; + + if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { + result = (int)state->state; + } + if (state) { + XFree(state); + } + return result; +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_key_modifier_bit(sapp_keycode key) { + switch (key) { + case SAPP_KEYCODE_LEFT_SHIFT: + case SAPP_KEYCODE_RIGHT_SHIFT: + return SAPP_MODIFIER_SHIFT; + case SAPP_KEYCODE_LEFT_CONTROL: + case SAPP_KEYCODE_RIGHT_CONTROL: + return SAPP_MODIFIER_CTRL; + case SAPP_KEYCODE_LEFT_ALT: + case SAPP_KEYCODE_RIGHT_ALT: + return SAPP_MODIFIER_ALT; + case SAPP_KEYCODE_LEFT_SUPER: + case SAPP_KEYCODE_RIGHT_SUPER: + return SAPP_MODIFIER_SUPER; + default: + return 0; + } +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_button_modifier_bit(sapp_mousebutton btn) { + switch (btn) { + case SAPP_MOUSEBUTTON_LEFT: return SAPP_MODIFIER_LMB; + case SAPP_MOUSEBUTTON_RIGHT: return SAPP_MODIFIER_RMB; + case SAPP_MOUSEBUTTON_MIDDLE: return SAPP_MODIFIER_MMB; + default: return 0; + } +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) { + uint32_t mods = 0; + if (x11_mods & ShiftMask) { + mods |= SAPP_MODIFIER_SHIFT; + } + if (x11_mods & ControlMask) { + mods |= SAPP_MODIFIER_CTRL; + } + if (x11_mods & Mod1Mask) { + mods |= SAPP_MODIFIER_ALT; + } + if (x11_mods & Mod4Mask) { + mods |= SAPP_MODIFIER_SUPER; + } + if (x11_mods & Button1Mask) { + mods |= SAPP_MODIFIER_LMB; + } + if (x11_mods & Button2Mask) { + mods |= SAPP_MODIFIER_MMB; + } + if (x11_mods & Button3Mask) { + mods |= SAPP_MODIFIER_RMB; + } + return mods; +} + +_SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { + switch (event->xbutton.button) { + case Button1: return SAPP_MOUSEBUTTON_LEFT; + case Button2: return SAPP_MOUSEBUTTON_MIDDLE; + case Button3: return SAPP_MOUSEBUTTON_RIGHT; + default: return SAPP_MOUSEBUTTON_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y, bool clear_dxdy) { + if (!_sapp.mouse.locked) { + const float new_x = (float)x; + const float new_y = (float)y; + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } else 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); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = mods; + _sapp.event.scroll_x = x; + _sapp.event.scroll_y = y; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = chr; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { + if ((scancode >= 0) && (scancode < _SAPP_X11_MAX_X11_KEYCODES)) { + return _sapp.keycodes[scancode]; + } else { + return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { + int min = 0; + int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1; + int mid; + + /* First check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + { + return keysym; + } + + /* Also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) { + return keysym & 0x00ffffff; + } + + /* Binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (_sapp_x11_keysymtab[mid].keysym < keysym) { + min = mid + 1; + } else if (_sapp_x11_keysymtab[mid].keysym > keysym) { + max = mid - 1; + } else { + return _sapp_x11_keysymtab[mid].ucs; + } + } + + /* No matching Unicode value found */ + return -1; +} + +_SOKOL_PRIVATE bool _sapp_x11_keypress_repeat(int keycode) { + bool repeat = false; + if ((keycode >= 0) && (keycode < _SAPP_X11_MAX_X11_KEYCODES)) { + repeat = _sapp.x11.key_repeat[keycode]; + _sapp.x11.key_repeat[keycode] = true; + } + return repeat; +} + +_SOKOL_PRIVATE void _sapp_x11_keyrelease_repeat(int keycode) { + if ((keycode >= 0) && (keycode < _SAPP_X11_MAX_X11_KEYCODES)) { + _sapp.x11.key_repeat[keycode] = false; + } +} + +_SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { + SOKOL_ASSERT(src); + SOKOL_ASSERT(_sapp.drop.buffer); + + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + + /* + src is (potentially percent-encoded) string made of one or multiple paths + separated by \r\n, each path starting with 'file://' + */ + bool err = false; + int src_count = 0; + char src_chr = 0; + char* dst_ptr = _sapp.drop.buffer; + const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0 + while (0 != (src_chr = *src++)) { + src_count++; + char dst_chr = 0; + /* check leading 'file://' */ + if (src_count <= 7) { + if (((src_count == 1) && (src_chr != 'f')) || + ((src_count == 2) && (src_chr != 'i')) || + ((src_count == 3) && (src_chr != 'l')) || + ((src_count == 4) && (src_chr != 'e')) || + ((src_count == 5) && (src_chr != ':')) || + ((src_count == 6) && (src_chr != '/')) || + ((src_count == 7) && (src_chr != '/'))) + { + _SAPP_ERROR(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME); + err = true; + break; + } + } else if (src_chr == '\r') { + // skip + } else if (src_chr == '\n') { + src_count = 0; + _sapp.drop.num_files++; + // too many files is not an error + if (_sapp.drop.num_files >= _sapp.drop.max_files) { + break; + } + dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length; + dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); + } else if ((src_chr == '%') && src[0] && src[1]) { + // 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); + } else { + dst_chr = src_chr; + } + if (dst_chr) { + // dst_end_ptr already has adjustment for terminating zero + if (dst_ptr < dst_end_ptr) { + *dst_ptr++ = dst_chr; + } else { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + err = true; + break; + } + } + } + if (err) { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + return false; + } else { + return true; + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_genericevent(XEvent* event) { + if (_sapp.mouse.locked && _sapp.x11.xi.available) { + if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { + if (XGetEventData(_sapp.x11.display, &event->xcookie)) { + if (event->xcookie.evtype == XI_RawMotion) { + XIRawEvent* re = (XIRawEvent*) event->xcookie.data; + if (re->valuators.mask_len) { + const double* values = re->raw_values; + if (XIMaskIsSet(re->valuators.mask, 0)) { + _sapp.mouse.dx = (float) *values; + values++; + } + if (XIMaskIsSet(re->valuators.mask, 1)) { + _sapp.mouse.dy = (float) *values; + } + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); + } + } + XFreeEventData(_sapp.x11.display, &event->xcookie); + } + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_focusin(XEvent* event) { + // 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); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_focusout(XEvent* event) { + // if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock + if (_sapp.mouse.locked) { + _sapp_x11_lock_mouse(false); + } + // 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); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_keypress(XEvent* event) { + int keycode = (int)event->xkey.keycode; + + const sapp_keycode key = _sapp_x11_translate_key(keycode); + const bool repeat = _sapp_x11_keypress_repeat(keycode); + uint32_t mods = _sapp_x11_mods(event->xkey.state); + // X11 doesn't set modifier bit on key down, so emulate that + mods |= _sapp_x11_key_modifier_bit(key); + if (key != SAPP_KEYCODE_INVALID) { + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); + } + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + int32_t chr = _sapp_x11_keysym_to_unicode(keysym); + if (chr > 0) { + _sapp_x11_char_event((uint32_t)chr, repeat, mods); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_keyrelease(XEvent* event) { + int keycode = (int)event->xkey.keycode; + const sapp_keycode key = _sapp_x11_translate_key(keycode); + _sapp_x11_keyrelease_repeat(keycode); + if (key != SAPP_KEYCODE_INVALID) { + uint32_t mods = _sapp_x11_mods(event->xkey.state); + // X11 doesn't clear modifier bit on key up, so emulate that + mods &= ~_sapp_x11_key_modifier_bit(key); + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_buttonpress(XEvent* event) { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); + 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 + mods |= _sapp_x11_button_modifier_bit(btn); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); + _sapp.x11.mouse_buttons |= (1 << btn); + } else { + // might be a scroll event + switch (event->xbutton.button) { + case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; + case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; + case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; + case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_buttonrelease(XEvent* event) { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + uint32_t mods = _sapp_x11_mods(event->xbutton.state); + // X11 doesn't clear modifier bit on button up, so emulate that + mods &= ~_sapp_x11_button_modifier_bit(btn); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods); + _sapp.x11.mouse_buttons &= ~(1 << btn); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_enternotify(XEvent* event) { + // 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, true); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_leavenotify(XEvent* event) { + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_motionnotify(XEvent* event) { + if (!_sapp.mouse.locked) { + _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y, false); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_propertynotify(XEvent* event) { + if (event->xproperty.state == PropertyNewValue) { + if (event->xproperty.atom == _sapp.x11.WM_STATE) { + const int state = _sapp_x11_get_window_state(); + if (state != _sapp.x11.window_state) { + _sapp.x11.window_state = state; + if (state == IconicState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); + } else if (state == NormalState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_selectionnotify(XEvent* event) { + if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { + char* data = 0; + uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (unsigned char**) &data); + if (_sapp.drop.enabled && result) { + if (_sapp_x11_parse_dropped_files_list(data)) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + if (_sapp_events_enabled()) { + // FIXME: Figure out how to get modifier key state here. + // The XSelection event has no 'state' item, and + // XQueryKeymap() always returns a zeroed array. + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } + } + } + if (_sapp.x11.xdnd.version >= 2) { + _SAPP_STRUCT(XEvent, reply); + reply.type = ClientMessage; + 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; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + if (data) { + XFree(data); + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_clientmessage(XEvent* event) { + if (XFilterEvent(event, None)) { + return; + } + if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { + const Atom protocol = (Atom)event->xclient.data.l[0]; + if (protocol == _sapp.x11.WM_DELETE_WINDOW) { + _sapp.quit_requested = true; + } + } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { + const bool is_list = 0 != (event->xclient.data.l[1] & 1); + _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; + _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _sapp.x11.xdnd.format = None; + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + uint32_t count = 0; + Atom* formats = 0; + if (is_list) { + count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); + } else { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + for (uint32_t i = 0; i < count; i++) { + if (formats[i] == _sapp.x11.xdnd.text_uri_list) { + _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; + break; + } + } + if (is_list && formats) { + XFree(formats); + } + } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + Time time = CurrentTime; + if (_sapp.x11.xdnd.format) { + if (_sapp.x11.xdnd.version >= 1) { + time = (Time)event->xclient.data.l[2]; + } + XConvertSelection(_sapp.x11.display, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.xdnd.format, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.window, + time); + } else if (_sapp.x11.xdnd.version >= 2) { + _SAPP_STRUCT(XEvent, reply); + reply.type = ClientMessage; + 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; + reply.xclient.data.l[1] = 0; // drag was rejected + reply.xclient.data.l[2] = None; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { + // drag operation has moved over the window + // FIXME: we could track the mouse position here, but + // this isn't implemented on other platforms either so far + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + _SAPP_STRUCT(XEvent, reply); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + if (_sapp.x11.xdnd.format) { + /* reply that we are ready to copy the dragged data */ + reply.xclient.data.l[1] = 1; // accept with no rectangle + if (_sapp.x11.xdnd.version >= 2) { + reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; + } + } + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_selectionrequest(XEvent* event) { + XSelectionRequestEvent* req = &event->xselectionrequest; + if (req->selection != _sapp.x11.CLIPBOARD) { + return; + } + if (!_sapp.clipboard.enabled) { + return; + } + SOKOL_ASSERT(_sapp.clipboard.buffer); + _SAPP_STRUCT(XSelectionEvent, reply); + reply.type = SelectionNotify; + reply.display = req->display; + reply.requestor = req->requestor; + reply.selection = req->selection; + reply.target = req->target; + reply.property = req->property; + reply.time = req->time; + if (req->target == _sapp.x11.UTF8_STRING) { + XChangeProperty(_sapp.x11.display, + req->requestor, + req->property, + _sapp.x11.UTF8_STRING, + 8, + PropModeReplace, + (unsigned char*) _sapp.clipboard.buffer, + strlen(_sapp.clipboard.buffer)); + } else if (req->target == _sapp.x11.TARGETS) { + XChangeProperty(_sapp.x11.display, + req->requestor, + req->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*) &_sapp.x11.UTF8_STRING, + 1); + } else { + reply.property = None; + } + XSendEvent(_sapp.x11.display, req->requestor, False, 0, (XEvent*) &reply); +} + +_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { + switch (event->type) { + case GenericEvent: + _sapp_x11_on_genericevent(event); + break; + case FocusIn: + _sapp_x11_on_focusin(event); + break; + case FocusOut: + _sapp_x11_on_focusout(event); + break; + case KeyPress: + _sapp_x11_on_keypress(event); + break; + case KeyRelease: + _sapp_x11_on_keyrelease(event); + break; + case ButtonPress: + _sapp_x11_on_buttonpress(event); + break; + case ButtonRelease: + _sapp_x11_on_buttonrelease(event); + break; + case EnterNotify: + _sapp_x11_on_enternotify(event); + break; + case LeaveNotify: + _sapp_x11_on_leavenotify(event); + break; + case MotionNotify: + _sapp_x11_on_motionnotify(event); + break; + case PropertyNotify: + _sapp_x11_on_propertynotify(event); + break; + case SelectionNotify: + _sapp_x11_on_selectionnotify(event); + break; + case SelectionRequest: + _sapp_x11_on_selectionrequest(event); + break; + case DestroyNotify: + // not a bug + break; + case ClientMessage: + _sapp_x11_on_clientmessage(event); + break; + } +} + +#if defined(_SAPP_EGL) + +_SOKOL_PRIVATE void _sapp_egl_init(void) { + #if defined(SOKOL_GLCORE) + 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_GLCORE) + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #elif defined(SOKOL_GLES3) + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_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); + } + + _SAPP_STRUCT(XVisualInfo, 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[] = { + EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl.major_version, + EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl.minor_version, + #if defined(SOKOL_GLCORE) + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + #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); + } + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + + eglSwapInterval(_sapp.egl.display, _sapp.swap_interval); +} + +_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_EGL + +_SOKOL_PRIVATE void _sapp_linux_frame(void) { + _sapp_x11_update_dimensions_from_window_size(); + #if defined(SOKOL_WGPU) + _sapp_wgpu_frame(); + #elif defined(SOKOL_VULKAN) + _sapp_vk_frame(); + #else + _sapp_frame(); + #if defined(_SAPP_GLX) + _sapp_glx_swap_buffers(); + #elif defined(_SAPP_EGL) + eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface); + #endif + #endif +} + +_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 + the compiler or linker options. They have no other purpose. + */ + pthread_attr_t pthread_attr; + pthread_attr_init(&pthread_attr); + pthread_attr_destroy(&pthread_attr); + + _sapp_init_state(desc); + _sapp.x11.window_state = NormalState; + + XInitThreads(); + XrmInitialize(); + _sapp.x11.display = XOpenDisplay(NULL); + if (!_sapp.x11.display) { + _SAPP_PANIC(LINUX_X11_OPEN_DISPLAY_FAILED); + } + _sapp.x11.screen = DefaultScreen(_sapp.x11.display); + _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); + _sapp_x11_query_system_dpi(); + // NOTE: on Linux system-window-size to frame-buffer-size mapping is always 1:1 + _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; + _sapp_x11_init_extensions(); + _sapp_x11_create_standard_cursors(); + XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); + _sapp_x11_init_keytable(); + #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); + #elif defined(_SAPP_EGL) + _sapp_egl_init(); + #elif defined(SOKOL_WGPU) + _sapp_x11_create_window(0, 0); + _sapp_wgpu_init(); + #elif defined(SOKOL_VULKAN) + _sapp_x11_create_window(0, 0); + _sapp_vk_init(); + #endif + sapp_set_icon(&desc->icon); + _sapp.valid = true; + _sapp_x11_show_window(); + if (_sapp.fullscreen) { + _sapp_x11_set_fullscreen(true); + } + + XFlush(_sapp.x11.display); + while (!_sapp.quit_ordered) { + _sapp_timing_measure(&_sapp.timing); + int count = XPending(_sapp.x11.display); + while (count--) { + XEvent event; + XNextEvent(_sapp.x11.display, &event); + _sapp_x11_process_event(&event); + } + _sapp_linux_frame(); + XFlush(_sapp.x11.display); + // handle quit-requested, either from window or from sapp_request_quit() + if (_sapp.quit_requested && !_sapp.quit_ordered) { + // give user code a chance to intervene + _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* if user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + } + _sapp_call_cleanup(); + #if defined(_SAPP_GLX) + _sapp_glx_destroy_context(); + #elif defined(_SAPP_EGL) + _sapp_egl_destroy(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_discard(); + #elif defined(SOKOL_VULKAN) + _sapp_vk_discard(); + #endif + _sapp_x11_destroy_window(); + _sapp_x11_destroy_standard_cursors(); + XCloseDisplay(_sapp.x11.display); + _sapp_discard_state(); +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_linux_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_LINUX */ + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +#if defined(SOKOL_NO_ENTRY) +SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { + SOKOL_ASSERT(desc); + #if defined(_SAPP_MACOS) + _sapp_macos_run(desc); + #elif defined(_SAPP_IOS) + _sapp_ios_run(desc); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_run(desc); + #elif defined(_SAPP_WIN32) + _sapp_win32_run(desc); + #elif defined(_SAPP_LINUX) + _sapp_linux_run(desc); + #else + #error "sapp_run() not supported on this platform" + #endif +} + +/* this is just a stub so the linker doesn't complain */ +sapp_desc sokol_main(int argc, char* argv[]) { + _SOKOL_UNUSED(argc); + _SOKOL_UNUSED(argv); + _SAPP_STRUCT(sapp_desc, desc); + return desc; +} +#else +/* likewise, in normal mode, sapp_run() is just an empty stub */ +SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { + _SOKOL_UNUSED(desc); +} +#endif + +SOKOL_API_IMPL bool sapp_isvalid(void) { + return _sapp.valid; +} + +SOKOL_API_IMPL void* sapp_userdata(void) { + return _sapp.desc.user_data; +} + +SOKOL_API_IMPL sapp_desc sapp_query_desc(void) { + return _sapp.desc; +} + +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; +} + +SOKOL_API_IMPL float sapp_widthf(void) { + return (float)sapp_width(); +} + +SOKOL_API_IMPL int sapp_height(void) { + return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; +} + +SOKOL_API_IMPL float sapp_heightf(void) { + return (float)sapp_height(); +} + +SOKOL_API_IMPL sapp_pixel_format sapp_color_format(void) { + #if defined(SOKOL_WGPU) + switch (_sapp.wgpu.render_format) { + case WGPUTextureFormat_RGBA8Unorm: + return SAPP_PIXELFORMAT_RGBA8; + case WGPUTextureFormat_BGRA8Unorm: + return SAPP_PIXELFORMAT_BGRA8; + default: + SOKOL_UNREACHABLE; + return SAPP_PIXELFORMAT_NONE; + } + #elif defined(SOKOL_VULKAN) + switch (_sapp.vk.surface_format.format) { + case VK_FORMAT_R8G8B8A8_UNORM: + return SAPP_PIXELFORMAT_RGBA8; + case VK_FORMAT_B8G8R8A8_UNORM: + return SAPP_PIXELFORMAT_BGRA8; + default: + // FIXME! + SOKOL_UNREACHABLE; + return SAPP_PIXELFORMAT_NONE; + } + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + return SAPP_PIXELFORMAT_BGRA8; + #else + return SAPP_PIXELFORMAT_RGBA8; + #endif +} + +SOKOL_API_IMPL sapp_pixel_format sapp_depth_format(void) { + return SAPP_PIXELFORMAT_DEPTH_STENCIL; +} + +SOKOL_API_IMPL int sapp_sample_count(void) { + return _sapp.sample_count; +} + +SOKOL_API_IMPL bool sapp_high_dpi(void) { + return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); +} + +SOKOL_API_IMPL float sapp_dpi_scale(void) { + return _sapp.dpi_scale; +} + +SOKOL_API_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_EGL) + return _sapp.egl.display; + #else + return 0; + #endif +} + +SOKOL_API_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_EGL) + return _sapp.egl.context; + #else + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_show_keyboard(bool show) { + #if defined(_SAPP_IOS) + _sapp_ios_show_keyboard(show); + #elif defined(_SAPP_ANDROID) + _sapp_android_show_keyboard(show); + #else + _SOKOL_UNUSED(show); + #endif +} + +SOKOL_API_IMPL bool sapp_keyboard_shown(void) { + return _sapp.onscreen_keyboard_shown; +} + +SOKOL_API_IMPL bool sapp_is_fullscreen(void) { + return _sapp.fullscreen; +} + +SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { + #if defined(_SAPP_MACOS) + _sapp_macos_toggle_fullscreen(); + #elif defined(_SAPP_WIN32) + _sapp_win32_toggle_fullscreen(); + #elif defined(_SAPP_LINUX) + _sapp_x11_toggle_fullscreen(); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_toggle_fullscreen(); + #endif +} + +_SOKOL_PRIVATE void _sapp_update_cursor(sapp_mouse_cursor cursor, bool shown) { + #if defined(_SAPP_MACOS) + _sapp_macos_update_cursor(cursor, shown); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_cursor(cursor, shown, false); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_cursor(cursor, shown); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_update_cursor(cursor, shown); + #endif + _sapp.mouse.current_cursor = cursor; + _sapp.mouse.shown = shown; +} + +/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ +SOKOL_API_IMPL void sapp_show_mouse(bool show) { + if (_sapp.mouse.shown != show) { + _sapp_update_cursor(_sapp.mouse.current_cursor, show); + } +} + +SOKOL_API_IMPL bool sapp_mouse_shown(void) { + return _sapp.mouse.shown; +} + +SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { + #if defined(_SAPP_MACOS) + _sapp_macos_lock_mouse(lock); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_lock_mouse(lock); + #elif defined(_SAPP_WIN32) + _sapp_win32_lock_mouse(lock); + #elif defined(_SAPP_LINUX) + _sapp_x11_lock_mouse(lock); + #else + _sapp.mouse.locked = lock; + #endif +} + +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) { + _sapp_update_cursor(cursor, _sapp.mouse.shown); + } +} + +SOKOL_API_IMPL sapp_mouse_cursor sapp_get_mouse_cursor(void) { + return _sapp.mouse.current_cursor; +} + +SOKOL_API_IMPL sapp_mouse_cursor sapp_bind_mouse_cursor_image(sapp_mouse_cursor cursor, const sapp_image_desc* desc) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + // NOTE: It seems that for some reason, the hotspot doesn't work if it is one less + // than the dimension of the cursor image (or more), on windows. So for a cursor + // that is 32 by 32 px, a hotspot of x = 30 works, but not x = 31. + // The cursor simply dissapears in such cases. Asserting for all platforms to make + // the behaviour consistent. + SOKOL_ASSERT(desc->cursor_hotspot_x < desc->width - 1 && desc->cursor_hotspot_y < desc->height - 1); + SOKOL_ASSERT(desc->width * desc->height * 4 == (int) desc->pixels.size); + + sapp_unbind_mouse_cursor_image(cursor); + + bool res = false; + #if defined(_SAPP_MACOS) + res = _sapp_macos_make_custom_mouse_cursor(cursor, desc); + #elif defined(_SAPP_EMSCRIPTEN) + res = _sapp_emsc_make_custom_mouse_cursor(cursor, desc); + #elif defined(_SAPP_WIN32) + res = _sapp_win32_make_custom_mouse_cursor(cursor, desc); + #elif defined(_SAPP_LINUX) + res = _sapp_x11_make_custom_mouse_cursor(cursor, desc); + #else + _SOKOL_UNUSED(desc); + #endif + _sapp.custom_cursor_bound[(int)cursor] = res; + + // Update the displayed cursor in case the current cursor is the one we just bound. + if (_sapp.mouse.current_cursor == cursor) { + _sapp_update_cursor(cursor, _sapp.mouse.shown); + } + return cursor; // returning the passed-in cursor puerly for convenience, in case you want to asign the value to a variable. +} + +SOKOL_APP_API_DECL void sapp_unbind_mouse_cursor_image(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (_sapp.custom_cursor_bound[(int)cursor]) { + // if this is the active cursor, first restore it to its default image, + // this must be done before attempting to destroy any cursor image + // resources which at least on win32 would fail if the cursor is still in use + _sapp.custom_cursor_bound[(int)cursor] = false; + if (_sapp.mouse.current_cursor == cursor) { + _sapp_update_cursor(cursor, _sapp.mouse.shown); + } + #if defined(_SAPP_MACOS) + _sapp_macos_destroy_custom_mouse_cursor(cursor); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_destroy_custom_mouse_cursor(cursor); + #elif defined(_SAPP_WIN32) + _sapp_win32_destroy_custom_mouse_cursor(cursor); + #elif defined(_SAPP_LINUX) + _sapp_x11_destroy_custom_mouse_cursor(cursor); + #endif + } +} + +SOKOL_API_IMPL void sapp_request_quit(void) { + _sapp.quit_requested = true; +} + +SOKOL_API_IMPL void sapp_cancel_quit(void) { + _sapp.quit_requested = false; +} + +SOKOL_API_IMPL void sapp_quit(void) { + _sapp.quit_ordered = true; +} + +SOKOL_API_IMPL void sapp_consume_event(void) { + _sapp.event_consumed = true; +} + +/* 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) { + if (!_sapp.clipboard.enabled) { + return; + } + SOKOL_ASSERT(str); + #if defined(_SAPP_MACOS) + _sapp_macos_set_clipboard_string(str); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_set_clipboard_string(str); + #elif defined(_SAPP_WIN32) + _sapp_win32_set_clipboard_string(str); + #elif defined(_SAPP_LINUX) + _sapp_x11_set_clipboard_string(str); + #else + /* not implemented */ + #endif + _sapp_strcpy(str, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); +} + +SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { + if (!_sapp.clipboard.enabled) { + return ""; + } + #if defined(_SAPP_MACOS) + return _sapp_macos_get_clipboard_string(); + #elif defined(_SAPP_EMSCRIPTEN) + return _sapp.clipboard.buffer; + #elif defined(_SAPP_WIN32) + return _sapp_win32_get_clipboard_string(); + #elif defined(_SAPP_LINUX) + return _sapp_x11_get_clipboard_string(); + #else + /* not implemented */ + return _sapp.clipboard.buffer; + #endif +} + +SOKOL_API_IMPL void sapp_set_window_title(const char* title) { + SOKOL_ASSERT(title); + _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title)); + #if defined(_SAPP_MACOS) + _sapp_macos_update_window_title(); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_window_title(); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_window_title(); + #endif +} + +SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) { + SOKOL_ASSERT(desc); + if (desc->sokol_default) { + if (0 == _sapp.default_icon_pixels) { + _sapp_setup_default_icon(); + } + SOKOL_ASSERT(0 != _sapp.default_icon_pixels); + desc = &_sapp.default_icon_desc; + } + const int num_images = _sapp_icon_num_images(desc); + if (num_images == 0) { + return; + } + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + if (!_sapp_validate_icon_desc(desc, num_images)) { + return; + } + #if defined(_SAPP_MACOS) + _sapp_macos_set_icon(desc, num_images); + #elif defined(_SAPP_WIN32) + _sapp_win32_set_icon(desc, num_images); + #elif defined(_SAPP_LINUX) + _sapp_x11_set_icon(desc, num_images); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_set_icon(desc, num_images); + #endif +} + +SOKOL_API_IMPL int sapp_get_num_dropped_files(void) { + SOKOL_ASSERT(_sapp.drop.enabled); + return _sapp.drop.num_files; +} + +SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + SOKOL_ASSERT(_sapp.drop.buffer); + if (!_sapp.drop.enabled) { + return ""; + } + if ((index < 0) || (index >= _sapp.drop.max_files)) { + return ""; + } + return (const char*) _sapp_dropped_file_path_ptr(index); +} + +SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + #if defined(_SAPP_EMSCRIPTEN) + if (!_sapp.drop.enabled) { + return 0; + } + return sapp_js_dropped_file_size(index); + #else + (void)index; + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT(request); + SOKOL_ASSERT(request->callback); + 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) { + error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL; + } + if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) { + _sapp_emsc_invoke_fetch_cb(index, + false, // success + (int)error_code, + request->callback, + 0, // fetched_size + (void*)request->buffer.ptr, + request->buffer.size, + request->user_data); + } else { + sapp_js_fetch_dropped_file(index, + request->callback, + (void*)request->buffer.ptr, + request->buffer.size, + request->user_data); + } + #else + (void)request; + #endif +} + +SOKOL_API_IMPL sapp_environment sapp_get_environment(void) { + SOKOL_ASSERT(_sapp.valid); + _SAPP_STRUCT(sapp_environment, res); + res.defaults.color_format = sapp_color_format(); + res.defaults.depth_format = sapp_depth_format(); + res.defaults.sample_count = sapp_sample_count(); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + res.metal.device = (__bridge const void*) _sapp.macos.mtl_device; + #else + res.metal.device = (__bridge const void*) _sapp.ios.mtl_device; + #endif + #endif + #if defined(SOKOL_D3D11) + res.d3d11.device = (const void*) _sapp.d3d11.device; + res.d3d11.device_context = (const void*) _sapp.d3d11.device_context; + #endif + #if defined(SOKOL_WGPU) + res.wgpu.device = (const void*) _sapp.wgpu.device; + #endif + #if defined(SOKOL_VULKAN) + res.vulkan.instance = (const void*) _sapp.vk.instance; + res.vulkan.physical_device = (const void*) _sapp.vk.physical_device; + res.vulkan.device = (const void*) _sapp.vk.device; + res.vulkan.queue = (const void*) _sapp.vk.queue; + res.vulkan.queue_family_index = _sapp.vk.queue_family_index; + #endif + return res; +} + +SOKOL_API_IMPL sapp_swapchain sapp_get_swapchain(void) { + SOKOL_ASSERT(_sapp.valid); + _SAPP_STRUCT(sapp_swapchain, res); + res.width = sapp_width(); + res.height = sapp_height(); + res.color_format = sapp_color_format(); + res.depth_format = sapp_depth_format(); + res.sample_count = sapp_sample_count(); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + res.metal.current_drawable = (__bridge const void*) [_sapp.macos.view currentDrawable]; + res.metal.depth_stencil_texture = (__bridge const void*) [_sapp.macos.view depthStencilTexture]; + res.metal.msaa_color_texture = (__bridge const void*) [_sapp.macos.view multisampleColorTexture]; + #else + res.metal.current_drawable = (__bridge const void*) [_sapp.ios.view currentDrawable]; + res.metal.depth_stencil_texture = (__bridge const void*) [_sapp.ios.view depthStencilTexture]; + res.metal.msaa_color_texture = (__bridge const void*) [_sapp.ios.view multisampleColorTexture]; + #endif + #endif + #if defined(SOKOL_D3D11) + SOKOL_ASSERT(_sapp.d3d11.rtv); + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.d3d11.msaa_rtv); + res.d3d11.render_view = (const void*) _sapp.d3d11.msaa_rtv; + res.d3d11.resolve_view = (const void*) _sapp.d3d11.rtv; + } else { + res.d3d11.render_view = (const void*) _sapp.d3d11.rtv; + } + res.d3d11.depth_stencil_view = (const void*) _sapp.d3d11.dsv; + #endif + #if defined(SOKOL_WGPU) + SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view); + _sapp_wgpu_swapchain_next(); + // FIXME: swapchain_view being null must be allowed and should skip the frame + SOKOL_ASSERT(_sapp.wgpu.swapchain_view); + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.wgpu.msaa_view); + res.wgpu.render_view = (const void*) _sapp.wgpu.msaa_view; + res.wgpu.resolve_view = (const void*) _sapp.wgpu.swapchain_view; + } else { + res.wgpu.render_view = (const void*) _sapp.wgpu.swapchain_view; + } + res.wgpu.depth_stencil_view = (const void*) _sapp.wgpu.depth_stencil_view; + #endif + #if defined(SOKOL_VULKAN) + _sapp_vk_swapchain_next(); + // FIXME: swapchain_view being null must be allowed and should skip the frame + uint32_t img_idx = _sapp.vk.cur_swapchain_image_index; + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.vk.msaa.img && _sapp.vk.msaa.view); + res.vulkan.render_image = (const void*) _sapp.vk.msaa.img; + res.vulkan.render_view = (const void*) _sapp.vk.msaa.view; + res.vulkan.resolve_image = (const void*) _sapp.vk.swapchain_images[img_idx]; + res.vulkan.resolve_view = (const void*) _sapp.vk.swapchain_views[img_idx]; + } else { + res.vulkan.render_image = (const void*) _sapp.vk.swapchain_images[img_idx]; + res.vulkan.render_view = (const void*) _sapp.vk.swapchain_views[img_idx]; + } + res.vulkan.depth_stencil_image = (const void*) _sapp.vk.depth.img; + res.vulkan.depth_stencil_view = (const void*) _sapp.vk.depth.view; + // NOTE: using the current swapchain image index here is *NOT* a bug! The render_finished_semaphore *must* + // be associated with its swapchain image in case the swapchain implementation doesn't return swapchain images in order + res.vulkan.render_finished_semaphore = _sapp.vk.sync[img_idx].render_finished_sem; + res.vulkan.present_complete_semaphore = _sapp.vk.sync[_sapp.vk.sync_slot].present_complete_sem; + #endif + #if defined(_SAPP_ANY_GL) + res.gl.framebuffer = _sapp.gl.framebuffer; + #endif + return res; +} + +SOKOL_API_IMPL const void* sapp_macos_get_window(void) { + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.window; + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_ios_get_window(void) { + #if defined(_SAPP_IOS) + const void* obj = (__bridge const void*) _sapp.ios.window; + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_swap_chain(void) { + SOKOL_ASSERT(_sapp.valid); +#if defined(SOKOL_D3D11) + return _sapp.d3d11.swap_chain; +#else + return 0; +#endif +} + +SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_WIN32) + return _sapp.win32.hwnd; + #else + return 0; + #endif +} + +SOKOL_API_IMPL int sapp_gl_get_major_version(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANY_GL) + return _sapp.desc.gl.major_version; + #else + return 0; + #endif +} + +SOKOL_API_IMPL int sapp_gl_get_minor_version(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANY_GL) + return _sapp.desc.gl.minor_version; + #else + return 0; + #endif +} + +SOKOL_API_IMPL bool sapp_gl_is_gles(void) { + #if defined(SOKOL_GLES3) + return true; + #else + return false; + #endif +} + +SOKOL_API_IMPL const void* sapp_x11_get_window(void) { + #if defined(_SAPP_LINUX) + return (void*)_sapp.x11.window; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_x11_get_display(void) { + #if defined(_SAPP_LINUX) + return (void*)_sapp.x11.display; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { + // 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 + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) { + _sapp.html5_ask_leave_site = ask; +} + +#endif /* SOKOL_APP_IMPL */ diff --git a/thirdparty/sokol/sokol_args.h b/thirdparty/sokol/sokol_args.h new file mode 100644 index 0000000..b532c90 --- /dev/null +++ b/thirdparty/sokol/sokol_args.h @@ -0,0 +1,854 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_ARGS_IMPL) +#define SOKOL_ARGS_IMPL +#endif +#ifndef SOKOL_ARGS_INCLUDED +/* + sokol_args.h -- cross-platform key/value arg-parsing for web and native + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_ARGS_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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: -) + + If sokol_args.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_ARGS_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + OVERVIEW + ======== + sokol_args.h provides a simple unified argument parsing API for WebAssembly and + native apps. + + When running as a WebAssembly app, arguments are taken from the page URL: + + https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc + + The same arguments provided to a command line app: + + kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc + + You can also use standalone keys without value: + + https://floooh.github.io/tiny8bit/kc85.html?bla&blub + + On the command line: + + kc85 bla blub + + Such value-less keys are reported as the value being an empty string, but they + can be tested with `sapp_exists("bla")` or `sapp_boolean("blub")`. + + ARGUMENT FORMATTING + =================== + On the web platform, arguments must be formatted as a valid URL query string + with 'percent encoding' used for special characters. + + Strings are expected to be UTF-8 encoded (although sokol_args.h doesn't + contain any special UTF-8 handling). See below on how to obtain + UTF-8 encoded argc/argv values on Windows when using WinMain() as + entry point. + + On native platforms the following rules must be followed: + + Arguments have the general form + + key=value + + or + + key + + When a key has no value, the value will be assigned an empty string. + + Key/value pairs are separated by 'whitespace', valid whitespace + characters are space and tab. + + Whitespace characters in front and after the separating '=' character + are ignored: + + key = value + + ...is the same as + + key=value + + The 'key' string must be a simple string without escape sequences or whitespace. + + The 'value' string can be quoted, and quoted value strings can contain + whitespace: + + key = 'single-quoted value' + key = "double-quoted value" + + Single-quoted value strings can contain double quotes, and vice-versa: + + key = 'single-quoted value "can contain double-quotes"' + key = "double-quoted value 'can contain single-quotes'" + + Note that correct quoting can be tricky on some shells, since command + shells may remove quotes, unless they're escaped. + + Value strings can contain a small selection of escape sequences: + + \n - newline + \r - carriage return + \t - tab + \\ - escaped backslash + + (more escape codes may be added in the future). + + CODE EXAMPLE + ============ + + int main(int argc, char* argv[]) { + // initialize sokol_args with default parameters + sargs_setup(&(sargs_desc){ + .argc = argc, + .argv = argv + }); + + // check if a key exists... + if (sargs_exists("bla")) { + ... + } + + // get value string for key, if not found, return empty string "" + const char* val0 = sargs_value("bla"); + + // get value string for key, or default string if key not found + const char* val1 = sargs_value_def("bla", "default_value"); + + // check if a key matches expected value + if (sargs_equals("type", "kc85_4")) { + ... + } + + // check if a key's value is "true", "yes" or "on" or if this is a standalone key + if (sargs_boolean("joystick_enabled")) { + ... + } + + // iterate over keys and values + for (int i = 0; i < sargs_num_args(); i++) { + printf("key: %s, value: %s\n", sargs_key_at(i), sargs_value_at(i)); + } + + // lookup argument index by key string, will return -1 if key + // is not found, sargs_key_at() and sargs_value_at() will return + // an empty string for invalid indices + int index = sargs_find("bla"); + printf("key: %s, value: %s\n", sargs_key_at(index), sargs_value_at(index)); + + // shutdown sokol-args + sargs_shutdown(); + } + + WINMAIN AND ARGC / ARGV + ======================= + On Windows with WinMain() based apps, getting UTF8-encoded command line + arguments is a bit more complicated: + + First call GetCommandLineW(), this returns the entire command line + as UTF-16 string. Then call CommandLineToArgvW(), this parses the + command line string into the usual argc/argv format (but in UTF-16). + Finally convert the UTF-16 strings in argv[] into UTF-8 via + WideCharToMultiByte(). + + See the function _sapp_win32_command_line_to_utf8_argv() in sokol_app.h + for example code how to do this (if you're using sokol_app.h, it will + already convert the command line arguments to UTF-8 for you of course, + so you can plug them directly into sokol_app.h). + + API DOCUMENTATION + ================= + void sargs_setup(const sargs_desc* desc) + Initialize sokol_args, desc contains the following configuration + parameters: + int argc - the main function's argc parameter + char** argv - the main function's argv parameter + int max_args - max number of key/value pairs, default is 16 + int buf_size - size of the internal string buffer, default is 16384 + + Note that on the web, argc and argv will be ignored and the arguments + will be taken from the page URL instead. + + sargs_setup() will allocate 2 memory chunks: one for keeping track + of the key/value args of size 'max_args*8', and a string buffer + of size 'buf_size'. + + void sargs_shutdown(void) + Shutdown sokol-args and free any allocated memory. + + bool sargs_isvalid(void) + Return true between sargs_setup() and sargs_shutdown() + + bool sargs_exists(const char* key) + Test if an argument exists by its key name. + + const char* sargs_value(const char* key) + Return value associated with key. Returns an empty string ("") if the + key doesn't exist, or if the key doesn't have a value. + + const char* sargs_value_def(const char* key, const char* default) + Return value associated with key, or the provided default value if the + key doesn't exist, or this is a value-less key. + + bool sargs_equals(const char* key, const char* val); + Return true if the value associated with key matches + the 'val' argument. + + bool sargs_boolean(const char* key) + Return true if the value string of 'key' is one of 'true', 'yes', 'on', + or this is a key without value. + + int sargs_find(const char* key) + Find argument by key name and return its index, or -1 if not found. + + int sargs_num_args(void) + Return number of key/value pairs. + + const char* sargs_key_at(int index) + Return the key name of argument at index. Returns empty string if + is index is outside range. + + const char* sargs_value_at(int index) + Return the value of argument at index. Returns empty string + if the key at index has no value, or the index is out-of-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_fn = my_alloc, + .free_fn = 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? + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 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_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 +#endif +#ifndef SOKOL_ARGS_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_ARGS_IMPL) +#define SOKOL_ARGS_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_ARGS_API_DECL __declspec(dllimport) +#else +#define SOKOL_ARGS_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +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_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sargs_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(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 +SOKOL_ARGS_API_DECL void sargs_setup(const sargs_desc* desc); +// shutdown sokol-args +SOKOL_ARGS_API_DECL void sargs_shutdown(void); +// true between sargs_setup() and sargs_shutdown() +SOKOL_ARGS_API_DECL bool sargs_isvalid(void); +// test if an argument exists by key name +SOKOL_ARGS_API_DECL bool sargs_exists(const char* key); +// get value by key name, return empty string if key doesn't exist or an existing key has no value +SOKOL_ARGS_API_DECL const char* sargs_value(const char* key); +// get value by key name, return provided default if key doesn't exist or has no value +SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def); +// return true if val arg matches the value associated with key +SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val); +// return true if key's value is "true", "yes", "on" or an existing key has no value +SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key); +// get index of arg by key name, return -1 if not exists +SOKOL_ARGS_API_DECL int sargs_find(const char* key); +// get number of parsed arguments +SOKOL_ARGS_API_DECL int sargs_num_args(void); +// get key name of argument at index, or empty string +SOKOL_ARGS_API_DECL const char* sargs_key_at(int index); +// get value string of argument at index, or empty string +SOKOL_ARGS_API_DECL const char* sargs_value_at(int index); + +#ifdef __cplusplus +} // extern "C" + +// reference-based equivalents for c++ +inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); } + +#endif +#endif // SOKOL_ARGS_INCLUDED + +//--- IMPLEMENTATION ----------------------------------------------------------- +#ifdef SOKOL_ARGS_IMPL +#define SOKOL_ARGS_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 sargs_desc.allocator to override memory allocation functions" +#endif + +#include // memset, strcmp +#include // malloc, free + +#if defined(__EMSCRIPTEN__) +#include +#endif + +#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 + +#define _sargs_def(val, def) (((val) == 0) ? (def) : (val)) + +#define _SARGS_MAX_ARGS_DEF (16) +#define _SARGS_BUF_SIZE_DEF (16*1024) + +// parser state +#define _SARGS_EXPECT_KEY (1<<0) +#define _SARGS_EXPECT_SEP (1<<1) +#define _SARGS_EXPECT_VAL (1<<2) +#define _SARGS_PARSING_KEY (1<<3) +#define _SARGS_PARSING_VAL (1<<4) +#define _SARGS_ERROR (1<<5) + +// a key/value pair struct +typedef struct { + int key; // index to start of key string in buf + int val; // index to start of value string in buf +} _sargs_kvp_t; + +// sokol-args state +typedef struct { + int max_args; // number of key/value pairs in args array + int num_args; // number of valid items in args array + _sargs_kvp_t* args; // key/value pair array + int buf_size; // size of buffer in bytes + int buf_pos; // current buffer position + char* buf; // character buffer, first char is reserved and zero for 'empty string' + bool valid; + 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_fn) { + ptr = _sargs.allocator.alloc_fn(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_fn) { + _sargs.allocator.free_fn(ptr, _sargs.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE void _sargs_putc(char c) { + if ((_sargs.buf_pos+2) < _sargs.buf_size) { + _sargs.buf[_sargs.buf_pos++] = c; + } +} + +_SOKOL_PRIVATE const char* _sargs_str(int index) { + SOKOL_ASSERT((index >= 0) && (index < _sargs.buf_size)); + return &_sargs.buf[index]; +} + +//-- argument parser functions -------------------- +_SOKOL_PRIVATE void _sargs_expect_key(void) { + _sargs.parse_state = _SARGS_EXPECT_KEY; +} + +_SOKOL_PRIVATE bool _sargs_key_expected(void) { + return 0 != (_sargs.parse_state & _SARGS_EXPECT_KEY); +} + +_SOKOL_PRIVATE void _sargs_expect_val(void) { + _sargs.parse_state = _SARGS_EXPECT_VAL; +} + +_SOKOL_PRIVATE bool _sargs_val_expected(void) { + return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL); +} + +_SOKOL_PRIVATE void _sargs_expect_sep_or_key(void) { + _sargs.parse_state = _SARGS_EXPECT_SEP | _SARGS_EXPECT_KEY; +} + +_SOKOL_PRIVATE bool _sargs_any_expected(void) { + return 0 != (_sargs.parse_state & (_SARGS_EXPECT_KEY | _SARGS_EXPECT_VAL | _SARGS_EXPECT_SEP)); +} + +_SOKOL_PRIVATE bool _sargs_is_separator(char c) { + return c == '='; +} + +_SOKOL_PRIVATE bool _sargs_is_quote(char c) { + if (0 == _sargs.quote) { + return (c == '\'') || (c == '"'); + } else { + return c == _sargs.quote; + } +} + +_SOKOL_PRIVATE void _sargs_begin_quote(char c) { + _sargs.quote = c; +} + +_SOKOL_PRIVATE void _sargs_end_quote(void) { + _sargs.quote = 0; +} + +_SOKOL_PRIVATE bool _sargs_in_quotes(void) { + return 0 != _sargs.quote; +} + +_SOKOL_PRIVATE bool _sargs_is_whitespace(char c) { + return !_sargs_in_quotes() && ((c == ' ') || (c == '\t')); +} + +_SOKOL_PRIVATE void _sargs_start_key(void) { + SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args)); + _sargs.parse_state = _SARGS_PARSING_KEY; + _sargs.args[_sargs.num_args].key = _sargs.buf_pos; +} + +_SOKOL_PRIVATE void _sargs_end_key(void) { + SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args)); + _sargs_putc(0); + // declare val as empty string in case this is a key-only arg + _sargs.args[_sargs.num_args].val = _sargs.buf_pos - 1; + _sargs.num_args++; + _sargs.parse_state = 0; +} + +_SOKOL_PRIVATE bool _sargs_parsing_key(void) { + return 0 != (_sargs.parse_state & _SARGS_PARSING_KEY); +} + +_SOKOL_PRIVATE void _sargs_start_val(void) { + SOKOL_ASSERT((_sargs.num_args > 0) && (_sargs.num_args <= _sargs.max_args)); + _sargs.parse_state = _SARGS_PARSING_VAL; + _sargs.args[_sargs.num_args - 1].val = _sargs.buf_pos; +} + +_SOKOL_PRIVATE void _sargs_end_val(void) { + _sargs_putc(0); + _sargs.parse_state = 0; +} + +_SOKOL_PRIVATE bool _sargs_is_escape(char c) { + return '\\' == c; +} + +_SOKOL_PRIVATE void _sargs_start_escape(void) { + _sargs.in_escape = true; +} + +_SOKOL_PRIVATE bool _sargs_in_escape(void) { + return _sargs.in_escape; +} + +_SOKOL_PRIVATE char _sargs_escape(char c) { + switch (c) { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + case '\\': return '\\'; + default: return c; + } +} + +_SOKOL_PRIVATE void _sargs_end_escape(void) { + _sargs.in_escape = false; +} + +_SOKOL_PRIVATE bool _sargs_parsing_val(void) { + return 0 != (_sargs.parse_state & _SARGS_PARSING_VAL); +} + +_SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { + char c; + while (0 != (c = *src++)) { + if (_sargs_in_escape()) { + c = _sargs_escape(c); + _sargs_end_escape(); + } else if (_sargs_is_escape(c) && _sargs_parsing_val()) { + _sargs_start_escape(); + continue; + } + if (_sargs_any_expected()) { + if (!_sargs_is_whitespace(c)) { + // start of key, value or separator + if (_sargs_is_separator(c)) { + // skip separator and expect value + _sargs_expect_val(); + continue; + } else if (_sargs_key_expected()) { + // start of new key + _sargs_start_key(); + } else if (_sargs_val_expected()) { + // start of value + if (_sargs_is_quote(c)) { + _sargs_begin_quote(c); + continue; + } + _sargs_start_val(); + } + } else { + // skip white space + continue; + } + } else if (_sargs_parsing_key()) { + if (_sargs_is_whitespace(c) || _sargs_is_separator(c)) { + // end of key string + _sargs_end_key(); + if (_sargs_is_separator(c)) { + _sargs_expect_val(); + } else { + _sargs_expect_sep_or_key(); + } + continue; + } + } else if (_sargs_parsing_val()) { + if (_sargs_in_quotes()) { + /* when in quotes, whitespace is a normal character + and a matching quote ends the value string + */ + if (_sargs_is_quote(c)) { + _sargs_end_quote(); + _sargs_end_val(); + _sargs_expect_key(); + continue; + } + } else if (_sargs_is_whitespace(c)) { + // end of value string (no quotes) + _sargs_end_val(); + _sargs_expect_key(); + continue; + } + } + _sargs_putc(c); + } + if (_sargs_parsing_key()) { + _sargs_end_key(); + _sargs_expect_sep_or_key(); + } else if (_sargs_parsing_val() && !_sargs_in_quotes()) { + _sargs_end_val(); + _sargs_expect_key(); + } + return true; +} + +_SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) { + _sargs_expect_key(); + bool retval = true; + for (int i = 1; i < argc; i++) { + retval &= _sargs_parse_carg(argv[i]); + } + _sargs.parse_state = 0; + return retval; +} + +//-- EMSCRIPTEN IMPLEMENTATION ------------------------------------------------- +#if defined(__EMSCRIPTEN__) + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(EM_JS_DEPS) +EM_JS_DEPS(sokol_audio, "$withStackSave,$stringToUTF8OnStack") +#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) { + return; + } + + /* copy key string */ + char c; + _sargs.args[_sargs.num_args].key = _sargs.buf_pos; + const char* ptr = key; + while (0 != (c = *ptr++)) { + _sargs_putc(c); + } + _sargs_putc(0); + + // copy value string + _sargs.args[_sargs.num_args].val = _sargs.buf_pos; + ptr = val; + while (0 != (c = *ptr++)) { + _sargs_putc(c); + } + _sargs_putc(0); + + _sargs.num_args++; +} +#ifdef __cplusplus +} // extern "C" +#endif + +// JS function to extract arguments from the page URL +EM_JS(void, sargs_js_parse_url, (void), { + 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 = stringToUTF8OnStack(key); + const val_cstr = stringToUTF8OnStack(val); + __sargs_add_kvp(key_cstr, val_cstr) + }); + } +}) + +#endif // EMSCRIPTEN + +//== PUBLIC IMPLEMENTATION FUNCTIONS =========================================== +SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) { + SOKOL_ASSERT(desc); + _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*) _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 + _sargs_parse_cargs(desc->argc, (const char**) desc->argv); + + #if defined(__EMSCRIPTEN__) + // on emscripten, also parse the page URL + sargs_js_parse_url(); + #endif +} + +SOKOL_API_IMPL void sargs_shutdown(void) { + SOKOL_ASSERT(_sargs.valid); + if (_sargs.args) { + _sargs_free(_sargs.args); + _sargs.args = 0; + } + if (_sargs.buf) { + _sargs_free(_sargs.buf); + _sargs.buf = 0; + } + _sargs.valid = false; +} + +SOKOL_API_IMPL bool sargs_isvalid(void) { + return _sargs.valid; +} + +SOKOL_API_IMPL int sargs_find(const char* key) { + SOKOL_ASSERT(_sargs.valid && key); + for (int i = 0; i < _sargs.num_args; i++) { + if (0 == strcmp(_sargs_str(_sargs.args[i].key), key)) { + return i; + } + } + return -1; +} + +SOKOL_API_IMPL int sargs_num_args(void) { + SOKOL_ASSERT(_sargs.valid); + return _sargs.num_args; +} + +SOKOL_API_IMPL const char* sargs_key_at(int index) { + SOKOL_ASSERT(_sargs.valid); + if ((index >= 0) && (index < _sargs.num_args)) { + return _sargs_str(_sargs.args[index].key); + } else { + // index 0 is always the empty string + return _sargs_str(0); + } +} + +SOKOL_API_IMPL const char* sargs_value_at(int index) { + SOKOL_ASSERT(_sargs.valid); + if ((index >= 0) && (index < _sargs.num_args)) { + return _sargs_str(_sargs.args[index].val); + } else { + // index 0 is always the empty string + return _sargs_str(0); + } +} + +SOKOL_API_IMPL bool sargs_exists(const char* key) { + SOKOL_ASSERT(_sargs.valid && key); + return -1 != sargs_find(key); +} + +SOKOL_API_IMPL const char* sargs_value(const char* key) { + SOKOL_ASSERT(_sargs.valid && key); + return sargs_value_at(sargs_find(key)); +} + +SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) { + SOKOL_ASSERT(_sargs.valid && key && def); + int arg_index = sargs_find(key); + if (-1 != arg_index) { + const char* res = sargs_value_at(arg_index); + SOKOL_ASSERT(res); + if (res[0] == 0) { + return def; + } else { + return res; + } + } else { + return def; + } +} + +SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) { + SOKOL_ASSERT(_sargs.valid && key && val); + return 0 == strcmp(sargs_value(key), val); +} + +SOKOL_API_IMPL bool sargs_boolean(const char* key) { + if (sargs_exists(key)) { + const char* val = sargs_value(key); + return (0 == strcmp("true", val)) || + (0 == strcmp("yes", val)) || + (0 == strcmp("on", val)) || + (0 == strcmp("", val)); + } else { + return false; + } +} + +#endif // SOKOL_ARGS_IMPL diff --git a/thirdparty/sokol/sokol_audio.h b/thirdparty/sokol/sokol_audio.h new file mode 100644 index 0000000..bce5631 --- /dev/null +++ b/thirdparty/sokol/sokol_audio.h @@ -0,0 +1,2663 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_AUDIO_IMPL) +#define SOKOL_AUDIO_IMPL +#endif +#ifndef SOKOL_AUDIO_INCLUDED +/* + sokol_audio.h -- cross-platform audio-streaming API + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_AUDIO_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_DUMMY_BACKEND - use a dummy backend + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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: -) + + 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 + + If sokol_audio.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_AUDIO_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Link with the following libraries: + + - on macOS: AudioToolbox + - on iOS: AudioToolbox, AVFoundation + - on FreeBSD: asound + - on Linux: asound + - on Android: 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 + - on Vita: SceAudio + - on 3DS: NDSP (libctru) + + FEATURE OVERVIEW + ================ + You provide a mono- or stereo-stream of 32-bit float samples, which + Sokol Audio feeds into platform-specific audio backends: + + - Windows: WASAPI + - Linux: ALSA + - FreeBSD: ALSA + - macOS: CoreAudio + - iOS: CoreAudio+AVAudioSession + - emscripten: WebAudio with ScriptProcessorNode + - Android: AAudio + - Vita: SceAudio + - 3DS: NDSP (libctru) + + 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 + mixing yourself before forwarding the data to Sokol Audio. + + There are two mutually exclusive ways to provide the sample data: + + 1. Callback model: You provide a callback function, which will be called + when Sokol Audio needs new samples. On all platforms except emscripten, + this function is called from a separate thread. + 2. Push model: Your code pushes small blocks of sample data from your + main loop or a thread you created. The pushed data is stored in + a ring buffer where it is pulled by the backend code when + needed. + + The callback model is preferred because it is the most direct way to + feed sample data into the audio backends and also has less moving parts + (there is no ring buffer between your code and the audio backend). + + Sometimes it is not possible to generate the audio stream directly in a + callback function running in a separate thread, for such cases Sokol Audio + provides the push-model as a convenience. + + SOKOL AUDIO, SOLOUD AND MINIAUDIO + ================================= + The WASAPI, ALSA and CoreAudio backend code has been taken from the + SoLoud library (with some modifications, so any bugs in there are most + likely my fault). If you need a more fully-featured audio solution, check + out SoLoud, it's excellent: + + https://github.com/jarikomppa/soloud + + Another alternative which feature-wise is somewhere inbetween SoLoud and + sokol-audio might be MiniAudio: + + https://github.com/mackron/miniaudio + + GLOSSARY + ======== + - stream buffer: + The internal audio data buffer, usually provided by the backend API. The + size of the stream buffer defines the base latency, smaller buffers have + lower latency but may cause audio glitches. Bigger buffers reduce or + eliminate glitches, but have a higher base latency. + + - stream callback: + Optional callback function which is called by Sokol Audio when it + needs new samples. On Windows, macOS/iOS and Linux, this is called in + a separate thread, on WebAudio, this is called per-frame in the + browser thread. + + - channel: + A discrete track of audio data, currently 1-channel (mono) and + 2-channel (stereo) is supported and tested. + + - sample: + The magnitude of an audio signal on one channel at a given time. In + Sokol Audio, samples are 32-bit float numbers in the range -1.0 to + +1.0. + + - frame: + The tightly packed set of samples for all channels at a given time. + For mono 1 frame is 1 sample. For stereo, 1 frame is 2 samples. + + - packet: + In Sokol Audio, a small chunk of audio data that is moved from the + main thread to the audio streaming thread in order to decouple the + rate at which the main thread provides new audio data, and the + streaming thread consuming audio data. + + WORKING WITH SOKOL AUDIO + ======================== + First call saudio_setup() with your preferred audio playback options. + In most cases you can stick with the default values, these provide + 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){ .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 + that, use the alternative stream_userdata_cb and also set the user_data pointer: + + 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 + saudio_desc struct: + + General parameters (both for stream-callback and push-model): + + int sample_rate -- the sample rate in Hz, default: 44100 + int num_channels -- number of channels, default: 1 (mono) + int buffer_frames -- number of frames in streaming buffer, default: 2048 + + The stream callback prototype (either with or without userdata): + + void (*stream_cb)(float* buffer, int num_frames, int num_channels) + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data) + Function pointer to the user-provide stream callback. + + Push-model parameters: + + int packet_frames -- number of frames in a packet, default: 128 + int num_packets -- number of packets in ring buffer, default: 64 + + The sample_rate and num_channels parameters are only hints for the audio + backend, it isn't guaranteed that those are the values used for actual + playback. + + To get the actual parameters, call the following functions after + saudio_setup(): + + int saudio_sample_rate(void) + int saudio_channels(void); + + It's unlikely that the number of channels will be different than requested, + but a different sample rate isn't uncommon. + + (NOTE: there's an yet unsolved issue when an audio backend might switch + to a different sample rate when switching output devices, for instance + plugging in a bluetooth headset, this case is currently not handled in + Sokol Audio). + + You can check if audio initialization was successful with + saudio_isvalid(). If backend initialization failed for some reason + (for instance when there's no audio device in the machine), this + will return false. Not checking for success won't do any harm, all + Sokol Audio function will silently fail when called after initialization + has failed, so apart from missing audio output, nothing bad will happen. + + Before your application exits, you should call + + saudio_shutdown(); + + This stops the audio thread (on Linux, Windows and macOS/iOS) and + properly shuts down the audio backend. + + THE STREAM CALLBACK MODEL + ========================= + To use Sokol Audio in stream-callback-mode, provide a callback function + like this in the saudio_desc struct when calling saudio_setup(): + + void stream_cb(float* buffer, int num_frames, int num_channels) { + ... + } + + Or the alternative version with a user-data argument: + + void stream_userdata_cb(float* buffer, int num_frames, int num_channels, void* user_data) { + my_data_t* my_data = (my_data_t*) user_data; + ... + } + + The job of the callback function is to fill the *buffer* with 32-bit + float sample values. + + To output silence, fill the buffer with zeros: + + void stream_cb(float* buffer, int num_frames, int num_channels) { + const int num_samples = num_frames * num_channels; + for (int i = 0; i < num_samples; i++) { + buffer[i] = 0.0f; + } + } + + For stereo output (num_channels == 2), the samples for the left + and right channel are interleaved: + + void stream_cb(float* buffer, int num_frames, int num_channels) { + assert(2 == num_channels); + for (int i = 0; i < num_frames; i++) { + buffer[2*i + 0] = ...; // left channel + buffer[2*i + 1] = ...; // right channel + } + } + + Please keep in mind that the stream callback function is running in a + separate thread, if you need to share data with the main thread you need + to take care yourself to make the access to the shared data thread-safe! + + THE PUSH MODEL + ============== + To use the push-model for providing audio data, simply don't set (keep + zero-initialized) the stream_cb field in the saudio_desc struct when + calling saudio_setup(). + + To provide sample data with the push model, call the saudio_push() + function at regular intervals (for instance once per frame). You can + call the saudio_expect() function to ask Sokol Audio how much room is + in the ring buffer, but if you provide a continuous stream of data + at the right sample rate, saudio_expect() isn't required (it's a simple + way to sync/throttle your sample generation code with the playback + rate though). + + With saudio_push() you may need to maintain your own intermediate sample + buffer, since pushing individual sample values isn't very efficient. + The following example is from the MOD player sample in + sokol-samples (https://github.com/floooh/sokol-samples): + + const int num_frames = saudio_expect(); + if (num_frames > 0) { + const int num_samples = num_frames * saudio_channels(); + read_samples(flt_buf, num_samples); + saudio_push(flt_buf, num_frames); + } + + Another option is to ignore saudio_expect(), and just push samples as they + are generated in small batches. In this case you *need* to generate the + samples at the right sample rate: + + The following example is taken from the Tiny Emulators project + (https://github.com/floooh/chips-test), this is for mono playback, + so (num_samples == num_frames): + + // tick the sound generator + if (ay38910_tick(&sys->psg)) { + // new sample is ready + sys->sample_buffer[sys->sample_pos++] = sys->psg.sample; + if (sys->sample_pos == sys->num_samples) { + // new sample packet is ready + saudio_push(sys->sample_buffer, sys->num_samples); + sys->sample_pos = 0; + } + } + + THE WEBAUDIO BACKEND + ==================== + The WebAudio backend is currently using a ScriptProcessorNode callback to + feed the sample data into WebAudio. ScriptProcessorNode has been + deprecated for a while because it is running from the main thread, with + the default initialization parameters it works 'pretty well' though. + Ultimately Sokol Audio will use Audio Worklets, but this requires a few + more things to fall into place (Audio Worklets implemented everywhere, + SharedArrayBuffers enabled again, and I need to figure out a 'low-cost' + solution in terms of implementation effort, since Audio Worklets are + a lot more complex than ScriptProcessorNode if the audio data needs to come + from the main thread). + + The WebAudio backend is automatically selected when compiling for + emscripten (__EMSCRIPTEN__ define exists). + + https://developers.google.com/web/updates/2017/12/audio-worklet + https://developers.google.com/web/updates/2018/06/audio-worklet-design-pattern + + "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). + Since the CoreAudio API is implemented in C (not Objective-C) on macOS the + implementation part of Sokol Audio can be included into a C source file. + + However on iOS, Sokol Audio must be compiled as Objective-C due to it's + reliance on the AVAudioSession object. The iOS code path support both + being compiled with or without ARC (Automatic Reference Counting). + + For thread synchronisation, the CoreAudio backend will use the + pthread_mutex_* functions. + + The incoming floating point samples will be directly forwarded to + CoreAudio without further conversion. + + macOS and iOS applications that use Sokol Audio need to link with + the AudioToolbox framework. + + THE WASAPI BACKEND + ================== + The WASAPI backend is automatically selected when compiling on Windows + (_WIN32 is defined). + + For thread synchronisation a Win32 critical section is used. + + WASAPI may use a different size for its own streaming buffer then requested, + so the base latency may be slightly bigger. The current backend implementation + converts the incoming floating point sample values to signed 16-bit + integers. + + The required Windows system DLLs are linked with #pragma comment(lib, ...), + so you shouldn't need to add additional linker libs in the build process + (otherwise this is a bug which should be fixed in sokol_audio.h). + + THE ALSA BACKEND + ================ + The ALSA backend is automatically selected when compiling on Linux + ('linux' is defined). + + For thread synchronisation, the pthread_mutex_* functions are used. + + Samples are directly forwarded to ALSA in 32-bit float format, no + further conversion is taking place. + + You need to link with the 'asound' library, and the + header must be present (usually both are installed with some sort + of ALSA development package). + + THE VITA BACKEND + ================ + The VITA backend is automatically selected when compiling with vitasdk + ('PSP2_SDK_VERSION' is defined). + + For thread synchronisation, the pthread_mutex_* functions are used. + + Samples are converted from float to short (uint16_t) to maintain + all the same interface/api as other platforms. + + You may use any supported sample rate you wish, but all audio MUST + match the same sample rate you choose. + + This uses the "BGM" port to allow selecting the sample rate ("Main" + port is restricted to 48000 only). + + You need to link with the 'SceAudio' library, and the + header must be present (usually both are installed with the vitasdk). + + THE 3DS BACKEND + ================ + The 3DS backend is automatically selected when compiling with libctru + ('__3DS__' is defined). + + Running a separate thread on the older 3ds is not a good idea and I + was not able to get it working without slowing down the main thread + too much (it has a single core available with cooperative threads). + + The NDSP seems to work better by using its ndspSetCallback method. + + You may use any supported sample rate you wish, but all audio MUST + match the same sample rate you choose or it will sound slowed down + or sped up. + + The queue size and other NDSP specific parameters can be chosen by + the provided 'saudio_n3ds_desc' type. Defaults will be used if + nothing is provided. + + There is a known issue of a noticeable delay when starting a new + sound on emulators. I was not able to improve this to my liking + and ~300ms can be expected. This can be improved by using a lower + buffer size than the 2048 default but I would not suggest under + 1536. It may crash under 1408, and they must be in multiples of 128. + Note: I was NOT able to reproduce this issue on a real device and + the audio worked perfectly. + + + 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_fn = my_alloc, + .free_fn = 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 + ======= + + zlib/libpng license + + Copyright (c) 2018 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_AUDIO_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_AUDIO_API_DECL) +#define SOKOL_AUDIO_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_AUDIO_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_AUDIO_IMPL) +#define SOKOL_AUDIO_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_AUDIO_API_DECL __declspec(dllimport) +#else +#define SOKOL_AUDIO_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +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(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") \ + _SAUDIO_LOGITEM_XMACRO(VITA_SCEAUDIO_OPEN_FAILED, "sceAudioOutOpenPort() failed") \ + _SAUDIO_LOGITEM_XMACRO(VITA_PTHREAD_CREATE_FAILED, "pthread_create() failed") \ + _SAUDIO_LOGITEM_XMACRO(N3DS_NDSP_OPEN_FAILED, "ndspInit() failed") \ + +#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_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct saudio_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} saudio_allocator; + +typedef enum saudio_n3ds_ndspinterptype { + SAUDIO_N3DS_DSP_INTERP_POLYPHASE = 0, + SAUDIO_N3DS_DSP_INTERP_LINEAR = 1, + SAUDIO_N3DS_DSP_INTERP_NONE = 2, +} saudio_n3ds_ndspinterptype; + +typedef struct saudio_n3ds_desc { + /* the 3DS requires multiple queues that it alternates between. */ + /* a single buffer will "work" but is choppy due to a slight */ + /* delay when it changes queues. */ + int queue_count; /* default value = 2 */ + + /* NDSP_INTERP_POLYPHASE = 0 (high quality, slower) */ + /* NDSP_INTERP_LINEAR = 1 (med quality, medium) */ + /* NDSP_INTERP_NONE = 2 (low quality, fast) */ + saudio_n3ds_ndspinterptype interpolation_type; /* default value = 0 */ + + /* 3DS supports different audio channels. they can be used */ + /* in a variety of ways as independent streams etc. */ + /* this implementation in sokol does NOT allow multiple */ + /* due to calling the global ndspInit/ndspExit functions. */ + /* valid range 0-23 */ + int channel_id; /* default value = 0 */ +} saudio_n3ds_desc; + +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 + saudio_n3ds_desc n3ds; // optional data for use on n3ds + saudio_allocator allocator; // optional allocation override functions + saudio_logger logger; // optional logging function (default: NO LOGGING!) +} saudio_desc; + +/* setup sokol-audio */ +SOKOL_AUDIO_API_DECL void saudio_setup(const saudio_desc* desc); +/* shutdown sokol-audio */ +SOKOL_AUDIO_API_DECL void saudio_shutdown(void); +/* true after setup if audio backend was successfully initialized */ +SOKOL_AUDIO_API_DECL bool saudio_isvalid(void); +/* return the saudio_desc.user_data pointer */ +SOKOL_AUDIO_API_DECL void* saudio_userdata(void); +/* return a copy of the original saudio_desc struct */ +SOKOL_AUDIO_API_DECL saudio_desc saudio_query_desc(void); +/* actual sample rate */ +SOKOL_AUDIO_API_DECL int saudio_sample_rate(void); +/* return actual backend buffer size in number of frames */ +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 */ +SOKOL_AUDIO_API_DECL int saudio_push(const float* frames, int num_frames); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); } + +#endif +#endif // SOKOL_AUDIO_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>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 + +#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 defines +#if defined(SOKOL_DUMMY_BACKEND) + // nothing +#elif defined(__APPLE__) + #define _SAUDIO_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + #define _SAUDIO_IOS (1) + #else + #define _SAUDIO_MACOS (1) + #endif +#elif defined(__EMSCRIPTEN__) + #define _SAUDIO_EMSCRIPTEN (1) +#elif defined(_WIN32) + #define _SAUDIO_WINDOWS (1) + #include + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #error "sokol_audio.h no longer supports UWP" + #endif +#elif defined(__ANDROID__) + #define _SAUDIO_ANDROID (1) +#elif defined(__linux__) || defined(__unix__) + #define _SAUDIO_LINUX (1) +#elif defined(PSP2_SDK_VERSION) + #define _SAUDIO_VITA (1) + #include +#elif defined(__3DS__) + #define _SAUDIO_N3DS (1) + #include <3ds.h> +#else +#error "sokol_audio.h: Unknown platform" +#endif + +// platform-specific headers and definitions +#if defined(SOKOL_DUMMY_BACKEND) + #define _SAUDIO_NOTHREADS (1) +#elif defined(_SAUDIO_WINDOWS) + #if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunknown-pragmas" + #endif + #define _SAUDIO_WINTHREADS (1) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #pragma comment (lib, "kernel32") + #pragma comment (lib, "ole32") + #ifndef CINTERFACE + #define CINTERFACE + #endif + #ifndef COBJMACROS + #define COBJMACROS + #endif + #ifndef CONST_VTABLE + #define CONST_VTABLE + #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_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 + #define _SOKOL_AUDIO_WIN32COM_ID(x) (&x) + #endif + /* fix for Visual Studio 2015 SDKs */ + #ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM + #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 + #endif + #ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY + #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 + #endif + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #endif + #if defined(__GNUC__) + #pragma GCC diagnostic pop + #endif +#elif defined(_SAUDIO_APPLE) + #define _SAUDIO_PTHREADS (1) + #include + #if defined(_SAUDIO_IOS) + // always use system headers on iOS (for now at least) + #if !defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + #define SAUDIO_OSX_USE_SYSTEM_HEADERS (1) + #endif + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_audio.h on iOS requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #include + #else + #if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + #include + #endif + #endif +#elif defined(_SAUDIO_ANDROID) + #define _SAUDIO_PTHREADS (1) + #include + #include "aaudio/AAudio.h" +#elif defined(_SAUDIO_LINUX) + #if !defined(__FreeBSD__) + #include + #endif + #define _SAUDIO_PTHREADS (1) + #include + #define ALSA_PCM_NEW_HW_PARAMS_API + #include +#elif defined(__EMSCRIPTEN__) + #define _SAUDIO_NOTHREADS (1) + #include +#elif defined(_SAUDIO_VITA) + #define _SAUDIO_PTHREADS (1) + #include +#elif defined(_SAUDIO_N3DS) + #define _SAUDIO_NOTHREADS (1) +#endif + +#define _saudio_def(val, def) (((val) == 0) ? (def) : (val)) +#define _saudio_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) + +#define _SAUDIO_DEFAULT_SAMPLE_RATE (44100) +#define _SAUDIO_DEFAULT_BUFFER_FRAMES (2048) +#define _SAUDIO_DEFAULT_PACKET_FRAMES (128) +#define _SAUDIO_DEFAULT_NUM_PACKETS ((_SAUDIO_DEFAULT_BUFFER_FRAMES/_SAUDIO_DEFAULT_PACKET_FRAMES)*4) + +#ifndef SAUDIO_RING_MAX_SLOTS +#define SAUDIO_RING_MAX_SLOTS (1024) +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +#if defined(_SAUDIO_PTHREADS) + +typedef struct { + pthread_mutex_t mutex; +} _saudio_mutex_t; + +#elif defined(_SAUDIO_WINTHREADS) + +typedef struct { + CRITICAL_SECTION critsec; +} _saudio_mutex_t; + +#elif defined(_SAUDIO_NOTHREADS) + +typedef struct { + int dummy_mutex; +} _saudio_mutex_t; + +#endif + +#if defined(SOKOL_DUMMY_BACKEND) + +typedef struct { + int dummy; +} _saudio_dummy_backend_t; + +#elif defined(_SAUDIO_APPLE) + +#if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + +typedef AudioQueueRef _saudio_AudioQueueRef; +typedef AudioQueueBufferRef _saudio_AudioQueueBufferRef; +typedef AudioStreamBasicDescription _saudio_AudioStreamBasicDescription; +typedef OSStatus _saudio_OSStatus; + +#define _saudio_kAudioFormatLinearPCM (kAudioFormatLinearPCM) +#define _saudio_kLinearPCMFormatFlagIsFloat (kLinearPCMFormatFlagIsFloat) +#define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked) + +#else +#ifdef __cplusplus +extern "C" { +#endif + +// embedded AudioToolbox declarations +typedef uint32_t _saudio_AudioFormatID; +typedef uint32_t _saudio_AudioFormatFlags; +typedef int32_t _saudio_OSStatus; +typedef uint32_t _saudio_SMPTETimeType; +typedef uint32_t _saudio_SMPTETimeFlags; +typedef uint32_t _saudio_AudioTimeStampFlags; +typedef void* _saudio_CFRunLoopRef; +typedef void* _saudio_CFStringRef; +typedef void* _saudio_AudioQueueRef; + +#define _saudio_kAudioFormatLinearPCM ('lpcm') +#define _saudio_kLinearPCMFormatFlagIsFloat (1U << 0) +#define _saudio_kAudioFormatFlagIsPacked (1U << 3) + +typedef struct _saudio_AudioStreamBasicDescription { + double mSampleRate; + _saudio_AudioFormatID mFormatID; + _saudio_AudioFormatFlags mFormatFlags; + uint32_t mBytesPerPacket; + uint32_t mFramesPerPacket; + uint32_t mBytesPerFrame; + uint32_t mChannelsPerFrame; + uint32_t mBitsPerChannel; + uint32_t mReserved; +} _saudio_AudioStreamBasicDescription; + +typedef struct _saudio_AudioStreamPacketDescription { + int64_t mStartOffset; + uint32_t mVariableFramesInPacket; + uint32_t mDataByteSize; +} _saudio_AudioStreamPacketDescription; + +typedef struct _saudio_SMPTETime { + int16_t mSubframes; + int16_t mSubframeDivisor; + uint32_t mCounter; + _saudio_SMPTETimeType mType; + _saudio_SMPTETimeFlags mFlags; + int16_t mHours; + int16_t mMinutes; + int16_t mSeconds; + int16_t mFrames; +} _saudio_SMPTETime; + +typedef struct _saudio_AudioTimeStamp { + double mSampleTime; + uint64_t mHostTime; + double mRateScalar; + uint64_t mWordClockTime; + _saudio_SMPTETime mSMPTETime; + _saudio_AudioTimeStampFlags mFlags; + uint32_t mReserved; +} _saudio_AudioTimeStamp; + +typedef struct _saudio_AudioQueueBuffer { + const uint32_t mAudioDataBytesCapacity; + void* const mAudioData; + uint32_t mAudioDataByteSize; + void * mUserData; + const uint32_t mPacketDescriptionCapacity; + _saudio_AudioStreamPacketDescription* const mPacketDescriptions; + uint32_t mPacketDescriptionCount; +} _saudio_AudioQueueBuffer; +typedef _saudio_AudioQueueBuffer* _saudio_AudioQueueBufferRef; + +typedef void (*_saudio_AudioQueueOutputCallback)(void* user_data, _saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer); + +extern _saudio_OSStatus AudioQueueNewOutput(const _saudio_AudioStreamBasicDescription* inFormat, _saudio_AudioQueueOutputCallback inCallbackProc, void* inUserData, _saudio_CFRunLoopRef inCallbackRunLoop, _saudio_CFStringRef inCallbackRunLoopMode, uint32_t inFlags, _saudio_AudioQueueRef* outAQ); +extern _saudio_OSStatus AudioQueueDispose(_saudio_AudioQueueRef inAQ, bool inImmediate); +extern _saudio_OSStatus AudioQueueAllocateBuffer(_saudio_AudioQueueRef inAQ, uint32_t inBufferByteSize, _saudio_AudioQueueBufferRef* outBuffer); +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 { + _saudio_AudioQueueRef ca_audio_queue; + #if defined(_SAUDIO_IOS) + id ca_interruption_handler; + #endif +} _saudio_apple_backend_t; + +#elif defined(_SAUDIO_LINUX) + +typedef struct { + snd_pcm_t* device; + float* buffer; + int buffer_byte_size; + int buffer_frames; + pthread_t thread; + bool thread_stop; +} _saudio_alsa_backend_t; + +#elif defined(_SAUDIO_ANDROID) + +typedef struct { + AAudioStreamBuilder* builder; + AAudioStream* stream; + pthread_t thread; + pthread_mutex_t mutex; +} _saudio_aaudio_backend_t; + +#elif defined(_SAUDIO_WINDOWS) + +typedef struct { + HANDLE thread_handle; + HANDLE buffer_end_event; + bool stop; + UINT32 dst_buffer_frames; + int src_buffer_frames; + int src_buffer_byte_size; + int src_buffer_pos; + float* src_buffer; +} _saudio_wasapi_thread_data_t; + +typedef struct { + IMMDeviceEnumerator* device_enumerator; + IMMDevice* device; + IAudioClient* audio_client; + IAudioRenderClient* render_client; + _saudio_wasapi_thread_data_t thread; +} _saudio_wasapi_backend_t; + +#elif defined(_SAUDIO_EMSCRIPTEN) + +typedef struct { + uint8_t* buffer; +} _saudio_web_backend_t; + +#elif defined(_SAUDIO_VITA) + +typedef struct { + int device; + float* buffer; + int16_t* buffer_vita; + int buffer_byte_size; + int buffer_frames; + pthread_t thread; + bool thread_stop; +} _saudio_vita_backend_t; + +#elif defined(_SAUDIO_N3DS) + +typedef struct { + saudio_n3ds_desc n3ds_desc; /* n3ds specific data */ + float* buffer; /* used by sokol as floats */ + int16_t* buffer_n3ds; /* sokol buffer converted to int16 */ + ndspWaveBuf* queue_n3ds; /* device queues on 3DS */ + int samples_per_buffer; /* frames * channel count */ + int buffer_byte_size; + bool thread_stop; +} _saudio_n3ds_backend_t; + +#else +#error "unknown platform" +#endif + +#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) +typedef _saudio_aaudio_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_LINUX) +typedef _saudio_alsa_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_VITA) +typedef _saudio_vita_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_N3DS) +typedef _saudio_n3ds_backend_t _saudio_backend_t; +#endif + +/* a ringbuffer structure */ +typedef struct { + int head; // next slot to write to + int tail; // next slot to read from + int num; // number of slots in queue + int queue[SAUDIO_RING_MAX_SLOTS]; +} _saudio_ring_t; + +/* a packet FIFO structure */ +typedef struct { + bool valid; + int packet_size; /* size of a single packets in bytes(!) */ + int num_packets; /* number of packet in fifo */ + uint8_t* base_ptr; /* packet memory chunk base pointer (dynamically allocated) */ + int cur_packet; /* current write-packet */ + int cur_offset; /* current byte-offset into current write packet */ + _saudio_mutex_t mutex; /* mutex for thread-safe access */ + _saudio_ring_t read_queue; /* buffers with data, ready to be streamed */ + _saudio_ring_t write_queue; /* empty buffers, ready to be pushed to */ +} _saudio_fifo_t; + +/* sokol-audio state */ +typedef struct { + bool valid; + bool setup_called; + void (*stream_cb)(float* buffer, int num_frames, int num_channels); + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); + void* user_data; + int sample_rate; /* sample rate */ + int buffer_frames; /* number of frames in streaming buffer */ + int bytes_per_frame; /* filled by backend */ + int packet_frames; /* number of frames in a packet */ + int num_packets; /* number of packets in packet queue */ + int num_channels; /* actual number of channels */ + saudio_desc desc; + _saudio_fifo_t fifo; + _saudio_backend_t backend; +} _saudio_state_t; + +_SOKOL_PRIVATE _saudio_state_t _saudio; + +_SOKOL_PRIVATE bool _saudio_has_callback(void) { + return (_saudio.stream_cb || _saudio.stream_userdata_cb); +} + +_SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int num_channels) { + if (_saudio.stream_cb) { + _saudio.stream_cb(buffer, num_frames, num_channels); + } + else if (_saudio.stream_userdata_cb) { + _saudio.stream_userdata_cb(buffer, num_frames, num_channels, _saudio.user_data); + } +} + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>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, (uint32_t)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_fn) { + ptr = _saudio.desc.allocator.alloc_fn(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_fn) { + _saudio.desc.allocator.free_fn(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; } +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { (void)m; } + +#elif defined(_SAUDIO_PTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&m->mutex, &attr); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + pthread_mutex_destroy(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + pthread_mutex_lock(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + pthread_mutex_unlock(&m->mutex); +} + +#elif defined(_SAUDIO_WINTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + InitializeCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + DeleteCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + EnterCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + LeaveCriticalSection(&m->critsec); +} +#elif defined(_SAUDIO_VITA) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&m->mutex, &attr); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + pthread_mutex_destroy(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + pthread_mutex_lock(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + pthread_mutex_unlock(&m->mutex); +} + +#else +#error "sokol_audio.h: unknown platform!" +#endif + +// ██████ ██ ███ ██ ██████ ██████ ██ ██ ███████ ███████ ███████ ██████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ███ ██████ ██ ██ █████ █████ █████ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ████ ██████ ██████ ██████ ██ ██ ███████ ██ ██ +// +// >>ringbuffer +_SOKOL_PRIVATE int _saudio_ring_idx(_saudio_ring_t* ring, int i) { + return (i % ring->num); +} + +_SOKOL_PRIVATE void _saudio_ring_init(_saudio_ring_t* ring, int num_slots) { + SOKOL_ASSERT((num_slots + 1) <= SAUDIO_RING_MAX_SLOTS); + ring->head = 0; + ring->tail = 0; + /* one slot reserved to detect 'full' vs 'empty' */ + ring->num = num_slots + 1; +} + +_SOKOL_PRIVATE bool _saudio_ring_full(_saudio_ring_t* ring) { + return _saudio_ring_idx(ring, ring->head + 1) == ring->tail; +} + +_SOKOL_PRIVATE bool _saudio_ring_empty(_saudio_ring_t* ring) { + return ring->head == ring->tail; +} + +_SOKOL_PRIVATE int _saudio_ring_count(_saudio_ring_t* ring) { + int count; + if (ring->head >= ring->tail) { + count = ring->head - ring->tail; + } + else { + count = (ring->head + ring->num) - ring->tail; + } + SOKOL_ASSERT(count < ring->num); + return count; +} + +_SOKOL_PRIVATE void _saudio_ring_enqueue(_saudio_ring_t* ring, int val) { + SOKOL_ASSERT(!_saudio_ring_full(ring)); + ring->queue[ring->head] = val; + ring->head = _saudio_ring_idx(ring, ring->head + 1); +} + +_SOKOL_PRIVATE int _saudio_ring_dequeue(_saudio_ring_t* ring) { + SOKOL_ASSERT(!_saudio_ring_empty(ring)); + int val = ring->queue[ring->tail]; + ring->tail = _saudio_ring_idx(ring, ring->tail + 1); + return val; +} + +// ███████ ██ ███████ ██████ +// ██ ██ ██ ██ ██ +// █████ ██ █████ ██ ██ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ +// +// >>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, + thus the fifo init must already be protected from access by the fifo_read() func. + */ + _saudio_mutex_lock(&fifo->mutex); + SOKOL_ASSERT((packet_size > 0) && (num_packets > 0)); + fifo->packet_size = packet_size; + fifo->num_packets = num_packets; + 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); + _saudio_ring_init(&fifo->write_queue, num_packets); + for (int i = 0; i < num_packets; i++) { + _saudio_ring_enqueue(&fifo->write_queue, i); + } + SOKOL_ASSERT(_saudio_ring_full(&fifo->write_queue)); + SOKOL_ASSERT(_saudio_ring_count(&fifo->write_queue) == num_packets); + SOKOL_ASSERT(_saudio_ring_empty(&fifo->read_queue)); + SOKOL_ASSERT(_saudio_ring_count(&fifo->read_queue) == 0); + fifo->valid = true; + _saudio_mutex_unlock(&fifo->mutex); +} + +_SOKOL_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) { + SOKOL_ASSERT(fifo->base_ptr); + _saudio_free(fifo->base_ptr); + fifo->base_ptr = 0; + fifo->valid = false; +} + +_SOKOL_PRIVATE int _saudio_fifo_writable_bytes(_saudio_fifo_t* fifo) { + _saudio_mutex_lock(&fifo->mutex); + int num_bytes = (_saudio_ring_count(&fifo->write_queue) * fifo->packet_size); + if (fifo->cur_packet != -1) { + num_bytes += fifo->packet_size - fifo->cur_offset; + } + _saudio_mutex_unlock(&fifo->mutex); + SOKOL_ASSERT((num_bytes >= 0) && (num_bytes <= (fifo->num_packets * fifo->packet_size))); + return num_bytes; +} + +/* write new data to the write queue, this is called from main thread */ +_SOKOL_PRIVATE int _saudio_fifo_write(_saudio_fifo_t* fifo, const uint8_t* ptr, int num_bytes) { + /* returns the number of bytes written, this will be smaller then requested + if the write queue runs full + */ + int all_to_copy = num_bytes; + while (all_to_copy > 0) { + /* need to grab a new packet? */ + if (fifo->cur_packet == -1) { + _saudio_mutex_lock(&fifo->mutex); + if (!_saudio_ring_empty(&fifo->write_queue)) { + fifo->cur_packet = _saudio_ring_dequeue(&fifo->write_queue); + } + _saudio_mutex_unlock(&fifo->mutex); + SOKOL_ASSERT(fifo->cur_offset == 0); + } + /* append data to current write packet */ + if (fifo->cur_packet != -1) { + int to_copy = all_to_copy; + const int max_copy = fifo->packet_size - fifo->cur_offset; + if (to_copy > max_copy) { + to_copy = max_copy; + } + uint8_t* dst = fifo->base_ptr + fifo->cur_packet * fifo->packet_size + fifo->cur_offset; + memcpy(dst, ptr, (size_t)to_copy); + ptr += to_copy; + fifo->cur_offset += to_copy; + all_to_copy -= to_copy; + SOKOL_ASSERT(fifo->cur_offset <= fifo->packet_size); + SOKOL_ASSERT(all_to_copy >= 0); + } + else { + /* early out if we're starving */ + int bytes_copied = num_bytes - all_to_copy; + SOKOL_ASSERT((bytes_copied >= 0) && (bytes_copied < num_bytes)); + return bytes_copied; + } + /* if write packet is full, push to read queue */ + if (fifo->cur_offset == fifo->packet_size) { + _saudio_mutex_lock(&fifo->mutex); + _saudio_ring_enqueue(&fifo->read_queue, fifo->cur_packet); + _saudio_mutex_unlock(&fifo->mutex); + fifo->cur_packet = -1; + fifo->cur_offset = 0; + } + } + SOKOL_ASSERT(all_to_copy == 0); + return num_bytes; +} + +/* read queued data, this is called form the stream callback (maybe separate thread) */ +_SOKOL_PRIVATE int _saudio_fifo_read(_saudio_fifo_t* fifo, uint8_t* ptr, int num_bytes) { + /* NOTE: fifo_read might be called before the fifo is properly initialized */ + _saudio_mutex_lock(&fifo->mutex); + int num_bytes_copied = 0; + if (fifo->valid) { + SOKOL_ASSERT(0 == (num_bytes % fifo->packet_size)); + SOKOL_ASSERT(num_bytes <= (fifo->packet_size * fifo->num_packets)); + const int num_packets_needed = num_bytes / fifo->packet_size; + uint8_t* dst = ptr; + /* either pull a full buffer worth of data, or nothing */ + if (_saudio_ring_count(&fifo->read_queue) >= num_packets_needed) { + for (int i = 0; i < num_packets_needed; i++) { + int packet_index = _saudio_ring_dequeue(&fifo->read_queue); + _saudio_ring_enqueue(&fifo->write_queue, packet_index); + const uint8_t* src = fifo->base_ptr + packet_index * fifo->packet_size; + memcpy(dst, src, (size_t)fifo->packet_size); + dst += fifo->packet_size; + num_bytes_copied += fifo->packet_size; + } + SOKOL_ASSERT(num_bytes == num_bytes_copied); + } + } + _saudio_mutex_unlock(&fifo->mutex); + return num_bytes_copied; +} + +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ +// +// >>dummy +#if defined(SOKOL_DUMMY_BACKEND) +_SOKOL_PRIVATE bool _saudio_dummy_backend_init(void) { + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + return true; +} +_SOKOL_PRIVATE void _saudio_dummy_backend_shutdown(void) { } + +// █████ ██ ███████ █████ +// ██ ██ ██ ██ ██ ██ +// ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ██ +// +// >>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) + +_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; +} + +// ██████ ██████ ██████ ███████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ █████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ███████ ██ ██ ██████ ██████ ██ ██████ +// +// >>coreaudio +#elif defined(_SAUDIO_APPLE) + +#if defined(_SAUDIO_IOS) +#if __has_feature(objc_arc) +#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +@interface _saudio_interruption_handler : NSObject { } +@end + +@implementation _saudio_interruption_handler +-(id)init { + self = [super init]; + AVAudioSession* session = [AVAudioSession sharedInstance]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session]; + return self; +} + +-(void)dealloc { + [self remove_handler]; + #if !__has_feature(objc_arc) + [super dealloc]; + #endif +} + +-(void)remove_handler { + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil]; +} + +-(void)handle_interruption:(NSNotification*)notification { + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + NSDictionary* dict = notification.userInfo; + SOKOL_ASSERT(dict); + NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + 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]; + if (_saudio.backend.ca_audio_queue) { + AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + } + break; + default: + break; + } +} +@end +#endif // _SAUDIO_IOS + +/* NOTE: the buffer data callback is called on a separate thread! */ +_SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQueueRef queue, _saudio_AudioQueueBufferRef buffer) { + _SOKOL_UNUSED(user_data); + if (_saudio_has_callback()) { + const int num_frames = (int)buffer->mAudioDataByteSize / _saudio.bytes_per_frame; + const int num_channels = _saudio.num_channels; + _saudio_stream_callback((float*)buffer->mAudioData, num_frames, num_channels); + } + else { + uint8_t* ptr = (uint8_t*)buffer->mAudioData; + 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 */ + _saudio_clear(ptr, (size_t)num_bytes); + } + } + AudioQueueEnqueueBuffer(queue, buffer, 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; + } + #if defined(_SAUDIO_IOS) + /* remove interruption handler */ + if (_saudio.backend.ca_interruption_handler != nil) { + [_saudio.backend.ca_interruption_handler remove_handler]; + _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler); + } + /* deactivate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + [session setActive:false error:nil];; + #endif // _SAUDIO_IOS +} + +_SOKOL_PRIVATE bool _saudio_coreaudio_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 error:nil]; + [session setActive:true error:nil]; + + /* 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; + } + 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; + } + buf->mAudioDataByteSize = buf_byte_size; + _saudio_clear(buf->mAudioData, buf->mAudioDataByteSize); + AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); + } + + /* init or modify actual playback parameters */ + _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; + + /* ...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; +} + +// ██ ██ ██ ████████ █████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ +// █████ ██ ██ ██ ██ +// +// >>vita +#elif defined(_SAUDIO_VITA) + +/* the streaming callback runs in a separate thread */ +_SOKOL_PRIVATE void* _saudio_vita_cb(void* param) { + _SOKOL_UNUSED(param); + while (!_saudio.backend.thread_stop) { + for (int i = 0; i < (_saudio.buffer_frames * _saudio.num_channels); i++) { + _saudio.backend.buffer_vita[i] = (int16_t)(_saudio.backend.buffer[i] * 32767.0f); + } + int write_res = sceAudioOutOutput(_saudio.backend.device, _saudio.backend.buffer_vita); + if (write_res < 0) { + /* underrun occurred */ + } + 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_vita_backend_init(void) { + SceAudioOutMode sceAudioOutMode = _saudio.num_channels == 1 ? SCE_AUDIO_OUT_MODE_MONO : SCE_AUDIO_OUT_MODE_STEREO; + int rc = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_BGM, _saudio.buffer_frames, _saudio.sample_rate, sceAudioOutMode); + if (rc < 0) { + _SAUDIO_ERROR(VITA_SCEAUDIO_OPEN_FAILED); + return false; + } + _saudio.backend.device = rc; + + /* read back actual sample rate and channels */ + _saudio.sample_rate = (int)_saudio.sample_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); + _saudio.backend.buffer_vita = (int16_t*) _saudio_malloc_clear((size_t)(_saudio.buffer_frames * _saudio.num_channels * (int)sizeof(int16_t))); + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_vita_cb, 0)) { + _SAUDIO_ERROR(VITA_PTHREAD_CREATE_FAILED); + if (_saudio.backend.device >= 0) { + sceAudioOutReleasePort(_saudio.backend.device); + _saudio.backend.device = -1; + } + return false; + } + + return true; +} + +_SOKOL_PRIVATE void _saudio_vita_backend_shutdown(void) { + _saudio.backend.thread_stop = true; + pthread_join(_saudio.backend.thread, 0); + sceAudioOutReleasePort(_saudio.backend.device); + _saudio_free(_saudio.backend.buffer_vita); + _saudio_free(_saudio.backend.buffer); +} + +// ███████ ██████ ███████ +// ██ ██ ██ ██ +// ██████ ██ ██ ███████ +// ██ ██ ██ ██ +// ███████ ██████ ███████ +// +// >>3ds +#elif defined(_SAUDIO_N3DS) + +/* NDSP triggers a callback for _saudio_n3ds_cb on the main thread */ +_SOKOL_PRIVATE void _saudio_n3ds_cb(void*) { + if(_saudio.backend.thread_stop) { + return; + } + + const float scale = 32767.0f; + + ndspWaveBuf* bufferPtr = 0; + bufferPtr = 0; + int i = 0; + + /* pick an available queue */ + for (i = 0; i < _saudio.backend.n3ds_desc.queue_count; ++i) { + if (_saudio.backend.queue_n3ds[i].status == NDSP_WBUF_DONE) { + bufferPtr = &_saudio.backend.queue_n3ds[i]; + break; + } + } + + if (!bufferPtr) { + /* no buffers are available. we don't want to play */ + /* anything, but we also don't want to drain the queue */ + return; + } + + int16_t* target_buffer = bufferPtr->data_pcm16; + const float* source_buffer = _saudio.backend.buffer; + for (i = 0; i < _saudio.backend.samples_per_buffer; i++) { + /* data_pcm16 points to a region in the linear alloc _saudio.backend.buffer_n3ds */ + target_buffer[i] = (int16_t)(source_buffer[i] * scale); + } + + bufferPtr->nsamples = _saudio.buffer_frames; /* nsamples is actually frames */ + ndspChnWaveBufAdd(_saudio.backend.n3ds_desc.channel_id, bufferPtr); + DSP_FlushDataCache(target_buffer, bufferPtr->nsamples * sizeof(int16_t)); + + /* fill the streaming buffer with new data */ + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.buffer, _saudio.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); + } + } +} + +_SOKOL_PRIVATE bool _saudio_n3ds_backend_init(void) { + int rc = ndspInit(); + if (rc != 0) { + _SAUDIO_ERROR(N3DS_NDSP_OPEN_FAILED); + return false; + } + + /* set defaults if not provided */ + _saudio.backend.n3ds_desc.queue_count = _saudio_def(_saudio.desc.n3ds.queue_count, 2); + _saudio.backend.n3ds_desc.interpolation_type = _saudio_def(_saudio.desc.n3ds.interpolation_type, SAUDIO_N3DS_DSP_INTERP_POLYPHASE); + _saudio.backend.n3ds_desc.channel_id = _saudio_def(_saudio.desc.n3ds.channel_id, 0); + + /* clamp to 2 channels max */ + if (_saudio.num_channels > 2) { + _saudio.num_channels = 2; + } + + ndspChnReset(_saudio.backend.n3ds_desc.channel_id); + ndspChnWaveBufClear(_saudio.backend.n3ds_desc.channel_id); + ndspChnSetInterp(_saudio.backend.n3ds_desc.channel_id, (ndspInterpType)_saudio.backend.n3ds_desc.interpolation_type); /* cast to n3ds enum */ + ndspChnSetRate(_saudio.backend.n3ds_desc.channel_id, _saudio.sample_rate); + ndspChnSetFormat(_saudio.backend.n3ds_desc.channel_id, _saudio.num_channels == 1 ? NDSP_FORMAT_MONO_PCM16 : NDSP_FORMAT_STEREO_PCM16); + ndspSetOutputMode(_saudio.num_channels == 1 ? NDSP_OUTPUT_MONO : NDSP_OUTPUT_STEREO); + + /* read back actual sample rate and channels */ + _saudio.sample_rate = (int)_saudio.sample_rate; + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + /* allocate the streaming buffer */ + _saudio.backend.samples_per_buffer = _saudio.buffer_frames * _saudio.num_channels; + _saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame; + _saudio.backend.buffer = (float*) _saudio_malloc_clear((size_t)_saudio.backend.buffer_byte_size); + _saudio.backend.buffer_n3ds = (int16_t*)linearAlloc(_saudio.backend.n3ds_desc.queue_count * _saudio.backend.samples_per_buffer * sizeof(int16_t)); + _saudio.backend.queue_n3ds = (ndspWaveBuf*)_saudio_malloc(_saudio.backend.n3ds_desc.queue_count * sizeof(ndspWaveBuf)); + + /* prepare the 3ds audio queues */ + int16_t* bufferPtrCopy = _saudio.backend.buffer_n3ds; + for (int i = 0; i < _saudio.backend.n3ds_desc.queue_count; ++i) { + _saudio.backend.queue_n3ds[i].data_vaddr = bufferPtrCopy; /* point the queue at the section of the linear buffer */ + _saudio.backend.queue_n3ds[i].looping = false; /* the user should handle looping on their end */ + _saudio.backend.queue_n3ds[i].status = NDSP_WBUF_DONE; /* default to done status for buffering logic */ + + bufferPtrCopy += _saudio.backend.samples_per_buffer; + } + + /* instead of a thread, ndsp will trigger a callback */ + /* when it needs more data. */ + ndspSetCallback(_saudio_n3ds_cb, 0); + + return true; +} + +_SOKOL_PRIVATE void _saudio_n3ds_backend_shutdown(void) { + _saudio.backend.thread_stop = true; + + if (_saudio.backend.n3ds_desc.channel_id >= 0) { + ndspChnWaveBufClear(_saudio.backend.n3ds_desc.channel_id); + _saudio.backend.n3ds_desc.channel_id = -1; + } + + ndspExit(); + + _saudio_free(_saudio.backend.queue_n3ds); + _saudio_free(_saudio.backend.buffer_n3ds); + _saudio_free(_saudio.backend.buffer); +} +#else +#error "unsupported platform" +#endif + +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) + return _saudio_aaudio_backend_init(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_init(); + #elif defined(_SAUDIO_VITA) + return _saudio_vita_backend_init(); + #elif defined(_SAUDIO_N3DS) + return _saudio_n3ds_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) + _saudio_aaudio_backend_shutdown(); + #elif defined(_SAUDIO_APPLE) + _saudio_coreaudio_backend_shutdown(); + #elif defined(_SAUDIO_VITA) + _saudio_vita_backend_shutdown(); + #elif defined(_SAUDIO_N3DS) + _saudio_n3ds_backend_shutdown(); + #else + #error "unknown platform" + #endif +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { + SOKOL_ASSERT(!_saudio.valid); + SOKOL_ASSERT(!_saudio.setup_called); + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _saudio_clear(&_saudio, sizeof(_saudio)); + _saudio.setup_called = true; + _saudio.desc = *desc; + _saudio.stream_cb = desc->stream_cb; + _saudio.stream_userdata_cb = desc->stream_userdata_cb; + _saudio.user_data = desc->user_data; + _saudio.sample_rate = _saudio_def(_saudio.desc.sample_rate, _SAUDIO_DEFAULT_SAMPLE_RATE); + _saudio.buffer_frames = _saudio_def(_saudio.desc.buffer_frames, _SAUDIO_DEFAULT_BUFFER_FRAMES); + _saudio.packet_frames = _saudio_def(_saudio.desc.packet_frames, _SAUDIO_DEFAULT_PACKET_FRAMES); + _saudio.num_packets = _saudio_def(_saudio.desc.num_packets, _SAUDIO_DEFAULT_NUM_PACKETS); + _saudio.num_channels = _saudio_def(_saudio.desc.num_channels, 1); + _saudio_fifo_init_mutex(&_saudio.fifo); + if (_saudio_backend_init()) { + /* the backend might not support the requested exact buffer size, + make sure the actual buffer size is still a multiple of + the requested packet size + */ + if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) { + _SAUDIO_ERROR(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE); + _saudio_backend_shutdown(); + return; + } + SOKOL_ASSERT(_saudio.bytes_per_frame > 0); + _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) { + SOKOL_ASSERT(_saudio.setup_called); + _saudio.setup_called = false; + if (_saudio.valid) { + _saudio_backend_shutdown(); + _saudio_fifo_shutdown(&_saudio.fifo); + _saudio_fifo_destroy_mutex(&_saudio.fifo); + _saudio.valid = false; + } +} + +SOKOL_API_IMPL bool saudio_isvalid(void) { + return _saudio.valid; +} + +SOKOL_API_IMPL void* saudio_userdata(void) { + SOKOL_ASSERT(_saudio.setup_called); + return _saudio.desc.user_data; +} + +SOKOL_API_IMPL saudio_desc saudio_query_desc(void) { + SOKOL_ASSERT(_saudio.setup_called); + return _saudio.desc; +} + +SOKOL_API_IMPL int saudio_sample_rate(void) { + SOKOL_ASSERT(_saudio.setup_called); + return _saudio.sample_rate; +} + +SOKOL_API_IMPL int saudio_buffer_frames(void) { + SOKOL_ASSERT(_saudio.setup_called); + return _saudio.buffer_frames; +} + +SOKOL_API_IMPL int saudio_channels(void) { + SOKOL_ASSERT(_saudio.setup_called); + return _saudio.num_channels; +} + +SOKOL_API_IMPL bool saudio_suspended(void) { + SOKOL_ASSERT(_saudio.setup_called); + #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) { + SOKOL_ASSERT(_saudio.setup_called); + if (_saudio.valid) { + const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame; + return num_frames; + } + else { + return 0; + } +} + +SOKOL_API_IMPL int saudio_push(const float* frames, int num_frames) { + SOKOL_ASSERT(_saudio.setup_called); + SOKOL_ASSERT(frames && (num_frames > 0)); + if (_saudio.valid) { + const int num_bytes = num_frames * _saudio.bytes_per_frame; + const int num_written = _saudio_fifo_write(&_saudio.fifo, (const uint8_t*)frames, num_bytes); + return num_written / _saudio.bytes_per_frame; + } + else { + return 0; + } +} + +#undef _saudio_def +#undef _saudio_def_flt + +#if defined(_SAUDIO_WINDOWS) +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif + +#endif /* SOKOL_AUDIO_IMPL */ diff --git a/thirdparty/sokol/sokol_fetch.h b/thirdparty/sokol/sokol_fetch.h new file mode 100644 index 0000000..acd10b8 --- /dev/null +++ b/thirdparty/sokol/sokol_fetch.h @@ -0,0 +1,2810 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_FETCH_IMPL) +#define SOKOL_FETCH_IMPL +#endif +#ifndef SOKOL_FETCH_INCLUDED +/* + sokol_fetch.h -- asynchronous data loading/streaming + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_FETCH_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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 + SOKOL_API_IMPL - public function implementation prefix (default: -) + SFETCH_MAX_PATH - max length of UTF-8 filesystem path / URL (default: 1024 bytes) + SFETCH_MAX_USERDATA_UINT64 - max size of embedded userdata in number of uint64_t, userdata + will be copied into an 8-byte aligned memory region associated + with each in-flight request, default value is 16 (== 128 bytes) + SFETCH_MAX_CHANNELS - max number of IO channels (default is 16, also see sfetch_desc_t.num_channels) + + If sokol_fetch.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_FETCH_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + NOTE: The following documentation talks a lot about "IO threads". Actual + threads are only used on platforms where threads are available. The web + version (emscripten/wasm) doesn't use POSIX-style threads, but instead + asynchronous Javascript calls chained together by callbacks. The actual + source code differences between the two approaches have been kept to + a minimum though. + + FEATURE OVERVIEW + ================ + + - Asynchronously load complete files, or stream files incrementally via + HTTP (on web platform), or the local file system (on native platforms) + + - Request / response-callback model, user code sends a request + to initiate a file-load, sokol_fetch.h calls the response callback + on the same thread when data is ready or user-code needs + to respond otherwise + + - Not limited to the main-thread or a single thread: A sokol-fetch + "context" can live on any thread, and multiple contexts + can operate side-by-side on different threads. + + - Memory management for data buffers is under full control of user code. + sokol_fetch.h won't allocate memory after it has been setup. + + - Automatic rate-limiting guarantees that only a maximum number of + requests is processed at any one time, allowing a zero-allocation + model, where all data is streamed into fixed-size, pre-allocated + buffers. + + - Active Requests can be paused, continued and cancelled from anywhere + in the user-thread which sent this request. + + + TL;DR EXAMPLE CODE + ================== + This is the most-simple example code to load a single data file with a + known maximum size: + + (1) initialize sokol-fetch with default parameters (but NOTE that the + default setup parameters provide a safe-but-slow "serialized" + operation). In order to see any logging output in case of errors + you should always provide a logging function + (such as 'slog_func' from sokol_log.h): + + 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: + + static uint8_t buf[MAX_FILE_SIZE]; + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = response_callback, + .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 + the user-code must respond to state changes of the request + (most importantly when data has been loaded): + + void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // 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 + // is finished, no matter if loading was successful or failed, + // so any cleanup-work should happen here... + ... + if (response->failed) { + // 'failed' is true in (addition to 'finished') if something + // went wrong (file doesn't exist, or less bytes could be + // read from the file than expected) + } + } + } + + (4) pump the sokol-fetch message queues, and invoke response callbacks + by calling: + + 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. + + (5) finally, call sfetch_shutdown() at the end of the application: + + There's many other loading-scenarios, for instance one doesn't have to + provide a buffer upfront, this can also happen in the response callback. + + Or it's possible to stream huge files into small fixed-size buffer, + complete with pausing and continuing the download. + + It's also possible to improve the 'pipeline throughput' by fetching + multiple files in parallel, but at the same time limit the maximum + number of requests that can be 'in-flight'. + + For how this all works, please read the following documentation sections :) + + + API DOCUMENTATION + ================= + + void sfetch_setup(const sfetch_desc_t* desc) + -------------------------------------------- + First call sfetch_setup(const sfetch_desc_t*) on any thread before calling + any other sokol-fetch functions on the same thread. + + sfetch_setup() takes a pointer to an sfetch_desc_t struct with setup + parameters. Parameters which should use their default values must + be zero-initialized: + + - max_requests (uint32_t): + The maximum number of requests that can be alive at any time, the + default is 128. + + - num_channels (uint32_t): + The number of "IO channels" used to parallelize and prioritize + requests, the default is 1. + + - num_lanes (uint32_t): + The number of "lanes" on a single channel. Each request which is + currently 'inflight' on a channel occupies one lane until the + request is finished. This is used for automatic rate-limiting + (search below for CHANNELS AND LANES for more details). The + default number of lanes is 1. + + For example, to setup sokol-fetch for max 1024 active requests, 4 channels, + and 8 lanes per channel in C99: + + sfetch_setup(&(sfetch_desc_t){ + .max_requests = 1024, + .num_channels = 4, + .num_lanes = 8 + }); + + sfetch_setup() is the only place where sokol-fetch will allocate memory. + + NOTE that the default setup parameters of 1 channel and 1 lane per channel + has a very poor 'pipeline throughput' since this essentially serializes + IO requests (a new request will only be processed when the last one has + finished), and since each request needs at least one roundtrip between + the user- and IO-thread the throughput will be at most one request per + frame. Search for LATENCY AND THROUGHPUT below for more information on + how to increase throughput. + + NOTE that you can call sfetch_setup() on multiple threads, each thread + will get its own thread-local sokol-fetch instance, which will work + independently from sokol-fetch instances on other threads. + + void sfetch_shutdown(void) + -------------------------- + Call sfetch_shutdown() at the end of the application to stop any + IO threads and free all memory that was allocated in sfetch_setup(). + + sfetch_handle_t sfetch_send(const sfetch_request_t* request) + ------------------------------------------------------------ + Call sfetch_send() to start loading data, the function takes a pointer to an + sfetch_request_t struct with request parameters and returns a + sfetch_handle_t identifying the request for later calls. At least + a path/URL and callback must be provided: + + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = my_response_callback + }); + + sfetch_send() will return an invalid handle if no request can be allocated + from the internal pool because all available request items are 'in-flight'. + + The sfetch_request_t struct contains the following parameters (optional + parameters that are not provided must be zero-initialized): + + - path (const char*, required) + Pointer to an UTF-8 encoded C string describing the filesystem + path or HTTP URL. The string will be copied into an internal data + structure, and passed "as is" (apart from any required + encoding-conversions) to fopen(), CreateFileW() or + the html fetch API call. The maximum length of the string is defined by + the SFETCH_MAX_PATH configuration define, the default is 1024 bytes + including the 0-terminator byte. + + - callback (sfetch_callback_t, required) + Pointer to a response-callback function which is called when the + request needs "user code attention". Search below for REQUEST + STATES AND THE RESPONSE CALLBACK for detailed information about + handling responses in the response callback. + + - channel (uint32_t, optional) + Index of the IO channel where the request should be processed. + Channels are used to parallelize and prioritize requests relative + to each other. Search below for CHANNELS AND LANES for more + information. The default channel is 0. + + - chunk_size (uint32_t, optional) + The chunk_size member is used for streaming data incrementally + in small chunks. After 'chunk_size' bytes have been loaded into + to the streaming buffer, the response callback will be called + with the buffer containing the fetched data for the current chunk. + If chunk_size is 0 (the default), than the whole file will be loaded. + Please search below for CHUNK SIZE AND HTTP COMPRESSION for + important information how streaming works if the web server + is serving compressed data. + + - 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, + it must be big enough to either hold the entire file (if chunk_size + is zero), or the *uncompressed* data for one downloaded chunk + (if chunk_size is > 0). + + - 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 + involving a request handle must happen on that same thread. + + bool sfetch_handle_valid(sfetch_handle_t request) + ------------------------------------------------- + This checks if the provided request handle is valid, and is associated with + a currently active request. It will return false if: + + - sfetch_send() returned an invalid handle because it couldn't allocate + a new request from the internal request pool (because they're all + in flight) + - the request associated with the handle is no longer alive (because + it either finished successfully, or the request failed for some + reason) + + void sfetch_dowork(void) + ------------------------ + Call sfetch_dowork(void) in regular intervals (for instance once per frame) + on the same thread as sfetch_setup() to "turn the gears". If you are sending + requests but never hear back from them in the response callback function, then + the most likely reason is that you forgot to add the call to sfetch_dowork() + in the per-frame function. + + sfetch_dowork() roughly performs the following work: + + - any new requests that have been sent with sfetch_send() since the + last call to sfetch_dowork() will be dispatched to their IO channels + and assigned a free lane. If all lanes on that channel are occupied + by requests 'in flight', incoming requests must wait until + a lane becomes available + + - for all new requests which have been enqueued on a channel which + don't already have a buffer assigned the response callback will be + called with (response->dispatched == true) so that the response + callback can inspect the dynamically assigned lane and bind a buffer + to the request (search below for CHANNELS AND LANE for more info) + + - a state transition from "user side" to "IO thread side" happens for + each new request that has been dispatched to a channel. + + - requests dispatched to a channel are either forwarded into that + channel's worker thread (on native platforms), or cause an HTTP + request to be sent via an asynchronous fetch() call (on the web + platform) + + - for all requests which have finished their current IO operation a + state transition from "IO thread side" to "user side" happens, + and the response callback is called so that the fetched data + can be processed. + + - requests which are completely finished (either because the entire + file content has been loaded, or they are in the FAILED state) are + freed (this just changes their state in the 'request pool', no actual + memory is freed) + + - requests which are not yet finished are fed back into the + 'incoming' queue of their channel, and the cycle starts again, this + only happens for requests which perform data streaming (not load + the entire file at once). + + void sfetch_cancel(sfetch_handle_t request) + ------------------------------------------- + This cancels a request in the next sfetch_dowork() call and invokes the + response callback with (response.failed == true) and (response.finished + == true) to give user-code a chance to do any cleanup work for the + request. If sfetch_cancel() is called for a request that is no longer + alive, nothing bad will happen (the call will simply do nothing). + + void sfetch_pause(sfetch_handle_t request) + ------------------------------------------ + This pauses an active request in the next sfetch_dowork() call and puts + it into the PAUSED state. For all requests in PAUSED state, the response + callback will be called in each call to sfetch_dowork() to give user-code + a chance to CONTINUE the request (by calling sfetch_continue()). Pausing + a request makes sense for dynamic rate-limiting in streaming scenarios + (like video/audio streaming with a fixed number of streaming buffers. As + soon as all available buffers are filled with download data, downloading + more data must be prevented to allow video/audio playback to catch up and + free up empty buffers for new download data. + + void sfetch_continue(sfetch_handle_t request) + --------------------------------------------- + Continues a paused request, counterpart to the sfetch_pause() function. + + void sfetch_bind_buffer(sfetch_handle_t request, sfetch_range_t buffer) + ---------------------------------------------------------------------------------------- + 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. + + void* sfetch_unbind_buffer(sfetch_handle_t request) + --------------------------------------------------- + This removes the current buffer binding from the request and returns + a pointer to the previous buffer (useful if the buffer was dynamically + allocated and it must be freed). + + sfetch_unbind_buffer() *must* be called from inside the response callback. + + The usual code sequence to bind a different buffer in the response + callback might look like this: + + void response_callback(const sfetch_response_t* response) { + if (response.fetched) { + ... + // switch to a different buffer (in the FETCHED state it is + // guaranteed that the request has a buffer, otherwise it + // would have gone into the FAILED state + void* old_buf_ptr = sfetch_unbind_buffer(response.handle); + free(old_buf_ptr); + void* new_buf_ptr = malloc(new_buf_size); + sfetch_bind_buffer(response.handle, new_buf_ptr, new_buf_size); + } + if (response.finished) { + // unbind and free the currently associated buffer, + // the buffer pointer could be null if the request has failed + // NOTE that it is legal to call free() with a nullptr, + // this happens if the request failed to open its file + // and never goes into the OPENED state + void* buf_ptr = sfetch_unbind_buffer(response.handle); + free(buf_ptr); + } + } + + sfetch_desc_t sfetch_desc(void) + ------------------------------- + sfetch_desc() returns a copy of the sfetch_desc_t struct passed to + sfetch_setup(), with zero-initialized values replaced with + their default values. + + int sfetch_max_userdata_bytes(void) + ----------------------------------- + This returns the value of the SFETCH_MAX_USERDATA_UINT64 config + define, but in number of bytes (so SFETCH_MAX_USERDATA_UINT64*8). + + int sfetch_max_path(void) + ------------------------- + Returns the value of the SFETCH_MAX_PATH config define. + + + REQUEST STATES AND THE RESPONSE CALLBACK + ======================================== + A request goes through a number of states during its lifetime. Depending + on the current state of a request, it will be 'owned' either by the + "user-thread" (where the request was sent) or an IO thread. + + You can think of a request as "ping-ponging" between the IO thread and + user thread, any actual IO work is done on the IO thread, while + invocations of the response-callback happen on the user-thread. + + All state transitions and callback invocations happen inside the + sfetch_dowork() function. + + An active request goes through the following states: + + ALLOCATED (user-thread) + + The request has been allocated in sfetch_send() and is + waiting to be dispatched into its IO channel. When this + happens, the request will transition into the DISPATCHED state. + + DISPATCHED (IO thread) + + The request has been dispatched into its IO channel, and a + lane has been assigned to the request. + + If a buffer was provided in sfetch_send() the request will + immediately transition into the FETCHING state and start loading + data into the buffer. + + If no buffer was provided in sfetch_send(), the response + callback will be called with (response->dispatched == true), + so that the response callback can bind a buffer to the + request. Binding the buffer in the response callback makes + sense if the buffer isn't dynamically allocated, but instead + a pre-allocated buffer must be selected from the request's + channel and lane. + + Note that it isn't possible to get a file size in the response callback + which would help with allocating a buffer of the right size, this is + because it isn't possible in HTTP to query the file size before the + entire file is downloaded (...when the web server serves files compressed). + + If opening the file failed, the request will transition into + the FAILED state with the error code SFETCH_ERROR_FILE_NOT_FOUND. + + FETCHING (IO thread) + + While a request is in the FETCHING state, data will be loaded into + the user-provided buffer. + + If no buffer was provided, the request will go into the FAILED + state with the error code SFETCH_ERROR_NO_BUFFER. + + If a buffer was provided, but it is too small to contain the + fetched data, the request will go into the FAILED state with + error code SFETCH_ERROR_BUFFER_TOO_SMALL. + + If less data can be read from the file than expected, the request + will go into the FAILED state with error code SFETCH_ERROR_UNEXPECTED_EOF. + + If loading data into the provided buffer works as expected, the + request will go into the FETCHED state. + + FETCHED (user thread) + + The request goes into the FETCHED state either when the entire file + has been loaded into the provided buffer (when request.chunk_size == 0), + or a chunk has been loaded (and optionally decompressed) into the + buffer (when request.chunk_size > 0). + + The response callback will be called so that the user-code can + process the loaded data using the following sfetch_response_t struct members: + + - 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) + + Once all file data has been loaded, the 'finished' flag will be set + in the response callback's sfetch_response_t argument. + + After the user callback returns, and all file data has been loaded + (response.finished flag is set) the request has reached its end-of-life + and will be recycled. + + Otherwise, if there's still data to load (because streaming was + requested by providing a non-zero request.chunk_size), the request + will switch back to the FETCHING state to load the next chunk of data. + + Note that it is ok to associate a different buffer or buffer-size + with the request by calling sfetch_bind_buffer() in the response-callback. + + To check in the response callback for the FETCHED state, and + independently whether the request is finished: + + void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // request is in FETCHED state, the loaded data is available + // 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 + // has been loaded, the request has been cancelled, + // or the file operation has failed, this is where + // any required per-request cleanup work should happen + } + } + + + FAILED (user thread) + + A request will transition into the FAILED state in the following situations: + + - if the file doesn't exist or couldn't be opened for other + reasons (SFETCH_ERROR_FILE_NOT_FOUND) + - if no buffer is associated with the request in the FETCHING state + (SFETCH_ERROR_NO_BUFFER) + - if the provided buffer is too small to hold the entire file + (if request.chunk_size == 0), or the (potentially decompressed) + partial data chunk (SFETCH_ERROR_BUFFER_TOO_SMALL) + - if less bytes could be read from the file then expected + (SFETCH_ERROR_UNEXPECTED_EOF) + - if a request has been cancelled via sfetch_cancel() + (SFETCH_ERROR_CANCELLED) + + The response callback will be called once after a request goes into + the FAILED state, with the 'response->finished' and + 'response->failed' flags set to true. + + This gives the user-code a chance to cleanup any resources associated + with the request. + + To check for the failed state in the response callback: + + void response_callback(const sfetch_response_t* response) { + if (response->failed) { + // specifically check for the failed state... + } + // or you can do a catch-all check via the finished-flag: + if (response->finished) { + if (response->failed) { + // if more detailed error handling is needed: + switch (response->error_code) { + ... + } + } + } + } + + PAUSED (user thread) + + A request will transition into the PAUSED state after user-code + calls the function sfetch_pause() on the request's handle. Usually + this happens from within the response-callback in streaming scenarios + when the data streaming needs to wait for a data decoder (like + a video/audio player) to catch up. + + While a request is in PAUSED state, the response-callback will be + called in each sfetch_dowork(), so that the user-code can either + continue the request by calling sfetch_continue(), or cancel + the request by calling sfetch_cancel(). + + When calling sfetch_continue() on a paused request, the request will + transition into the FETCHING state. Otherwise if sfetch_cancel() is + called, the request will switch into the FAILED state. + + To check for the PAUSED state in the response callback: + + void response_callback(const sfetch_response_t* response) { + if (response->paused) { + // we can check here whether the request should + // continue to load data: + if (should_continue(response->handle)) { + sfetch_continue(response->handle); + } + } + } + + + CHUNK SIZE AND HTTP COMPRESSION + =============================== + TL;DR: for streaming scenarios, the provided chunk-size must be smaller + than the provided buffer-size because the web server may decide to + serve the data compressed and the chunk-size must be given in 'compressed + bytes' while the buffer receives 'uncompressed bytes'. It's not possible + in HTTP to query the uncompressed size for a compressed download until + that download has finished. + + With vanilla HTTP, it is not possible to query the actual size of a file + without downloading the entire file first (the Content-Length response + header only provides the compressed size). Furthermore, for HTTP + range-requests, the range is given on the compressed data, not the + uncompressed data. So if the web server decides to serve the data + compressed, the content-length and range-request parameters don't + correspond to the uncompressed data that's arriving in the sokol-fetch + buffers, and there's no way from JS or WASM to either force uncompressed + downloads (e.g. by setting the Accept-Encoding field), or access the + compressed data. + + This has some implications for sokol_fetch.h, most notably that buffers + can't be provided in the exactly right size, because that size can't + be queried from HTTP before the data is actually downloaded. + + When downloading whole files at once, it is basically expected that you + know the maximum files size upfront through other means (for instance + through a separate meta-data-file which contains the file sizes and + other meta-data for each file that needs to be loaded). + + For streaming downloads the situation is a bit more complicated. These + use HTTP range-requests, and those ranges are defined on the (potentially) + compressed data which the JS/WASM side doesn't have access to. However, + the JS/WASM side only ever sees the uncompressed data, and it's not possible + to query the uncompressed size of a range request before that range request + has finished. + + If the provided buffer is too small to contain the uncompressed data, + the request will fail with error code SFETCH_ERROR_BUFFER_TOO_SMALL. + + + CHANNELS AND LANES + ================== + 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 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 + processed is selected manually when sending a message: + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = my_response_callback, + .channel = 2 + }); + + The number of channels is configured at startup in sfetch_setup() and + cannot be changed afterwards. + + Channels are completely separate from each other, and a request will + never "hop" from one channel to another. + + Each channel consists of a fixed number of "lanes" for automatic rate + limiting: + + When a request is sent to a channel via sfetch_send(), a "free lane" will + be picked and assigned to the request. The request will occupy this lane + for its entire life time (also while it is paused). If all lanes of a + channel are currently occupied, new requests will wait until a + lane becomes unoccupied. + + Since the number of channels and lanes is known upfront, it is guaranteed + that there will never be more than "num_channels * num_lanes" requests + in flight at any one time. + + This guarantee eliminates unexpected load- and memory-spikes when + many requests are sent in very short time, and it allows to pre-allocate + a fixed number of memory buffers which can be reused for the entire + "lifetime" of a sokol-fetch context. + + In the most simple scenario - when a maximum file size is known - buffers + can be statically allocated like this: + + uint8_t buffer[NUM_CHANNELS][NUM_LANES][MAX_FILE_SIZE]; + + Then in the user callback pick a buffer by channel and lane, + and associate it with the request like this: + + void response_callback(const sfetch_response_t* response) { + if (response->dispatched) { + void* ptr = buffer[response->channel][response->lane]; + sfetch_bind_buffer(response->handle, ptr, MAX_FILE_SIZE); + } + ... + } + + + NOTES ON OPTIMIZING PIPELINE LATENCY AND THROUGHPUT + =================================================== + With the default configuration of 1 channel and 1 lane per channel, + sokol_fetch.h will appear to have a shockingly bad loading performance + if several files are loaded. + + This has two reasons: + + (1) all parallelization when loading data has been disabled. A new + request will only be processed, when the last request has finished. + + (2) every invocation of the response-callback adds one frame of latency + to the request, because callbacks will only be called from within + sfetch_dowork() + + sokol-fetch takes a few shortcuts to improve step (2) and reduce + the 'inherent latency' of a request: + + - if a buffer is provided upfront, the response-callback won't be + 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 + separately when loading has finished (or the request has failed), + instead the finished and failed flags will be set as part of + the last FETCHED invocation + + This means providing a big-enough buffer to fit the entire file is the + best case, the response callback will only be called once, ideally in + 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 DISPATCHED state so that + the user code can bind a buffer. + + This means the best case for a request without an upfront-provided + buffer is 2 frames (or 3 calls to sfetch_dowork()). + + That's about what can be done to improve the latency for a single request, + but the really important step is to improve overall throughput. If you + need to load thousands of files you don't want that to be completely + serialized. + + The most important action to increase throughput is to increase the + number of lanes per channel. This defines how many requests can be + 'in flight' on a single channel at the same time. The guiding decision + factor for how many lanes you can "afford" is the memory size you want + to set aside for buffers. Each lane needs its own buffer so that + the data loaded for one request doesn't scribble over the data + loaded for another request. + + Here's a simple example of sending 4 requests without upfront buffer + on a channel with 1, 2 and 4 lanes, each line is one frame: + + 1 LANE (8 frames): + Lane 0: + ------------- + REQ 0 DISPATCHED + REQ 0 FETCHED + REQ 1 DISPATCHED + REQ 1 FETCHED + REQ 2 DISPATCHED + REQ 2 FETCHED + 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 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 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 lanes, + they don't add any processing overhead. + + The last option for tweaking latency and throughput is channels. Each + channel works independently from other channels, so while one + channel is busy working through a large number of requests (or one + very long streaming download), you can set aside a high-priority channel + for requests that need to start as soon as possible. + + On platforms with threading support, each channel runs on its own + thread, but this is mainly an implementation detail to work around + the traditional blocking 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_fn = my_alloc, + .free_fn = 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) + - Move buffer-management into the API? The "manual management" + can be quite tricky especially for dynamic allocation scenarios, + API support for buffer management would simplify cases like + preventing that requests scribble over each other's buffers, or + an automatic garbage collection for dynamically allocated buffers, + or automatically falling back to dynamic allocation if static + buffers aren't big enough. + - Pluggable request handlers to load data from other "sources" + (especially HTTP downloads on native platforms via e.g. libcurl + would be useful) + - I'm currently not happy how the user-data block is handled, this + should getting and updating the user-data should be wrapped by + API functions (similar to bind/unbind buffer) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2019 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_FETCH_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_FETCH_API_DECL) +#define SOKOL_FETCH_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_FETCH_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_FETCH_IMPL) +#define SOKOL_FETCH_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_FETCH_API_DECL __declspec(dllimport) +#else +#define SOKOL_FETCH_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +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_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sfetch_allocator_t; + +/* configuration values for sfetch_setup() */ +typedef struct sfetch_desc_t { + 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() */ +typedef struct sfetch_handle_t { uint32_t id; } sfetch_handle_t; + +/* error codes */ +typedef enum sfetch_error_t { + SFETCH_ERROR_NO_ERROR, + SFETCH_ERROR_FILE_NOT_FOUND, + SFETCH_ERROR_NO_BUFFER, + SFETCH_ERROR_BUFFER_TOO_SMALL, + SFETCH_ERROR_UNEXPECTED_EOF, + SFETCH_ERROR_INVALID_HTTP_STATUS, + SFETCH_ERROR_CANCELLED, + SFETCH_ERROR_JS_OTHER, // check browser console for detailed error info +} 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 + 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; + +/* request parameters passed to sfetch_send() */ +typedef struct sfetch_request_t { + uint32_t channel; // index of channel this request is assigned to (default: 0) + const char* path; // filesystem path or HTTP URL (required) + void (*callback) (const sfetch_response_t*); // 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) */ +SOKOL_FETCH_API_DECL void sfetch_setup(const sfetch_desc_t* desc); +/* discard a sokol-fetch context */ +SOKOL_FETCH_API_DECL void sfetch_shutdown(void); +/* return true if sokol-fetch has been setup */ +SOKOL_FETCH_API_DECL bool sfetch_valid(void); +/* get the desc struct that was passed to sfetch_setup() */ +SOKOL_FETCH_API_DECL sfetch_desc_t sfetch_desc(void); +/* return the max userdata size in number of bytes (SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) */ +SOKOL_FETCH_API_DECL int sfetch_max_userdata_bytes(void); +/* return the value of the SFETCH_MAX_PATH implementation config value */ +SOKOL_FETCH_API_DECL int sfetch_max_path(void); + +/* send a fetch-request, get handle to request back */ +SOKOL_FETCH_API_DECL sfetch_handle_t sfetch_send(const sfetch_request_t* request); +/* return true if a handle is valid *and* the request is alive */ +SOKOL_FETCH_API_DECL bool sfetch_handle_valid(sfetch_handle_t h); +/* do per-frame work, moves requests into and out of IO threads, and invokes response-callbacks */ +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, 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) */ +SOKOL_FETCH_API_DECL void sfetch_cancel(sfetch_handle_t h); +/* pause a request (will call response callback each frame with .paused) */ +SOKOL_FETCH_API_DECL void sfetch_pause(sfetch_handle_t h); +/* continue a paused request */ +SOKOL_FETCH_API_DECL void sfetch_continue(sfetch_handle_t h); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sfetch_setup(const sfetch_desc_t& desc) { return sfetch_setup(&desc); } +inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfetch_send(&request); } + +#endif +#endif // SOKOL_FETCH_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>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 +#define SFETCH_MAX_PATH (1024) +#endif +#ifndef SFETCH_MAX_USERDATA_UINT64 +#define SFETCH_MAX_USERDATA_UINT64 (16) +#endif +#ifndef SFETCH_MAX_CHANNELS +#define SFETCH_MAX_CHANNELS (16) +#endif + +#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 + +#if defined(__EMSCRIPTEN__) + #include + #define _SFETCH_PLATFORM_EMSCRIPTEN (1) + #define _SFETCH_PLATFORM_WINDOWS (0) + #define _SFETCH_PLATFORM_POSIX (0) + #define _SFETCH_HAS_THREADS (0) +#elif defined(_WIN32) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #define _SFETCH_PLATFORM_WINDOWS (1) + #define _SFETCH_PLATFORM_EMSCRIPTEN (0) + #define _SFETCH_PLATFORM_POSIX (0) + #define _SFETCH_HAS_THREADS (1) +#else + #include + #include /* fopen, fread, fseek, fclose */ + #define _SFETCH_PLATFORM_POSIX (1) + #define _SFETCH_PLATFORM_EMSCRIPTEN (0) + #define _SFETCH_PLATFORM_WINDOWS (0) + #define _SFETCH_HAS_THREADS (1) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4724) // potential mod by 0 +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +typedef struct _sfetch_path_t { + char buf[SFETCH_MAX_PATH]; +} _sfetch_path_t; + +/* a thread with incoming and outgoing message queue syncing */ +#if _SFETCH_PLATFORM_POSIX +typedef struct { + pthread_t thread; + pthread_cond_t incoming_cond; + pthread_mutex_t incoming_mutex; + pthread_mutex_t outgoing_mutex; + pthread_mutex_t running_mutex; + pthread_mutex_t stop_mutex; + bool stop_requested; + bool valid; +} _sfetch_thread_t; +#elif _SFETCH_PLATFORM_WINDOWS +typedef struct { + HANDLE thread; + HANDLE incoming_event; + CRITICAL_SECTION incoming_critsec; + CRITICAL_SECTION outgoing_critsec; + CRITICAL_SECTION running_critsec; + CRITICAL_SECTION stop_critsec; + bool stop_requested; + bool valid; +} _sfetch_thread_t; +#endif + +/* file handle abstraction */ +#if _SFETCH_PLATFORM_POSIX +typedef FILE* _sfetch_file_handle_t; +#define _SFETCH_INVALID_FILE_HANDLE (0) +typedef void*(*_sfetch_thread_func_t)(void*); +#elif _SFETCH_PLATFORM_WINDOWS +typedef HANDLE _sfetch_file_handle_t; +#define _SFETCH_INVALID_FILE_HANDLE (INVALID_HANDLE_VALUE) +typedef LPTHREAD_START_ROUTINE _sfetch_thread_func_t; +#endif + +/* user-side per-request state */ +typedef struct { + bool pause; /* switch item to PAUSED state if true */ + bool cont; /* switch item back to FETCHING if true */ + bool cancel; /* cancel the request, switch into FAILED state */ + /* transfer IO => user thread */ + uint32_t fetched_offset; /* number of bytes fetched so far */ + uint32_t fetched_size; /* size of last fetched chunk */ + sfetch_error_t error_code; + bool finished; + /* user thread only */ + size_t user_data_size; + uint64_t user_data[SFETCH_MAX_USERDATA_UINT64]; +} _sfetch_item_user_t; + +/* thread-side per-request state */ +typedef struct { + /* transfer IO => user thread */ + uint32_t fetched_offset; + uint32_t fetched_size; + sfetch_error_t error_code; + bool failed; + bool finished; + /* IO thread only */ + #if _SFETCH_PLATFORM_EMSCRIPTEN + uint32_t http_range_offset; + #else + _sfetch_file_handle_t file_handle; + #endif + uint32_t content_size; +} _sfetch_item_thread_t; + +/* a request goes through the following states, ping-ponging between IO and user thread */ +typedef enum _sfetch_state_t { + _SFETCH_STATE_INITIAL, /* internal: request has just been initialized */ + _SFETCH_STATE_ALLOCATED, /* internal: request has been allocated from internal pool */ + _SFETCH_STATE_DISPATCHED, /* user thread: request has been dispatched to its IO channel */ + _SFETCH_STATE_FETCHING, /* IO thread: waiting for data to be fetched */ + _SFETCH_STATE_FETCHED, /* user thread: fetched data available */ + _SFETCH_STATE_PAUSED, /* user thread: request has been paused via sfetch_pause() */ + _SFETCH_STATE_FAILED, /* user thread: follow state or FETCHING if something went wrong */ +} _sfetch_state_t; + +/* an internal request item */ +#define _SFETCH_INVALID_LANE (0xFFFFFFFF) +typedef struct { + sfetch_handle_t handle; + _sfetch_state_t state; + uint32_t channel; + uint32_t lane; + uint32_t chunk_size; + void (*callback) (const sfetch_response_t*); + sfetch_range_t buffer; + + /* updated by IO-thread, off-limits to user thread */ + _sfetch_item_thread_t thread; + + /* accessible by user-thread, off-limits to IO thread */ + _sfetch_item_user_t user; + + /* big stuff at the end */ + _sfetch_path_t path; +} _sfetch_item_t; + +/* a pool of internal per-request items */ +typedef struct { + uint32_t size; + uint32_t free_top; + _sfetch_item_t* items; + uint32_t* free_slots; + uint32_t* gen_ctrs; + bool valid; +} _sfetch_pool_t; + +/* a ringbuffer for pool-slot ids */ +typedef struct { + uint32_t head; + uint32_t tail; + uint32_t num; + uint32_t* buf; +} _sfetch_ring_t; + +/* 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 + _sfetch_ring_t free_lanes; + _sfetch_ring_t user_sent; + _sfetch_ring_t user_incoming; + _sfetch_ring_t user_outgoing; + #if _SFETCH_HAS_THREADS + _sfetch_ring_t thread_incoming; + _sfetch_ring_t thread_outgoing; + _sfetch_thread_t thread; + #endif + void (*request_handler)(struct _sfetch_t* ctx, uint32_t slot_id); + bool valid; +} _sfetch_channel_t; + +/* the sfetch global state */ +typedef struct _sfetch_t { + bool setup; + bool valid; + bool in_callback; + sfetch_desc_t desc; + _sfetch_pool_t pool; + _sfetch_channel_t chn[SFETCH_MAX_CHANNELS]; +} _sfetch_t; +#if _SFETCH_HAS_THREADS +#if defined(_MSC_VER) +static __declspec(thread) _sfetch_t* _sfetch; +#else +static __thread _sfetch_t* _sfetch; +#endif +#else +static _sfetch_t* _sfetch; +#endif +#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, (uint32_t)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_fn) { + ptr = allocator->alloc_fn(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_fn) { + _sfetch->desc.allocator.free_fn(ptr, _sfetch->desc.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE _sfetch_t* _sfetch_ctx(void) { + return _sfetch; +} + +_SOKOL_PRIVATE void _sfetch_path_copy(_sfetch_path_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src && (strlen(src) < SFETCH_MAX_PATH)) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, SFETCH_MAX_PATH, src, (SFETCH_MAX_PATH-1)); + #else + strncpy(dst->buf, src, SFETCH_MAX_PATH); + #endif + dst->buf[SFETCH_MAX_PATH-1] = 0; + } else { + _sfetch_clear(dst->buf, SFETCH_MAX_PATH); + } +} + +_SOKOL_PRIVATE _sfetch_path_t _sfetch_path_make(const char* str) { + _sfetch_path_t res; + _sfetch_path_copy(&res, str); + return res; +} + +// ███ ███ ███████ ███████ ███████ █████ ██████ ███████ ██████ ██ ██ ███████ ██ ██ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ███████ ███████ ███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ███████ ██ ██ ██████ ███████ ██████ ██████ ███████ ██████ ███████ +// ▀▀ +// >>message queue +_SOKOL_PRIVATE uint32_t _sfetch_ring_wrap(const _sfetch_ring_t* rb, uint32_t i) { + return i % rb->num; +} + +_SOKOL_PRIVATE void _sfetch_ring_discard(_sfetch_ring_t* rb) { + SOKOL_ASSERT(rb); + if (rb->buf) { + _sfetch_free(rb->buf); + rb->buf = 0; + } + rb->head = 0; + rb->tail = 0; + rb->num = 0; +} + +_SOKOL_PRIVATE bool _sfetch_ring_init(_sfetch_ring_t* rb, uint32_t num_slots) { + SOKOL_ASSERT(rb && (num_slots > 0)); + SOKOL_ASSERT(0 == rb->buf); + rb->head = 0; + rb->tail = 0; + /* 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*) _sfetch_malloc_clear(queue_size); + if (rb->buf) { + return true; + } else { + _sfetch_ring_discard(rb); + return false; + } +} + +_SOKOL_PRIVATE bool _sfetch_ring_full(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + return _sfetch_ring_wrap(rb, rb->head + 1) == rb->tail; +} + +_SOKOL_PRIVATE bool _sfetch_ring_empty(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + return rb->head == rb->tail; +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_count(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + uint32_t count; + if (rb->head >= rb->tail) { + count = rb->head - rb->tail; + } else { + count = (rb->head + rb->num) - rb->tail; + } + SOKOL_ASSERT(count < rb->num); + return count; +} + +_SOKOL_PRIVATE void _sfetch_ring_enqueue(_sfetch_ring_t* rb, uint32_t slot_id) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_full(rb)); + SOKOL_ASSERT(rb->head < rb->num); + rb->buf[rb->head] = slot_id; + rb->head = _sfetch_ring_wrap(rb, rb->head + 1); +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_dequeue(_sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_empty(rb)); + SOKOL_ASSERT(rb->tail < rb->num); + uint32_t slot_id = rb->buf[rb->tail]; + rb->tail = _sfetch_ring_wrap(rb, rb->tail + 1); + return slot_id; +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_peek(const _sfetch_ring_t* rb, uint32_t index) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_empty(rb)); + SOKOL_ASSERT(index < _sfetch_ring_count(rb)); + uint32_t rb_index = _sfetch_ring_wrap(rb, rb->tail + index); + return rb->buf[rb_index]; +} + +// ██████ ███████ ██████ ██ ██ ███████ ███████ ████████ ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ██ ██ ██ ██ █████ ███████ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██████ ██████ ███████ ███████ ██ ██ ██████ ██████ ███████ +// ▀▀ +// >>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); + _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 = 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))) + { + 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)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); +} + +_SOKOL_PRIVATE void _sfetch_pool_discard(_sfetch_pool_t* pool) { + SOKOL_ASSERT(pool); + if (pool->free_slots) { + _sfetch_free(pool->free_slots); + pool->free_slots = 0; + } + if (pool->gen_ctrs) { + _sfetch_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + } + if (pool->items) { + _sfetch_free(pool->items); + pool->items = 0; + } + pool->size = 0; + pool->free_top = 0; + pool->valid = false; +} + +_SOKOL_PRIVATE bool _sfetch_pool_init(_sfetch_pool_t* pool, uint32_t num_items) { + SOKOL_ASSERT(pool && (num_items > 0) && (num_items < ((1<<16)-1))); + SOKOL_ASSERT(0 == pool->items); + /* NOTE: item slot 0 is reserved for the special "invalid" item index 0*/ + 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*) _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*) _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*) _sfetch_malloc_clear(free_slots_size); + if (pool->items && pool->free_slots) { + /* 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; + } + pool->valid = true; + } else { + /* allocation error */ + _sfetch_pool_discard(pool); + } + return pool->valid; +} + +_SOKOL_PRIVATE uint32_t _sfetch_pool_item_alloc(_sfetch_pool_t* pool, const sfetch_request_t* request) { + SOKOL_ASSERT(pool && pool->valid); + if (pool->free_top > 0) { + uint32_t slot_index = pool->free_slots[--pool->free_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + uint32_t slot_id = _sfetch_make_id(slot_index, ++pool->gen_ctrs[slot_index]); + _sfetch_item_init(&pool->items[slot_index], slot_id, request); + pool->items[slot_index].state = _SFETCH_STATE_ALLOCATED; + return slot_id; + } else { + /* pool exhausted, return the 'invalid handle' */ + return _sfetch_make_id(0, 0); + } +} + +_SOKOL_PRIVATE void _sfetch_pool_item_free(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + uint32_t slot_index = _sfetch_slot_index(slot_id); + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + SOKOL_ASSERT(pool->items[slot_index].handle.id == slot_id); + #if defined(SOKOL_DEBUG) + /* debug check against double-free */ + for (uint32_t i = 0; i < pool->free_top; i++) { + SOKOL_ASSERT(pool->free_slots[i] != slot_index); + } + #endif + _sfetch_item_discard(&pool->items[slot_index]); + pool->free_slots[pool->free_top++] = slot_index; + SOKOL_ASSERT(pool->free_top <= (pool->size - 1)); +} + +/* return pointer to item by handle without matching id check */ +_SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_at(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + uint32_t slot_index = _sfetch_slot_index(slot_id); + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return &pool->items[slot_index]; +} + +/* return pointer to item by handle with matching id check */ +_SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_lookup(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + if (0 != slot_id) { + _sfetch_item_t* item = _sfetch_pool_item_at(pool, slot_id); + if (item->handle.id == slot_id) { + return item; + } + } + return 0; +} + +// ██████ ██████ ███████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ███████ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ███████ ██ ██ ██ +// +// >>posix +#if _SFETCH_PLATFORM_POSIX +_SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { + return fopen(path->buf, "rb"); +} + +_SOKOL_PRIVATE void _sfetch_file_close(_sfetch_file_handle_t h) { + fclose(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_handle_valid(_sfetch_file_handle_t h) { + return h != _SFETCH_INVALID_FILE_HANDLE; +} + +_SOKOL_PRIVATE uint32_t _sfetch_file_size(_sfetch_file_handle_t h) { + fseek(h, 0, SEEK_END); + return (uint32_t) ftell(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_read(_sfetch_file_handle_t h, uint32_t offset, uint32_t num_bytes, void* ptr) { + fseek(h, (long)offset, SEEK_SET); + return num_bytes == fread(ptr, 1, num_bytes, h); +} + +_SOKOL_PRIVATE bool _sfetch_thread_init(_sfetch_thread_t* thread, _sfetch_thread_func_t thread_func, void* thread_arg) { + SOKOL_ASSERT(thread && !thread->valid && !thread->stop_requested); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->incoming_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->outgoing_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->running_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->stop_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); + pthread_cond_init(&thread->incoming_cond, &cond_attr); + pthread_condattr_destroy(&cond_attr); + + /* FIXME: in debug mode, the threads should be named */ + pthread_mutex_lock(&thread->running_mutex); + int res = pthread_create(&thread->thread, 0, thread_func, thread_arg); + thread->valid = (0 == res); + pthread_mutex_unlock(&thread->running_mutex); + return thread->valid; +} + +_SOKOL_PRIVATE void _sfetch_thread_request_stop(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->stop_mutex); + thread->stop_requested = true; + pthread_mutex_unlock(&thread->stop_mutex); +} + +_SOKOL_PRIVATE bool _sfetch_thread_stop_requested(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->stop_mutex); + bool stop_requested = thread->stop_requested; + pthread_mutex_unlock(&thread->stop_mutex); + return stop_requested; +} + +_SOKOL_PRIVATE void _sfetch_thread_join(_sfetch_thread_t* thread) { + SOKOL_ASSERT(thread); + if (thread->valid) { + pthread_mutex_lock(&thread->incoming_mutex); + _sfetch_thread_request_stop(thread); + pthread_cond_signal(&thread->incoming_cond); + pthread_mutex_unlock(&thread->incoming_mutex); + pthread_join(thread->thread, 0); + thread->valid = false; + } + pthread_mutex_destroy(&thread->stop_mutex); + pthread_mutex_destroy(&thread->running_mutex); + pthread_mutex_destroy(&thread->incoming_mutex); + pthread_mutex_destroy(&thread->outgoing_mutex); + pthread_cond_destroy(&thread->incoming_cond); +} + +/* called when the thread-func is entered, this blocks the thread func until + the _sfetch_thread_t object is fully initialized +*/ +_SOKOL_PRIVATE void _sfetch_thread_entered(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->running_mutex); +} + +/* called by the thread-func right before it is left */ +_SOKOL_PRIVATE void _sfetch_thread_leaving(_sfetch_thread_t* thread) { + pthread_mutex_unlock(&thread->running_mutex); +} + +_SOKOL_PRIVATE void _sfetch_thread_enqueue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming, _sfetch_ring_t* src) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + SOKOL_ASSERT(src && src->buf); + if (!_sfetch_ring_empty(src)) { + pthread_mutex_lock(&thread->incoming_mutex); + while (!_sfetch_ring_full(incoming) && !_sfetch_ring_empty(src)) { + _sfetch_ring_enqueue(incoming, _sfetch_ring_dequeue(src)); + } + pthread_cond_signal(&thread->incoming_cond); + pthread_mutex_unlock(&thread->incoming_mutex); + } +} + +_SOKOL_PRIVATE uint32_t _sfetch_thread_dequeue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + pthread_mutex_lock(&thread->incoming_mutex); + while (_sfetch_ring_empty(incoming) && !thread->stop_requested) { + pthread_cond_wait(&thread->incoming_cond, &thread->incoming_mutex); + } + uint32_t item = 0; + if (!thread->stop_requested) { + item = _sfetch_ring_dequeue(incoming); + } + pthread_mutex_unlock(&thread->incoming_mutex); + return item; +} + +_SOKOL_PRIVATE bool _sfetch_thread_enqueue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, uint32_t item) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(0 != item); + pthread_mutex_lock(&thread->outgoing_mutex); + bool result = false; + if (!_sfetch_ring_full(outgoing)) { + _sfetch_ring_enqueue(outgoing, item); + } + pthread_mutex_unlock(&thread->outgoing_mutex); + return result; +} + +_SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, _sfetch_ring_t* dst) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(dst && dst->buf); + pthread_mutex_lock(&thread->outgoing_mutex); + while (!_sfetch_ring_full(dst) && !_sfetch_ring_empty(outgoing)) { + _sfetch_ring_enqueue(dst, _sfetch_ring_dequeue(outgoing)); + } + pthread_mutex_unlock(&thread->outgoing_mutex); +} +#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)); + _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)) { + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); + return true; + } else { + /* input string doesn't fit into destination buffer */ + return false; + } +} + +_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))) { + _SFETCH_ERROR(FILE_PATH_UTF8_DECODING_FAILED); + return 0; + } + _sfetch_file_handle_t h = CreateFileW( + w_path, /* lpFileName */ + GENERIC_READ, /* dwDesiredAccess */ + FILE_SHARE_READ, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, /* dwFlagsAndAttributes */ + NULL); /* hTemplateFile */ + return h; +} + +_SOKOL_PRIVATE void _sfetch_file_close(_sfetch_file_handle_t h) { + CloseHandle(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_handle_valid(_sfetch_file_handle_t h) { + return h != _SFETCH_INVALID_FILE_HANDLE; +} + +_SOKOL_PRIVATE uint32_t _sfetch_file_size(_sfetch_file_handle_t h) { + return GetFileSize(h, NULL); +} + +_SOKOL_PRIVATE bool _sfetch_file_read(_sfetch_file_handle_t h, uint32_t offset, uint32_t num_bytes, void* ptr) { + LARGE_INTEGER offset_li; + offset_li.QuadPart = offset; + BOOL seek_res = SetFilePointerEx(h, offset_li, NULL, FILE_BEGIN); + if (seek_res) { + DWORD bytes_read = 0; + BOOL read_res = ReadFile(h, ptr, (DWORD)num_bytes, &bytes_read, NULL); + return read_res && (bytes_read == num_bytes); + } else { + return false; + } +} + +_SOKOL_PRIVATE bool _sfetch_thread_init(_sfetch_thread_t* thread, _sfetch_thread_func_t thread_func, void* thread_arg) { + SOKOL_ASSERT(thread && !thread->valid && !thread->stop_requested); + + thread->incoming_event = CreateEventA(NULL, FALSE, FALSE, NULL); + SOKOL_ASSERT(NULL != thread->incoming_event); + InitializeCriticalSection(&thread->incoming_critsec); + InitializeCriticalSection(&thread->outgoing_critsec); + InitializeCriticalSection(&thread->running_critsec); + InitializeCriticalSection(&thread->stop_critsec); + + EnterCriticalSection(&thread->running_critsec); + const SIZE_T stack_size = 512 * 1024; + thread->thread = CreateThread(NULL, stack_size, thread_func, thread_arg, 0, NULL); + thread->valid = (NULL != thread->thread); + LeaveCriticalSection(&thread->running_critsec); + return thread->valid; +} + +_SOKOL_PRIVATE void _sfetch_thread_request_stop(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->stop_critsec); + thread->stop_requested = true; + LeaveCriticalSection(&thread->stop_critsec); +} + +_SOKOL_PRIVATE bool _sfetch_thread_stop_requested(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->stop_critsec); + bool stop_requested = thread->stop_requested; + LeaveCriticalSection(&thread->stop_critsec); + return stop_requested; +} + +_SOKOL_PRIVATE void _sfetch_thread_join(_sfetch_thread_t* thread) { + if (thread->valid) { + EnterCriticalSection(&thread->incoming_critsec); + _sfetch_thread_request_stop(thread); + BOOL set_event_res = SetEvent(thread->incoming_event); + _SOKOL_UNUSED(set_event_res); + SOKOL_ASSERT(set_event_res); + LeaveCriticalSection(&thread->incoming_critsec); + WaitForSingleObject(thread->thread, INFINITE); + CloseHandle(thread->thread); + thread->valid = false; + } + CloseHandle(thread->incoming_event); + DeleteCriticalSection(&thread->stop_critsec); + DeleteCriticalSection(&thread->running_critsec); + DeleteCriticalSection(&thread->outgoing_critsec); + DeleteCriticalSection(&thread->incoming_critsec); +} + +_SOKOL_PRIVATE void _sfetch_thread_entered(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->running_critsec); +} + +/* called by the thread-func right before it is left */ +_SOKOL_PRIVATE void _sfetch_thread_leaving(_sfetch_thread_t* thread) { + LeaveCriticalSection(&thread->running_critsec); +} + +_SOKOL_PRIVATE void _sfetch_thread_enqueue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming, _sfetch_ring_t* src) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + SOKOL_ASSERT(src && src->buf); + if (!_sfetch_ring_empty(src)) { + EnterCriticalSection(&thread->incoming_critsec); + while (!_sfetch_ring_full(incoming) && !_sfetch_ring_empty(src)) { + _sfetch_ring_enqueue(incoming, _sfetch_ring_dequeue(src)); + } + LeaveCriticalSection(&thread->incoming_critsec); + BOOL set_event_res = SetEvent(thread->incoming_event); + _SOKOL_UNUSED(set_event_res); + SOKOL_ASSERT(set_event_res); + } +} + +_SOKOL_PRIVATE uint32_t _sfetch_thread_dequeue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + EnterCriticalSection(&thread->incoming_critsec); + while (_sfetch_ring_empty(incoming) && !thread->stop_requested) { + LeaveCriticalSection(&thread->incoming_critsec); + WaitForSingleObject(thread->incoming_event, INFINITE); + EnterCriticalSection(&thread->incoming_critsec); + } + uint32_t item = 0; + if (!thread->stop_requested) { + item = _sfetch_ring_dequeue(incoming); + } + LeaveCriticalSection(&thread->incoming_critsec); + return item; +} + +_SOKOL_PRIVATE bool _sfetch_thread_enqueue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, uint32_t item) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + EnterCriticalSection(&thread->outgoing_critsec); + bool result = false; + if (!_sfetch_ring_full(outgoing)) { + _sfetch_ring_enqueue(outgoing, item); + } + LeaveCriticalSection(&thread->outgoing_critsec); + return result; +} + +_SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, _sfetch_ring_t* dst) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(dst && dst->buf); + EnterCriticalSection(&thread->outgoing_critsec); + while (!_sfetch_ring_full(dst) && !_sfetch_ring_empty(outgoing)) { + _sfetch_ring_enqueue(dst, _sfetch_ring_dequeue(outgoing)); + } + LeaveCriticalSection(&thread->outgoing_critsec); +} +#endif /* _SFETCH_PLATFORM_WINDOWS */ + +// ██████ ██ ██ █████ ███ ██ ███ ██ ███████ ██ ███████ +// ██ ██ ██ ██ ██ ████ ██ ████ ██ ██ ██ ██ +// ██ ███████ ███████ ██ ██ ██ ██ ██ ██ █████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ████ ██ ████ ███████ ███████ ███████ +// +// >>channels + +/* per-channel request handler for native platforms accessing the local filesystem */ +#if _SFETCH_HAS_THREADS +_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_range_t* buffer; + uint32_t chunk_size; + { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (!item) { + return; + } + state = item->state; + SOKOL_ASSERT((state == _SFETCH_STATE_FETCHING) || + (state == _SFETCH_STATE_PAUSED) || + (state == _SFETCH_STATE_FAILED)); + path = &item->path; + thread = &item->thread; + buffer = &item->buffer; + chunk_size = item->chunk_size; + } + if (thread->failed) { + return; + } + if (state == _SFETCH_STATE_FETCHING) { + if ((buffer->ptr == 0) || (buffer->size == 0)) { + thread->error_code = SFETCH_ERROR_NO_BUFFER; + thread->failed = true; + } else { + /* open file if not happened yet */ + if (!_sfetch_file_handle_valid(thread->file_handle)) { + SOKOL_ASSERT(path->buf[0]); + SOKOL_ASSERT(thread->fetched_offset == 0); + SOKOL_ASSERT(thread->fetched_size == 0); + thread->file_handle = _sfetch_file_open(path); + if (_sfetch_file_handle_valid(thread->file_handle)) { + thread->content_size = _sfetch_file_size(thread->file_handle); + } else { + thread->error_code = SFETCH_ERROR_FILE_NOT_FOUND; + thread->failed = true; + } + } + if (!thread->failed) { + uint32_t read_offset = 0; + uint32_t bytes_to_read = 0; + if (chunk_size == 0) { + /* load entire file */ + if (thread->content_size <= buffer->size) { + bytes_to_read = thread->content_size; + read_offset = 0; + } else { + /* provided buffer to small to fit entire file */ + thread->error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + thread->failed = true; + } + } else { + if (chunk_size <= buffer->size) { + bytes_to_read = chunk_size; + read_offset = thread->fetched_offset; + if ((read_offset + bytes_to_read) > thread->content_size) { + bytes_to_read = thread->content_size - read_offset; + } + } else { + /* provided buffer to small to fit next chunk */ + thread->error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + thread->failed = true; + } + } + if (!thread->failed) { + 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; + } else { + thread->error_code = SFETCH_ERROR_UNEXPECTED_EOF; + thread->failed = true; + } + } + } + } + SOKOL_ASSERT(thread->fetched_offset <= thread->content_size); + if (thread->failed || (thread->fetched_offset == thread->content_size)) { + if (_sfetch_file_handle_valid(thread->file_handle)) { + _sfetch_file_close(thread->file_handle); + thread->file_handle = _SFETCH_INVALID_FILE_HANDLE; + } + thread->finished = true; + } + } + /* ignore items in PAUSED or FAILED state */ +} + +#if _SFETCH_PLATFORM_WINDOWS +_SOKOL_PRIVATE DWORD WINAPI _sfetch_channel_thread_func(LPVOID arg) { +#else +_SOKOL_PRIVATE void* _sfetch_channel_thread_func(void* arg) { +#endif + _sfetch_channel_t* chn = (_sfetch_channel_t*) arg; + _sfetch_thread_entered(&chn->thread); + while (!_sfetch_thread_stop_requested(&chn->thread)) { + /* block until work arrives */ + uint32_t slot_id = _sfetch_thread_dequeue_incoming(&chn->thread, &chn->thread_incoming); + /* slot_id will be invalid if the thread was woken up to join */ + if (!_sfetch_thread_stop_requested(&chn->thread)) { + SOKOL_ASSERT(0 != slot_id); + chn->request_handler(chn->ctx, slot_id); + SOKOL_ASSERT(!_sfetch_ring_full(&chn->thread_outgoing)); + _sfetch_thread_enqueue_outgoing(&chn->thread, &chn->thread_outgoing, slot_id); + } + } + _sfetch_thread_leaving(&chn->thread); + return 0; +} +#endif /* _SFETCH_HAS_THREADS */ + +#if _SFETCH_PLATFORM_EMSCRIPTEN +EM_JS(void, sfetch_js_send_head_request, (uint32_t slot_id, const char* path_cstr), { + const path_str = UTF8ToString(path_cstr); + fetch(path_str, { method: 'HEAD' }).then((response) => { + if (response.ok) { + const content_length = response.headers.get('Content-Length'); + if (content_length === null) { + console.warn(`sokol_fetch.h: HEAD ${path_str} response has no Content-Length`); + __sfetch_emsc_failed_other(slot_id); + } else { + __sfetch_emsc_head_response(slot_id, Number(content_length)); + } + } else { + __sfetch_emsc_failed_http_status(slot_id, response.status); + } + }).catch((err) => { + console.error(`sokol_fetch.h: HEAD ${path_str} failed with: `, err); + __sfetch_emsc_failed_other(slot_id); + }); +}) + +/* 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), { + const path_str = UTF8ToString(path_cstr); + const headers = new Headers(); + const range_request = bytes_to_read > 0; + if (range_request) { + headers.append('Range', `bytes=${offset}-${offset+bytes_to_read-1}`); + } + fetch(path_str, { method: 'GET', headers }).then((response) => { + if (response.ok) { + response.arrayBuffer().then((data) => { + const u8_data = new Uint8Array(data); + if (u8_data.length <= buf_size) { + HEAPU8.set(u8_data, buf_ptr); + __sfetch_emsc_get_response(slot_id, bytes_to_read, u8_data.length); + } else { + __sfetch_emsc_failed_buffer_too_small(slot_id); + } + }).catch((err) => { + console.error(`sokol_fetch.h: GET ${path_str} failed with: `, err); + __sfetch_emsc_failed_other(slot_id); + }); + } else { + __sfetch_emsc_failed_http_status(slot_id, response.status); + } + }).catch((err) => { + console.error(`sokol_fetch.h: GET ${path_str} failed with: `, err); + __sfetch_emsc_failed_other(slot_id); + }); +}) + +/*=== emscripten specific C helper functions =================================*/ +#ifdef __cplusplus +extern "C" { +#endif +void _sfetch_emsc_send_get_request(uint32_t slot_id, _sfetch_item_t* item) { + if ((item->buffer.ptr == 0) || (item->buffer.size == 0)) { + item->thread.error_code = SFETCH_ERROR_NO_BUFFER; + item->thread.failed = true; + } else { + uint32_t offset = 0; + uint32_t bytes_to_read = 0; + if (item->chunk_size > 0) { + /* send HTTP range request */ + SOKOL_ASSERT(item->thread.content_size > 0); + SOKOL_ASSERT(item->thread.http_range_offset < item->thread.content_size); + bytes_to_read = item->thread.content_size - item->thread.http_range_offset; + if (bytes_to_read > item->chunk_size) { + bytes_to_read = item->chunk_size; + } + 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, (void*)item->buffer.ptr, item->buffer.size); + } +} + +/* called by JS when an initial HEAD request finished successfully (only when streaming chunks) */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_head_response(uint32_t slot_id, uint32_t content_length) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + SOKOL_ASSERT(item->buffer.ptr && (item->buffer.size > 0)); + item->thread.content_size = content_length; + _sfetch_emsc_send_get_request(slot_id, item); + } + } +} + +/* called by JS when a followup GET request finished successfully */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_get_response(uint32_t slot_id, uint32_t range_fetched_size, uint32_t content_fetched_size) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.fetched_size = content_fetched_size; + item->thread.fetched_offset += content_fetched_size; + item->thread.http_range_offset += range_fetched_size; + if (item->chunk_size == 0) { + item->thread.finished = true; + } else if (item->thread.http_range_offset >= item->thread.content_size) { + item->thread.finished = true; + } + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +/* called by JS when an error occurred */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_http_status(uint32_t slot_id, uint32_t http_status) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + if (http_status == 404) { + item->thread.error_code = SFETCH_ERROR_FILE_NOT_FOUND; + } else { + item->thread.error_code = SFETCH_ERROR_INVALID_HTTP_STATUS; + } + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_buffer_too_small(uint32_t slot_id) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_other(uint32_t slot_id) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.error_code = SFETCH_ERROR_JS_OTHER; + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +_SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (!item) { + return; + } + if (item->state == _SFETCH_STATE_FETCHING) { + if ((item->chunk_size > 0) && (item->thread.content_size == 0)) { + /* if streaming download is requested, and the content-length isn't known + yet, need to send a HEAD request first + */ + sfetch_js_send_head_request(slot_id, item->path.buf); + } else { + /* otherwise, this is either a request to load the entire file, or + to load the next streaming chunk + */ + _sfetch_emsc_send_get_request(slot_id, item); + } + } else { + /* just move all other items (e.g. paused or cancelled) + into the outgoing queue, so they won't get lost + */ + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + if (item->thread.failed) { + item->thread.finished = true; + } +} +#endif /* _SFETCH_PLATFORM_EMSCRIPTEN */ + +_SOKOL_PRIVATE void _sfetch_channel_discard(_sfetch_channel_t* chn) { + SOKOL_ASSERT(chn); + #if _SFETCH_HAS_THREADS + if (chn->valid) { + _sfetch_thread_join(&chn->thread); + } + _sfetch_ring_discard(&chn->thread_incoming); + _sfetch_ring_discard(&chn->thread_outgoing); + #endif + _sfetch_ring_discard(&chn->free_lanes); + _sfetch_ring_discard(&chn->user_sent); + _sfetch_ring_discard(&chn->user_incoming); + _sfetch_ring_discard(&chn->user_outgoing); + _sfetch_ring_discard(&chn->free_lanes); + chn->valid = false; +} + +_SOKOL_PRIVATE bool _sfetch_channel_init(_sfetch_channel_t* chn, _sfetch_t* ctx, uint32_t num_items, uint32_t num_lanes, void (*request_handler)(_sfetch_t* ctx, uint32_t)) { + SOKOL_ASSERT(chn && (num_items > 0) && request_handler); + SOKOL_ASSERT(!chn->valid); + bool valid = true; + chn->request_handler = request_handler; + chn->ctx = ctx; + valid &= _sfetch_ring_init(&chn->free_lanes, num_lanes); + for (uint32_t lane = 0; lane < num_lanes; lane++) { + _sfetch_ring_enqueue(&chn->free_lanes, lane); + } + valid &= _sfetch_ring_init(&chn->user_sent, num_items); + valid &= _sfetch_ring_init(&chn->user_incoming, num_lanes); + valid &= _sfetch_ring_init(&chn->user_outgoing, num_lanes); + #if _SFETCH_HAS_THREADS + valid &= _sfetch_ring_init(&chn->thread_incoming, num_lanes); + valid &= _sfetch_ring_init(&chn->thread_outgoing, num_lanes); + #endif + if (valid) { + chn->valid = true; + #if _SFETCH_HAS_THREADS + _sfetch_thread_init(&chn->thread, _sfetch_channel_thread_func, chn); + #endif + return true; + } else { + _sfetch_channel_discard(chn); + return false; + } +} + +/* put a request into the channels sent-queue, this is where all new requests + are stored until a lane becomes free. +*/ +_SOKOL_PRIVATE bool _sfetch_channel_send(_sfetch_channel_t* chn, uint32_t slot_id) { + SOKOL_ASSERT(chn && chn->valid); + if (!_sfetch_ring_full(&chn->user_sent)) { + _sfetch_ring_enqueue(&chn->user_sent, slot_id); + return true; + } else { + _SFETCH_ERROR(SEND_QUEUE_FULL); + return false; + } +} + +_SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { + sfetch_response_t response; + _sfetch_clear(&response, sizeof(response)); + response.handle = item->handle; + response.dispatched = (item->state == _SFETCH_STATE_DISPATCHED); + response.fetched = (item->state == _SFETCH_STATE_FETCHED); + response.paused = (item->state == _SFETCH_STATE_PAUSED); + response.finished = item->user.finished; + response.failed = (item->state == _SFETCH_STATE_FAILED); + response.cancelled = item->user.cancel; + response.error_code = item->user.error_code; + response.channel = item->channel; + response.lane = item->lane; + response.path = item->path.buf; + response.user_data = item->user.user_data; + 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); +} + +_SOKOL_PRIVATE void _sfetch_cancel_item(_sfetch_item_t* item) { + item->state = _SFETCH_STATE_FAILED; + item->user.finished = true; + item->user.error_code = SFETCH_ERROR_CANCELLED; +} + +/* per-frame channel stuff: move requests in and out of the IO threads, call response callbacks */ +_SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_t* pool) { + + /* move items from sent- to incoming-queue permitting free lanes */ + const uint32_t num_sent = _sfetch_ring_count(&chn->user_sent); + const uint32_t avail_lanes = _sfetch_ring_count(&chn->free_lanes); + const uint32_t num_move = (num_sent < avail_lanes) ? num_sent : avail_lanes; + for (uint32_t i = 0; i < num_move; i++) { + const uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_sent); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item); + SOKOL_ASSERT(item->state == _SFETCH_STATE_ALLOCATED); + // if the item was cancelled early, kick it out immediately + if (item->user.cancel) { + _sfetch_cancel_item(item); + _sfetch_invoke_response_callback(item); + _sfetch_pool_item_free(pool, slot_id); + continue; + } + item->state = _SFETCH_STATE_DISPATCHED; + item->lane = _sfetch_ring_dequeue(&chn->free_lanes); + // if no buffer provided yet, invoke response callback to do so + if (0 == item->buffer.ptr) { + _sfetch_invoke_response_callback(item); + } + _sfetch_ring_enqueue(&chn->user_incoming, slot_id); + } + + /* prepare incoming items for being moved into the IO thread */ + const uint32_t num_incoming = _sfetch_ring_count(&chn->user_incoming); + for (uint32_t i = 0; i < num_incoming; i++) { + const uint32_t slot_id = _sfetch_ring_peek(&chn->user_incoming, i); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item); + SOKOL_ASSERT(item->state != _SFETCH_STATE_INITIAL); + SOKOL_ASSERT(item->state != _SFETCH_STATE_FETCHING); + /* transfer input params from user- to thread-data */ + if (item->user.pause) { + item->state = _SFETCH_STATE_PAUSED; + item->user.pause = false; + } + if (item->user.cont) { + if (item->state == _SFETCH_STATE_PAUSED) { + item->state = _SFETCH_STATE_FETCHED; + } + item->user.cont = false; + } + if (item->user.cancel) { + _sfetch_cancel_item(item); + } + switch (item->state) { + case _SFETCH_STATE_DISPATCHED: + case _SFETCH_STATE_FETCHED: + item->state = _SFETCH_STATE_FETCHING; + break; + default: break; + } + } + + #if _SFETCH_HAS_THREADS + /* move new items into the IO threads and processed items out of IO threads */ + _sfetch_thread_enqueue_incoming(&chn->thread, &chn->thread_incoming, &chn->user_incoming); + _sfetch_thread_dequeue_outgoing(&chn->thread, &chn->thread_outgoing, &chn->user_outgoing); + #else + /* without threading just directly dequeue items from the user_incoming queue and + call the request handler, the user_outgoing queue will be filled as the + asynchronous HTTP requests sent by the request handler are completed + */ + while (!_sfetch_ring_empty(&chn->user_incoming)) { + uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_incoming); + _sfetch_request_handler(chn->ctx, slot_id); + } + #endif + + /* drain the outgoing queue, prepare items for invoking the response + callback, and finally call the response callback, free finished items + */ + while (!_sfetch_ring_empty(&chn->user_outgoing)) { + const uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_outgoing); + SOKOL_ASSERT(slot_id); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item && item->callback); + SOKOL_ASSERT(item->state != _SFETCH_STATE_INITIAL); + SOKOL_ASSERT(item->state != _SFETCH_STATE_ALLOCATED); + SOKOL_ASSERT(item->state != _SFETCH_STATE_DISPATCHED); + SOKOL_ASSERT(item->state != _SFETCH_STATE_FETCHED); + /* transfer output params from thread- to user-data */ + item->user.fetched_offset = item->thread.fetched_offset; + item->user.fetched_size = item->thread.fetched_size; + if (item->user.cancel) { + _sfetch_cancel_item(item); + } else { + item->user.error_code = item->thread.error_code; + } + if (item->thread.finished) { + item->user.finished = true; + } + /* state transition */ + if (item->thread.failed) { + item->state = _SFETCH_STATE_FAILED; + } else if (item->state == _SFETCH_STATE_FETCHING) { + item->state = _SFETCH_STATE_FETCHED; + } + _sfetch_invoke_response_callback(item); + + /* when the request is finished, free the lane for another request, + otherwise feed it back into the incoming queue + */ + if (item->user.finished) { + _sfetch_ring_enqueue(&chn->free_lanes, item->lane); + _sfetch_pool_item_free(pool, slot_id); + } else { + _sfetch_ring_enqueue(&chn->user_incoming, slot_id); + } + } +} + +_SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_request_t* req) { + 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; +} + +_SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + 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_desc_t desc = _sfetch_desc_defaults(desc_); + _sfetch = (_sfetch_t*) _sfetch_malloc_with_allocator(&desc.allocator, sizeof(_sfetch_t)); + SOKOL_ASSERT(_sfetch); + _sfetch_t* ctx = _sfetch_ctx(); + _sfetch_clear(ctx, sizeof(_sfetch_t)); + ctx->desc = desc; + ctx->setup = true; + ctx->valid = true; + + /* replace zero-init items with default values */ + if (ctx->desc.num_channels > SFETCH_MAX_CHANNELS) { + ctx->desc.num_channels = SFETCH_MAX_CHANNELS; + _SFETCH_WARN(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS); + } + + /* setup the global request item pool */ + ctx->valid &= _sfetch_pool_init(&ctx->pool, ctx->desc.max_requests); + + /* setup IO channels (one thread per channel) */ + for (uint32_t i = 0; i < ctx->desc.num_channels; i++) { + ctx->valid &= _sfetch_channel_init(&ctx->chn[i], ctx, ctx->desc.max_requests, ctx->desc.num_lanes, _sfetch_request_handler); + } +} + +SOKOL_API_IMPL void sfetch_shutdown(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + ctx->valid = false; + /* IO threads must be shutdown first */ + for (uint32_t i = 0; i < ctx->desc.num_channels; i++) { + if (ctx->chn[i].valid) { + _sfetch_channel_discard(&ctx->chn[i]); + } + } + _sfetch_pool_discard(&ctx->pool); + ctx->setup = false; + _sfetch_free(ctx); + _sfetch = 0; +} + +SOKOL_API_IMPL bool sfetch_valid(void) { + _sfetch_t* ctx = _sfetch_ctx(); + return ctx && ctx->valid; +} + +SOKOL_API_IMPL sfetch_desc_t sfetch_desc(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + return ctx->desc; +} + +SOKOL_API_IMPL int sfetch_max_userdata_bytes(void) { + return SFETCH_MAX_USERDATA_UINT64 * 8; +} + +SOKOL_API_IMPL int sfetch_max_path(void) { + return SFETCH_MAX_PATH; +} + +SOKOL_API_IMPL bool sfetch_handle_valid(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + /* shortcut invalid handle */ + if (h.id == 0) { + return false; + } + return 0 != _sfetch_pool_item_lookup(&ctx->pool, h.id); +} + +SOKOL_API_IMPL sfetch_handle_t sfetch_send(const sfetch_request_t* request) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + + const sfetch_handle_t invalid_handle = _sfetch_make_handle(0); + if (!ctx->valid) { + return invalid_handle; + } + if (!_sfetch_validate_request(ctx, request)) { + return invalid_handle; + } + SOKOL_ASSERT(request->channel < ctx->desc.num_channels); + + uint32_t slot_id = _sfetch_pool_item_alloc(&ctx->pool, request); + if (0 == slot_id) { + _SFETCH_WARN(REQUEST_POOL_EXHAUSTED); + return invalid_handle; + } + if (!_sfetch_channel_send(&ctx->chn[request->channel], slot_id)) { + /* send failed because the channels sent-queue overflowed */ + _sfetch_pool_item_free(&ctx->pool, slot_id); + return invalid_handle; + } + return _sfetch_make_handle(slot_id); +} + +SOKOL_API_IMPL void sfetch_dowork(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + if (!ctx->valid) { + return; + } + /* we're pumping each channel 2x so that unfinished request items coming out the + IO threads can be moved back into the IO-thread immediately without + having to wait a frame + */ + ctx->in_callback = true; + for (int pass = 0; pass < 2; pass++) { + for (uint32_t chn_index = 0; chn_index < ctx->desc.num_channels; chn_index++) { + _sfetch_channel_dowork(&ctx->chn[chn_index], &ctx->pool); + } + } + ctx->in_callback = false; +} + +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 = buffer; + } +} + +SOKOL_API_IMPL void* sfetch_unbind_buffer(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + SOKOL_ASSERT(ctx->in_callback); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + void* prev_buf_ptr = (void*)item->buffer.ptr; + item->buffer.ptr = 0; + item->buffer.size = 0; + return prev_buf_ptr; + } else { + return 0; + } +} + +SOKOL_API_IMPL void sfetch_pause(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.pause = true; + item->user.cont = false; + } +} + +SOKOL_API_IMPL void sfetch_continue(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.cont = true; + item->user.pause = false; + } +} + +SOKOL_API_IMPL void sfetch_cancel(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.cont = false; + item->user.pause = false; + item->user.cancel = true; + } +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* SOKOL_FETCH_IMPL */ diff --git a/thirdparty/sokol/sokol_gfx.h b/thirdparty/sokol/sokol_gfx.h new file mode 100644 index 0000000..539dec2 --- /dev/null +++ b/thirdparty/sokol/sokol_gfx.h @@ -0,0 +1,26670 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_IMPL +#endif +#ifndef SOKOL_GFX_INCLUDED +/* + sokol_gfx.h -- simple 3D API wrapper + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the rendering + backend: + #define SOKOL_GLCORE + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_VULKAN + #define SOKOL_DUMMY_BACKEND + + I.e. for the desktop GL it should look like this: + + #include ... + #include ... + #define SOKOL_IMPL + #define SOKOL_GLCORE + #include "sokol_gfx.h" + + The dummy backend replaces the platform-specific backend code with empty + stub functions. This is useful for writing tests that need to run on the + command line. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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 + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) + SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case + sokol_gfx.h will not include any platform GL headers and disable + the integrated Win32 GL loader + + If sokol_gfx.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if NDEBUG is not defined + + Link with the following system libraries (note that sokol_app.h has + additional linker requirements): + + - on macOS/iOS with Metal: Metal + - on macOS with GL: OpenGL + - on iOS with GL: OpenGLES + - on Linux with EGL: GL or GLESv2 + - on Linux with GLX: GL + - on Linux with Vulkan: vulkan + - on Android: GLESv3, log, android + - on Windows: + - with Vulkan: link with vulkan-1 (this is explicit in case you want to + use your own Vulkan loader library) + - with D3D11: + - on MSVC or Clang: no action needed, libs are defined in-source via pragma-comment-lib + - on MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined and link with -ld3d11 + - with GL: no linking needed since sokol_gfx.h comes with its own GL loader on Windows + + On macOS and iOS, the implementation must be compiled as Objective-C. + + For Linux+Vulkan install the following packages (or equivalents): + - libvulkan-dev + - vulkan-validationlayers + - vulkan-tools + + For Windows+Vulkan install the Vulkan SDK and in your build system: + - add a header search path to $ENV{VULKAN_SDK}/Include + - add a link search path to $ENV{VULKAN_SDK}/Env + + On Emscripten: + - for WebGL2: add the linker option `-s USE_WEBGL2=1` + - for WebGPU: compile and link with `--use-port=emdawnwebgpu` + (for more exotic situations, read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) + + sokol_gfx DOES NOT: + =================== + - create a window, swapchain or the 3D-API context/device, you must do this + before sokol_gfx is initialized, and pass any required information + (like 3D device pointers) to the sokol_gfx initialization call + + - present the rendered frame, how this is done exactly usually depends + on how the window and 3D-API context/device was created + + - provide a unified shader language, instead 3D-API-specific shader + source-code or shader-bytecode must be provided (for the "official" + offline shader cross-compiler / code-generator, 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 + context/device, call: + + sg_setup(const sg_desc*) + + Depending on the selected 3D backend, sokol-gfx requires some + information about its runtime environment, like a GPU device pointer, + default swapchain 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){ + .environment = sglue_environment(), + }); + + 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 (buffers, images, views, samplers, shaders + and pipeline objects) + + sg_buffer sg_make_buffer(const sg_buffer_desc*) + sg_image sg_make_image(const sg_image_desc*) + sg_view sg_make_view(const sg_view_desc*) + sg_sampler sg_make_sampler(const sg_sampler_desc*) + sg_shader sg_make_shader(const sg_shader_desc*) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) + + --- start a render- or compute-pass: + + sg_begin_pass(const sg_pass* pass); + + Typically, render passes render into an externally provided swapchain which + presents the rendering result on the display. Such a 'swapchain pass' + is started like this: + + sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() }) + + ...where .action is an sg_pass_action struct containing actions to be performed + at the start and end of a render pass (such as clearing the render surfaces to + a specific color), and .swapchain is an sg_swapchain struct with all the required + information to render into the swapchain's surfaces. + + To start an 'offscreen render pass' into sokol-gfx image objects, populate + the sg_pass.attachments nested struct with attachment view objects + (1..4 color-attachment-views for to render into, a depth-stencil-attachment-view + to provide the depth-stencil-buffer, and optionally 1..4 resolve-attachment-views + for an MSAA-resolve operation: + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .attachments = { + .colors[0] = color_attachment_view, + .resolves[0] = optional_resolve_attachment_view, + .depth_stencil = depth_stencil_attachment_view, + }, + }); + + To start a compute-pass, just set the .compute item to true: + + sg_begin_pass(&(sg_pass){ .compute = true }); + + --- set the pipeline state for the next draw call with: + + sg_apply_pipeline(sg_pipeline pip) + + --- fill an sg_bindings struct with the resource bindings for the next + draw- or dispatch-call (0..N vertex buffers, 0 or 1 index buffer, 0..N views, + 0..N samplers), and call + + sg_apply_bindings(const sg_bindings* bindings) + + ...to update the resource bindings. Note that in a compute pass, no vertex- + or index-buffer bindings can be used, and in render passes, no storage-image bindings + are allowed. Those restrictions will be checked by the sokol-gfx validation layer. + + --- optionally update shader uniform data with: + + sg_apply_uniforms(int ub_slot, 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: + + sg_draw(int base_element, int num_elements, int num_instances) + + The sg_draw() function unifies all the different ways to render primitives + in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced + rendering). In case of indexed rendering, base_element and num_element specify + indices in the currently bound index buffer. In case of non-indexed rendering + base_element and num_elements specify vertices in the currently bound + vertex-buffer(s). To perform instanced rendering, the rendering pipeline + must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer + containing per-instance data must be bound, and the num_instances parameter + must be > 1. + + Alternatively, call: + + sg_draw_ex(...) + + to provide a base-vertex and/or base-instance which allows to render + from different sections of a vertex buffer without rebinding the + vertex buffer with a different offset. Note that the `sg_draw_ex()` + only has limited portability on OpenGL, check the sg_limits struct + members .draw_base_vertex and .draw_base_instance for runtime support, + those are generally true on non-GL-backends, and on GL the feature + flags are set according to the GL version: + + - on GL base_instance != 0 is only supported since GL 4.2 + - on GLES3.x, base_instance != 0 is not supported + - on GLES3.x, base_vertex is only supported since GLES3.2 + (e.g. not supported on WebGL2) + + --- ...or kick of a dispatch call to invoke a compute shader workload: + + sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) + + The dispatch args define the number of 'compute workgroups' processed + by the currently applied compute shader. + + --- finish the current pass with: + + sg_end_pass() + + --- when done with the current frame, call + + sg_commit() + + --- at the end of your program, shutdown sokol_gfx with: + + sg_shutdown() + + --- if you need to destroy resources before sg_shutdown(), call: + + sg_destroy_buffer(sg_buffer buf) + sg_destroy_image(sg_image img) + sg_destroy_sampler(sg_sampler smp) + sg_destroy_shader(sg_shader shd) + sg_destroy_pipeline(sg_pipeline pip) + sg_destroy_view(sg_view view) + + --- to set a new viewport rectangle, call: + + sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) + + ...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) + + --- to set a new scissor rect, call: + + sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) + + ...or with float values: + + sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) + + Both sg_apply_viewport() and sg_apply_scissor_rect() must be called + inside a rendering pass (e.g. not in a compute pass, or outside a pass) + + Note that sg_begin_pass() will reset both the viewport and scissor + rectangles to cover the entire framebuffer. + + --- to update (overwrite) the content of buffer and image resources, call: + + sg_update_buffer(sg_buffer buf, const sg_range* data) + sg_update_image(sg_image img, const sg_image_data* data) + + Buffers and images to be updated must have been created with + sg_buffer_desc.usage.dynamic_update or .stream_update. + + Only one update per frame is allowed for buffer and image resources when + using the sg_update_*() functions. The rationale is to have a simple + protection from the CPU scribbling over data the GPU is currently + using, or the CPU having to wait for the GPU + + Buffer and image updates can be partial, as long as a rendering + operation only references the valid (updated) data in the + buffer or image. + + --- to append a chunk of data to a buffer resource, call: + + int sg_append_buffer(sg_buffer buf, const sg_range* data) + + The difference to sg_update_buffer() is that sg_append_buffer() + can be called multiple times per frame to append new data to the + buffer piece by piece, optionally interleaved with draw calls referencing + the previously written data. + + sg_append_buffer() returns a byte offset to the start of the + written data, this offset can be assigned to + sg_bindings.vertex_buffer_offsets[n] or + sg_bindings.index_buffer_offset + + Code example: + + for (...) { + const void* data = ...; + const int num_bytes = ...; + int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); + bindings.vertex_buffer_offsets[0] = offset; + sg_apply_pipeline(pip); + sg_apply_bindings(&bindings); + sg_apply_uniforms(...); + sg_draw(...); + } + + A buffer to be used with sg_append_buffer() must have been created + with sg_buffer_desc.usage.dynamic_update or .stream_update. + + If the application appends more data to the buffer then fits into + the buffer, the buffer will go into the "overflow" state for the + rest of the frame. + + Any draw calls attempting to render an overflown buffer will be + silently dropped (in debug mode this will also result in a + validation error). + + You can also check manually if a buffer is in overflow-state by calling + + 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 + when the number of indices in a call to sg_append_buffer() is + odd. This isn't a problem when each call to sg_append_buffer() + is associated with one draw call, but will be problematic when + a single indexed draw call spans several appended chunks of indices. + + --- to check at runtime for optional features, limits and pixelformat support, + call: + + sg_features sg_query_features() + sg_limits sg_query_limits() + sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) + + --- if you need to call into the underlying 3D-API directly, you must call: + + sg_reset_state_cache() + + ...before calling sokol_gfx functions again + + --- you can inspect the original sg_desc structure handed to sg_setup() + 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 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_sampler_desc sg_query_sampler_desc(sg_sampler smp) + sg_shader_desc sq_query_shader_desc(sg_shader shd) + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) + sg_view_desc sg_query_view_desc(sg_view view) + + ...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_sampler_desc sg_query_sampler_defaults(const sg_sampler_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_view_desc sg_query_view_defaults(const sg_view_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) + sg_sampler_info sg_query_sampler_info(sg_sampler smp) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_view_info sg_query_view_info(sg_view view) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + -- you can query the type/flavour and parent resource of a view: + + sg_view_type sg_query_view_type(sg_view view) + sg_image sg_query_view_image(sg_view view) + sg_buffer sg_query_view_buffer(sg_view view) + + --- you can query stats and control stats collection via: + + sg_query_stats() + sg_enable_stats() + sg_disable_stats() + sg_stats_enabled() + + --- you can ask at runtime what backend sokol_gfx.h has been compiled for: + + sg_backend sg_query_backend(void) + + --- call the following helper functions to compute the number of + bytes in a texture row or surface for a specific pixel format. + These functions might be helpful when preparing image data for consumption + by sg_make_image() or sg_update_image(): + + int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes); + int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); + + Width and height are generally in number pixels, but note that 'row' has different meaning + for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical + with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*. + + This is why calling sg_query_surface_pitch() for a compressed pixel format and height + N, N+1, N+2, ... may return the same result. + + The row_align_bytes parameter is for added flexibility. For image data that goes into + the sg_make_image() or sg_update_image() this should generally be 1, because these + functions take tightly packed image data as input no matter what alignment restrictions + exist in the backend 3D APIs. + + ON INITIALIZATION: + ================== + When calling sg_setup(), a pointer to an sg_desc struct must be provided + which contains initialization options. These options provide two types + of information to sokol-gfx: + + (1) upper bounds and limits needed to allocate various internal + data structures: + - the max number of resources of each type that can + be alive at the same time, this is used for allocating + internal pools + - the max overall size of uniform data that can be + updated per frame, including a worst-case alignment + per uniform update (this worst-case alignment is 256 bytes) + - the max size of all dynamic resource updates (sg_update_buffer, + sg_append_buffer and sg_update_image) per frame + - the max number of compute-dispatch calls in a compute pass + Not all of those limit values are used by all backends, but it is + good practice to provide them none-the-less. + + (2) 3D backend "environment information" in a nested sg_environment struct: + - pointers to backend-specific context- or device-objects (for instance + the D3D11, WebGPU or Metal device objects) + - defaults for external swapchain pixel formats and sample counts, + these will be used as default values in image and pipeline objects, + and the sg_swapchain struct passed into sg_begin_pass() + Usually you provide a complete sg_environment struct through + a helper function, as an example look at the sglue_environment() + function in the sokol_glue.h header. + + See the documentation block of the sg_desc struct below for more information. + + + ON RENDER PASSES + ================ + Relevant samples: + - https://floooh.github.io/sokol-html5/offscreen-sapp.html + - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html + - https://floooh.github.io/sokol-html5/mrt-sapp.html + - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html + + A render pass groups rendering commands into a set of render target images + (called 'render pass attachments'). Render target images can be used in subsequent + passes as textures (it is invalid to use the same image both as render target + and as texture in the same pass). + + The following sokol-gfx functions must only be called inside a render-pass: + + sg_apply_viewport[f] + sg_apply_scissor_rect[f] + sg_draw + + The following function may be called inside a render- or compute-pass, but + not outside a pass: + + sg_apply_pipeline + sg_apply_bindings + sg_apply_uniforms + + A frame must have at least one 'swapchain render pass' which renders into an + externally provided swapchain provided as an sg_swapchain struct to the + sg_begin_pass() function. If you use sokol_gfx.h together with sokol_app.h, + just call the sglue_swapchain() helper function in sokol_glue.h to + provide the swapchain information. Otherwise the following information + must be provided: + + - the color pixel-format of the swapchain's render surface + - an optional depth/stencil pixel format if the swapchain + has a depth/stencil buffer + - an optional sample-count for MSAA rendering + - NOTE: the above three values can be zero-initialized, in that + case the defaults from the sg_environment struct will be used that + had been passed to the sg_setup() function. + - a number of backend specific objects: + - GL/GLES3: just a GL framebuffer handle + - D3D11: + - an ID3D11RenderTargetView for the rendering surface + - if MSAA is used, an ID3D11RenderTargetView as + MSAA resolve-target + - an optional ID3D11DepthStencilView for the + depth/stencil buffer + - WebGPU + - a WGPUTextureView object for the rendering surface + - if MSAA is used, a WGPUTextureView object as MSAA resolve target + - an optional WGPUTextureView for the + - Metal (NOTE that the roles of provided surfaces is slightly + different in Metal than in D3D11 or WebGPU, notably, the + CAMetalDrawable is either rendered to directly, or serves + as MSAA resolve target): + - a CAMetalDrawable object which is either rendered + into directly, or in case of MSAA rendering, serves + as MSAA-resolve-target + - if MSAA is used, an multisampled MTLTexture where + rendering goes into + - an optional MTLTexture for the depth/stencil buffer + + It's recommended that you create a helper function which returns an + initialized sg_swapchain struct by value. This can then be directly plugged + into the sg_begin_pass function like this: + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() }); + + As an example for such a helper function check out the function sglue_swapchain() + in the sokol_glue.h header. + + For offscreen render passes, the render target images used in a render pass + must be provided as sg_view objects specialized for the specific pass-attachment + types: + + - color-attachment-views for color-rendering + - depth-stencil-attachment-views for the depth-stencil-buffer surface + - resolve-attachment-views for MSAA-resolve operations + + For a simple offscreen scenario with one color-, one depth-stencil-render + target and without multisampling, setting up the required image- + and view-objects looks like this: + + First create two render target images, one with a color pixel format, + and one with the depth- or depth-stencil pixel format. Both images + must have the same dimensions. Also not the usage flags: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .usage.color_attachment = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .usage.depth_stencil_attachment = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 1, + }); + + NOTE: when creating render target images, have in mind that some default values + are aligned with the default environment attributes in the sg_environment struct + that was passed into the sg_setup() call: + + - the default value for sg_image_desc.pixel_format is taken from + sg_environment.defaults.color_format + - the default value for sg_image_desc.sample_count is taken from + sg_environment.defaults.sample_count + - the default value for sg_image_desc.num_mipmaps is always 1 + + Next, create two view objects, one color-attachment-view and one + depth-stencil-attachment view: + + const sg_view color_att_view = sg_make_view(&(sg_view_desc){ + .color_attachment.image = color_img, + }); + const sg_view depth_att_view = sg_make_view(&(sg_view_desc){ + .depth_stencil_attachment.image = depth_img, + }); + + You'll typically also want to create a texture-view on the color image + to sample the color attachment image as texture in a later pass: + + const sg_view tex_view = sg_make_view(&(sg_view_desc){ + .texture.image = color_img, + }); + + The attachment-view objects are then passed into the sg_begin_pass function in + place of the nested swapchain struct: + + sg_begin_pass(&(sg_pass){ + .attachments = { + .colors[0] = color_att_view, + .depth_stencil = depth_att_view, + }, + }); + + ...in a later pass when you want to sample the color attachment image as + texture, use the texture view in the sg_apply_bindings() call: + + sg_apply_bindings(&(sg_bindings){ + .vertex_buffers[0] = ..., + .index_buffer = ..., + .views[VIEW_tex] = tex_view, + .samplers[SMP_smp] = smp, + }); + + Swapchain and offscreen passes form dependency trees with a swapchain + pass at the root, offscreen passes as nodes, and attachment images as + dependencies between passes. + + sg_pass_action structs are used to define actions that should happen at the + start and end of render passes (such as clearing pass attachments to a + specific color or depth-value, or performing an MSAA resolve operation at + the end of a pass). + + A typical sg_pass_action object which clears the color attachment to black + might look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + This omits the defaults for the color attachment store action, and + the depth-stencil-attachments actions. The same pass action with the + defaults explicitly filled in would look like this: + + const sg_pass_action pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_STORE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + }, + .depth = = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 1.0f, + }, + .stencil = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 0 + } + }; + + With the sg_pass object and sg_pass_action struct in place everything + is ready now for the actual render pass: + + Using such this prepared sg_pass_action in a swapchain pass looks like + this: + + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .swapchain = sglue_swapchain() + }); + ... + sg_end_pass(); + + ...of alternatively in one offscreen pass: + + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .attachments = { + .colors[0] = color_att_view, + .depth_stencil = ds_att_view, + }, + }); + ... + sg_end_pass(); + + Offscreen rendering can also go into a mipmap, or a slice/face of + a cube-, array- or 3d-image (which some restrictions, for instance + it's not possible to create a 3D image with a depth/stencil pixel format, + these exceptions are generally caught by the sokol-gfx validation layer). + + The mipmap/slice selection is baked into the attachment-view objects, for + instance to create a color-attachment-view for rendering into mip-level + 2 and slice 3 of an array texture: + + const sg_view color_att_view = sg_make_view(&(sg_view_desc){ + .color_attachment = { + .image = color_img, + .mip_level = 2, + .slice = 3, + }, + }); + + If MSAA offscreen rendering is desired, the multi-sample rendering result + must be 'resolved' into a separate 'resolve image', before that image can + be used as texture. + + Setting up MSAA offscreen 3D rendering requires three image objects + (one color-attachment image with a sample count > 1), a resolve-attachment + image with a sample count of 1, and a depth-stencil-attachment image + with the same sample count as the color-attachment image: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .usage.color_attachment = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .usage.resolve_attachment = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .usage.depth_stencil_attachment = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 4, + }); + + Next you'll need the corresponding attachment-view objects: + + const sg_view color_att_view = sg_make_view(&(sg_view_desc){ + .color_attachment.image = color_img, + }); + const sg_view resolve_att_view = sg_make_view(&(sg_view_desc){ + .resolve_attachment.image = resolve_img, + }); + const sg_view depth_att_view = sg_make_view(&(sg_view_desc){ + .depth_stencil_attachment.image = depth_img, + }); + + To sample the rendered image as a texture in a later pass you'll also + need a texture-view on the resolve-attachment-image (not the color-attachment-image!): + + const sg_view tex_view = sg_make_view(&(sg_view_desc){ + .texture.image = resolve_img, + }); + + Next start the render pass with all attachment-views, as soon as a + resolve-attachment-view is provided, an MSAA resolve operation will happen + at the end of the pass. Also note that the content of the MSAA color-attachment-image + doesn't need to be preserved, since it's only needed until the MSAA-resolve + at the end of the pass, so the .store_action should be set to "don't care": + + sg_begin_pass(&(sg_pass){ + .attachments = { + .colors[0] = color_att_view, + .resolves[0] = resolve_att_view, + .depth_stencil = depth_att_view, + }, + .action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }, + } + }, + }); + + ...in a later pass, use the texture-view that had been created on the + resolve-image to use the rendering result as texture: + + sg_apply_bindings(&(sg_bindings){ + .vertex_buffers[0] = ..., + .index_buffer = ..., + .views[VIEW_tex] = tex_view, + .samplers[SMP_smp] = smp, + }); + + ON COMPUTE PASSES + ================= + Compute passes are used to update the content of storage buffers and + storage images by running compute shader code on + the GPU. Updating storage resources with a compute shader will almost always + be more efficient than computing the same data on the CPU and then uploading + it via `sg_update_buffer()` or `sg_update_image()`. + + NOTE: compute passes are only supported on the following platforms and + backends: + + - macOS and iOS with Metal + - Windows with D3D11 and OpenGL + - Linux with OpenGL or GLES3.1+ + - Web with WebGPU + - Android with GLES3.1+ + + ...this means compute shaders can't be used on the following platform/backend + combos (the same restrictions apply to using storage buffers without compute + shaders): + + - macOS with GL + - iOS with GLES3 + - Web with WebGL2 + + A compute pass is started with: + + sg_begin_pass(&(sg_pass){ .compute = true }); + + ...and finished with a regular: + + sg_end_pass(); + + Typically the following functions will be called inside a compute pass: + + sg_apply_pipeline() + sg_apply_bindings() + sg_apply_uniforms() + sg_dispatch() + + The following functions are disallowed inside a compute pass + and will cause validation layer errors: + + sg_apply_viewport[f]() + sg_apply_scissor_rect[f]() + sg_draw() + + Only special 'compute shaders' and 'compute pipelines' can be used in + compute passes. A compute shader only has a compute-function instead + of a vertex- and fragment-function pair, and it doesn't accept vertex- + and index-buffers as bindings, only storage-buffer-views (readable + and writable), storage-image-views (read/write or writeonly) and + texture-views (read-only). + + A compute pipeline is created by providing a compute shader object, + setting the .compute creation parameter to true and not defining any + 'render state': + + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .compute = true, + .shader = compute_shader, + }); + + The sg_apply_bindings and sg_apply_uniforms calls are the same as in + render passes, with the exception that no vertex- and index-buffers + can be bound in the sg_apply_bindings call. + + Finally to kick off a compute workload, call sg_dispatch with the + number of workgroups in the x, y and z-dimension: + + sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) + + Also see the following compute-shader samples: + + - https://floooh.github.io/sokol-webgpu/instancing-compute-sapp.html + - https://floooh.github.io/sokol-webgpu/computeboids-sapp.html + - https://floooh.github.io/sokol-webgpu/imageblur-sapp.html + + + ON SHADER CREATION + ================== + sokol-gfx doesn't come with an integrated shader cross-compiler, instead + backend-specific shader sources or binary blobs need to be provided when + creating a shader object, along with reflection information about the + shader resource binding interface needed to bind sokol-gfx resources to the + proper shader inputs. + + The easiest way to provide all this shader creation data is to use the + sokol-shdc shader compiler tool to compile shaders from a common + GLSL syntax into backend-specific sources or binary blobs, along with + shader interface information and uniform blocks and storage buffer array items + mapped to C structs. + + To create a shader using a C header which has been code-generated by sokol-shdc: + + // include the C header code-generated by sokol-shdc: + #include "myshader.glsl.h" + ... + + // create shader using a code-generated helper function from the C header: + sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend())); + + The samples in the 'sapp' subdirectory of the sokol-samples project + also use the sokol-shdc approach: + + https://github.com/floooh/sokol-samples/tree/master/sapp + + If you're planning to use sokol-shdc, you can stop reading here, instead + continue with the sokol-shdc documentation: + + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md + + To create shaders with backend-specific shader code or binary blobs, + the sg_make_shader() function requires the following information: + + - Shader code or shader binary blobs for the vertex- and fragment-, or the + compute-shader-stage: + - for the desktop GL backend, source code can be provided in '#version 410' or + '#version 430', version 430 is required when using storage buffers and + compute shaders, but note that this is not available on macOS + - for the GLES3 backend, source code must be provided in '#version 300 es' or + '#version 310 es' syntax (version 310 is required for storage buffer and + compute shader support, but note that this is not supported on WebGL2) + - for the D3D11 backend, shaders can be provided as source or binary + blobs, the source code should be in HLSL4.0 (for compatibility with old + low-end GPUs) or preferably in HLSL5.0 syntax, note that when + shader source code is provided for the D3D11 backend, sokol-gfx will + dynamically load 'd3dcompiler_47.dll' + - for the Metal backends, shaders can be provided as source or binary blobs, the + MSL version should be in 'metal-1.1' (other versions may work but are not tested) + - for the WebGPU backend, shaders must be provided as WGSL source code + - optionally the following shader-code related attributes can be provided: + - an entry function name (only on D3D11 or Metal, but not OpenGL) + - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0") + + - Information about the input vertex attributes used by the vertex shader, + most of that backend-specific: + - An optional 'base type' (float, signed-/unsigned-int) for each vertex + attribute. When provided, this is used by the validation layer to check + that the CPU-side input vertex format is compatible with the input + vertex declaration of the vertex shader. + - Metal: no location information needed since vertex attributes are always bound + by their attribute location defined in the shader via '[[attribute(N)]]' + - WebGPU: no location information needed since vertex attributes are always + bound by their attribute location defined in the shader via `@location(N)` + - GLSL: vertex attribute names can be optionally provided, in that case their + location will be looked up by name, otherwise, the vertex attribute location + can be defined with 'layout(location = N)' + - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex + attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader, + the semantic name would be 'TEXCOORD', and the semantic index would be '1' + + NOTE that vertex attributes currently must not have gaps. This requirement + may be relaxed in the future. + + - Specifically for Metal compute shaders, the 'number of threads per threadgroup' + must be provided. Normally this is extracted by sokol-shdc from the GLSL + shader source code. For instance the following statement in the input + GLSL: + + layout(local_size_x=64, local_size_y=1, local_size_z=1) in; + + ...will be communicated to the sokol-gfx Metal backend in the + code-generated sg_shader_desc struct: + + (sg_shader_desc){ + .mtl_threads_per_threadgroup = { .x = 64, .y = 1, .z = 1 }, + } + + - Information about each uniform block binding used in the shader: + - the shader stage of the uniform block (vertex, fragment or compute) + - the size of the uniform block in number of bytes + - a memory layout hint (currently 'native' or 'std140') where 'native' defines a + backend-specific memory layout which shouldn't be used for cross-platform code. + Only std140 guarantees a backend-agnostic memory layout. + - a backend-specific bind slot: + - D3D11/HLSL: the buffer register N (`register(bN)`) where N is 0..7 + - Metal/MSL: the buffer bind slot N (`[[buffer(N)]]`) where N is 0..7 + - WebGPU: the binding N in `@group(0) @binding(N)` where N is 0..15 + - For GLSL only: a description of the internal uniform block layout, which maps + member types and their offsets on the CPU side to uniform variable names + in the GLSL shader + - please also NOTE the documentation sections about UNIFORM DATA LAYOUT + and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + + - A description of each resource binding (texture-, storage-buffer- + and storage-image-bindings) which directly map to the sg_bindings.view[] + array slots. + + Each resource binding slot comes in three flavours: + + 1. Texture bindings with the following properties: + - the shader stage of the texture (vertex, fragment or compute) + - the expected image type: + - SG_IMAGETYPE_2D + - SG_IMAGETYPE_CUBE + - SG_IMAGETYPE_3D + - SG_IMAGETYPE_ARRAY + - the expected 'image sample type': + - SG_IMAGESAMPLETYPE_FLOAT + - SG_IMAGESAMPLETYPE_DEPTH + - SG_IMAGESAMPLETYPE_SINT + - SG_IMAGESAMPLETYPE_UINT + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT + - a flag whether the texture is expected to be multisampled + - a backend-specific bind slot: + - D3D11/HLSL: the texture register N (`register(tN)`) where N is 0..31 + (in HLSL, readonly storage buffers and texture share the same bind space) + - Metal/MSL: the texture bind slot N (`[[texture(N)]]`) where N is 0..31 + (the bind slot must not collide with storage image bindings on the same stage) + - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127 + + 2. Storage buffer bindings with the following properties: + - the shader stage of the storage buffer + - a boolean 'readonly' flag, this is used for validation and hazard + tracking in some 3D backends. Note that in render passes, only + readonly storage buffer bindings are allowed. In compute passes, any + read/write storage buffer binding is assumed to be written to by the + compute shader. + - a backend-specific bind slot: + - D3D11/HLSL: + - for readonly storage buffer bindings: the texture register N + (`register(tN)`) where N is 0..31 (in HLSL, readonly storage + buffers and textures share the same bind space for + 'shader resource views') + - for read/write storage buffer buffer bindings: the UAV register N + (`register(uN)`) where N is 0..31 (in HLSL, readwrite storage + buffers use their own bind space for 'unordered access views') + - Metal/MSL: the buffer bind slot N (`[[buffer(N)]]`) where N is 8..23 + - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127 + - GL/GLSL: the buffer binding N in `layout(binding=N)` + where N is 0..sg_limits.max_storage_buffer_bindings_per_stage + - note that storage buffer bindings are not supported on all backends + and platforms + + 3. Storage image bindings with the following properties: + - the shader stage (*must* be compute) + - the expected image type: + - SG_IMAGETYPE_2D + - SG_IMAGETYPE_CUBE + - SG_IMAGETYPE_3D + - SG_IMAGETYPE_ARRAY + - the 'access pixel format', this is currently limited to: + - SG_PIXELFORMAT_RGBA8 + - SG_PIXELFORMAT_RGBA8SN/UI/SI + - SG_PIXELFORMAT_RGBA16UI/SI/F + - SG_PIXELFORMAT_R32UIUI/SI/F + - SG_PIXELFORMAT_RG32UI/SI/F + - SG_PIXELFORMAT_RGBA32UI/SI/F + - the access type (readwrite or writeonly) + - a backend-specific bind slot: + - D3D11/HLSL: the UAV register N (`register(uN)` where N is 0..31, the + bind slot must not collide with UAV storage buffer bindings + - Metal/MSL: the texture bind slot N (`[[texture(N)]])` where N is 0..31, + the bind slot must not collide with other texture bindings on the same + stage + - WebGPU/WGSL: the binding N in `@group(1) @binding(N)` where N is 0..127 + - GL/GLSL: the buffer binding N in `layout(binding=N)` + where N is 0.._sg.max_storage_image_bindings_per_stage + - note that storage image bindings are not supported on all backends and platforms + + - A description of each sampler used in the shader: + - the shader stage of the sampler (vertex, fragment or compute) + - the expected sampler type: + - SG_SAMPLERTYPE_FILTERING, + - SG_SAMPLERTYPE_NONFILTERING, + - SG_SAMPLERTYPE_COMPARISON, + - a backend-specific bind slot: + - D3D11/HLSL: the sampler register N (`register(sN)`) where N is 0..SG_MAX_SAMPLER_BINDINGS + - Metal/MSL: the sampler bind slot N (`[[sampler(N)]]`) where N is 0..SG_MAX_SAMPLER_BINDINGS + - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127 + + - An array of 'texture-sampler-pairs' used by the shader to sample textures, + for D3D11, Metal and WebGPU this is used for validation purposes to check + whether the texture and sampler are compatible with each other (especially + WebGPU is very picky about combining the correct + texture-sample-type with the correct sampler-type). For GLSL an + additional 'combined-image-sampler name' must be provided because 'OpenGL + style GLSL' cannot handle separate texture and sampler objects, but still + groups them into a traditional GLSL 'sampler object'. + + Compatibility rules for image-sample-type vs sampler-type are as follows: + + - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING) + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON + + Backend-specific bindslot ranges (not relevant when using sokol-shdc): + + - D3D11/HLSL: + - separate bindslot space per shader stage + - uniform block bindings (as cbuffer): `register(b0..b7)` + - texture- and readonly storage buffer bindings: `register(t0..t31)` + - read/write storage buffer and storage image bindings: `register(u0..u31)` + - samplers: `register(s0..s11)` + - Metal/MSL: + - separate bindslot space per shader stage + - uniform blocks: `[[buffer(0..7)]]` + - storage buffers: `[[buffer(8..23)]]` + - textures and storage image bindings: `[[texture(0..31)]]` + - samplers: `[[sampler(0..11)]]` + - WebGPU/WGSL: + - common bindslot space across shader stages + - uniform blocks: `@group(0) @binding(0..15)` + - textures, storage-images, storage-buffers and sampler: `@group(1) @binding(0..127)` + - GL/GLSL: + - uniforms and image-samplers are bound by name + - storage buffer bindings: `layout(std430, binding=0..sg_limits.max_storage_buffer_bindings_per_stage` (common + bindslot space across shader stages) + - storage image bindings: `layout(binding=0..sg_limits.max_storage_image_bindings_per_stage, [access_format])` + + For example code of how to create backend-specific shader objects, + please refer to the following samples: + + - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal + - for OpenGL: https://github.com/floooh/sokol-samples/tree/master/glfw + - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5 + - for WebGPU: https://github.com/floooh/sokol-samples/tree/master/wgpu + + + ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING + ======================================================================== + The WebGPU backend introduces the concept of 'unfilterable-float' textures, + which can only be combined with 'nonfiltering' samplers (this is a restriction + specific to WebGPU, but since the same sokol-gfx code should work across + all backend, the sokol-gfx validation layer also enforces this restriction + - the alternative would be undefined behaviour in some backend APIs on + some devices). + + The background is that some mobile devices (most notably iOS devices) can + not perform linear filtering when sampling textures with certain pixel + formats, most notable the 32F formats: + + - SG_PIXELFORMAT_R32F + - SG_PIXELFORMAT_RG32F + - SG_PIXELFORMAT_RGBA32F + + The information of whether a shader is going to be used with such an + unfilterable-float texture must already be provided in the sg_shader_desc + struct when creating the shader (see the above section "ON SHADER CREATION"). + + If you are using the sokol-shdc shader compiler, the information whether a + texture/sampler binding expects an 'unfilterable-float/nonfiltering' + texture/sampler combination cannot be inferred from the shader source + alone, you'll need to provide this hint via annotation-tags. For instance + here is an example from the ozz-skin-sapp.c sample shader which samples an + RGBA32F texture with skinning matrices in the vertex shader: + + ```glsl + @image_sample_type joint_tex unfilterable_float + uniform texture2D joint_tex; + @sampler_type smp nonfiltering + uniform sampler smp; + ``` + + This will result in SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT and + SG_SAMPLERTYPE_NONFILTERING being written to the code-generated + sg_shader_desc struct. + + + ON VERTEX FORMATS + ================= + Sokol-gfx implements the same strict mapping rules from CPU-side + vertex component formats to GPU-side vertex input data types: + + - float and packed normalized CPU-side formats must be used as + floating point base type in the vertex shader + - packed signed-integer CPU-side formats must be used as signed + integer base type in the vertex shader + - packed unsigned-integer CPU-side formats must be used as unsigned + integer base type in the vertex shader + + These mapping rules are enforced by the sokol-gfx validation layer, + but only when sufficient reflection information is provided in + `sg_shader_desc.attrs[].base_type`. This is the case when sokol-shdc + is used, otherwise the default base_type will be SG_SHADERATTRBASETYPE_UNDEFINED + which causes the sokol-gfx validation check to be skipped (of course you + can also provide the per-attribute base type information manually when + not using sokol-shdc). + + The detailed mapping rules from SG_VERTEXFORMAT_* to GLSL data types + are as follows: + + - FLOAT[*] => float, vec* + - BYTE4N => vec* (scaled to -1.0 .. +1.0) + - UBYTE4N => vec* (scaled to 0.0 .. +1.0) + - SHORT[*]N => vec* (scaled to -1.0 .. +1.0) + - USHORT[*]N => vec* (scaled to 0.0 .. +1.0) + - INT[*] => int, ivec* + - UINT[*] => uint, uvec* + - BYTE4 => int* + - UBYTE4 => uint* + - SHORT[*] => int* + - USHORT[*] => uint* + + NOTE that sokol-gfx only provides vertex formats with sizes of a multiple + of 4 (e.g. BYTE4N but not BYTE2N). This is because vertex components must + be 4-byte aligned anyway. + + + 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 tackle the common uniform block layout problem is + to use the sokol-shdc shader cross-compiler tool! + + + ON STORAGE BUFFERS + ================== + The two main purpose of storage buffers are: + + - to be populated by compute shaders with dynamically generated data + - for providing random-access data to all shader stages + + Storage buffers can be used to pass large amounts of random access structured + data from the CPU side to the shaders. They are similar to data textures, but are + more convenient to use both on the CPU and shader side since they can be accessed + in shaders as as a 1-dimensional array of struct items. + + Storage buffers are *NOT* supported on the following platform/backend combos: + + - macOS+GL (because storage buffers require GL 4.3, while macOS only goes up to GL 4.1) + - platforms which only support a GLES3.0 context (WebGL2 and iOS) + + To use storage buffers, the following steps are required: + + - write a shader which uses storage buffers (vertex- and fragment-shaders + can only read from storage buffers, while compute-shaders can both read + and write storage buffers) + - create one or more storage buffers via sg_make_buffer() with the + `.usage.storage_buffer = true` + - when creating a shader via sg_make_shader(), populate the sg_shader_desc + struct with binding info (when using sokol-shdc, this step will be taken care + of automatically) + - which storage buffer bind slots on the vertex-, fragment- or compute-stage + are occupied + - whether the storage buffer on that bind slot is readonly (readonly + bindings are required for vertex- and fragment-shaders, and in compute + shaders the readonly flag is used to control hazard tracking in some + 3D backends) + + - when calling sg_apply_bindings(), apply the matching bind slots with the previously + created storage buffers + - ...and that's it. + + For more details, see the following backend-agnostic sokol samples: + + - simple vertex pulling from a storage buffer: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl + - instanced rendering via storage buffers (vertex- and instance-pulling): + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl + - storage buffers both on the vertex- and fragment-stage: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl + - the Ozz animation sample rewritten to pull all rendering data from storage buffers: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl + - the instancing sample modified to use compute shaders: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-compute-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-compute-sapp.glsl + - the Compute Boids sample ported to sokol-gfx: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/computeboids-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/computeboids-sapp.glsl + + ...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc): + + - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c + - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c + - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c + - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c + + ...and the backend specific compute shader samples: + + - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/instancing-compute-d3d11.c + - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/instancing-compute-glfw.c + - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/instancing-compute-metal.c + - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/instancing-compute-wgpu.c + + Storage buffer shader authoring caveats when using sokol-shdc: + + - declare a read-only storage buffer interface block with `layout(binding=N) readonly buffer [name] { ... }` + (where 'N' is the index in `sg_bindings.storage_buffers[N]`) + - ...or a read/write storage buffer interface block with `layout(binding=N) buffer [name] { ... }` + - declare a struct which describes a single array item in the storage buffer interface block + - only put a single flexible array member into the storage buffer interface block + + E.g. a complete example in 'sokol-shdc GLSL': + + ```glsl + @vs + // declare a struct: + struct sb_vertex { + vec3 pos; + vec4 color; + } + // declare a buffer interface block with a single flexible struct array: + layout(binding=0) readonly buffer vertices { + sb_vertex vtx[]; + } + // in the shader function, access the storage buffer like this: + void main() { + vec3 pos = vtx[gl_VertexIndex].pos; + ... + } + @end + ``` + + In a compute shader you can read and write the same item in the same + storage buffer (but you'll have to be careful for random access since + many threads of the same compute function run in parallel): + + @cs + struct sb_item { + vec3 pos; + vec3 vel; + } + layout(binding=0) buffer items_ssbo { + sb_item items[]; + } + layout(local_size_x=64, local_size_y=1, local_size_z=1) in; + void main() { + uint idx = gl_GlobalInvocationID.x; + vec3 pos = items[idx].pos; + ... + items[idx].pos = pos; + } + @end + + Backend-specific storage-buffer caveats (not relevant when using sokol-shdc): + + D3D11: + - storage buffers are created as 'raw' Byte Address Buffers + (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers) + - in HLSL, use a ByteAddressBuffer for readonly access of the buffer content: + (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer) + - ...or RWByteAddressBuffer for read/write access: + (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer) + - readonly-storage buffers and textures are both bound as 'shader-resource-view' and + share the same bind slots (declared as `register(tN)` in HLSL), where N must be in the range 0..23) + - read/write storage buffers and storage images are bound as 'unordered-access-view' + (declared as `register(uN)` in HLSL where N is in the range 0..11) + + Metal: + - in Metal there is no internal difference between vertex-, uniform- and + storage-buffers, all are bound to the same 'buffer bind slots' with the + following reserved ranges: + - vertex shader stage: + - uniform buffers: slots 0..7 + - storage buffers: slots 8..15 + - vertex buffers: slots 15..23 + - fragment shader stage: + - uniform buffers: slots 0..7 + - storage buffers: slots 8..15 + - this means in MSL, storage buffer bindings start at [[buffer(8)]] both in + the vertex and fragment stage + + GL: + - the GL backend doesn't use name-lookup to find storage buffer bindings, this + means you must annotate buffers with `layout(std430, binding=N)` in GLSL + - ...where N is 0..sg_limits.max_storage_buffer_bindings_per_stage. + + WebGPU: + - in WGSL, textures, samplers and storage buffers all use a shared + bindspace across all shader stages on bindgroup 1: + + `@group(1) @binding(0..127) + + ON STORAGE IMAGES: + ================== + To write pixel data to texture objects in compute shaders, first an image + object must be created with `storage_image usage`: + + sg_image storage_image = sg_make_image(&(sg_image_desc){ + .usage.storage_image = true, + }, + .width = ..., + .height = ..., + .pixel_format = ..., + }); + + Next a storage-image-view object is required which also allows to pick + a specific mip-level or slice for the compute-shader to access: + + sg_view simg_view = sg_make_view(&(sg_view_desc){ + .storage_image = { + .image = storage_image, + .mip_level = ..., + .slice = ... + }, + }); + + Finally 'bind' the storage-image-view via a regular sg_apply_bindings() call + inside a compute pass: + + sg_begin_pass(&(sg_pass){ .compute = true }); + sg_apply_pipeline(...); + sg_apply_bindings(&(sg_bindings){ + .views[VIEW_simg] = simg_view, + }); + sg_dispatch(...); + sg_end_pass(); + + Currently, storage images can only be used with `readwrite` or `writeonly` access in + shaders. For readonly access use a regular texture binding instead. + + For an example of using storage images in compute shaders see imageblur-sapp: + + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/imageblur-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/imageblur-sapp.glsl + + TRACE HOOKS: + ============ + sokol_gfx.h optionally allows to install "trace hook" callbacks for + each public API functions. When a public API function is called, and + a trace hook callback has been installed for this function, the + callback will be invoked with the parameters and result of the function. + This is useful for things like debugging- and profiling-tools, or + keeping track of resource creation and destruction. + + To use the trace hook feature: + + --- Define SOKOL_TRACE_HOOKS before including the implementation. + + --- Setup an sg_trace_hooks structure with your callback function + pointers (keep all function pointers you're not interested + in zero-initialized), optionally set the user_data member + in the sg_trace_hooks struct. + + --- Install the trace hooks by calling sg_install_trace_hooks(), + the return value of this function is another sg_trace_hooks + struct which contains the previously set of trace hooks. + You should keep this struct around, and call those previous + functions pointers from your own trace callbacks for proper + chaining. + + As an example of how trace hooks are used, have a look at the + imgui/sokol_gfx_imgui.h header which implements a realtime + debugging UI for sokol_gfx.h on top of Dear ImGui. + + + 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_fn = my_alloc, + .free_fn = 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_sampler sg_make_sampler(const sg_sampler_desc* desc) + sg_shader sg_make_shader(const sg_shader_desc* desc) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) + sg_view sg_make_view(const sg_view_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 succeeded + } + + 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_sampler sg_alloc_sampler(void) + sg_shader sg_alloc_shader(void) + sg_pipeline sg_alloc_pipeline(void) + sg_view sg_alloc_view(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_sampler(sg_sampler smp, const sg_sampler_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_view(sg_view view, const sg_view_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_sampler(sg_sampler smp) + void sg_uninit_shader(sg_shader shd) + void sg_uninit_pipeline(sg_pipeline pip) + void sg_uninit_view(sg_view view) + + 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_sampler(sg_sampler smp) + void sg_dealloc_shader(sg_shader shd) + void sg_dealloc_pipeline(sg_pipeline pip) + void sg_dealloc_view(sg_view view) + + 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_sampler(sg_sampler smp) + void sg_destroy_shader(sg_shader shd) + void sg_destroy_pipeline(sg_pipeline pip) + void sg_destroy_view(sg_view view) + + 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_sampler(sg_sampler smp) + sg_fail_shader(sg_shader shd) + sg_fail_pipeline(sg_pipeline pip) + sg_fail_view(sg_view view) + + This is recommended if anything went wrong outside of sokol-gfx during asynchronous + resource setup (for instance a 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, + images and views, but not for samplers, shaders or pipelines. 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. + + + WEBGPU CAVEATS + ============== + For a general overview and design notes of the WebGPU backend see: + + https://floooh.github.io/2023/10/16/sokol-webgpu.html + + In general, don't expect an automatic speedup when switching from the WebGL2 + backend to the WebGPU backend. Some WebGPU functions currently actually + have a higher CPU overhead than similar WebGL2 functions, leading to the + paradoxical situation that some WebGPU code may be slower than similar WebGL2 + code. + + - when writing WGSL shader code by hand, a specific bind-slot convention + must be used: + + All uniform block structs must use `@group(0)` and bindings in the + range 0..15 + + @group(0) @binding(0..15) + + All textures, samplers, storage-buffers and storage-images must use `@group(1)` + and bindings must be in the range 0..127: + + @group(1) @binding(0..127) + + Note that the number of texture, sampler, storage-buffer storage-image bindings + is still limited despite the large bind range: + + - up to 16 textures and sampler across all shader stages + - up to 8 storage buffers across all shader stages + - up to 4 storage images on the compute shader stage + + If you use sokol-shdc to generate WGSL shader code, you don't need to worry + about the above binding conventions since sokol-shdc will allocate + the WGSL bindslots). + + - The sokol-gfx WebGPU backend uses the sg_desc.uniform_buffer_size item + to allocate a single per-frame uniform buffer which must be big enough + to hold all data written by sg_apply_uniforms() during a single frame, + including a worst-case 256-byte alignment (e.g. each sg_apply_uniform + call will cost at least 256 bytes of uniform buffer size). The default size + is 4 MB, which is enough for 16384 sg_apply_uniform() calls per + frame (assuming the uniform data 'payload' is less than 256 bytes + per call). These rules are the same as for the Metal backend, so if + you are already using the Metal backend you'll be fine. + + - sg_apply_bindings(): the sokol-gfx WebGPU backend implements a bindgroup + cache to prevent excessive creation and destruction of BindGroup objects + when calling sg_apply_bindings(). The number of slots in the bindgroups + cache is defined in sg_desc.wgpu.bindgroups_cache_size when calling + sg_setup. The cache size must be a power-of-2 number, with the default being + 1024. The bindgroups cache behaviour can be observed by calling the new + function sg_query_stats(), where the following struct items are + of interest: + + .wgpu.num_bindgroup_cache_hits + .wgpu.num_bindgroup_cache_misses + .wgpu.num_bindgroup_cache_collisions + .wgpu_num_bindgroup_cache_invalidates + .wgpu.num_bindgroup_cache_vs_hash_key_mismatch + + The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`, + if this number is consistently higher than a few percent of the + .wgpu.num_set_bindgroup value, it might be a good idea to bump the + bindgroups cache size to the next power-of-2. + + - sg_apply_viewport(): WebGPU currently has a unique restriction that viewport + rectangles must be contained entirely within the framebuffer. As a shitty + workaround sokol_gfx.h will clip incoming viewport rectangles against + the framebuffer, but this will distort the clipspace-to-screenspace mapping. + There's no proper way to handle this inside sokol_gfx.h, this must be fixed + in a future WebGPU update (see: https://github.com/gpuweb/gpuweb/issues/373 + and https://github.com/gpuweb/gpuweb/pull/5025) + + - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);` + into the WGSL output. Currently only the Chrome WebGPU implementation seems + to recognize this. + + - Likewise, the following sokol-gfx pixel formats are not supported in WebGPU: + R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN. + Unlike unsupported vertex formats, unsupported pixel formats can be queried + in cross-backend code via sg_query_pixelformat() though. + + - The Emscripten WebGPU shim currently doesn't support the Closure minification + post-link-step (e.g. currently the emcc argument '--closure 1' or '--closure 2' + will generate broken Javascript code. + + - sokol-gfx requires the WebGPU device feature `depth32float-stencil8` to be enabled + (this should be widely supported) + + - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be + enabled (since this would exclude all iOS devices) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 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_GFX_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL) +#define SOKOL_GFX_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Resource id typedefs: + + sg_buffer: vertex- and index-buffers + sg_image: images used as textures and render-pass attachments + sg_sampler sampler objects describing how a texture is sampled in a shader + sg_shader: vertex- and fragment-shaders and shader interface information + sg_pipeline: associated shader and vertex-layouts, and render states + sg_view: a resource view object used for bindings and render-pass attachments + + Instead of pointers, resource creation functions return a 32-bit + handle which uniquely identifies the resource object. + + The 32-bit resource id is split into a 16-bit pool index in the lower bits, + and a 16-bit 'generation counter' in the upper bits. The index allows fast + pool lookups, and combined with the generation-counter it allows to detect + 'dangling accesses' (trying to use an object which no longer exists, and + its pool slot has been reused for a new object) + + The resource ids are wrapped into a strongly-typed struct so that + trying to pass an incompatible resource id is a compile error. +*/ +typedef struct sg_buffer { uint32_t id; } sg_buffer; +typedef struct sg_image { uint32_t id; } sg_image; +typedef struct sg_sampler { uint32_t id; } sg_sampler; +typedef struct sg_shader { uint32_t id; } sg_shader; +typedef struct sg_pipeline { uint32_t id; } sg_pipeline; +typedef struct sg_view { uint32_t id; } sg_view; + +/* + sg_range is a pointer-size-pair struct used to pass memory blobs into + sokol-gfx. When initialized from a value type (array or struct), you can + use the SG_RANGE() macro to build an sg_range struct. For functions which + take either a sg_range pointer, or a (C++) sg_range reference, use the + SG_RANGE_REF macro as a solution which compiles both in C and C++. +*/ +typedef struct sg_range { + const void* ptr; + size_t size; +} sg_range; + +// 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 SG_RANGE(x) sg_range{ &x, sizeof(x) } +#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) } +#else +#define SG_RANGE(x) (sg_range){ &x, sizeof(x) } +#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) } +#endif + +// various compile-time constants in the public API +enum { + SG_INVALID_ID = 0, + SG_NUM_INFLIGHT_FRAMES = 2, + SG_MAX_COLOR_ATTACHMENTS = 8, + SG_MAX_UNIFORMBLOCK_MEMBERS = 16, + SG_MAX_VERTEX_ATTRIBUTES = 16, + SG_MAX_MIPMAPS = 16, + SG_MAX_VERTEXBUFFER_BINDSLOTS = 8, + SG_MAX_UNIFORMBLOCK_BINDSLOTS = 8, + SG_MAX_VIEW_BINDSLOTS = 32, + SG_MAX_SAMPLER_BINDSLOTS = 12, + SG_MAX_TEXTURE_SAMPLER_PAIRS = 32, // same as SG_MAX_VIEW_BINDSLOTS + SG_MAX_PORTABLE_COLOR_ATTACHMENTS = 4, + SG_MAX_PORTABLE_TEXTURE_BINDINGS_PER_STAGE = 16, + SG_MAX_PORTABLE_STORAGEBUFFER_BINDINGS_PER_STAGE = 8, // assuming sg_features.compute = true + SG_MAX_PORTABLE_STORAGEIMAGE_BINDINGS_PER_STAGE = 4, // assuming sg_features.compute = true +}; + +/* + sg_color + + An RGBA color value. +*/ +typedef struct sg_color { float r, g, b, a; } sg_color; + +/* + sg_backend + + The active 3D-API backend, use the function sg_query_backend() + to get the currently active backend. +*/ +typedef enum sg_backend { + SG_BACKEND_GLCORE, + SG_BACKEND_GLES3, + SG_BACKEND_D3D11, + SG_BACKEND_METAL_IOS, + SG_BACKEND_METAL_MACOS, + SG_BACKEND_METAL_SIMULATOR, + SG_BACKEND_WGPU, + SG_BACKEND_VULKAN, + SG_BACKEND_DUMMY, +} sg_backend; + +/* + sg_pixel_format + + sokol_gfx.h basically uses the same pixel formats as WebGPU, since these + are supported on most newer GPUs. + + A pixelformat name consist of three parts: + + - components (R, RG, RGB or RGBA) + - bit width per component (8, 16 or 32) + - component data type: + - unsigned normalized (no postfix) + - signed normalized (SN postfix) + - unsigned integer (UI postfix) + - signed integer (SI postfix) + - float (F postfix) + + Not all pixel formats can be used for everything, call sg_query_pixelformat() + to inspect the capabilities of a given pixelformat. The function returns + an sg_pixelformat_info struct with the following members: + + - sample: the pixelformat can be sampled as texture at least with + nearest filtering + - filter: the pixelformat can be sampled as texture with linear + filtering + - render: the pixelformat can be used as render-pass attachment + - blend: blending is supported when used as render-pass attachment + - msaa: multisample-antialiasing is supported when used + as render-pass attachment + - depth: the pixelformat can be used for depth-stencil attachments + - compressed: this is a block-compressed format + - bytes_per_pixel: the numbers of bytes in a pixel (0 for compressed formats) + + The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. + + The default pixel format for render target images is platform-dependent + and taken from the sg_environment struct passed into sg_setup(). Typically + the default formats are: + + - for the Metal, D3D11 and WebGPU backends: SG_PIXELFORMAT_BGRA8 + - for GL backends: SG_PIXELFORMAT_RGBA8 +*/ +typedef enum sg_pixel_format { + _SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init + SG_PIXELFORMAT_NONE, + + SG_PIXELFORMAT_R8, + SG_PIXELFORMAT_R8SN, + SG_PIXELFORMAT_R8UI, + SG_PIXELFORMAT_R8SI, + + SG_PIXELFORMAT_R16, + SG_PIXELFORMAT_R16SN, + SG_PIXELFORMAT_R16UI, + SG_PIXELFORMAT_R16SI, + SG_PIXELFORMAT_R16F, + SG_PIXELFORMAT_RG8, + SG_PIXELFORMAT_RG8SN, + SG_PIXELFORMAT_RG8UI, + SG_PIXELFORMAT_RG8SI, + + SG_PIXELFORMAT_R32UI, + SG_PIXELFORMAT_R32SI, + SG_PIXELFORMAT_R32F, + SG_PIXELFORMAT_RG16, + SG_PIXELFORMAT_RG16SN, + SG_PIXELFORMAT_RG16UI, + SG_PIXELFORMAT_RG16SI, + SG_PIXELFORMAT_RG16F, + SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_SRGB8A8, + SG_PIXELFORMAT_RGBA8SN, + SG_PIXELFORMAT_RGBA8UI, + SG_PIXELFORMAT_RGBA8SI, + SG_PIXELFORMAT_BGRA8, + SG_PIXELFORMAT_RGB10A2, + SG_PIXELFORMAT_RG11B10F, + SG_PIXELFORMAT_RGB9E5, + + SG_PIXELFORMAT_RG32UI, + SG_PIXELFORMAT_RG32SI, + SG_PIXELFORMAT_RG32F, + SG_PIXELFORMAT_RGBA16, + SG_PIXELFORMAT_RGBA16SN, + SG_PIXELFORMAT_RGBA16UI, + SG_PIXELFORMAT_RGBA16SI, + SG_PIXELFORMAT_RGBA16F, + + SG_PIXELFORMAT_RGBA32UI, + SG_PIXELFORMAT_RGBA32SI, + SG_PIXELFORMAT_RGBA32F, + + SG_PIXELFORMAT_DEPTH, + SG_PIXELFORMAT_DEPTH_STENCIL, + + // NOTE: don't put any new compressed format in front of here + SG_PIXELFORMAT_BC1_RGBA, + SG_PIXELFORMAT_BC2_RGBA, + SG_PIXELFORMAT_BC3_RGBA, + SG_PIXELFORMAT_BC3_SRGBA, + SG_PIXELFORMAT_BC4_R, + SG_PIXELFORMAT_BC4_RSN, + SG_PIXELFORMAT_BC5_RG, + SG_PIXELFORMAT_BC5_RGSN, + SG_PIXELFORMAT_BC6H_RGBF, + SG_PIXELFORMAT_BC6H_RGBUF, + SG_PIXELFORMAT_BC7_RGBA, + SG_PIXELFORMAT_BC7_SRGBA, + SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_SRGB8, + SG_PIXELFORMAT_ETC2_RGB8A1, + SG_PIXELFORMAT_ETC2_RGBA8, + SG_PIXELFORMAT_ETC2_SRGB8A8, + SG_PIXELFORMAT_EAC_R11, + SG_PIXELFORMAT_EAC_R11SN, + SG_PIXELFORMAT_EAC_RG11, + SG_PIXELFORMAT_EAC_RG11SN, + + SG_PIXELFORMAT_ASTC_4x4_RGBA, + SG_PIXELFORMAT_ASTC_4x4_SRGBA, + + _SG_PIXELFORMAT_NUM, + _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_pixel_format; + +/* + Runtime information about a pixel format, returned by sg_query_pixelformat(). +*/ +typedef struct sg_pixelformat_info { + bool sample; // pixel format can be sampled in shaders at least with nearest filtering + bool filter; // pixel format can be sampled with linear filtering + bool render; // pixel format can be used as render-pass attachment + bool blend; // pixel format supports alpha-blending when used as render-pass attachment + bool msaa; // pixel format supports MSAA when used as render-pass attachment + bool depth; // pixel format is a depth format + bool compressed; // true if this is a hardware-compressed format + bool read; // true if format supports compute shader read access + bool write; // true if format supports compute shader write access + int bytes_per_pixel; // NOTE: this is 0 for compressed formats, use sg_query_row_pitch() / sg_query_surface_pitch() as alternative +} sg_pixelformat_info; + +/* + Runtime information about available optional features, returned by sg_query_features() +*/ +typedef struct sg_features { + bool origin_top_left; // framebuffer- and texture-origin is in top left corner + bool image_clamp_to_border; // border color and clamp-to-border uv-wrap mode is supported + bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state + bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks + bool compute; // storage buffers and compute shaders are supported + bool msaa_texture_bindings; // if true, multisampled images can be bound as textures + bool separate_buffer_types; // cannot use the same buffer for vertex and indices (only WebGL2) + bool draw_base_vertex; // draw with (base vertex > 0) && (base_instance == 0) supported + bool draw_base_instance; // draw with (base instance > 0) supported + bool dual_source_blending; // dual-source-blending supported + bool gl_texture_views; // supports 'proper' texture views (GL 4.3+) +} sg_features; + +/* + Runtime information about resource limits, returned by sg_query_limit() +*/ +typedef struct sg_limits { + int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images + int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images + int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images + int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images + int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images + int max_vertex_attrs; // max number of vertex attributes, clamped to SG_MAX_VERTEX_ATTRIBUTES + int max_color_attachments; // max number of render pass color attachments, clamped to SG_MAX_COLOR_ATTACHMENTS + int max_texture_bindings_per_stage; // max number of texture bindings per shader stage, clamped to SG_MAX_VIEW_BINDSLOTS + int max_storage_buffer_bindings_per_stage; // max number of storage buffer bindings per shader stage, clamped to SG_MAX_VIEW_BINDSLOTS + int max_storage_image_bindings_per_stage; // max number of storage image bindings per shader stage, clamped to SG_MAX_VIEW_BINDSLOTS + int gl_max_vertex_uniform_components; // GL_MAX_VERTEX_UNIFORM_COMPONENTS (only on GL backends) + int gl_max_combined_texture_image_units; // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends) + int d3d11_max_unordered_access_views; // 8 on feature level 11.0, otherwise 32 (clamped to SG_MAX_VIEW_BINDSLOTS) + int vk_min_uniform_buffer_offset_alignment; +} sg_limits; + +/* + sg_resource_state + + The current state of a resource in its resource pool. + Resources start in the INITIAL state, which means the + pool slot is unoccupied and can be allocated. When a resource is + created, first an id is allocated, and the resource pool slot + is set to state ALLOC. After allocation, the resource is + initialized, which may result in the VALID or FAILED state. The + reason why allocation and initialization are separate is because + some resource types (e.g. buffers and images) might be asynchronously + initialized by the user application. If a resource which is not + in the VALID state is attempted to be used for rendering, rendering + operations will silently be dropped. + + The special INVALID state is returned in sg_query_xxx_state() if no + resource object exists for the provided resource id. +*/ +typedef enum sg_resource_state { + SG_RESOURCESTATE_INITIAL, + SG_RESOURCESTATE_ALLOC, + SG_RESOURCESTATE_VALID, + SG_RESOURCESTATE_FAILED, + SG_RESOURCESTATE_INVALID, + _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sg_resource_state; + +/* + sg_index_type + + Indicates whether indexed rendering (fetching vertex-indices from an + index buffer) is used, and if yes, the index data type (16- or 32-bits). + + This is used in the sg_pipeline_desc.index_type member when creating a + pipeline object. + + The default index type is SG_INDEXTYPE_NONE. +*/ +typedef enum sg_index_type { + _SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init + SG_INDEXTYPE_NONE, + SG_INDEXTYPE_UINT16, + SG_INDEXTYPE_UINT32, + _SG_INDEXTYPE_NUM, + _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_index_type; + +/* + sg_image_type + + Indicates the basic type of an image object (2D-texture, cubemap, + 3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when + creating an image, and in sg_shader_image_desc to describe a sampled texture + in the shader (both must match and will be checked in the validation layer + when calling sg_apply_bindings). + + The default image type when creating an image is SG_IMAGETYPE_2D. +*/ +typedef enum sg_image_type { + _SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGETYPE_2D, + SG_IMAGETYPE_CUBE, + SG_IMAGETYPE_3D, + SG_IMAGETYPE_ARRAY, + _SG_IMAGETYPE_NUM, + _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_type; + +/* + sg_image_sample_type + + The basic data type of a texture sample as expected by a shader. + Must be provided in sg_shader_image and used by the validation + layer in sg_apply_bindings() to check if the provided image object + is compatible with what the shader expects. Apart from the sokol-gfx + validation layer, WebGPU is the only backend API which actually requires + matching texture and sampler type to be provided upfront for validation + (other 3D APIs treat texture/sampler type mismatches as undefined behaviour). + + NOTE that the following texture pixel formats require the use + of SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, combined with a sampler + of type SG_SAMPLERTYPE_NONFILTERING: + + - SG_PIXELFORMAT_R32F + - SG_PIXELFORMAT_RG32F + - SG_PIXELFORMAT_RGBA32F + + (when using sokol-shdc, also check out the meta tags `@image_sample_type` + and `@sampler_type`) +*/ +typedef enum sg_image_sample_type { + _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGESAMPLETYPE_FLOAT, + SG_IMAGESAMPLETYPE_DEPTH, + SG_IMAGESAMPLETYPE_SINT, + SG_IMAGESAMPLETYPE_UINT, + SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, + _SG_IMAGESAMPLETYPE_NUM, + _SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_sample_type; + +/* + sg_sampler_type + + The basic type of a texture sampler (sampling vs comparison) as + defined in a shader. Must be provided in sg_shader_sampler_desc. + + sg_image_sample_type and sg_sampler_type for a texture/sampler + pair must be compatible with each other, specifically only + the following pairs are allowed: + + - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING) + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON +*/ +typedef enum sg_sampler_type { + _SG_SAMPLERTYPE_DEFAULT, + SG_SAMPLERTYPE_FILTERING, + SG_SAMPLERTYPE_NONFILTERING, + SG_SAMPLERTYPE_COMPARISON, + _SG_SAMPLERTYPE_NUM, + _SG_SAMPLERTYPE_FORCE_U32, +} sg_sampler_type; + +/* + sg_primitive_type + + This is the common subset of 3D primitive types supported across all 3D + APIs. This is used in the sg_pipeline_desc.primitive_type member when + creating a pipeline object. + + The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. +*/ +typedef enum sg_primitive_type { + _SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init + SG_PRIMITIVETYPE_POINTS, + SG_PRIMITIVETYPE_LINES, + SG_PRIMITIVETYPE_LINE_STRIP, + SG_PRIMITIVETYPE_TRIANGLES, + SG_PRIMITIVETYPE_TRIANGLE_STRIP, + _SG_PRIMITIVETYPE_NUM, + _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_primitive_type; + +/* + sg_filter + + The filtering mode when sampling a texture image. This is + used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter + and sg_sampler_desc.mipmap_filter members when creating a sampler object. + + For the default is SG_FILTER_NEAREST. +*/ +typedef enum sg_filter { + _SG_FILTER_DEFAULT, // value 0 reserved for default-init + SG_FILTER_NEAREST, + SG_FILTER_LINEAR, + _SG_FILTER_NUM, + _SG_FILTER_FORCE_U32 = 0x7FFFFFFF +} sg_filter; + +/* + sg_wrap + + The texture coordinates wrapping mode when sampling a texture + image. This is used in the sg_image_desc.wrap_u, .wrap_v + and .wrap_w members when creating an image. + + The default wrap mode is SG_WRAP_REPEAT. + + NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends + and platforms. To check for support, call sg_query_features() + and check the "clamp_to_border" boolean in the returned + sg_features struct. + + Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back + to SG_WRAP_CLAMP_TO_EDGE without a validation error. +*/ +typedef enum sg_wrap { + _SG_WRAP_DEFAULT, // value 0 reserved for default-init + SG_WRAP_REPEAT, + SG_WRAP_CLAMP_TO_EDGE, + SG_WRAP_CLAMP_TO_BORDER, + SG_WRAP_MIRRORED_REPEAT, + _SG_WRAP_NUM, + _SG_WRAP_FORCE_U32 = 0x7FFFFFFF +} sg_wrap; + +/* + sg_border_color + + The border color to use when sampling a texture, and the UV wrap + mode is SG_WRAP_CLAMP_TO_BORDER. + + The default border color is SG_BORDERCOLOR_OPAQUE_BLACK +*/ +typedef enum sg_border_color { + _SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init + SG_BORDERCOLOR_TRANSPARENT_BLACK, + SG_BORDERCOLOR_OPAQUE_BLACK, + SG_BORDERCOLOR_OPAQUE_WHITE, + _SG_BORDERCOLOR_NUM, + _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF +} sg_border_color; + +/* + sg_vertex_format + + The data type of a vertex component. This is used to describe + the layout of input vertex data when creating a pipeline object. + + NOTE that specific mapping rules exist from the CPU-side vertex + formats to the vertex attribute base type in the vertex shader code + (see doc header section 'ON VERTEX FORMATS'). +*/ +typedef enum sg_vertex_format { + SG_VERTEXFORMAT_INVALID, + SG_VERTEXFORMAT_FLOAT, + SG_VERTEXFORMAT_FLOAT2, + SG_VERTEXFORMAT_FLOAT3, + SG_VERTEXFORMAT_FLOAT4, + SG_VERTEXFORMAT_INT, + SG_VERTEXFORMAT_INT2, + SG_VERTEXFORMAT_INT3, + SG_VERTEXFORMAT_INT4, + SG_VERTEXFORMAT_UINT, + SG_VERTEXFORMAT_UINT2, + SG_VERTEXFORMAT_UINT3, + SG_VERTEXFORMAT_UINT4, + SG_VERTEXFORMAT_BYTE4, + SG_VERTEXFORMAT_BYTE4N, + SG_VERTEXFORMAT_UBYTE4, + SG_VERTEXFORMAT_UBYTE4N, + SG_VERTEXFORMAT_SHORT2, + SG_VERTEXFORMAT_SHORT2N, + SG_VERTEXFORMAT_USHORT2, + SG_VERTEXFORMAT_USHORT2N, + SG_VERTEXFORMAT_SHORT4, + SG_VERTEXFORMAT_SHORT4N, + SG_VERTEXFORMAT_USHORT4, + SG_VERTEXFORMAT_USHORT4N, + SG_VERTEXFORMAT_UINT10_N2, + SG_VERTEXFORMAT_HALF2, + SG_VERTEXFORMAT_HALF4, + _SG_VERTEXFORMAT_NUM, + _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_format; + +/* + sg_vertex_step + + Defines whether the input pointer of a vertex input stream is advanced + 'per vertex' or 'per instance'. The default step-func is + SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with + instanced-rendering. + + The vertex-step is part of the vertex-layout definition + when creating pipeline objects. +*/ +typedef enum sg_vertex_step { + _SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init + SG_VERTEXSTEP_PER_VERTEX, + SG_VERTEXSTEP_PER_INSTANCE, + _SG_VERTEXSTEP_NUM, + _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_step; + +/* + sg_uniform_type + + The data type of a uniform block member. This is used to + describe the internal layout of uniform blocks when creating + a shader object. This is only required for the GL backend, all + other backends will ignore the interior layout of uniform blocks. +*/ +typedef enum sg_uniform_type { + SG_UNIFORMTYPE_INVALID, + SG_UNIFORMTYPE_FLOAT, + 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 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 an opaque 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 + + The face-culling mode, this is used in the + sg_pipeline_desc.cull_mode member when creating a + pipeline object. + + The default cull mode is SG_CULLMODE_NONE +*/ +typedef enum sg_cull_mode { + _SG_CULLMODE_DEFAULT, // value 0 reserved for default-init + SG_CULLMODE_NONE, + SG_CULLMODE_FRONT, + SG_CULLMODE_BACK, + _SG_CULLMODE_NUM, + _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF +} sg_cull_mode; + +/* + sg_face_winding + + The vertex-winding rule that determines a front-facing primitive. This + is used in the member sg_pipeline_desc.face_winding + when creating a pipeline object. + + The default winding is SG_FACEWINDING_CW (clockwise) +*/ +typedef enum sg_face_winding { + _SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init + SG_FACEWINDING_CCW, + SG_FACEWINDING_CW, + _SG_FACEWINDING_NUM, + _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF +} sg_face_winding; + +/* + sg_compare_func + + The compare-function for configuring depth- and stencil-ref tests + in pipeline objects, and for texture samplers which perform a comparison + instead of regular sampling operation. + + Used in the following structs: + + sg_pipeline_desc + .depth + .compare + .stencil + .front.compare + .back.compare + + sg_sampler_desc + .compare + + The default compare func for depth- and stencil-tests is + SG_COMPAREFUNC_ALWAYS. + + The default compare func for samplers is SG_COMPAREFUNC_NEVER. +*/ +typedef enum sg_compare_func { + _SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init + SG_COMPAREFUNC_NEVER, + SG_COMPAREFUNC_LESS, + SG_COMPAREFUNC_EQUAL, + SG_COMPAREFUNC_LESS_EQUAL, + SG_COMPAREFUNC_GREATER, + SG_COMPAREFUNC_NOT_EQUAL, + SG_COMPAREFUNC_GREATER_EQUAL, + SG_COMPAREFUNC_ALWAYS, + _SG_COMPAREFUNC_NUM, + _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF +} sg_compare_func; + +/* + sg_stencil_op + + The operation performed on a currently stored stencil-value when a + comparison test passes or fails. This is used when creating a pipeline + object in the following sg_pipeline_desc struct items: + + sg_pipeline_desc + .stencil + .front + .fail_op + .depth_fail_op + .pass_op + .back + .fail_op + .depth_fail_op + .pass_op + + The default value is SG_STENCILOP_KEEP. +*/ +typedef enum sg_stencil_op { + _SG_STENCILOP_DEFAULT, // value 0 reserved for default-init + SG_STENCILOP_KEEP, + SG_STENCILOP_ZERO, + SG_STENCILOP_REPLACE, + SG_STENCILOP_INCR_CLAMP, + SG_STENCILOP_DECR_CLAMP, + SG_STENCILOP_INVERT, + SG_STENCILOP_INCR_WRAP, + SG_STENCILOP_DECR_WRAP, + _SG_STENCILOP_NUM, + _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF +} sg_stencil_op; + +/* + sg_blend_factor + + The source and destination factors in blending operations. + This is used in the following members when creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .src_factor_rgb + .dst_factor_rgb + .src_factor_alpha + .dst_factor_alpha + + The default value is SG_BLENDFACTOR_ONE for source + factors, and for the destination SG_BLENDFACTOR_ZERO if the associated + blend-op is ADD, SUBTRACT or REVERSE_SUBTRACT or SG_BLENDFACTOR_ONE + if the associated blend-op is MIN or MAX. +*/ +typedef enum sg_blend_factor { + _SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init + SG_BLENDFACTOR_ZERO, + SG_BLENDFACTOR_ONE, + SG_BLENDFACTOR_SRC_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SG_BLENDFACTOR_SRC_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SG_BLENDFACTOR_DST_COLOR, + SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SG_BLENDFACTOR_DST_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SG_BLENDFACTOR_SRC_ALPHA_SATURATED, + SG_BLENDFACTOR_BLEND_COLOR, + SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, + SG_BLENDFACTOR_BLEND_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, + SG_BLENDFACTOR_SRC1_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR, + SG_BLENDFACTOR_SRC1_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA, + _SG_BLENDFACTOR_NUM, + _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF +} sg_blend_factor; + +/* + sg_blend_op + + Describes how the source and destination values are combined in the + fragment blending operation. It is used in the following struct items + when creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .op_rgb + .op_alpha + + The default value is SG_BLENDOP_ADD. +*/ +typedef enum sg_blend_op { + _SG_BLENDOP_DEFAULT, // value 0 reserved for default-init + SG_BLENDOP_ADD, + SG_BLENDOP_SUBTRACT, + SG_BLENDOP_REVERSE_SUBTRACT, + SG_BLENDOP_MIN, + SG_BLENDOP_MAX, + _SG_BLENDOP_NUM, + _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF +} sg_blend_op; + +/* + sg_color_mask + + Selects the active color channels when writing a fragment color to the + framebuffer. This is used in the members + sg_pipeline_desc.colors[i].write_mask when creating a pipeline object. + + The default colormask is SG_COLORMASK_RGBA (write all colors channels) + + NOTE: since the color mask value 0 is reserved for the default value + (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels + should be disabled. +*/ +typedef enum sg_color_mask { + _SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init + SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled + SG_COLORMASK_R = 0x1, + SG_COLORMASK_G = 0x2, + SG_COLORMASK_RG = 0x3, + SG_COLORMASK_B = 0x4, + SG_COLORMASK_RB = 0x5, + SG_COLORMASK_GB = 0x6, + SG_COLORMASK_RGB = 0x7, + SG_COLORMASK_A = 0x8, + SG_COLORMASK_RA = 0x9, + SG_COLORMASK_GA = 0xA, + SG_COLORMASK_RGA = 0xB, + SG_COLORMASK_BA = 0xC, + SG_COLORMASK_RBA = 0xD, + SG_COLORMASK_GBA = 0xE, + SG_COLORMASK_RGBA = 0xF, + _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF +} sg_color_mask; + +/* + sg_load_action + + Defines the load action that should be performed at the start of a render pass: + + SG_LOADACTION_CLEAR: clear the render target + SG_LOADACTION_LOAD: load the previous content of the render target + SG_LOADACTION_DONTCARE: leave the render target in an undefined state + + This is used in the sg_pass_action structure. + + The default load action for all pass attachments is SG_LOADACTION_CLEAR, + with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0. + + If you want to override the default behaviour, it is important to not + only set the clear color, but the 'action' field as well (as long as this + is _SG_LOADACTION_DEFAULT, the value fields will be ignored). +*/ +typedef enum sg_load_action { + _SG_LOADACTION_DEFAULT, + SG_LOADACTION_CLEAR, + SG_LOADACTION_LOAD, + SG_LOADACTION_DONTCARE, + _SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF +} sg_load_action; + +/* + sg_store_action + + Defines the store action that should be performed at the end of a render pass: + + SG_STOREACTION_STORE: store the rendered content to the color attachment image + SG_STOREACTION_DONTCARE: allows the GPU to discard the rendered content +*/ +typedef enum sg_store_action { + _SG_STOREACTION_DEFAULT, + SG_STOREACTION_STORE, + SG_STOREACTION_DONTCARE, + _SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF +} sg_store_action; + + +/* + sg_pass_action + + The sg_pass_action struct defines the actions to be performed + at the start and end of a render pass. + + - at the start of the pass: whether the render attachments should be cleared, + loaded with their previous content, or start in an undefined state + - for clear operations: the clear value (color, depth, or stencil values) + - at the end of the pass: whether the rendering result should be + stored back into the render attachment or discarded +*/ +typedef struct sg_color_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_STORE + sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f } +} sg_color_attachment_action; + +typedef struct sg_depth_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + float clear_value; // default: 1.0 +} sg_depth_attachment_action; + +typedef struct sg_stencil_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + uint8_t clear_value; // default: 0 +} sg_stencil_attachment_action; + +typedef struct sg_pass_action { + sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_depth_attachment_action depth; + sg_stencil_attachment_action stencil; +} sg_pass_action; + +/* + sg_swapchain + + Used in sg_begin_pass() to provide details about an external swapchain + (pixel formats, sample count and backend-API specific render surface objects). + + The following information must be provided: + + - the width and height of the swapchain surfaces in number of pixels, + - the pixel format of the render- and optional msaa-resolve-surface + - the pixel format of the optional depth- or depth-stencil-surface + - the MSAA sample count for the render and depth-stencil surface + + If the pixel formats and MSAA sample counts are left zero-initialized, + their defaults are taken from the sg_environment struct provided in the + sg_setup() call. + + The width and height *must* be > 0. + + Additionally the following backend API specific objects must be passed in + as 'type erased' void pointers: + + GL: + - on all GL backends, a GL framebuffer object must be provided. This + can be zero for the default framebuffer. + + D3D11: + - an ID3D11RenderTargetView for the rendering surface, without + MSAA rendering this surface will also be displayed + - an optional ID3D11DepthStencilView for the depth- or depth/stencil + buffer surface + - when MSAA rendering is used, another ID3D11RenderTargetView + which serves as MSAA resolve target and will be displayed + + WebGPU (same as D3D11, except different types) + - a WGPUTextureView for the rendering surface, without + MSAA rendering this surface will also be displayed + - an optional WGPUTextureView for the depth- or depth/stencil + buffer surface + - when MSAA rendering is used, another WGPUTextureView + which serves as MSAA resolve target and will be displayed + + Metal (NOTE that the roles of provided surfaces is slightly different + than on D3D11 or WebGPU in case of MSAA vs non-MSAA rendering): + + - A current CAMetalDrawable (NOT an MTLDrawable!) which will be presented. + This will either be rendered to directly (if no MSAA is used), or serve + as MSAA-resolve target. + - an optional MTLTexture for the depth- or depth-stencil buffer + - an optional multisampled MTLTexture which serves as intermediate + rendering surface which will then be resolved into the + CAMetalDrawable. + + NOTE that for Metal you must use an ObjC __bridge cast to + properly tunnel the ObjC object id through a C void*, e.g.: + + swapchain.metal.current_drawable = (__bridge const void*) [mtkView currentDrawable]; + + On all other backends you shouldn't need to mess with the reference count. + + It's a good practice to write a helper function which returns an initialized + sg_swapchain struct, which can then be plugged directly into + sg_pass.swapchain. Look at the function sglue_swapchain() in the sokol_glue.h + as an example. +*/ +typedef struct sg_metal_swapchain { + const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!) + const void* depth_stencil_texture; // MTLTexture + const void* msaa_color_texture; // MTLTexture +} sg_metal_swapchain; + +typedef struct sg_d3d11_swapchain { + const void* render_view; // ID3D11RenderTargetView + const void* resolve_view; // ID3D11RenderTargetView + const void* depth_stencil_view; // ID3D11DepthStencilView +} sg_d3d11_swapchain; + +typedef struct sg_wgpu_swapchain { + const void* render_view; // WGPUTextureView + const void* resolve_view; // WGPUTextureView + const void* depth_stencil_view; // WGPUTextureView +} sg_wgpu_swapchain; + +typedef struct sg_vulkan_swapchain { + const void* render_image; // vkImage + const void* render_view; // vkImageView + const void* resolve_image; // vkImage + const void* resolve_view; // vkImageView + const void* depth_stencil_image; // vkImage + const void* depth_stencil_view; // vkImageView + const void* render_finished_semaphore; // vkSemaphore + const void* present_complete_semaphore; // vkSemaphore +} sg_vulkan_swapchain; + +typedef struct sg_gl_swapchain { + uint32_t framebuffer; // GL framebuffer object +} sg_gl_swapchain; + +typedef struct sg_swapchain { + int width; + int height; + int sample_count; + sg_pixel_format color_format; + sg_pixel_format depth_format; + sg_metal_swapchain metal; + sg_d3d11_swapchain d3d11; + sg_wgpu_swapchain wgpu; + sg_vulkan_swapchain vulkan; + sg_gl_swapchain gl; +} sg_swapchain; + +/* + sg_attachments + + Used in sg_pass to provide render pass attachment views. Each + type of pass attachment has it corresponding view type: + + sg_attachments.colors[]: + populate with color-attachment views, e.g.: + + sg_make_view(&(sg_view_desc){ + .color_attachment = { ... }, + }); + + sg_attachments.resolves[]: + populate with resolve-attachment views, e.g.: + + sg_make_view(&(sg_view_desc){ + .resolve_attachment = { ... }, + }); + + sg_attachments.depth_stencil: + populate with depth-stencil-attachment views, e.g.: + + sg_make_view(&(sg_view_desc){ + .depth_stencil_attachment = { ... }, + }); +*/ +typedef struct sg_attachments { + sg_view colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_view resolves[SG_MAX_COLOR_ATTACHMENTS]; + sg_view depth_stencil; +} sg_attachments; + +/* + sg_pass + + The sg_pass structure is passed as argument into the sg_begin_pass() + function. + + For a swapchain render pass, provide an sg_pass_action and sg_swapchain + struct (for instance via the sglue_swapchain() helper function from + sokol_glue.h): + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .swapchain = sglue_swapchain(), + }); + + For an offscreen render pass, provide an sg_pass_action struct with + attachment view objects: + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .attachments = { + .colors = { ... }, + .resolves = { ... }, + .depth_stencil = ..., + }, + }); + + You can also omit the .action object to get default pass action behaviour + (clear to color=grey, depth=1 and stencil=0). + + For a compute pass, just set the sg_pass.compute boolean to true: + + sg_begin_pass(&(sg_pass){ .compute = true }); +*/ +typedef struct sg_pass { + uint32_t _start_canary; + bool compute; + sg_pass_action action; + sg_attachments attachments; + sg_swapchain swapchain; + const char* label; + uint32_t _end_canary; +} sg_pass; + +/* + sg_bindings + + The sg_bindings structure defines the resource bindings for + the next draw call. + + To update the resource bindings, call sg_apply_bindings() with + a pointer to a populated sg_bindings struct. Note that + sg_apply_bindings() must be called after sg_apply_pipeline() + and that bindings are not preserved across sg_apply_pipeline() + calls, even when the new pipeline uses the same 'bindings layout'. + + A resource binding struct contains: + + - 1..N vertex buffers + - 1..N vertex buffer offsets + - 0..1 index buffer + - 0..1 index buffer offset + - 0..N resource views (texture-, storage-image, storage-buffer-views) + - 0..N samplers + + Where 'N' is defined in the following constants: + + - SG_MAX_VERTEXBUFFER_BINDSLOTS + - SG_MAX_VIEW_BINDSLOTS + - SG_MAX_SAMPLER_BINDSLOTS + + Note that inside compute passes vertex- and index-buffer-bindings are + disallowed. + + When using sokol-shdc for shader authoring, the `layout(binding=N)` + for texture-, storage-image- and storage-buffer-bindings directly + maps to the views-array index, for instance the following vertex- + and fragment-shader interface for sokol-shdc: + + @vs vs + layout(binding=0) uniform vs_params { ... }; + layout(binding=0) readonly buffer ssbo { ... }; + layout(binding=1) uniform texture2D vs_tex; + layout(binding=0) uniform sampler vs_smp; + ... + @end + + @fs fs + layout(binding=1) uniform fs_params { ... }; + layout(binding=2) uniform texture2D fs_tex; + layout(binding=1) uniform sampler fs_smp; + ... + @end + + ...would map to the following sg_bindings struct: + + const sg_bindings bnd = { + .vertex_buffers[0] = ..., + .views[0] = ssbo_view, + .views[1] = vs_tex_view, + .views[2] = fs_tex_view, + .samplers[0] = vs_smp, + .samplers[1] = fs_smp, + }; + + ...alternatively you can use code-generated slot indices: + + const sg_bindings bnd = { + .vertex_buffers[0] = ..., + .views[VIEW_ssbo] = ssbo_view, + .views[VIEW_vs_tex] = vs_tex_view, + .views[VIEW_fs_tex] = fs_tex_view, + .samplers[SMP_vs_smp] = vs_smp, + .samplers[SMP_fs_smp] = fs_smp, + }; + + Resource bindslots for a specific shader/pipeline may have gaps, and an + sg_bindings struct may have populated bind slots which are not used by a + specific shader. This allows to use the same sg_bindings struct across + different shader variants. + + When not using sokol-shdc, the bindslot indices in the sg_bindings + struct need to match the per-binding reflection info slot indices + in the sg_shader_desc struct (for details about that see the + sg_shader_desc struct documentation). + + The optional buffer offsets can be used to put different unrelated + chunks of vertex- and/or index-data into the same buffer objects. +*/ +typedef struct sg_bindings { + uint32_t _start_canary; + sg_buffer vertex_buffers[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + int vertex_buffer_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_view views[SG_MAX_VIEW_BINDSLOTS]; + sg_sampler samplers[SG_MAX_SAMPLER_BINDSLOTS]; + uint32_t _end_canary; +} sg_bindings; + +/* + sg_buffer_usage + + Describes how a buffer object is going to be used: + + .vertex_buffer (default: true) + the buffer will be bound as vertex buffer via sg_bindings.vertex_buffers[] + .index_buffer (default: false) + the buffer will be bound as index buffer via sg_bindings.index_buffer + .storage_buffer (default: false) + the buffer will be bound as storage buffer via storage-buffer-view + in sg_bindings.views[] + .immutable (default: true) + the buffer content will never be updated from the CPU side (but + may be written to by a compute shader) + .dynamic_update (default: false) + the buffer content will be infrequently updated from the CPU side + .stream_upate (default: false) + the buffer content will be updated each frame from the CPU side +*/ +typedef struct sg_buffer_usage { + bool vertex_buffer; + bool index_buffer; + bool storage_buffer; + bool immutable; + bool dynamic_update; + bool stream_update; +} sg_buffer_usage; + +/* + sg_buffer_desc + + Creation parameters for sg_buffer objects, used in the sg_make_buffer() call. + + The default configuration is: + + .size: 0 (*must* be >0 for buffers without data) + .usage { .vertex_buffer = true, .immutable = true } + .data.ptr 0 (*must* be valid for immutable buffers without storage buffer usage) + .data.size 0 (*must* be > 0 for immutable buffers without storage buffer usage) + .label 0 (optional string label) + + For immutable buffers which are initialized with initial data, + keep the .size item zero-initialized, and set the size together with the + pointer to the initial data in the .data item. + + For immutable or mutable buffers without initial data, keep the .data item + zero-initialized, and set the buffer size in the .size item instead. + + You can also set both size values, but currently both size values must + be identical (this may change in the future when the dynamic resource + management may become more flexible). + + NOTE: Immutable buffers without storage-buffer-usage *must* be created + with initial content, this restriction doesn't apply to storage buffer usage, + because storage buffers may also get their initial content by running + a compute shader on them. + + NOTE: Buffers without initial data will have undefined content, e.g. + do *not* expect the buffer to be zero-initialized! + + ADVANCED TOPIC: Injecting native 3D-API buffers: + + The following struct members allow to inject your own GL, Metal + or D3D11 buffers into sokol_gfx: + + .gl_buffers[SG_NUM_INFLIGHT_FRAMES] + .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] + .d3d11_buffer + + You must still provide all other struct items except the .data item, and + these must match the creation parameters of the native buffers you provide. + For sg_buffer_desc.usage.immutable buffers, only provide a single native + 3D-API buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers + (only for GL and Metal, not D3D11). Providing multiple buffers for GL and + Metal is necessary because sokol_gfx will rotate through them when calling + sg_update_buffer() to prevent lock-stalls. + + Note that it is expected that immutable injected buffer have already been + initialized with content, and the .content member must be 0! + + Also you need to call sg_reset_state_cache() after calling native 3D-API + functions, and before calling any sokol_gfx function. +*/ +typedef struct sg_buffer_desc { + uint32_t _start_canary; + size_t size; + sg_buffer_usage usage; + sg_range data; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; + const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; + const void* d3d11_buffer; + const void* wgpu_buffer; + uint32_t _end_canary; +} sg_buffer_desc; + +/* + sg_image_usage + + Describes the intended usage of an image object: + + .storage_image (default: false) + the image can be used as parent resource of a storage-image-view, + which allows compute shaders to write to the image in a compute + pass (for read-only access in compute shaders bind the image + via a texture view instead + .color_attachment (default: false) + the image can be used as parent resource of a color-attachment-view, + which is then passed into sg_begin_pass via sg_pass.attachments.colors[] + so that fragment shaders can render into the image + .resolve_attachment (default: false) + the image can be used as parent resource of a resolve-attachment-view, + which is then passed into sg_begin_pass via sg_pass.attachments.resolves[] + as target for an MSAA-resolve operation in sg_end_pass() + .depth_stencil_attachment (default: false) + the image can be used as parent resource of a depth-stencil-attachmnet-view + which is then passes into sg_begin_pass via sg_pass.attachments.depth_stencil + as depth-stencil-buffer + .immutable (default: true) + the image content cannot be updated from the CPU side + (but may be updated by the GPU in a render- or compute-pass) + .dynamic_update (default: false) + the image content is updated infrequently by the CPU + .stream_update (default: false) + the image content is updated each frame by the CPU via + + Note that creating a texture view from the image to be used for + texture-sampling in vertex-, fragment- or compute-shaders + is always implicitly allowed. +*/ +typedef struct sg_image_usage { + bool storage_image; + bool color_attachment; + bool resolve_attachment; + bool depth_stencil_attachment; + bool immutable; + bool dynamic_update; + bool stream_update; +} sg_image_usage; + +/* + sg_view_type + + Allows to query the type of a view object via the function sg_query_view_type() +*/ +typedef enum sg_view_type { + SG_VIEWTYPE_INVALID, + SG_VIEWTYPE_STORAGEBUFFER, + SG_VIEWTYPE_STORAGEIMAGE, + SG_VIEWTYPE_TEXTURE, + SG_VIEWTYPE_COLORATTACHMENT, + SG_VIEWTYPE_RESOLVEATTACHMENT, + SG_VIEWTYPE_DEPTHSTENCILATTACHMENT, + _SG_VIEWTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_view_type; + +/* + sg_image_data + + Defines the content of an image through an array of sg_range structs, each + range pointing to the pixel data for one mip-level. For array-, cubemap- and + 3D-images each mip-level contains all slice-surfaces for that mip-level in a + single tightly packed memory block. + + The size of a single surface in a mip-level for a regular 2D texture + can be computed via: + + sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + For array- and 3d-images the size of a single miplevel is: + + num_slices * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + For cubemap-images the size of a single mip-level is: + + 6 * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + The order of cubemap-faces is in a mip-level data chunk is: + + [0] => +X + [1] => -X + [2] => +Y + [3] => -Y + [4] => +Z + [5] => -Z +*/ +typedef struct sg_image_data { + sg_range mip_levels[SG_MAX_MIPMAPS]; +} sg_image_data; + +/* + sg_image_desc + + Creation parameters for sg_image objects, used in the sg_make_image() call. + + The default configuration is: + + .type SG_IMAGETYPE_2D + .usage .immutable = true + .width 0 (must be set to >0) + .height 0 (must be set to >0) + .num_slices 1 (3D textures: depth; array textures: number of layers) + .num_mipmaps 1 + .pixel_format SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.environment.defaults.color_format for render targets + .sample_count 1 for textures, or sg_desc.environment.defaults.sample_count for render targets + .data an sg_image_data struct to define the initial content + .label 0 (optional string label for trace hooks) + + Q: Why is the default sample_count for render targets identical with the + "default sample count" from sg_desc.environment.defaults.sample_count? + + A: So that it matches the default sample count in pipeline objects. Even + though it is a bit strange/confusing that offscreen render targets by default + get the same sample count as 'default swapchains', but it's better that + an offscreen render target created with default parameters matches + a pipeline object created with default parameters. + + NOTE: + + Regular images used as texture binding with usage.immutable must be fully + initialized by providing a valid .data member which points to initialization + data. + + Images with usage.*_attachment or usage.storage_image must + *not* be created with initial content. Be aware that the initial + content of pass attachment and storage images is undefined + (not guaranteed to be zeroed). + + ADVANCED TOPIC: Injecting native 3D-API textures: + + The following struct members allow to inject your own GL, Metal or D3D11 + textures into sokol_gfx: + + .gl_textures[SG_NUM_INFLIGHT_FRAMES] + .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .d3d11_texture + .wgpu_texture + + For GL, you can also specify the texture target or leave it empty to use + the default texture target for the image type (GL_TEXTURE_2D for + SG_IMAGETYPE_2D etc) + + The same rules apply as for injecting native buffers (see sg_buffer_desc + documentation for more details). +*/ +typedef struct sg_image_desc { + uint32_t _start_canary; + sg_image_type type; + sg_image_usage usage; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_pixel_format pixel_format; + int sample_count; + sg_image_data data; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; + uint32_t gl_texture_target; + const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; + const void* d3d11_texture; + const void* wgpu_texture; + uint32_t _end_canary; +} sg_image_desc; + +/* + sg_sampler_desc + + Creation parameters for sg_sampler objects, used in the sg_make_sampler() call + + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .mipmap_filter SG_FILTER_NEAREST + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .min_lod 0.0f + .max_lod FLT_MAX + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .compare SG_COMPAREFUNC_NEVER + .max_anisotropy 1 (must be 1..16) +*/ +typedef struct sg_sampler_desc { + uint32_t _start_canary; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_sampler; + const void* mtl_sampler; + const void* d3d11_sampler; + const void* wgpu_sampler; + uint32_t _end_canary; +} sg_sampler_desc; + +/* + sg_shader_desc + + Used as parameter of sg_make_shader() to create a shader object which + communicates shader source or bytecode and shader interface + reflection information to sokol-gfx. + + If you use sokol-shdc you can ignore the following information since + the sg_shader_desc struct will be code-generated. + + Otherwise you need to provide the following information to the + sg_make_shader() call: + + - a vertex- and fragment-shader function: + - the shader source or bytecode + - an optional entry point name + - for D3D11: an optional compile target when source code is provided + (the defaults are "vs_4_0" and "ps_4_0") + + - ...or alternatively, a compute function: + - the shader source or bytecode + - an optional entry point name + - for D3D11: an optional compile target when source code is provided + (the default is "cs_5_0") + + - vertex attributes required by some backends (not for compute shaders): + - the vertex attribute base type (undefined, float, signed int, unsigned int), + this information is only used in the validation layer to check that the + pipeline object vertex formats are compatible with the input vertex attribute + type used in the vertex shader. NOTE that the default base type + 'undefined' skips the validation layer check. + - for the GL backend: optional vertex attribute names used for name lookup + - for the D3D11 backend: semantic names and indices + + - only for compute shaders on the Metal backend: + - the workgroup size aka 'threads per thread-group' + + In other 3D APIs this is declared in the shader code: + - GLSL: `layout(local_size_x=x, local_size_y=y, local_size_y=z) in;` + - HLSL: `[numthreads(x, y, z)]` + - WGSL: `@workgroup_size(x, y, z)` + ...but in Metal the workgroup size is declared on the CPU side + + - reflection information for each uniform block binding used by the shader: + - the shader stage the uniform block appears in (SG_SHADERSTAGE_*) + - the size in bytes of the uniform block + - backend-specific bindslots: + - HLSL: the constant buffer register `register(b0..7)` + - MSL: the buffer attribute `[[buffer(0..7)]]` + - WGSL: the binding in `@group(0) @binding(0..15)` + - GLSL only: a description of the uniform block interior + - the memory layout standard (SG_UNIFORMLAYOUT_*) + - for each member in the uniform block: + - the member type (SG_UNIFORM_*) + - if the member is an array, the array count + - the member name + + - reflection information for each texture-, storage-buffer and + storage-image bindings by the shader, each with an associated + view type: + - texture bindings => texture views + - storage-buffer bindings => storage-buffer views + - storage-image bindings => storage-image views + + - texture bindings must provide the following information: + - the shader stage the texture binding appears in (SG_SHADERSTAGE_*) + - the image type (SG_IMAGETYPE_*) + - the image-sample type (SG_IMAGESAMPLETYPE_*) + - whether the texture is multisampled + - backend specific bindslots: + - HLSL: the texture register `register(t0..31)` + - MSL: the texture attribute `[[texture(0..31)]]` + - WGSL: the binding in `@group(1) @binding(0..127)` + + - storage-buffer bindings must provide the following information: + - the shader stage the storage buffer appears in (SG_SHADERSTAGE_*) + - whether the storage buffer is readonly + - backend specific bindslots: + - HLSL: + - for storage buffer bindings: `register(t0..31)` + - for read/write storage buffer bindings: `register(u0..31)` + - MSL: the buffer attribute `[[buffer(8..23)]]` + - WGSL: the binding in `@group(1) @binding(0..127)` + - GL: the binding in `layout(binding=0..sg_limits.max_storage_buffer_bindings_per_stage)` + + - storage-image bindings must provide the following information: + - the shader stage (*must* be SG_SHADERSTAGE_COMPUTE) + - whether the storage image is writeonly or readwrite (for readonly + access use a regular texture binding instead) + - the image type expected by the shader (SG_IMAGETYPE_*) + - the access pixel format expected by the shader (SG_PIXELFORMAT_*), + note that only a subset of pixel formats is allowed for storage image + bindings + - backend specific bindslots: + - HLSL: the UAV register `register(u0..31)` + - MSL: the texture attribute `[[texture(0..31)]]` + - WGSL: the binding in `@group(1) @binding(0..127)` + - GLSL: the binding in `layout(binding=0..sg_imits.max_storage_buffer_bindings_per_stage, [access_format])` + + - reflection information for each sampler used by the shader: + - the shader stage the sampler appears in (SG_SHADERSTAGE_*) + - the sampler type (SG_SAMPLERTYPE_*) + - backend specific bindslots: + - HLSL: the sampler register `register(s0..11)` + - MSL: the sampler attribute `[[sampler(0..11)]]` + - WGSL: the binding in `@group(0) @binding(0..127)` + + - reflection information for each texture-sampler pair used by + the shader: + - the shader stage (SG_SHADERSTAGE_*) + - the texture's array index in the sg_shader_desc.views[] array + - the sampler's array index in the sg_shader_desc.samplers[] array + - GLSL only: the name of the combined image-sampler object + + The number and order of items in the sg_shader_desc.attrs[] + array corresponds to the items in sg_pipeline_desc.layout.attrs. + + - sg_shader_desc.attrs[N] => sg_pipeline_desc.layout.attrs[N] + + NOTE that vertex attribute indices currently cannot have gaps. + + The items index in the sg_shader_desc.uniform_blocks[] array corresponds + to the ub_slot arg in sg_apply_uniforms(): + + - sg_shader_desc.uniform_blocks[N] => sg_apply_uniforms(N, ...) + + The items in the sg_shader_desc.views[] array directly map to + the views in the sg_bindings.views[] array! + + For all GL backends, shader source-code must be provided. For D3D11 and Metal, + either shader source-code or byte-code can be provided. + + NOTE that the uniform-block, view and sampler arrays may have gaps. This + allows to use the same sg_bindings struct for different but related + shader variations. + + For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded + on demand. If this fails, shader creation will fail. When compiling HLSL + source code, you can provide an optional target string via + sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the + vertex shader stage and "ps_4_0" for the pixel shader stage. + You may optionally provide the file path to enable the default #include handler + behavior when compiling source code. +*/ +typedef enum sg_shader_stage { + SG_SHADERSTAGE_NONE, + SG_SHADERSTAGE_VERTEX, + SG_SHADERSTAGE_FRAGMENT, + SG_SHADERSTAGE_COMPUTE, + _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF, +} sg_shader_stage; + +typedef struct sg_shader_function { + const char* source; + sg_range bytecode; + const char* entry; + const char* d3d11_target; // default: "vs_4_0" or "ps_4_0" + const char* d3d11_filepath; +} sg_shader_function; + +typedef enum sg_shader_attr_base_type { + SG_SHADERATTRBASETYPE_UNDEFINED, + SG_SHADERATTRBASETYPE_FLOAT, + SG_SHADERATTRBASETYPE_SINT, + SG_SHADERATTRBASETYPE_UINT, + _SG_SHADERATTRBASETYPE_FORCE_U32 = 0x7FFFFFFF, +} sg_shader_attr_base_type; + +typedef struct sg_shader_vertex_attr { + sg_shader_attr_base_type base_type; // default: UNDEFINED (disables validation) + const char* glsl_name; // [optional] GLSL attribute name + const char* hlsl_sem_name; // HLSL semantic name + uint8_t hlsl_sem_index; // HLSL semantic index +} sg_shader_vertex_attr; + +typedef struct sg_glsl_shader_uniform { + sg_uniform_type type; + uint16_t array_count; // 0 or 1 for scalars, >1 for arrays + const char* glsl_name; // glsl name binding is required on GL 4.1 and WebGL2 +} sg_glsl_shader_uniform; + +typedef struct sg_shader_uniform_block { + sg_shader_stage stage; + uint32_t size; + uint8_t hlsl_register_b_n; // HLSL register(bn) + uint8_t msl_buffer_n; // MSL [[buffer(n)]] + uint8_t wgsl_group0_binding_n; // WGSL @group(0) @binding(n) + uint8_t spirv_set0_binding_n; // Vulkan GLSL layout(set=0, binding=n) + sg_uniform_layout layout; + sg_glsl_shader_uniform glsl_uniforms[SG_MAX_UNIFORMBLOCK_MEMBERS]; +} sg_shader_uniform_block; + +typedef struct sg_shader_texture_view { + sg_shader_stage stage; + sg_image_type image_type; + sg_image_sample_type sample_type; + bool multisampled; + uint8_t hlsl_register_t_n; // HLSL register(tn) bind slot + uint8_t msl_texture_n; // MSL [[texture(n)]] bind slot + uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot + uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0) +} sg_shader_texture_view; + +typedef struct sg_shader_storage_buffer_view { + sg_shader_stage stage; + bool readonly; + uint8_t hlsl_register_t_n; // HLSL register(tn) bind slot (for readonly access) + uint8_t hlsl_register_u_n; // HLSL register(un) bind slot (for read/write access) + uint8_t msl_buffer_n; // MSL [[buffer(n)]] bind slot + uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot + uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0) + uint8_t glsl_binding_n; // GLSL layout(binding=n) +} sg_shader_storage_buffer_view; + +typedef struct sg_shader_storage_image_view { + sg_shader_stage stage; + sg_image_type image_type; + sg_pixel_format access_format; // shader-access pixel format + bool writeonly; // false means read/write access + uint8_t hlsl_register_u_n; // HLSL register(un) bind slot + uint8_t msl_texture_n; // MSL [[texture(n)]] bind slot + uint8_t wgsl_group1_binding_n; // WGSL @group(2) @binding(n) bind slot + uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0) + uint8_t glsl_binding_n; // GLSL layout(binding=n) +} sg_shader_storage_image_view; + +typedef struct sg_shader_view { + sg_shader_texture_view texture; + sg_shader_storage_buffer_view storage_buffer; + sg_shader_storage_image_view storage_image; +} sg_shader_view; + +typedef struct sg_shader_sampler { + sg_shader_stage stage; + sg_sampler_type sampler_type; + uint8_t hlsl_register_s_n; // HLSL register(sn) bind slot + uint8_t msl_sampler_n; // MSL [[sampler(n)]] bind slot + uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot + uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0) +} sg_shader_sampler; + +typedef struct sg_shader_texture_sampler_pair { + sg_shader_stage stage; + uint8_t view_slot; // must be SG_VIEWTYPE_TEXTURE + uint8_t sampler_slot; + const char* glsl_name; // glsl name binding required because of GL 4.1 and WebGL2 +} sg_shader_texture_sampler_pair; + +typedef struct sg_mtl_shader_threads_per_threadgroup { + int x, y, z; +} sg_mtl_shader_threads_per_threadgroup; + +typedef struct sg_shader_desc { + uint32_t _start_canary; + sg_shader_function vertex_func; + sg_shader_function fragment_func; + sg_shader_function compute_func; + sg_shader_vertex_attr attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_uniform_block uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + sg_shader_view views[SG_MAX_VIEW_BINDSLOTS]; + sg_shader_sampler samplers[SG_MAX_SAMPLER_BINDSLOTS]; + sg_shader_texture_sampler_pair texture_sampler_pairs[SG_MAX_TEXTURE_SAMPLER_PAIRS]; + sg_mtl_shader_threads_per_threadgroup mtl_threads_per_threadgroup; + const char* label; + uint32_t _end_canary; +} sg_shader_desc; + +/* + sg_pipeline_desc + + The sg_pipeline_desc struct defines all creation parameters for an + sg_pipeline object, used as argument to the sg_make_pipeline() function: + + Pipeline objects come in two flavours: + + - render pipelines for use in render passes + - compute pipelines for use in compute passes + + A compute pipeline only requires a compute shader object but no + 'render state', while a render pipeline requires a vertex/fragment shader + object and additional render state declarations: + + - the vertex layout for all input vertex buffers + - a shader object + - the 3D primitive type (points, lines, triangles, ...) + - the index type (none, 16- or 32-bit) + - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...) + + If the vertex data has no gaps between vertex components, you can omit + the .layout.buffers[].stride and layout.attrs[].offset items (leave them + default-initialized to 0), sokol-gfx will then compute the offsets and + strides from the vertex component formats (.layout.attrs[].format). + Please note that ALL vertex attribute offsets must be 0 in order for the + automatic offset computation to kick in. + + Note that if you use vertex-pulling from storage buffers instead of + fixed-function vertex input you can simply omit the entire nested .layout + struct. + + The default configuration is as follows: + + .compute: false (must be set to true for a compute pipeline) + .shader: 0 (must be initialized with a valid sg_shader id!) + .layout: + .buffers[]: vertex buffer layouts + .stride: 0 (if no stride is given it will be computed) + .step_func SG_VERTEXSTEP_PER_VERTEX + .step_rate 1 + .attrs[]: vertex attribute declarations + .buffer_index 0 the vertex buffer bind slot + .offset 0 (offsets can be omitted if the vertex layout has no gaps) + .format SG_VERTEXFORMAT_INVALID (must be initialized!) + .depth: + .pixel_format: sg_desc.context.depth_format + .compare: SG_COMPAREFUNC_ALWAYS + .write_enabled: false + .bias: 0.0f + .bias_slope_scale: 0.0f + .bias_clamp: 0.0f + .stencil: + .enabled: false + .front/back: + .compare: SG_COMPAREFUNC_ALWAYS + .fail_op: SG_STENCILOP_KEEP + .depth_fail_op: SG_STENCILOP_KEEP + .pass_op: SG_STENCILOP_KEEP + .read_mask: 0 + .write_mask: 0 + .ref: 0 + .color_count 1 + .colors[0..color_count] + .pixel_format sg_desc.context.color_format + .write_mask: SG_COLORMASK_RGBA + .blend: + .enabled: false + .src_factor_rgb: SG_BLENDFACTOR_ONE + .dst_factor_rgb: SG_BLENDFACTOR_ZERO + .op_rgb: SG_BLENDOP_ADD + .src_factor_alpha: SG_BLENDFACTOR_ONE + .dst_factor_alpha: SG_BLENDFACTOR_ZERO + .op_alpha: SG_BLENDOP_ADD + .primitive_type: SG_PRIMITIVETYPE_TRIANGLES + .index_type: SG_INDEXTYPE_NONE + .cull_mode: SG_CULLMODE_NONE + .face_winding: SG_FACEWINDING_CW + .sample_count: sg_desc.context.sample_count + .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f } + .alpha_to_coverage_enabled: false + .label 0 (optional string label for trace hooks) +*/ +typedef struct sg_vertex_buffer_layout_state { + int stride; + sg_vertex_step step_func; + int step_rate; +} sg_vertex_buffer_layout_state; + +typedef struct sg_vertex_attr_state { + int buffer_index; + int offset; + sg_vertex_format format; +} sg_vertex_attr_state; + +typedef struct sg_vertex_layout_state { + sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_vertex_layout_state; + +typedef struct sg_stencil_face_state { + sg_compare_func compare; + sg_stencil_op fail_op; + sg_stencil_op depth_fail_op; + sg_stencil_op pass_op; +} sg_stencil_face_state; + +typedef struct sg_stencil_state { + bool enabled; + sg_stencil_face_state front; + sg_stencil_face_state back; + uint8_t read_mask; + uint8_t write_mask; + uint8_t ref; +} sg_stencil_state; + +typedef struct sg_depth_state { + sg_pixel_format pixel_format; + sg_compare_func compare; + bool write_enabled; + float bias; + float bias_slope_scale; + float bias_clamp; +} sg_depth_state; + +typedef struct sg_blend_state { + bool enabled; + sg_blend_factor src_factor_rgb; + sg_blend_factor dst_factor_rgb; + sg_blend_op op_rgb; + sg_blend_factor src_factor_alpha; + sg_blend_factor dst_factor_alpha; + sg_blend_op op_alpha; +} sg_blend_state; + +typedef struct sg_color_target_state { + sg_pixel_format pixel_format; + sg_color_mask write_mask; + sg_blend_state blend; +} sg_color_target_state; + +typedef struct sg_pipeline_desc { + uint32_t _start_canary; + bool compute; + sg_shader shader; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_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; + sg_color blend_color; + bool alpha_to_coverage_enabled; + const char* label; + uint32_t _end_canary; +} sg_pipeline_desc; + +/* + sg_view_desc + + Creation params for sg_view objects, passed into sg_make_view() calls. + + View objects are passed into sg_apply_bindings() (for texture-, storage-buffer- + and storage-image views), and sg_begin_pass() (for color-, resolve- + and depth-stencil-attachment views). + + The view type is determined by initializing one of the sub-structs of + sg_view_desc: + + .texture a texture-view object will be created + .image the sg_image parent resource + .mip_levels optional mip-level range, keep zero-initialized for the + entire mipmap chain + .base the first mip level + .count number of mip levels, keeping this zero-initialized means + 'all remaining mip levels' + .slices optional slice range, keep zero-initialized to include + all slices + .base the first slice + .count number of slices, keeping this zero-initializied means 'all remaining slices' + + .storage_buffer a storage-buffer-view object will be created + .buffer the sg_buffer parent resource, must have been created + with `sg_buffer_desc.usage.storage_buffer = true` + .offset optional 256-byte aligned byte-offset into the buffer + + .storage_image a storage-image-view object will be created + .image the sg_image parent resource, must have been created + with `sg_image_desc.usage.storage_image = true` + .mip_level selects the mip-level for the compute shader to write + .slice selects the slice for the compute shader to write + + .color_attachment a color-attachment-view object will be created + .image the sg_image parent resource, must have been created + with `sg_image_desc.usage.color_attachment = true` + .mip_level selects the mip-level to render into + .slice selects the slice to render into + + .resolve_attachment a resolve-attachment-view object will be created + .image the sg_image parent resource, must have been created + with `sg_image_desc.usage.resolve_attachment = true` + .mip_level selects the mip-level to msaa-resolve into + .slice selects the slice to msaa-resolve into + + .depth_stencil_attachment a depth-stencil-attachment-view object will be created + .image the sg_image parent resource, must have been created + with `sg_image_desc.usage.depth_stencil_attachment = true` + .mip_level selects the mip-level to render into + .slice selects the slice to render into +*/ +typedef struct sg_buffer_view_desc { + sg_buffer buffer; + int offset; +} sg_buffer_view_desc; + +typedef struct sg_image_view_desc { + sg_image image; + int mip_level; + int slice; // cube texture: face; array texture: layer; 3D texture: depth-slice +} sg_image_view_desc; + +typedef struct sg_texture_view_range { + int base; + int count; +} sg_texture_view_range; + +typedef struct sg_texture_view_desc { + sg_image image; + sg_texture_view_range mip_levels; + sg_texture_view_range slices; // cube texture: face; array texture: layer; 3D texture: depth-slice +} sg_texture_view_desc; + +typedef struct sg_view_desc { + uint32_t _start_canary; + sg_texture_view_desc texture; + sg_buffer_view_desc storage_buffer; + sg_image_view_desc storage_image; + sg_image_view_desc color_attachment; + sg_image_view_desc resolve_attachment; + sg_image_view_desc depth_stencil_attachment; + const char* label; + uint32_t _end_canary; +} sg_view_desc; + +/* + sg_trace_hooks + + Installable callback functions to keep track of the sokol-gfx calls, + this is useful for debugging, or keeping track of resource creation + and destruction. + + Trace hooks are installed with sg_install_trace_hooks(), this returns + another sg_trace_hooks struct with the previous set of + trace hook function pointers. These should be invoked by the + new trace hooks to form a proper call chain. +*/ +typedef struct sg_trace_hooks { + void* user_data; + void (*reset_state_cache)(void* user_data); + void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); + void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data); + void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); + void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); + void (*make_view)(const sg_view_desc* desc, sg_view result, void* user_data); + void (*destroy_buffer)(sg_buffer buf, void* user_data); + void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_sampler)(sg_sampler smp, void* user_data); + void (*destroy_shader)(sg_shader shd, void* user_data); + void (*destroy_pipeline)(sg_pipeline pip, void* user_data); + void (*destroy_view)(sg_view view, void* user_data); + void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data); + void (*update_image)(sg_image img, const sg_image_data* data, void* user_data); + void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data); + void (*begin_pass)(const sg_pass* pass, void* user_data); + void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_pipeline)(sg_pipeline pip, void* user_data); + void (*apply_bindings)(const sg_bindings* bindings, void* user_data); + void (*apply_uniforms)(int ub_index, const sg_range* data, void* user_data); + void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*draw_ex)(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance, void* user_data); + void (*dispatch)(int num_groups_x, int num_groups_y, int num_groups_z, void* user_data); + void (*end_pass)(void* user_data); + void (*commit)(void* user_data); + void (*alloc_buffer)(sg_buffer result, void* user_data); + void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_sampler)(sg_sampler result, void* user_data); + void (*alloc_shader)(sg_shader result, void* user_data); + void (*alloc_pipeline)(sg_pipeline result, void* user_data); + void (*alloc_view)(sg_view result, void* user_data); + void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); + void (*dealloc_image)(sg_image img_id, void* user_data); + void (*dealloc_sampler)(sg_sampler smp_id, void* user_data); + void (*dealloc_shader)(sg_shader shd_id, void* user_data); + void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); + void (*dealloc_view)(sg_view view_id, void* user_data); + void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); + void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_sampler)(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data); + void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); + void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); + void (*init_view)(sg_view view_id, const sg_view_desc* desc, void* user_data); + void (*uninit_buffer)(sg_buffer buf_id, void* user_data); + void (*uninit_image)(sg_image img_id, void* user_data); + void (*uninit_sampler)(sg_sampler smp_id, void* user_data); + void (*uninit_shader)(sg_shader shd_id, void* user_data); + void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); + void (*uninit_view)(sg_view view_id, void* user_data); + void (*fail_buffer)(sg_buffer buf_id, void* user_data); + void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_sampler)(sg_sampler smp_id, void* user_data); + void (*fail_shader)(sg_shader shd_id, void* user_data); + void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); + void (*fail_view)(sg_view view_id, void* user_data); + void (*push_debug_group)(const char* name, void* user_data); + void (*pop_debug_group)(void* user_data); +} sg_trace_hooks; + +/* + sg_buffer_info + sg_image_info + sg_sampler_info + sg_shader_info + sg_pipeline_info + sg_view_info + + These structs contain various internal resource attributes which + might be useful for debug-inspection. Please don't rely on the + actual content of those structs too much, as they are quite closely + tied to sokol_gfx.h internals and may change more frequently than + the other public API elements. + + The *_info structs are used as the return values of the following functions: + + sg_query_buffer_info() + sg_query_image_info() + sg_query_sampler_info() + sg_query_shader_info() + sg_query_pipeline_info() + sg_query_view_info() +*/ +typedef struct sg_slot_info { + sg_resource_state state; // the current state of this resource slot + uint32_t res_id; // type-neutral resource if (e.g. sg_buffer.id) + uint32_t uninit_count; +} sg_slot_info; + +typedef struct sg_buffer_info { + sg_slot_info slot; // resource pool slot info + uint32_t update_frame_index; // frame index of last sg_update_buffer() + uint32_t append_frame_index; // frame index of last sg_append_buffer() + int append_pos; // current position in buffer for sg_append_buffer() + bool append_overflow; // is buffer in overflow state (due to sg_append_buffer) + int num_slots; // number of renaming-slots for dynamically updated buffers + int active_slot; // currently active write-slot for dynamically updated buffers +} sg_buffer_info; + +typedef struct sg_image_info { + sg_slot_info slot; // resource pool slot 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 +} sg_image_info; + +typedef struct sg_sampler_info { + sg_slot_info slot; // resource pool slot info +} sg_sampler_info; + +typedef struct sg_shader_info { + sg_slot_info slot; // resource pool slot info +} sg_shader_info; + +typedef struct sg_pipeline_info { + sg_slot_info slot; // resource pool slot info +} sg_pipeline_info; + +typedef struct sg_view_info { + sg_slot_info slot; // resource pool slot info +} sg_view_info; + +/* + sg_stats + + Allows to track generic and backend-specific rendering stats, + obtained via sg_query_stats(). +*/ +typedef struct sg_frame_stats_gl { + uint32_t num_bind_buffer; + uint32_t num_active_texture; + uint32_t num_bind_texture; + uint32_t num_bind_sampler; + uint32_t num_bind_image_texture; + uint32_t num_use_program; + uint32_t num_render_state; + uint32_t num_vertex_attrib_pointer; + uint32_t num_vertex_attrib_divisor; + uint32_t num_enable_vertex_attrib_array; + uint32_t num_disable_vertex_attrib_array; + uint32_t num_uniform; + uint32_t num_memory_barriers; +} sg_frame_stats_gl; + +typedef struct sg_frame_stats_d3d11_pass { + uint32_t num_om_set_render_targets; + uint32_t num_clear_render_target_view; + uint32_t num_clear_depth_stencil_view; + uint32_t num_resolve_subresource; +} sg_frame_stats_d3d11_pass; + +typedef struct sg_frame_stats_d3d11_pipeline { + uint32_t num_rs_set_state; + uint32_t num_om_set_depth_stencil_state; + uint32_t num_om_set_blend_state; + uint32_t num_ia_set_primitive_topology; + uint32_t num_ia_set_input_layout; + uint32_t num_vs_set_shader; + uint32_t num_vs_set_constant_buffers; + uint32_t num_ps_set_shader; + uint32_t num_ps_set_constant_buffers; + uint32_t num_cs_set_shader; + uint32_t num_cs_set_constant_buffers; +} sg_frame_stats_d3d11_pipeline; + +typedef struct sg_frame_stats_d3d11_bindings { + uint32_t num_ia_set_vertex_buffers; + uint32_t num_ia_set_index_buffer; + uint32_t num_vs_set_shader_resources; + uint32_t num_vs_set_samplers; + uint32_t num_ps_set_shader_resources; + uint32_t num_ps_set_samplers; + uint32_t num_cs_set_shader_resources; + uint32_t num_cs_set_samplers; + uint32_t num_cs_set_unordered_access_views; +} sg_frame_stats_d3d11_bindings; + +typedef struct sg_frame_stats_d3d11_uniforms { + uint32_t num_update_subresource; +} sg_frame_stats_d3d11_uniforms; + +typedef struct sg_frame_stats_d3d11_draw { + uint32_t num_draw_indexed_instanced; + uint32_t num_draw_indexed; + uint32_t num_draw_instanced; + uint32_t num_draw; +} sg_frame_stats_d3d11_draw; + +typedef struct sg_frame_stats_d3d11 { + sg_frame_stats_d3d11_pass pass; + sg_frame_stats_d3d11_pipeline pipeline; + sg_frame_stats_d3d11_bindings bindings; + sg_frame_stats_d3d11_uniforms uniforms; + sg_frame_stats_d3d11_draw draw; + uint32_t num_map; + uint32_t num_unmap; +} sg_frame_stats_d3d11; + +typedef struct sg_frame_stats_metal_idpool { + uint32_t num_added; + uint32_t num_released; + uint32_t num_garbage_collected; +} sg_frame_stats_metal_idpool; + +typedef struct sg_frame_stats_metal_pipeline { + uint32_t num_set_blend_color; + uint32_t num_set_cull_mode; + uint32_t num_set_front_facing_winding; + uint32_t num_set_stencil_reference_value; + uint32_t num_set_depth_bias; + uint32_t num_set_render_pipeline_state; + uint32_t num_set_depth_stencil_state; +} sg_frame_stats_metal_pipeline; + +typedef struct sg_frame_stats_metal_bindings { + uint32_t num_set_vertex_buffer; + uint32_t num_set_vertex_buffer_offset; + uint32_t num_skip_redundant_vertex_buffer; + uint32_t num_set_vertex_texture; + uint32_t num_skip_redundant_vertex_texture; + uint32_t num_set_vertex_sampler_state; + uint32_t num_skip_redundant_vertex_sampler_state; + uint32_t num_set_fragment_buffer; + uint32_t num_set_fragment_buffer_offset; + uint32_t num_skip_redundant_fragment_buffer; + uint32_t num_set_fragment_texture; + uint32_t num_skip_redundant_fragment_texture; + uint32_t num_set_fragment_sampler_state; + uint32_t num_skip_redundant_fragment_sampler_state; + uint32_t num_set_compute_buffer; + uint32_t num_set_compute_buffer_offset; + uint32_t num_skip_redundant_compute_buffer; + uint32_t num_set_compute_texture; + uint32_t num_skip_redundant_compute_texture; + uint32_t num_set_compute_sampler_state; + uint32_t num_skip_redundant_compute_sampler_state; +} sg_frame_stats_metal_bindings; + +typedef struct sg_frame_stats_metal_uniforms { + uint32_t num_set_vertex_buffer_offset; + uint32_t num_set_fragment_buffer_offset; + uint32_t num_set_compute_buffer_offset; +} sg_frame_stats_metal_uniforms; + +typedef struct sg_frame_stats_metal { + sg_frame_stats_metal_idpool idpool; + sg_frame_stats_metal_pipeline pipeline; + sg_frame_stats_metal_bindings bindings; + sg_frame_stats_metal_uniforms uniforms; +} sg_frame_stats_metal; + +typedef struct sg_frame_stats_wgpu_uniforms { + uint32_t num_set_bindgroup; + uint32_t size_write_buffer; +} sg_frame_stats_wgpu_uniforms; + +typedef struct sg_frame_stats_wgpu_bindings { + uint32_t num_set_vertex_buffer; + uint32_t num_skip_redundant_vertex_buffer; + uint32_t num_set_index_buffer; + uint32_t num_skip_redundant_index_buffer; + uint32_t num_create_bindgroup; + uint32_t num_discard_bindgroup; + uint32_t num_set_bindgroup; + uint32_t num_skip_redundant_bindgroup; + uint32_t num_bindgroup_cache_hits; + uint32_t num_bindgroup_cache_misses; + uint32_t num_bindgroup_cache_collisions; + uint32_t num_bindgroup_cache_invalidates; + uint32_t num_bindgroup_cache_hash_vs_key_mismatch; +} sg_frame_stats_wgpu_bindings; + +typedef struct sg_frame_stats_wgpu { + sg_frame_stats_wgpu_uniforms uniforms; + sg_frame_stats_wgpu_bindings bindings; +} sg_frame_stats_wgpu; + +typedef struct sg_frame_stats_vk { + uint32_t num_cmd_pipeline_barrier; + uint32_t num_allocate_memory; + uint32_t num_free_memory; + uint32_t size_allocate_memory; + uint32_t num_delete_queue_added; + uint32_t num_delete_queue_collected; + uint32_t num_cmd_copy_buffer; + uint32_t num_cmd_copy_buffer_to_image; + uint32_t num_cmd_set_descriptor_buffer_offsets; + uint32_t size_descriptor_buffer_writes; +} sg_frame_stats_vk; + +typedef struct sg_frame_resource_stats { + uint32_t allocated; // number of allocated objects in current frame + uint32_t deallocated; // number of deallocated object in current frame + uint32_t inited; // number of initialized objects in current frame + uint32_t uninited; // number of deinitialized objects in current frame +} sg_frame_resource_stats; + +typedef struct sg_total_resource_stats { + uint32_t alive; // number of live objects in pool + uint32_t free; // number of free objects in pool + uint32_t allocated; // total number of object allocations + uint32_t deallocated; // total number of object deallocations + uint32_t inited; // total number of object initializations + uint32_t uninited; // total number of object deinitializations +} sg_total_resource_stats; + +typedef struct sg_total_stats { + sg_total_resource_stats buffers; + sg_total_resource_stats images; + sg_total_resource_stats samplers; + sg_total_resource_stats views; + sg_total_resource_stats shaders; + sg_total_resource_stats pipelines; +} sg_total_stats; + +typedef struct sg_frame_stats { + uint32_t frame_index; // current frame counter, starts at 0 + + uint32_t num_passes; + uint32_t num_apply_viewport; + uint32_t num_apply_scissor_rect; + uint32_t num_apply_pipeline; + uint32_t num_apply_bindings; + uint32_t num_apply_uniforms; + uint32_t num_draw; + uint32_t num_draw_ex; + uint32_t num_dispatch; + uint32_t num_update_buffer; + uint32_t num_append_buffer; + uint32_t num_update_image; + + uint32_t size_apply_uniforms; + uint32_t size_update_buffer; + uint32_t size_append_buffer; + uint32_t size_update_image; + + sg_frame_resource_stats buffers; + sg_frame_resource_stats images; + sg_frame_resource_stats samplers; + sg_frame_resource_stats views; + sg_frame_resource_stats shaders; + sg_frame_resource_stats pipelines; + + sg_frame_stats_gl gl; + sg_frame_stats_d3d11 d3d11; + sg_frame_stats_metal metal; + sg_frame_stats_wgpu wgpu; + sg_frame_stats_vk vk; +} sg_frame_stats; + +typedef struct sg_stats { + sg_frame_stats prev_frame; + sg_frame_stats cur_frame; + sg_total_stats total; +} sg_stats; + +/* + sg_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. Note that these messages are only + visible when a logger function is installed in the sg_setup() call. +*/ +#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_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE, "GLSL storage buffer bindslot is out of range (sg_limits.max_storage_buffer_bindings_per_stage) (gl)") \ + _SG_LOGITEM_XMACRO(GL_STORAGEIMAGE_GLSL_BINDING_OUT_OF_RANGE, "GLSL storage image bindslot is out of range (sg.limits.max_storage_image_bindings_per_stage) (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; NOTE: may be caused by GL driver's GLSL compiler removing unused globals") \ + _SG_LOGITEM_XMACRO(GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER, "uniform block name not found in shader; NOTE: may be caused by GL driver's GLSL compiler removing unused globals") \ + _SG_LOGITEM_XMACRO(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER, "image-sampler name not found in shader; NOTE: may be caused by GL driver's GLSL compiler removing unused globals") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNDEFINED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNDEFINED (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNSUPPORTED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNSUPPORTED (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNKNOWN, "framebuffer completeness check failed (unknown reason) (gl)") \ + _SG_LOGITEM_XMACRO(D3D11_FEATURE_LEVEL_0_DETECTED, "D3D11 Feature Level 0 device detected, this restricts the number of UAV slots to 8! (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_SRV_FAILED, "CreateShaderResourceView() failed for storage buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_UAV_FAILED, "CreateUnorderedAccessView() failed for storage buffer (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_UNIFORMBLOCK_HLSL_REGISTER_B_OUT_OF_RANGE, "sg_shader_desc.uniform_blocks[].hlsl_register_b_n is out of range (must be 0..7)") \ + _SG_LOGITEM_XMACRO(D3D11_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE, "sg_shader_desc.views[].storage_buffer.hlsl_register_t_n is out of range (must be 0..31)") \ + _SG_LOGITEM_XMACRO(D3D11_STORAGEBUFFER_HLSL_REGISTER_U_OUT_OF_RANGE, "sg_shader_desc.views[].storage_buffer.hlsl_register_u_n is out of range (must be 0..31)") \ + _SG_LOGITEM_XMACRO(D3D11_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE, "sg_shader_desc.views[].texture.hlsl_register_t_n is out of range (must be 0..31)") \ + _SG_LOGITEM_XMACRO(D3D11_STORAGEIMAGE_HLSL_REGISTER_U_OUT_OF_RANGE, "sg_shader_desc.views[].storage_image.hlsl_register_u_n is out of range (must be 0..31)") \ + _SG_LOGITEM_XMACRO(D3D11_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE, "sampler 'hlsl_register_s_n' is out of rang (must be 0..11)") \ + _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_CREATE_UAV_FAILED, "CreateUnorderedAccessView() 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_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_ENTRY_NOT_FOUND, "shader entry function not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_UNIFORMBLOCK_MSL_BUFFER_SLOT_OUT_OF_RANGE, "uniform block 'msl_buffer_n' is out of range (must be 0..7)") \ + _SG_LOGITEM_XMACRO(METAL_STORAGEBUFFER_MSL_BUFFER_SLOT_OUT_OF_RANGE, "storage buffer 'msl_buffer_n' is out of range (must be 8..23)") \ + _SG_LOGITEM_XMACRO(METAL_STORAGEIMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE, "storage image 'msl_texture_n' is out of range (must be 0..31)") \ + _SG_LOGITEM_XMACRO(METAL_IMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE, "image 'msl_texture_n' is out of range (must be 0..31)") \ + _SG_LOGITEM_XMACRO(METAL_SAMPLER_MSL_SAMPLER_SLOT_OUT_OF_RANGE, "sampler 'msl_sampler_n' is out of range (must be 0..11)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_CPS_FAILED, "failed to create compute pipeline state (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_CPS_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu.bindgroups_cache_size must be > 1 (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu.bindgroups_cache_size must be a power of 2 (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_CREATEBINDGROUP_FAILED, "wgpuDeviceCreateBindGroup failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_BUFFER_FAILED, "wgpuDeviceCreateBuffer() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_FAILED, "wgpuDeviceCreateTexture() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_SAMPLER_FAILED, "wgpuDeviceCreateSampler() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \ + _SG_LOGITEM_XMACRO(WGPU_UNIFORMBLOCK_WGSL_GROUP0_BINDING_OUT_OF_RANGE, "uniform block 'wgsl_group0_binding_n' is out of range (must be 0..15)") \ + _SG_LOGITEM_XMACRO(WGPU_TEXTURE_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "texture 'wgsl_group1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(WGPU_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "storage buffer 'wgsl_group1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(WGPU_STORAGEIMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "storage image 'wgsl_group1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(WGPU_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "sampler 'wgsl_group1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_COMPUTE_PIPELINE_FAILED, "wgpuDeviceCreateComputePipeline() failed") \ + _SG_LOGITEM_XMACRO(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING, "vulkan: could not look up a required extension function pointer") \ + _SG_LOGITEM_XMACRO(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE, "vulkan: could not find suitable memory type") \ + _SG_LOGITEM_XMACRO(VULKAN_ALLOCATE_MEMORY_FAILED, "vulkan: vkAllocateMemory() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_ALLOC_BUFFER_DEVICE_MEMORY_FAILED, "vulkan: allocating buffer device memory failed") \ + _SG_LOGITEM_XMACRO(VULKAN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED, "vulkan: allocating image device memory failed") \ + _SG_LOGITEM_XMACRO(VULKAN_DELETE_QUEUE_EXHAUSTED, "vulkan: internal delete queue exhausted (too many objects destroyed per frame)") \ + _SG_LOGITEM_XMACRO(VULKAN_STAGING_CREATE_BUFFER_FAILED, "vulkan: vkCreateBuffer() failed for staging buffer") \ + _SG_LOGITEM_XMACRO(VULKAN_STAGING_ALLOCATE_MEMORY_FAILED, "vulkan: allocating device memory for staging buffer failed") \ + _SG_LOGITEM_XMACRO(VULKAN_STAGING_BIND_BUFFER_MEMORY_FAILED, "vulkan: vkBindBufferMemory() failed for staging buffer") \ + _SG_LOGITEM_XMACRO(VULKAN_STAGING_STREAM_BUFFER_OVERFLOW, "vulkan: per-frame stream staging buffer has overflown (sg_desc.vulkan.stream_staging_buffer_size)") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_SHARED_BUFFER_FAILED, "vulkan: vkCreateBuffer() failed for cpu/gpu-shared buffer") \ + _SG_LOGITEM_XMACRO(VULKAN_ALLOCATE_SHARED_BUFFER_MEMORY_FAILED, "vulkan: allocating device memory for cpu/gpu-shared buffer failed") \ + _SG_LOGITEM_XMACRO(VULKAN_BIND_SHARED_BUFFER_MEMORY_FAILED, "vulkan: vkBindBufferMemory() failed for cpu/gpu-shared buffer") \ + _SG_LOGITEM_XMACRO(VULKAN_MAP_SHARED_BUFFER_MEMORY_FAILED, "vulkan: vkMapMemory() failed on cpu/gpu-shared buffer") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_BUFFER_FAILED, "vulkan: vkCreateBuffer() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_BIND_BUFFER_MEMORY_FAILED, "vulkan: vkBindBufferMemory() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_IMAGE_FAILED, "vulkan: vkCreateImage() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_BIND_IMAGE_MEMORY_FAILED, "vulkan: vkBindImageMemory() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_SHADER_MODULE_FAILED, "vukan: vkCreateShaderModule() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_UNIFORMBLOCK_SPIRV_SET0_BINDING_OUT_OF_RANGE, "vulkan: uniform block 'spirv_set0_binding_n' is out of range (must be 0..15)") \ + _SG_LOGITEM_XMACRO(VULKAN_TEXTURE_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: texture 'spirv_set1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(VULKAN_STORAGEBUFFER_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: storage buffer 'spirv_set1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(VULKAN_STORAGEIMAGE_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: storage image 'spirv_set1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(VULKAN_SAMPLER_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: sampler 'spirv_set1_binding_n' is out of range (must be 0..127)") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_DESCRIPTOR_SET_LAYOUT_FAILED, "vulkan: vkCreateDescriptorSetLayout() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_SHADER_UNIFORM_DESCRIPTOR_SET_SIZE_VS_CACHE_SIZE, "vulkan: shader uniform descriptor set is too big for the descriptor set cache (please write a Github issue)") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_PIPELINE_LAYOUT_FAILED, "vulkan: vkCreatePipelineLayout() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_GRAPHICS_PIPELINE_FAILED, "vulkan: vkCreateGraphicsPipelines() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_COMPUTE_PIPELINE_FAILED, "vulkan: vkCreateComputePipelines() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_IMAGE_VIEW_FAILED, "vulkan: vkCreateImageView() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_VIEW_MAX_DESCRIPTOR_SIZE, "vulkan: required view descriptor size is greater than _SG_VK_MAX_DESCRIPTOR_DATA_SIZE") \ + _SG_LOGITEM_XMACRO(VULKAN_CREATE_SAMPLER_FAILED, "vulkan: vkCreateSampler() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_SAMPLER_MAX_DESCRIPTOR_SIZE, "vulkan: required sampler descriptor size is greater than _SG_VK_MAX_DESCRIPTOR_DATA_SIZE") \ + _SG_LOGITEM_XMACRO(VULKAN_WAIT_FOR_FENCE_FAILED, "vulkan: vkWaitForFence() failed!") \ + _SG_LOGITEM_XMACRO(VULKAN_UNIFORM_BUFFER_OVERFLOW, "vulkan: uniform buffer has overflown (increase sg_desc.uniform_buffer_size)") \ + _SG_LOGITEM_XMACRO(VULKAN_DESCRIPTOR_BUFFER_OVERFLOW, "vulkan: desccriptor buffer has overflown (increase sg_desc.vulkan.descriptor_buffer_size)") \ + _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 SOKOL_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_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler 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_VIEW_INVALID_STATE, "sg_dealloc_view(): view 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_SAMPLER_INVALID_STATE, "sg_init_sampler(): sampler 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_VIEW_INVALID_STATE, "sg_init_view(): view must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID, FAILED or ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID, FAILED or ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_INVALID_STATE, "sg_uninit_sampler(): sampler must be in VALID, FAILED or ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID, FAILED or ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID, FAILED or ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_VIEW_INVALID_STATE, "sg_uninit_view(): view must be in VALID, FAILED or ALLOC 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_SAMPLER_INVALID_STATE, "sg_fail_sampler(): sampler 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_VIEW_INVALID_STATE, "sg_fail_view(): view 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(SAMPLER_POOL_EXHAUSTED, "sampler pool exhausted") \ + _SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \ + _SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \ + _SG_LOGITEM_XMACRO(VIEW_POOL_EXHAUSTED, "view pool exhausted") \ + _SG_LOGITEM_XMACRO(BEGINPASS_TOO_MANY_COLOR_ATTACHMENTS, "sg_begin_pass: too many color attachments (sg_limits.max_color_attachments)") \ + _SG_LOGITEM_XMACRO(BEGINPASS_TOO_MANY_RESOLVE_ATTACHMENTS, "sg_begin_pass: too many resolve attachments (sg_limits.max_color_attachments)") \ + _SG_LOGITEM_XMACRO(BEGINPASS_ATTACHMENTS_ALIVE, "sg_begin_pass: an attachment was provided that no longer exists") \ + _SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on vertex shader stage (sg_limits.max_texture_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on fragment shader stage (sg_limits.max_texture_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on compute shader stage (sg_limits.max_texture_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEBUFFERS, "sg_shader_desc: too many storage buffer bindings on vertex shader stage (sg_limits.max_storage_buffer_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEBUFFERS, "sg_shader_desc: too many storage buffer bindings on fragment shader stage (sg_limits.max_storage_buffer_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEBUFFERS, "sg_shader_desc: too many storage buffer bindings on compute shader stage (sg_limits.max_storage_buffer_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEIMAGES, "sg_shader_desc: too many storage image bindings on vertex shader stage (sg_limits.max_storage_image_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEIMAGES, "sg_shader_desc: too many storage image bindings on fragment shader stage (sg_limits.max_storage_image_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEIMAGES, "sg_shader_desc: too many storage image bindings on compute shader stage (sg_limits.max_storage_image_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURESAMPLERPAIRS, "sg_shader_desc: too many texture-sampler-pairs on vertex shader stage (sg_limits.max_texture_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURESAMPLERPAIRS, "sg_shader_desc: too many texture-sampler-pairs on fragment shader stage (sg_limits.max_texture_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURESAMPLERPAIRS, "sg_shader_desc: too many texture-sampler-pairs on compute shader stage (sg_limits.max_texture_bindings_per_stage)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_IMMUTABLE_DYNAMIC_STREAM, "sg_buffer_desc.usage: only one of .immutable, .dynamic_update, .stream_update can be true") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SEPARATE_BUFFER_TYPES, "sg_buffer_desc.usage: on WebGL2, only one of .vertex_buffer or .index_buffer can be true (check sg_features.separate_buffer_types)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_NONZERO_SIZE, "sg_buffer_desc.size must be greater zero") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_MATCHING_DATA_SIZE, "sg_buffer_desc.size and .data.size must be equal") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE, "sg_buffer_desc.data.size expected to be zero") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_NO_DATA, "sg_buffer_desc.data.ptr must be null for dynamic/stream buffers") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_DATA, "sg_buffer_desc: initial content data must be provided for immutable buffers without storage buffer usage") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \ + _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_IMMUTABLE_DYNAMIC_STREAM, "sg_image_desc.usage: only one of .immutable, .dynamic_update, .stream_update can be true") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES, "sg_image_desc.num_slices must be exactly 1 for SG_IMAGETYPE_2D") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES, "sg_image_desc.num_slices must be exactly 6 for SG_IMAGETYPE_CUBE") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_array_layers)) for SG_IMAGETYPE_ARRAY") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_size_3d)) for SG_IMAGETYPE_ARRAY") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NUMSLICES, "sg_image_desc.num_slices must be > 0") \ + _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_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT, "non-attachment images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_IMMUTABLE, "attachment and storage images must be sg_image_usage.immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_NO_DATA, "render/storage attachment images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_PIXELFORMAT, "invalid pixel format for render attachment image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_RESOLVE_EXPECT_NO_MSAA, "resolve attachment images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_NO_MSAA_SUPPORT, "multisampling not supported for this pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_NUM_MIPMAPS, "multisample images must have num_mipmaps == 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_CUBE_IMAGE, "cube images cannot have sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_ARRAY_IMAGE, "array images cannot have sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_PIXELFORMAT, "invalid pixel format for storage image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_MSAA, "storage images cannot be multisampled") \ + _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-update images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VERTEX_SOURCE, "vertex shader source code expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_FRAGMENT_SOURCE, "fragment shader source code expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPUTE_SOURCE, "compute shader source code expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VERTEX_SOURCE_OR_BYTECODE, "vertex shader source or byte code expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_FRAGMENT_SOURCE_OR_BYTECODE, "fragment shader source or byte code expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPUTE_SOURCE_OR_BYTECODE, "compute shader source or byte code expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_INVALID_SHADER_COMBO, "cannot combine compute shaders with vertex or fragment shaders") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_INITIALIZED, "sg_shader_desc.mtl_threads_per_threadgroup must be initialized for compute shaders (metal)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_MULTIPLE_32, "sg_shader_desc.mtl_threads_per_threadgroup (x * y * z) must be a multiple of 32 (metal)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_CONT_MEMBERS, "sg_shader_desc.uniform_blocks[].glsl_uniforms[]: items must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_IS_ZERO, "sg_shader_desc.uniform_blocks[].size cannot be zero") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_METAL_BUFFER_SLOT_COLLISION, "sg_shader_desc.uniform_blocks[].msl_buffer_n must be unique across uniform blocks and storage buffers in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_HLSL_REGISTER_B_COLLISION, "sg_shader_desc.uniform_blocks[].hlsl_register_b_n must be unique across uniform blocks in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_WGSL_GROUP0_BINDING_COLLISION, "sg_shader_desc.uniform_blocks[].wgsl_group0_binding_n must be unique across all uniform blocks") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_SPIRV_SET0_BINDING_COLLISION, "sg_shader_desc.unifrom_blocks[].spirv_set0_binding_n must be unique across all uniform blocks") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_MEMBERS, "sg_shader_desc.uniform_blocks[].glsl_uniforms[]: GL backend requires uniform block member declarations") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_UNIFORM_GLSL_NAME, "sg_shader_desc.uniform_blocks[].glsl_uniforms[].glsl_name missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_MISMATCH, "sg_shader_desc.uniform_blocks[].glsl_uniforms[]: size of uniform block members doesn't match uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_ARRAY_COUNT, "sg_shader_desc.uniform_blocks[].glsl_uniforms[].array_count must be >= 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_STD140_ARRAY_TYPE, "sg_shader_desc.uniform_blocks[].glsl_uniforms[].type: uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_METAL_BUFFER_SLOT_COLLISION, "sg_shader_desc.views[].storage_buffer.storagemsl_buffer_n must be unique across uniform blocks and storage buffer in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_T_COLLISION, "sg_shader_desc.views[].storage_buffer.hlsl_register_t_n must be unique across read-only storage buffers and images in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_U_COLLISION, "sg_shader_desc.views[].storage_buffer.hlsl_register_u_n must be unique across read/write storage buffers and storage images in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_GLSL_BINDING_COLLISION, "sg_shader_desc.views[].storage_buffer.glsl_binding_n must be unique across shader stages") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.views[].storage_buffer.wgsl_group1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.views[].storage_buffer.spirv_set1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_EXPECT_COMPUTE_STAGE, "sg_shader_desc.views[].storage_image: storage images are allowed on the compute stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_METAL_TEXTURE_SLOT_COLLISION, "sg_shader_desc.views[].storage_image.msl_texture_n must be unique across images and storage images in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_HLSL_REGISTER_U_COLLISION, "sg_shader_desc.views[].storage_image.hlsl_register_u_n must be unique across storage images and read/write storage buffers in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_GLSL_BINDING_COLLISION, "sg_shader_desc.views[].storage_image.glsl_binding_n must be unique across shader stages") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.views[].storage_image.wgsl_group1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.views[].storage_image.spirv_set1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_METAL_TEXTURE_SLOT_COLLISION, "sg_shader_desc.views[].texture.msl_texture_n must be unique across textures and storage images in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_HLSL_REGISTER_T_COLLISION, "sg_shader_desc.views[].texture.hlsl_register_t_n must be unique across textures and storage buffers in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.views[].texture.wgsl_group1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.views[].texture.spirv_set1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_COLLISION, "sg_shader_desc.samplers[].msl_sampler_n must be unique in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_COLLISION, "sg_shader_desc.samplers[].hlsl_register_s_n must be unique in same shader stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.samplers[].wgsl_group1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.samplers[].spirv_set1_binding_n must be unique across all view and sampler bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_VIEW_SLOT_OUT_OF_RANGE, "texture-sampler-pair view slot index is out of range (sg_shader_desc.texture_sampler_pairs[].view_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "texture-sampler-pair sampler slot index is out of range (sg_shader_desc.texture_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_TEXTURE_STAGE_MISMATCH, "texture-sampler-pair stage doesn't match referenced texture stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_EXPECT_TEXTURE_VIEW, "texture-sampler-pair view must be a texture view (sg_shader_desc.texture_sampler_pairs[].view_slot => sg_shaders_desc.views[i].texture)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_STAGE_MISMATCH, "texture-sampler-pair stage doesn't match referenced sampler stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_GLSL_NAME, "texture-sampler-pair 'glsl_name' missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED, "image sample type UNFILTERABLE_FLOAT, UINT, SINT can only be used with NONFILTERING sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED, "image sample type DEPTH can only be used with COMPARISON sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXVIEW_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS, "one or more texture views are not referenced by by texture-sampler-pairs (sg_shader_desc.texture_sampler_pairs[].view_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS, "one or more samplers are not referenced by texture-sampler-pairs (sg_shader_desc.texture_sampler_pairs[].sampler_slot)") \ + _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_COMPUTE_SHADER_EXPECTED, "sg_pipeline_desc.shader must be a compute shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_COMPUTE_SHADER_EXPECTED, "sg_pipeline_desc.compute is false, but shader is a compute shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, "sg_pipeline_desc.layout.attrs[].format is incompatible with sg_shader_desc.attrs[].base_type") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER_READONLY_STORAGEBUFFERS, "sg_pipeline_desc.shader: only readonly storage buffer bindings allowed in render pipelines") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE, "SG_BLENDOP_MIN/MAX requires all blend factors to be SG_BLENDFACTOR_ONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_DUAL_SOURCE_BLENDING_NOT_SUPPORTED, "dual source blending not supported (sg_features.dual_source_blending)") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_CANARY, "sg_view_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE, "sg_view_desc: only one view type can be active") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_ANY_VIEWTYPE, "sg_view_desc: exactly one view type must be active") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOURCE_ALIVE, "sg_view_desc: resource object is no longer alive (.buffer or .image)") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOURCE_FAILED, "sg_view_desc: resource object cannot be in FAILED state (.buffer or .image)") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_VS_BUFFER_SIZE, "sg_view_desc.storage_buffer.offset is >= buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_MULTIPLE_256, "sg_view_desc.storage_buffer.offset must be a multiple of 256") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEBUFFER_USAGE, "sg_view_desc.storage_buffer.buffer must have been created with sg_buffer_desc.usage.storage_buffer = true") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEIMAGE_USAGE, "sg_view_desc.storage_image.image must have been created with sg_image_desc.usage.storage_image = true") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_COLORATTACHMENT_USAGE, "sg_view_desc.color_attachment.image must have been created with sg_image_desc.usage.color_attachment = true") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOLVEATTACHMENT_USAGE, "sg_view_desc.resolve_attachment.image must have been created with sg_image_desc.usage.resolve_attachment = true") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_USAGE, "sg_view_desc.depth_stencil_attachment.image must have been created with sg_image_desc.usage.depth_stencil_attachment = true") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_MIPLEVEL, "sg_view_desc: image/attachment view mip level is out of range (must be >=0 and =0 and <6)") \ + _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_ARRAY_SLICE, "sg_view_desc: image/attachment view slice is out of range for 2D array image (must be >=0 and =0 and =0 and 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET, "sg_begin_pass: expected pass.swapchain.width == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT, "sg_begin_pass: expected pass.swapchain.height > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET, "sg_begin_pass: expected pass.swapchain.height == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT, "sg_begin_pass: expected pass.swapchain.sample_count > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET, "sg_begin_pass: expected pass.swapchain.sample_count == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT, "sg_begin_pass: expected pass.swapchain.color_format to be valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.color_format to be unset") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.depth_format to be unset") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE, "sg_begin_pass: expected pass.swapchain.metal.current_drawable != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.current_drawable == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.render_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.render_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.render_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.render_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET, "sg_begin_pass: expected pass.swapchain.gl.framebuffer == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEWS_CONTINUOUS, "sg_begin_pass: color attachment view array must be continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_ALIVE, "sg_begin_pass: color attachment view no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_VALID, "sg_begin_pass: color attachment view not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_TYPE, "sg_begin_pass: color attachment view has wrong type (must be sg_view_desc.color_attachment)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: color attachment view's image object is uninitialized or no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: color attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SIZES, "sg_begin_pass: all color attachments must have the same width and height") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNT, "sg_begin_pass: when resolve attachments are provided, the color attachment sample count must be > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNTS_EQUAL, "sg_begin_pass: all color attachments must have the same sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_NO_COLORATTACHMENTVIEW, "sg_begin_pass: a resolve attachment view must have an associated color attachment view at the same index") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_ALIVE, "sg_begin_pass: resolve attachment view no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_VALID, "sg_begin_pass: resolve attachment view not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_TYPE, "sg_begin_pass: resolve attachment view has wrong type (must be sg_view_desc.resolve_attachment)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: resolve attachment view's image object is uninitialized or no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: resolve attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES, "sg_begin_pass: all attachments must have the same width and height") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEWS_CONTINUOUS, "sg_begin_pass: color attachment view array must be continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_ALIVE, "sg_begin_pass: depth-stencil attachment view no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_VALID, "sg_begin_pass: depth-stencil attachment view not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_TYPE, "sg_begin_pass: depth-stencil attachment view has wrong type (must be sg_view_desc.depth_stencil_attachment)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: depth-stencil attachment view's image object is uninitialized or no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: depth-stencil attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SIZES, "sg_begin_pass: attachments must have the same width and height") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SAMPLECOUNT, "sg_begin_pass: all color attachments must have the same sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_EXPECTED, "sg_begin_pass: offscreen render passes must have at least one color- or depth-stencil attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_AVP_RENDERPASS_EXPECTED, "sg_apply_viewport: must be called in a render pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_ASR_RENDERPASS_EXPECTED, "sg_apply_scissor_rect: must be called in a render pass") \ + _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_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PASS_EXPECTED, "sg_apply_pipeline: must be called in a pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_SHADER_ALIVE, "sg_apply_pipeline: shader object associated with pipeline no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_SHADER_VALID, "sg_apply_pipeline: shader object associated with pipeline not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COMPUTEPASS_EXPECTED, "sg_apply_pipeline: trying to apply compute pipeline in render pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_RENDERPASS_EXPECTED, "sg_apply_pipeline: trying to apply render pipeline in compute pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_COLOR_COUNT, "sg_apply_pipeline: the pipeline .color_count must be 1 in swapchain render passes") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_COLOR_FORMAT, "sg_apply_pipeline: the pipeline .colors[0].pixel_format doesn't match the sg_pass.swapchain.color_format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_DEPTH_FORMAT, "sg_apply_pipeline: the pipeline .depth.pixel_format doesn't match the sg_pass.swapchain.depth_format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_SAMPLE_COUNT, "sg_apply_pipeline: the pipeline .sample_count doesn't match the sg_pass.swapchain.sample_count") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATTACHMENTS_ALIVE, "sg_apply_pipeline: at least one pass attachment view or base image object is no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_COUNT, "sg_apply_pipeline: the pipeline .color_count doesn't match the number of render pass color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_VIEW_VALID, "sg_apply_pipeline: a pass color attachment view is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_IMAGE_VALID, "sg_apply_pipeline: a pass color attachment view's image object is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_FORMAT, "sg_apply_pipeline: a pipeline .colors[n].pixel_format doesn't match sg_pass.attachments.colors[n] image pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTHSTENCILATTACHMENT_VIEW_VALID, "sg_apply_pipeline: the pass depth-stencil attachment view is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTHSTENCILATTACHMENT_IMAGE_VALID, "sg_apply_pipeline: the pass depth-stencil attachment view's image object is not in valid state (SG_RESOURCESTATE_VALID)") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTHSTENCILATTACHMENT_FORMAT, "sg_apply_pipeline: pipeline .depth.pixel_format doesn't match sg_pass.attachments.depth_stencil image pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATTACHMENT_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match pass attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PASS_EXPECTED, "sg_apply_bindings: must be called in a pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EMPTY_BINDINGS, "sg_apply_bindings: the provided sg_bindings struct is empty") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_ALIVE, "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_PIPELINE_SHADER_ALIVE, "sg_apply_bindings: shader associated with currently applied pipeline is no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_SHADER_VALID, "sg_apply_bindings: shader associated with currently applied pipeline is not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_COMPUTE_EXPECTED_NO_VBUFS, "sg_apply_bindings: vertex buffer bindings not allowed in a compute pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_COMPUTE_EXPECTED_NO_IBUF, "sg_apply_bindings: index buffer binding not allowed in compute pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_VBUF, "sg_apply_bindings: vertex buffer binding is missing or buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBUF_ALIVE, "sg_apply_bindings: vertex buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBUF_USAGE, "sg_apply_bindings: buffer in vertex buffer bind slot must have usage.vertex_buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBUF_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer bind slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_NO_IBUF, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer binding provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_IBUF, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer binding provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IBUF_ALIVE, "sg_apply_bindings: index buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IBUF_USAGE, "sg_apply_bindings: buffer in index buffer bind slot must have usage.index_buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IBUF_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_VIEW_BINDING, "sg_apply_bindings: view binding is missing or the view handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VIEW_ALIVE, "sg_apply_bindings: view no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECT_TEXVIEW, "sg_apply_bindings: view type mismatch in bindslot (shader expects a texture view)") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECT_SBVIEW, "sg_apply_bindings: view type mismatch in bindslot (shader expects a storage buffer view)") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECT_SIMGVIEW, "sg_apply_bindings: view type mismatch in bindslot (shader expects a storage image view)") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_IMAGETYPE_MISMATCH, "sg_apply_bindings: image type of bound texture doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_MULTISAMPLED_IMAGE, "sg_apply_bindings: texture bindings expects image with sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_NON_MULTISAMPLED_IMAGE, "sg_apply_bindings: texture bindings expects image with sample_count == 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_SBVIEW_READWRITE_IMMUTABLE, "sg_apply_bindings: storage buffers bound as read/write must have usage immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_SIMGVIEW_COMPUTE_PASS_EXPECTED, "sg_apply_bindings: storage image bindings can only appear on compute passes") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_SIMGVIEW_IMAGETYPE_MISMATCH, "sg_apply_bindings: image type of bound storage image doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_SIMGVIEW_ACCESSFORMAT, "sg_apply_bindings: pixel format of storage image view doesn't match access format in shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING, but sampler has SG_FILTER_LINEAR filters") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_SAMPLER_ALIVE, "sg_apply_bindings: bound sampler no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_SAMPLER_VALID, "sg_apply_bindings: bound sampler not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_BINDING_VS_DEPTHSTENCIL_ATTACHMENT, "sg_apply_bindings: cannot bind texture in the same pass it is used as depth-stencil attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_BINDING_VS_COLOR_ATTACHMENT, "sg_apply_bindings: cannot bind texture in the same pass it is used as color attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_BINDING_VS_RESOLVE_ATTACHMENT, "sg_apply_bindings: cannot bind texture in the same pass it is used as resolve attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_VS_STORAGEIMAGE_BINDING, "sg_apply_bindings: an image cannot be bound as a texture and storage image at the same time") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_PASS_EXPECTED, "sg_apply_uniforms: must be called in a pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_ALIVE, "sg_apply_uniforms: currently applied pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_VALID, "sg_apply_uniforms: currently applied pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_SHADER_ALIVE, "sg_apply_uniforms: shader associated with currently applied pipeline is no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_SHADER_VALID, "sg_apply_uniforms: shader associated with currently applied pipeline is not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_NO_UNIFORMBLOCK_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ + _SG_LOGITEM_XMACRO(VALIDATE_AU_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_RENDERPASS_EXPECTED, "sg_draw: must be called in a render pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_BASEELEMENT_GE_ZERO, "sg_draw: base_element cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_NUMELEMENTS_GE_ZERO, "sg_draw: num_elements cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_NUMINSTANCES_GE_ZERO, "sg_draw: num_instances cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_RENDERPASS_EXPECTED, "sg_draw: must be called in a render pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEELEMENT_GE_ZERO, "sg_draw_ex: base_element cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_NUMELEMENTS_GE_ZERO, "sg_draw_ex: num_elements cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_NUMINSTANCES_GE_ZERO, "sg_draw_ex: num_instances cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_GE_ZERO, "sg_draw_ex: base_instance cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX_VS_INDEXED, "sg_draw_ex(): base_vertex must be == 0 for non-indexed rendering") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_VS_INSTANCED, "sg_draw_ex(): base_instance must be == 0 for non-instanced rendering") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX_NOT_SUPPORTED, "sg_draw_ex(): base_vertex != 0 not supported on this backend (sg_features.draw_base_vertex)") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_NOT_SUPPORTED, "sg_draw_ex(): base_instance > 0 not supported on this backend (sg_features.draw_base_instance)") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "sg_draw: call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \ + _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_COMPUTEPASS_EXPECTED, "sg_dispatch: must be called in a compute pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSX, "sg_dispatch: num_groups_x must be >=0 and <65536") \ + _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSY, "sg_dispatch: num_groups_y must be >=0 and <65536") \ + _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSZ, "sg_dispatch: num_groups_z must be >=0 and <65536") \ + _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "sg_dispatch: call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \ + _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 + + The sg_desc struct contains configuration values for sokol_gfx, + it is used as parameter to the sg_setup() call. + + The default configuration is: + + .buffer_pool_size 128 + .image_pool_size 128 + .sampler_pool_size 64 + .shader_pool_size 32 + .pipeline_pool_size 64 + .view_pool_size 256 + .uniform_buffer_size 4 MB (4*1024*1024) + .max_commit_listeners 1024 + .disable_validation false + .metal.force_managed_storage_mode false + .metal.use_command_buffer_with_retained_references false + .wgpu.disable_bindgroups_cache false + .wgpu.bindgroups_cache_size 1024 + .vulkan.copy_staging_buffer_size 4 MB + .vulkan.stream_staging_buffer_size 16 MB + .vulkan.descriptor_buffer_size 16 MB + + .allocator.alloc_fn 0 (in this case, malloc() will be called) + .allocator.free_fn 0 (in this case, free() will be called) + .allocator.user_data 0 + + .environment.defaults.color_format: default value depends on selected backend: + all GL backends: SG_PIXELFORMAT_RGBA8 + Metal and D3D11: SG_PIXELFORMAT_BGRA8 + WebGPU: *no default* (must be queried from WebGPU swapchain object) + .environment.defaults.depth_format: SG_PIXELFORMAT_DEPTH_STENCIL + .environment.defaults.sample_count: 1 + + Metal specific: + (NOTE: All Objective-C object references are transferred through + a bridged cast (__bridge const void*) to sokol_gfx, which will use an + unretained bridged cast (__bridge id) to retrieve the Objective-C + references back. Since the bridge cast is unretained, the caller + must hold a strong reference to the Objective-C object until sg_setup() + returns. + + .metal.force_managed_storage_mode + when enabled, Metal buffers and texture resources are created in managed storage + mode, otherwise sokol-gfx will decide whether to create buffers and + textures in managed or shared storage mode (this is mainly a debugging option) + .metal.use_command_buffer_with_retained_references + when true, the sokol-gfx Metal backend will use Metal command buffers which + bump the reference count of resource objects as long as they are inflight, + this is slower than the default command-buffer-with-unretained-references + method, this may be a workaround when confronted with lifetime validation + errors from the Metal validation layer until a proper fix has been implemented + .environment.metal.device + a pointer to the MTLDevice object + + D3D11 specific: + .environment.d3d11.device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .environment.d3d11.device_context + a pointer to the ID3D11DeviceContext object + .d3d11.shader_debugging + set this to true to compile shaders which are provided as HLSL source + code with debug information and without optimization, this allows + shader debugging in tools like RenderDoc, to output source code + instead of byte code from sokol-shdc, omit the `--binary` cmdline + option + + WebGPU specific: + .wgpu.disable_bindgroups_cache + When this is true, the WebGPU backend will create and immediately + release a BindGroup object in the sg_apply_bindings() call, only + use this for debugging purposes. + .wgpu.bindgroups_cache_size + The size of the bindgroups cache for re-using BindGroup objects + between sg_apply_bindings() calls. The smaller the cache size, + the more likely are cache slot collisions which will cause + a BindGroups object to be destroyed and a new one created. + Use the information returned by sg_query_stats() to check + if this is a frequent occurrence, and increase the cache size as + needed (the default is 1024). + NOTE: wgpu_bindgroups_cache_size must be a power-of-2 number! + .environment.wgpu.device + a WGPUDevice handle + + Vulkan specific: + .vulkan.copy_staging_buffer_size + Size of the staging buffer in bytes for uploading the initial + content of buffers and images, and for updating + .usage.dynamic_update resources. The default is 4 MB, + bigger resource updates are split into multiple chunks + of the staging buffer size + .vulkan.stream_staging_buffer_size + Size of the staging buffer in bytes for updating .usage.stream_update + resources. The default is 16 MB. The size must be big enough + to accomodate all update into .usage.stream_update resources. + Any additional data will cause an error log message and + incomplete rendering. Note that the actually allocated size + will be twice as much because the stream-staging-buffer is + double-buffered. + .vulkan.descriptor_buffer_size + Size of the descriptor-upload buffer in bytes. The default + size is 16 bytes. The size must be big enough to accomodate + all unifrom-block, view- and sampler-bindings in a single + frame (assume a worst-case of 256 bytes per binding). Note + that the actually allocated size will be twice as much + because the descriptor-buffer is double-buffered. + + When using sokol_gfx.h and sokol_app.h together, consider using the + helper function sglue_environment() in the sokol_glue.h header to + initialize the sg_desc.environment nested struct. sglue_environment() returns + a completely initialized sg_environment struct with information + provided by sokol_app.h. +*/ +typedef struct sg_environment_defaults { + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; +} sg_environment_defaults; + +typedef struct sg_metal_environment { + const void* device; +} sg_metal_environment; + +typedef struct sg_d3d11_environment { + const void* device; + const void* device_context; +} sg_d3d11_environment; + +typedef struct sg_wgpu_environment { + const void* device; +} sg_wgpu_environment; + +typedef struct sg_vulkan_environment { + const void* instance; + const void* physical_device; + const void* device; + const void* queue; + uint32_t queue_family_index; +} sg_vulkan_environment; + +typedef struct sg_environment { + sg_environment_defaults defaults; + sg_metal_environment metal; + sg_d3d11_environment d3d11; + sg_wgpu_environment wgpu; + sg_vulkan_environment vulkan; +} sg_environment; + +/* + 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_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sg_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(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 provide a + compatible logger function in the sg_setup() call + (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_d3d11_desc { + bool shader_debugging; // if true, HLSL shaders are compiled with D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION +} sg_d3d11_desc; + +typedef struct sg_metal_desc { + bool force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA + bool use_command_buffer_with_retained_references; // Metal: use a managed MTLCommandBuffer which ref-counts used resources +} sg_metal_desc; + +typedef struct sg_wgpu_desc { + bool disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache + int bindgroups_cache_size; // number of slots in the WebGPU bindgroup cache (must be 2^N) +} sg_wgpu_desc; + +typedef struct sg_vulkan_desc { + int copy_staging_buffer_size; // size of staging buffer for immutable and dynamic resources (default: 4 MB) + int stream_staging_buffer_size; // size of per-frame staging buffer for updating streaming resources (default: 16 MB) + int descriptor_buffer_size; // size of per-frame descriptor buffer for updating resource bindings (default: 16 MB) +} sg_vulkan_desc; + +typedef struct sg_desc { + uint32_t _start_canary; + int buffer_pool_size; + int image_pool_size; + int sampler_pool_size; + int shader_pool_size; + int pipeline_pool_size; + int view_pool_size; + int uniform_buffer_size; // max size of all sg_apply_uniform() calls per frame, with worst-case 256 byte alignment + int max_commit_listeners; // max number of commit listener hook functions + bool disable_validation; // disable validation layer even in debug mode, useful for tests + bool enforce_portable_limits; // if true, enforce portable resource binding limits (SG_MAX_PORTABLE_*) + sg_d3d11_desc d3d11; // d3d11-specific setup parameters + sg_metal_desc metal; // metal-specific setup parameters + sg_wgpu_desc wgpu; // webgpu-specific setup parameters + sg_vulkan_desc vulkan; // vulkan-specific setup parameters + sg_allocator allocator; // optional memory allocation hooks + sg_logger logger; // optional log function override + sg_environment environment; // required externally provided runtime objects and defaults + uint32_t _end_canary; +} sg_desc; + +// setup and misc functions +SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); +SOKOL_GFX_API_DECL void sg_shutdown(void); +SOKOL_GFX_API_DECL bool sg_isvalid(void); +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); +SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_view sg_make_view(const sg_view_desc* desc); +SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); +SOKOL_GFX_API_DECL void sg_destroy_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_destroy_view(sg_view view); +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); + +// render and compute functions +SOKOL_GFX_API_DECL void sg_begin_pass(const sg_pass* pass); +SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); +SOKOL_GFX_API_DECL void sg_apply_uniforms(int ub_slot, const sg_range* data); +SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); +SOKOL_GFX_API_DECL void sg_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance); +SOKOL_GFX_API_DECL void sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z); +SOKOL_GFX_API_DECL void sg_end_pass(void); +SOKOL_GFX_API_DECL void sg_commit(void); + +// getting information +SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); +SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); +SOKOL_GFX_API_DECL sg_features sg_query_features(void); +SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); +SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); +SOKOL_GFX_API_DECL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes); +SOKOL_GFX_API_DECL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); +// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) +SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); +SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); +SOKOL_GFX_API_DECL sg_resource_state sg_query_sampler_state(sg_sampler smp); +SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); +SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_resource_state sg_query_view_state(sg_view view); +// get runtime information about a resource +SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_info sg_query_sampler_info(sg_sampler smp); +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_view_info sg_query_view_info(sg_view view); +// 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_sampler_desc sg_query_sampler_desc(sg_sampler smp); +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_view_desc sg_query_view_desc(sg_view view); +// 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); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_view_desc sg_query_view_defaults(const sg_view_desc* desc); +// assorted query functions +SOKOL_GFX_API_DECL size_t sg_query_buffer_size(sg_buffer buf); +SOKOL_GFX_API_DECL sg_buffer_usage sg_query_buffer_usage(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_type sg_query_image_type(sg_image img); +SOKOL_GFX_API_DECL int sg_query_image_width(sg_image img); +SOKOL_GFX_API_DECL int sg_query_image_height(sg_image img); +SOKOL_GFX_API_DECL int sg_query_image_num_slices(sg_image img); +SOKOL_GFX_API_DECL int sg_query_image_num_mipmaps(sg_image img); +SOKOL_GFX_API_DECL sg_pixel_format sg_query_image_pixelformat(sg_image img); +SOKOL_GFX_API_DECL sg_image_usage sg_query_image_usage(sg_image img); +SOKOL_GFX_API_DECL int sg_query_image_sample_count(sg_image img); +SOKOL_GFX_API_DECL sg_view_type sg_query_view_type(sg_view view); +SOKOL_GFX_API_DECL sg_image sg_query_view_image(sg_view view); +SOKOL_GFX_API_DECL sg_buffer sg_query_view_buffer(sg_view view); + +// separate resource allocation and initialization (for async setup) +SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); +SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); +SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(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_view sg_alloc_view(void); +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_sampler(sg_sampler smp); +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_view(sg_view view); +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_sampler(sg_sampler smg, const sg_sampler_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_view(sg_view view, const sg_view_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_sampler(sg_sampler smp); +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_view(sg_view view); +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_sampler(sg_sampler smp); +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_view(sg_view view); + +// frame and total stats +SOKOL_GFX_API_DECL void sg_enable_stats(void); +SOKOL_GFX_API_DECL void sg_disable_stats(void); +SOKOL_GFX_API_DECL bool sg_stats_enabled(void); +SOKOL_GFX_API_DECL sg_stats sg_query_stats(void); + +/* Backend-specific structs and functions, these may come in handy for mixing + sokol-gfx rendering with 'native backend' rendering functions. + + This group of functions will be expanded as needed. +*/ + +typedef struct sg_d3d11_buffer_info { + const void* buf; // ID3D11Buffer* +} sg_d3d11_buffer_info; + +typedef struct sg_d3d11_image_info { + const void* tex2d; // ID3D11Texture2D* + const void* tex3d; // ID3D11Texture3D* + const void* res; // ID3D11Resource* (either tex2d or tex3d) +} sg_d3d11_image_info; + +typedef struct sg_d3d11_sampler_info { + const void* smp; // ID3D11SamplerState* +} sg_d3d11_sampler_info; + +typedef struct sg_d3d11_shader_info { + const void* cbufs[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; // ID3D11Buffer* (constant buffers by bind slot) + const void* vs; // ID3D11VertexShader* + const void* fs; // ID3D11PixelShader* +} sg_d3d11_shader_info; + +typedef struct sg_d3d11_pipeline_info { + const void* il; // ID3D11InputLayout* + const void* rs; // ID3D11RasterizerState* + const void* dss; // ID3D11DepthStencilState* + const void* bs; // ID3D11BlendState* +} sg_d3d11_pipeline_info; + +typedef struct sg_d3d11_view_info { + const void* srv; // ID3D11ShaderResourceView + const void* uav; // ID3D11UnorderedAccessView + const void* rtv; // ID3D11RenderTargetView + const void* dsv; // ID3D11DepthStencilView +} sg_d3d11_view_info; + +typedef struct sg_mtl_buffer_info { + const void* buf[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_buffer_info; + +typedef struct sg_mtl_image_info { + const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_image_info; + +typedef struct sg_mtl_sampler_info { + const void* smp; // id +} sg_mtl_sampler_info; + +typedef struct sg_mtl_shader_info { + const void* vertex_lib; // id + const void* fragment_lib; // id + const void* vertex_func; // id + const void* fragment_func; // id +} sg_mtl_shader_info; + +typedef struct sg_mtl_pipeline_info { + const void* rps; // id + const void* dss; // id +} sg_mtl_pipeline_info; + +typedef struct sg_wgpu_buffer_info { + const void* buf; // WGPUBuffer +} sg_wgpu_buffer_info; + +typedef struct sg_wgpu_image_info { + const void* tex; // WGPUTexture +} sg_wgpu_image_info; + +typedef struct sg_wgpu_sampler_info { + const void* smp; // WGPUSampler +} sg_wgpu_sampler_info; + +typedef struct sg_wgpu_shader_info { + const void* vs_mod; // WGPUShaderModule + const void* fs_mod; // WGPUShaderModule + const void* bgl; // WGPUBindGroupLayout; +} sg_wgpu_shader_info; + +typedef struct sg_wgpu_pipeline_info { + const void* render_pipeline; // WGPURenderPipeline + const void* compute_pipeline; // WGPUComputePipeline +} sg_wgpu_pipeline_info; + +typedef struct sg_wgpu_view_info { + const void* view; // WGPUTextureView +} sg_wgpu_view_info; + +typedef struct sg_gl_buffer_info { + uint32_t buf[SG_NUM_INFLIGHT_FRAMES]; + int active_slot; +} sg_gl_buffer_info; + +typedef struct sg_gl_image_info { + uint32_t tex[SG_NUM_INFLIGHT_FRAMES]; + uint32_t tex_target; + int active_slot; +} sg_gl_image_info; + +typedef struct sg_gl_sampler_info { + uint32_t smp; +} sg_gl_sampler_info; + +typedef struct sg_gl_shader_info { + uint32_t prog; +} sg_gl_shader_info; + +typedef struct sg_gl_view_info { + uint32_t tex_view[SG_NUM_INFLIGHT_FRAMES]; + uint32_t msaa_render_buffer; + uint32_t msaa_resolve_frame_buffer; +} sg_gl_view_info; + +// D3D11: return ID3D11Device +SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); +// D3D11: return ID3D11DeviceContext +SOKOL_GFX_API_DECL const void* sg_d3d11_device_context(void); +// D3D11: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf); +// D3D11: get internal image resource objects +SOKOL_GFX_API_DECL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img); +// D3D11: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp); +// D3D11: get internal shader resource objects +SOKOL_GFX_API_DECL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd); +// D3D11: get internal pipeline resource objects +SOKOL_GFX_API_DECL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip); +// D3D11: get internal view resource objects +SOKOL_GFX_API_DECL sg_d3d11_view_info sg_d3d11_query_view_info(sg_view view); + +// Metal: return __bridge-casted MTLDevice +SOKOL_GFX_API_DECL const void* sg_mtl_device(void); +// Metal: return __bridge-casted MTLRenderCommandEncoder when inside render pass (otherwise zero) +SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); +// Metal: return __bridge-casted MTLComputeCommandEncoder when inside compute pass (otherwise zero) +SOKOL_GFX_API_DECL const void* sg_mtl_compute_command_encoder(void); +// Metal: return __bridge-casted MTLCommandQueue +SOKOL_GFX_API_DECL const void* sg_mtl_command_queue(void); +// Metal: get internal __bridge-casted buffer resource objects +SOKOL_GFX_API_DECL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf); +// Metal: get internal __bridge-casted image resource objects +SOKOL_GFX_API_DECL sg_mtl_image_info sg_mtl_query_image_info(sg_image img); +// Metal: get internal __bridge-casted sampler resource objects +SOKOL_GFX_API_DECL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp); +// Metal: get internal __bridge-casted shader resource objects +SOKOL_GFX_API_DECL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd); +// Metal: get internal __bridge-casted pipeline resource objects +SOKOL_GFX_API_DECL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip); + +// WebGPU: return WGPUDevice object +SOKOL_GFX_API_DECL const void* sg_wgpu_device(void); +// WebGPU: return WGPUQueue object +SOKOL_GFX_API_DECL const void* sg_wgpu_queue(void); +// WebGPU: return this frame's WGPUCommandEncoder +SOKOL_GFX_API_DECL const void* sg_wgpu_command_encoder(void); +// WebGPU: return WGPURenderPassEncoder of current pass (returns 0 when outside pass or in a compute pass) +SOKOL_GFX_API_DECL const void* sg_wgpu_render_pass_encoder(void); +// WebGPU: return WGPUComputePassEncoder of current pass (returns 0 when outside pass or in a render pass) +SOKOL_GFX_API_DECL const void* sg_wgpu_compute_pass_encoder(void); +// WebGPU: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf); +// WebGPU: get internal image resource objects +SOKOL_GFX_API_DECL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img); +// WebGPU: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp); +// WebGPU: get internal shader resource objects +SOKOL_GFX_API_DECL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd); +// WebGPU: get internal pipeline resource objects +SOKOL_GFX_API_DECL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip); +// WebGPU: get internal view resource objects +SOKOL_GFX_API_DECL sg_wgpu_view_info sg_wgpu_query_view_info(sg_view view); + +// GL: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf); +// GL: get internal image resource objects +SOKOL_GFX_API_DECL sg_gl_image_info sg_gl_query_image_info(sg_image img); +// GL: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp); +// GL: get internal shader resource objects +SOKOL_GFX_API_DECL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd); +// GL: get internal view resource objects +SOKOL_GFX_API_DECL sg_gl_view_info sg_gl_query_view_info(sg_view view); + +#ifdef __cplusplus +} // extern "C" + +// reference-based equivalents for c++ +inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } + +inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } +inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); } +inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } +inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } +inline sg_view sg_make_view(const sg_view_desc& desc) { return sg_make_view(&desc); } +inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } + +inline void sg_begin_pass(const sg_pass& pass) { return sg_begin_pass(&pass); } +inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } +inline void sg_apply_uniforms(int ub_slot, const sg_range& data) { return sg_apply_uniforms(ub_slot, &data); } + +inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } +inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_defaults(&desc); } +inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } +inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } +inline sg_view_desc sg_query_view_defaults(const sg_view_desc& desc) { return sg_query_view_defaults(&desc); } + +inline void sg_init_buffer(sg_buffer buf, const sg_buffer_desc& desc) { return sg_init_buffer(buf, &desc); } +inline void sg_init_image(sg_image img, const sg_image_desc& desc) { return sg_init_image(img, &desc); } +inline void sg_init_sampler(sg_sampler smp, const sg_sampler_desc& desc) { return sg_init_sampler(smp, &desc); } +inline void sg_init_shader(sg_shader shd, const sg_shader_desc& desc) { return sg_init_shader(shd, &desc); } +inline void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip, &desc); } +inline void sg_init_view(sg_view view, const sg_view_desc& desc) { return sg_init_view(view, &desc); } + +inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } +inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } +#endif +#endif // SOKOL_GFX_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_GFX_IMPL +#define SOKOL_GFX_IMPL_INCLUDED (1) + +#if !(defined(SOKOL_GLCORE)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_VULKAN)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU, SOKOL_VULKAN or SOKOL_DUMMY_BACKEND" +#endif +#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, qsort +#include // memset +#include // FLT_MAX + +#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_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#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 + +#if defined(SOKOL_TRACE_HOOKS) +#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } +#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } +#else +#define _SG_TRACE_ARGS(fn, ...) +#define _SG_TRACE_NOARGS(fn) +#endif + +#ifdef __cplusplus +#define _SG_STRUCT(TYPE, NAME) TYPE NAME = {} +#else +#define _SG_STRUCT(TYPE, NAME) TYPE NAME = {0} +#endif + +// default clear values +#ifndef SG_DEFAULT_CLEAR_RED +#define SG_DEFAULT_CLEAR_RED (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_GREEN +#define SG_DEFAULT_CLEAR_GREEN (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_BLUE +#define SG_DEFAULT_CLEAR_BLUE (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_ALPHA +#define SG_DEFAULT_CLEAR_ALPHA (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_DEPTH +#define SG_DEFAULT_CLEAR_DEPTH (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_STENCIL +#define SG_DEFAULT_CLEAR_STENCIL (0) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union (needed by d3d11.h) +#pragma warning(disable:4054) // 'type cast': from function pointer +#pragma warning(disable:4055) // 'type cast': from data pointer +#endif + +#if defined(SOKOL_D3D11) + #if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunknown-pragmas" + #endif + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #if defined(__GNUC__) + #pragma GCC diagnostic pop + #endif +#elif defined(SOKOL_METAL) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #define _SG_TARGET_MACOS (1) + #else + #define _SG_TARGET_IOS (1) + #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR + #define _SG_TARGET_IOS_SIMULATOR (1) + #endif + #endif + #import + #import // needed for CAMetalDrawable +#elif defined(SOKOL_WGPU) + #include + #if defined(__EMSCRIPTEN__) + #include + #endif +#elif defined(SOKOL_VULKAN) + #include +#elif defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) + #define _SOKOL_ANY_GL (1) + + // include platform specific GL headers (or on Win32: use an embedded GL loader) + #if !defined(SOKOL_EXTERNAL_GL_LOADER) + #if defined(_WIN32) + #if defined(SOKOL_GLCORE) + #define _SOKOL_USE_WIN32_GL_LOADER (1) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #pragma comment (lib, "kernel32") // GetProcAddress() + #endif + #elif defined(__APPLE__) + #include + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #include + #else + #include + #include + #endif + #elif defined(__EMSCRIPTEN__) + #if defined(SOKOL_GLES3) + #include + #endif + #elif defined(__ANDROID__) + #if __ANDROID_API__ >= 24 + #include + #else + #include + #endif + #elif defined(__linux__) || defined(__unix__) + #if defined(SOKOL_GLCORE) + #define GL_GLEXT_PROTOTYPES + #include + #else + #include + #include + #endif + #endif + #endif + + // broad GL feature availability defines (DON'T merge this into the above ifdef-block!) + #if defined(_WIN32) + #define _SOKOL_GL_HAS_COLORMASKI (1) + #if defined(GL_VERSION_4_3) || defined(_SOKOL_USE_WIN32_GL_LOADER) + #define _SOKOL_GL_HAS_COMPUTE (1) + #define _SOKOL_GL_HAS_TEXVIEWS (1) + #endif + #if defined(GL_VERSION_4_2) || defined(_SOKOL_USE_WIN32_GL_LOADER) + #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #define _SOKOL_GL_HAS_BASEINSTANCE (1) + #endif + #if defined(GL_VERSION_3_3) || defined(_SOKOL_USE_WIN32_GL_LOADER) + #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1) + #endif + #if defined(GL_VERSION_3_2) || defined(_SOKOL_USE_WIN32_GL_LOADER) + #define _SOKOL_GL_HAS_BASEVERTEX (1) + #endif + #elif defined(__APPLE__) + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #define _SOKOL_GL_HAS_COLORMASKI (1) + #define _SOKOL_GL_HAS_BASEVERTEX (1) + #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1) + #else + #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #endif + #elif defined(__EMSCRIPTEN__) + #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #elif defined(__ANDROID__) + #define _SOKOL_GL_HAS_COMPUTE (1) + #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #if defined(GL_ES_VERSION_3_2) + #define _SOKOL_GL_HAS_COLORMASKI (1) + #endif + #elif defined(__linux__) || defined(__unix__) + #define _SOKOL_GL_HAS_COLORMASKI (1) + #if defined(SOKOL_GLCORE) + #if defined(GL_VERSION_4_3) + #define _SOKOL_GL_HAS_COMPUTE (1) + #define _SOKOL_GL_HAS_TEXVIEWS (1) + #endif + #if defined(GL_VERSION_4_2) + #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #define _SOKOL_GL_HAS_BASEINSTANCE (1) + #endif + #if defined(GL_VERSION_3_3) + #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1) + #endif + #if defined(GL_VERSION_3_2) + #define _SOKOL_GL_HAS_BASEVERTEX (1) + #endif + #else + #define _SOKOL_GL_HAS_COMPUTE (1) + #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #define _SOKOL_GL_HAS_BASEVERTEX (1) + #endif + #endif + + // optional GL loader definitions (only on Win32) + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + #define __gl_h_ 1 + #define __gl32_h_ 1 + #define __gl31_h_ 1 + #define __GL_H__ 1 + #define __glext_h_ 1 + #define __GLEXT_H_ 1 + #define __gltypes_h_ 1 + #define __glcorearb_h_ 1 + #define __gl_glcorearb_h_ 1 + #define GL_APIENTRY APIENTRY + + typedef unsigned int GLenum; + typedef unsigned int GLuint; + typedef int GLsizei; + typedef char GLchar; + typedef ptrdiff_t GLintptr; + typedef ptrdiff_t GLsizeiptr; + typedef double GLclampd; + typedef unsigned short GLushort; + typedef unsigned char GLubyte; + typedef unsigned char GLboolean; + typedef uint64_t GLuint64; + typedef double GLdouble; + typedef unsigned short GLhalf; + typedef float GLclampf; + typedef unsigned int GLbitfield; + typedef signed char GLbyte; + typedef short GLshort; + typedef void GLvoid; + typedef int64_t GLint64; + typedef float GLfloat; + typedef int GLint; + #define GL_INT_2_10_10_10_REV 0x8D9F + #define GL_R32F 0x822E + #define GL_PROGRAM_POINT_SIZE 0x8642 + #define GL_DEPTH_ATTACHMENT 0x8D00 + #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A + #define GL_COLOR_ATTACHMENT0 0x8CE0 + #define GL_R16F 0x822D + #define GL_DRAW_FRAMEBUFFER 0x8CA9 + #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 + #define GL_NUM_EXTENSIONS 0x821D + #define GL_INFO_LOG_LENGTH 0x8B84 + #define GL_VERTEX_SHADER 0x8B31 + #define GL_INCR 0x1E02 + #define GL_DYNAMIC_DRAW 0x88E8 + #define GL_STATIC_DRAW 0x88E4 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 + #define GL_TEXTURE_CUBE_MAP 0x8513 + #define GL_FUNC_SUBTRACT 0x800A + #define GL_FUNC_REVERSE_SUBTRACT 0x800B + #define GL_CONSTANT_COLOR 0x8001 + #define GL_DECR_WRAP 0x8508 + #define GL_R8 0x8229 + #define GL_LINEAR_MIPMAP_LINEAR 0x2703 + #define GL_ELEMENT_ARRAY_BUFFER 0x8893 + #define GL_SHORT 0x1402 + #define GL_DEPTH_TEST 0x0B71 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 + #define GL_LINK_STATUS 0x8B82 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 + #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E + #define GL_RGBA16F 0x881A + #define GL_CONSTANT_ALPHA 0x8003 + #define GL_READ_FRAMEBUFFER 0x8CA8 + #define GL_TEXTURE0 0x84C0 + #define GL_TEXTURE_MIN_LOD 0x813A + #define GL_CLAMP_TO_EDGE 0x812F + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_TEXTURE_WRAP_R 0x8072 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_NEAREST_MIPMAP_NEAREST 0x2700 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 + #define GL_SRC_ALPHA_SATURATE 0x0308 + #define GL_STREAM_DRAW 0x88E0 + #define GL_ONE 1 + #define GL_NEAREST_MIPMAP_LINEAR 0x2702 + #define GL_RGB10_A2 0x8059 + #define GL_RGBA8 0x8058 + #define GL_SRGB8_ALPHA8 0x8C43 + #define GL_RGBA4 0x8056 + #define GL_RGB8 0x8051 + #define GL_ARRAY_BUFFER 0x8892 + #define GL_STENCIL 0x1802 + #define GL_TEXTURE_2D 0x0DE1 + #define GL_DEPTH 0x1801 + #define GL_FRONT 0x0404 + #define GL_STENCIL_BUFFER_BIT 0x00000400 + #define GL_REPEAT 0x2901 + #define GL_RGBA 0x1908 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 + #define GL_DECR 0x1E03 + #define GL_FRAGMENT_SHADER 0x8B30 + #define GL_COMPUTE_SHADER 0x91B9 + #define GL_FLOAT 0x1406 + #define GL_TEXTURE_MAX_LOD 0x813B + #define GL_DEPTH_COMPONENT 0x1902 + #define GL_ONE_MINUS_DST_ALPHA 0x0305 + #define GL_COLOR 0x1800 + #define GL_TEXTURE_2D_ARRAY 0x8C1A + #define GL_TRIANGLES 0x0004 + #define GL_UNSIGNED_BYTE 0x1401 + #define GL_TEXTURE_MAG_FILTER 0x2800 + #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 + #define GL_NONE 0 + #define GL_SRC_COLOR 0x0300 + #define GL_SRC1_ALPHA 0x8589 + #define GL_SRC1_COLOR 0x88F9 + #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB + #define GL_ONE_MINUS_SRC1_COLOR 0x88FA + #define GL_BYTE 0x1400 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A + #define GL_LINE_STRIP 0x0003 + #define GL_TEXTURE_3D 0x806F + #define GL_CW 0x0900 + #define GL_LINEAR 0x2601 + #define GL_RENDERBUFFER 0x8D41 + #define GL_GEQUAL 0x0206 + #define GL_COLOR_BUFFER_BIT 0x00004000 + #define GL_RGBA32F 0x8814 + #define GL_BLEND 0x0BE2 + #define GL_ONE_MINUS_SRC_ALPHA 0x0303 + #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 + #define GL_TEXTURE_WRAP_T 0x2803 + #define GL_TEXTURE_WRAP_S 0x2802 + #define GL_TEXTURE_MIN_FILTER 0x2801 + #define GL_LINEAR_MIPMAP_NEAREST 0x2701 + #define GL_EXTENSIONS 0x1F03 + #define GL_NO_ERROR 0 + #define GL_REPLACE 0x1E01 + #define GL_KEEP 0x1E00 + #define GL_CCW 0x0901 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 + #define GL_RGB 0x1907 + #define GL_TRIANGLE_STRIP 0x0005 + #define GL_FALSE 0 + #define GL_ZERO 0 + #define GL_CULL_FACE 0x0B44 + #define GL_INVERT 0x150A + #define GL_INT 0x1404 + #define GL_UNSIGNED_INT 0x1405 + #define GL_UNSIGNED_SHORT 0x1403 + #define GL_NEAREST 0x2600 + #define GL_SCISSOR_TEST 0x0C11 + #define GL_LEQUAL 0x0203 + #define GL_STENCIL_TEST 0x0B90 + #define GL_DITHER 0x0BD0 + #define GL_DEPTH_COMPONENT32F 0x8CAC + #define GL_EQUAL 0x0202 + #define GL_FRAMEBUFFER 0x8D40 + #define GL_RGB5 0x8050 + #define GL_LINES 0x0001 + #define GL_DEPTH_BUFFER_BIT 0x00000100 + #define GL_SRC_ALPHA 0x0302 + #define GL_INCR_WRAP 0x8507 + #define GL_LESS 0x0201 + #define GL_MULTISAMPLE 0x809D + #define GL_FRAMEBUFFER_BINDING 0x8CA6 + #define GL_BACK 0x0405 + #define GL_ALWAYS 0x0207 + #define GL_FUNC_ADD 0x8006 + #define GL_ONE_MINUS_DST_COLOR 0x0307 + #define GL_NOTEQUAL 0x0205 + #define GL_DST_COLOR 0x0306 + #define GL_COMPILE_STATUS 0x8B81 + #define GL_RED 0x1903 + #define GL_DST_ALPHA 0x0304 + #define GL_RGB5_A1 0x8057 + #define GL_GREATER 0x0204 + #define GL_POLYGON_OFFSET_FILL 0x8037 + #define GL_TRUE 1 + #define GL_NEVER 0x0200 + #define GL_POINTS 0x0000 + #define GL_ONE_MINUS_SRC_COLOR 0x0301 + #define GL_MIRRORED_REPEAT 0x8370 + #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 + #define GL_RGB16UI 0x8D77 + #define GL_RGBA8UI 0x8D7C + #define GL_RGB8UI 0x8D7D + #define GL_RGBA32I 0x8D82 + #define GL_RGB32I 0x8D83 + #define GL_RGBA16I 0x8D88 + #define GL_RGB16I 0x8D89 + #define GL_RGBA8I 0x8D8E + #define GL_RGB8I 0x8D8F + #define GL_RED_INTEGER 0x8D94 + #define GL_RG 0x8227 + #define GL_RG_INTEGER 0x8228 + #define GL_R8 0x8229 + #define GL_R16 0x822A + #define GL_RG8 0x822B + #define GL_RG16 0x822C + #define GL_R16F 0x822D + #define GL_R32F 0x822E + #define GL_RG16F 0x822F + #define GL_RG32F 0x8230 + #define GL_R8I 0x8231 + #define GL_R8UI 0x8232 + #define GL_R16I 0x8233 + #define GL_R16UI 0x8234 + #define GL_R32I 0x8235 + #define GL_R32UI 0x8236 + #define GL_RG8I 0x8237 + #define GL_RG8UI 0x8238 + #define GL_RG16I 0x8239 + #define GL_RG16UI 0x823A + #define GL_RG32I 0x823B + #define GL_RG32UI 0x823C + #define GL_RGBA_INTEGER 0x8D99 + #define GL_R8_SNORM 0x8F94 + #define GL_RG8_SNORM 0x8F95 + #define GL_RGB8_SNORM 0x8F96 + #define GL_RGBA8_SNORM 0x8F97 + #define GL_R16_SNORM 0x8F98 + #define GL_RG16_SNORM 0x8F99 + #define GL_RGB16_SNORM 0x8F9A + #define GL_RGBA16_SNORM 0x8F9B + #define GL_RGBA16 0x805B + #define GL_MAX_TEXTURE_SIZE 0x0D33 + #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C + #define GL_MAX_3D_TEXTURE_SIZE 0x8073 + #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF + #define GL_MAX_VERTEX_ATTRIBS 0x8869 + #define GL_CLAMP_TO_BORDER 0x812D + #define GL_TEXTURE_BORDER_COLOR 0x1004 + #define GL_CURRENT_PROGRAM 0x8B8D + #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A + #define GL_UNPACK_ALIGNMENT 0x0CF5 + #define GL_FRAMEBUFFER_SRGB 0x8DB9 + #define GL_TEXTURE_COMPARE_MODE 0x884C + #define GL_TEXTURE_COMPARE_FUNC 0x884D + #define GL_COMPARE_REF_TO_TEXTURE 0x884E + #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F + #define GL_TEXTURE_MAX_LEVEL 0x813D + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 + #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 + #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD + #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 + #define GL_MAJOR_VERSION 0x821B + #define GL_MINOR_VERSION 0x821C + #define GL_TEXTURE_2D_MULTISAMPLE 0x9100 + #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 + #define GL_SHADER_STORAGE_BARRIER_BIT 0x2000 + #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 + #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 + #define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 + #define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 + #define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 + #define GL_MIN 0x8007 + #define GL_MAX 0x8008 + #define GL_WRITE_ONLY 0x88B9 + #define GL_READ_WRITE 0x88BA + #define GL_MAX_DRAW_BUFFERS 0x8824 + #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 + #define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD + #define GL_MAX_IMAGE_UNITS 0x8F38 + #endif + + #ifndef GL_UNSIGNED_INT_2_10_10_10_REV + #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 + #endif + #ifndef GL_UNSIGNED_INT_24_8 + #define GL_UNSIGNED_INT_24_8 0x84FA + #endif + #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + #endif + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F + #endif + #ifndef GL_COMPRESSED_RED_RGTC1 + #define GL_COMPRESSED_RED_RGTC1 0x8DBB + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_RGTC1 + #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC + #endif + #ifndef GL_COMPRESSED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE + #endif + #ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F + #endif + #ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 + #endif + #ifndef GL_COMPRESSED_SRGB8_ETC2 + #define GL_COMPRESSED_SRGB8_ETC2 0x9275 + #endif + #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + #endif + #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 + #endif + #ifndef GL_COMPRESSED_R11_EAC + #define GL_COMPRESSED_R11_EAC 0x9270 + #endif + #ifndef GL_COMPRESSED_SIGNED_R11_EAC + #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 + #endif + #ifndef GL_COMPRESSED_RG11_EAC + #define GL_COMPRESSED_RG11_EAC 0x9272 + #endif + #ifndef GL_COMPRESSED_SIGNED_RG11_EAC + #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 + #endif + #ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 + #endif + #ifndef GL_DEPTH24_STENCIL8 + #define GL_DEPTH24_STENCIL8 0x88F0 + #endif + #ifndef GL_HALF_FLOAT + #define GL_HALF_FLOAT 0x140B + #endif + #ifndef GL_DEPTH_STENCIL + #define GL_DEPTH_STENCIL 0x84F9 + #endif + #ifndef GL_LUMINANCE + #define GL_LUMINANCE 0x1909 + #endif + #ifndef GL_COMPUTE_SHADER + #define GL_COMPUTE_SHADER 0x91B9 + #endif + #ifndef _SG_GL_CHECK_ERROR + #if defined(__EMSCRIPTEN__) + // generally turn off glGetError() on WASM, it's a too big performance hit + // and WebGL provides much better diagnostics anyway + #define _SG_GL_CHECK_ERROR() + #elif defined(SOKOL_DEBUG) + // make sure that glGetError() is only called in debug mode + #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } + #else + #define _SG_GL_CHECK_ERROR() + #endif + #endif + // make some GL constants generally available to simplify compilation, + // use of those constants will be filtered by runtime flags + #ifndef GL_SHADER_STORAGE_BUFFER + #define GL_SHADER_STORAGE_BUFFER 0x90D2 + #endif +#endif + +#if defined(SOKOL_GLES3) + // on WebGL2, GL_FRAMEBUFFER_UNDEFINED technically doesn't exist (it is defined + // in the Emscripten headers, but may not exist in other WebGL2 shims) + // see: https://github.com/floooh/sokol/pull/933 + #ifndef GL_FRAMEBUFFER_UNDEFINED + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #endif +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs + +typedef struct { int x, y, w, h; } _sg_recti_t; +typedef struct { int width, height; } _sg_dimi_t; + +// resource pool slots +typedef struct { + uint32_t id; + uint32_t uninit_count; + sg_resource_state state; +} _sg_slot_t; + +// resource pool housekeeping struct +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sg_pool_t; + +// resource hazard tracking struct +typedef struct { + int num_slots; + int cur_slot; + uint32_t* slots; // tracked unique resource ids + uint32_t occupy_num_bytes; // size of occupy_bits array in bytes + uint8_t* occupy_bits; // one set bit for each unique resource (idx = (id & 0xFFFF) >> 3) +} _sg_track_t; + +// resource func forward decls +struct _sg_buffer_s; +struct _sg_image_s; +struct _sg_sampler_s; +struct _sg_shader_s; +struct _sg_pipeline_s; +struct _sg_view_s; + +// a general resource slot reference useful for caches +typedef struct _sg_sref_s { + uint32_t id; + uint32_t uninit_count; +} _sg_sref_t; + +// safe (in debug mode) internal resource references +typedef struct _sg_buffer_ref_s { + struct _sg_buffer_s* ptr; + _sg_sref_t sref; +} _sg_buffer_ref_t; + +typedef struct _sg_image_ref_s { + struct _sg_image_s* ptr; + _sg_sref_t sref; +} _sg_image_ref_t; + +typedef struct _sg_sampler_ref_t { + struct _sg_sampler_s* ptr; + _sg_sref_t sref; +} _sg_sampler_ref_t; + +typedef struct _sg_shader_ref_s { + struct _sg_shader_s* ptr; + _sg_sref_t sref; +} _sg_shader_ref_t; + +typedef struct _sg_pipeline_ref_s { + struct _sg_pipeline_s* ptr; + _sg_sref_t sref; +} _sg_pipeline_ref_t; + +typedef struct _sg_view_ref_s { + struct _sg_view_s* ptr; + _sg_sref_t sref; +} _sg_view_ref_t; + +// constants +enum { + _SG_STRING_SIZE = 32, + _SG_SLOT_SHIFT = 16, + _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, + _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), + _SG_DEFAULT_BUFFER_POOL_SIZE = 128, + _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SAMPLER_POOL_SIZE = 64, + _SG_DEFAULT_SHADER_POOL_SIZE = 32, + _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, + _SG_DEFAULT_VIEW_POOL_SIZE = 256, + _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, + _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, + _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE = 1024, + _SG_DEFAULT_VK_COPY_STAGING_SIZE = (4 * 1024 * 1024), + _SG_DEFAULT_VK_STREAM_STAGING_SIZE = (16 * 1024 * 1024), + _SG_DEFAULT_VK_DESCRIPTOR_BUFFER_SIZE = (16 * 1024 * 1024), + _SG_MAX_STORAGEBUFFER_BINDINGS_PER_STAGE = SG_MAX_VIEW_BINDSLOTS, + _SG_MAX_STORAGEIMAGE_BINDINGS_PER_STAGE = SG_MAX_VIEW_BINDSLOTS, + _SG_MAX_TEXTURE_BINDINGS_PER_STAGE = SG_MAX_VIEW_BINDSLOTS, + _SG_MAX_UNIFORMBLOCK_BINDINGS_PER_STAGE = 8, +}; + +// fixed-size string +typedef struct { + char buf[_SG_STRING_SIZE]; +} _sg_str_t; + +typedef struct { + int size; + int append_pos; + bool append_overflow; + uint32_t update_frame_index; + uint32_t append_frame_index; + int num_slots; + int active_slot; + sg_buffer_usage usage; +} _sg_buffer_common_t; + +typedef struct { + uint32_t upd_frame_index; + int num_slots; + int active_slot; + sg_image_type type; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_image_usage usage; + sg_pixel_format pixel_format; + int sample_count; +} _sg_image_common_t; + +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; +} _sg_sampler_common_t; + +typedef struct { + sg_shader_attr_base_type base_type; +} _sg_shader_attr_t; + +typedef struct { + sg_shader_stage stage; + uint32_t size; +} _sg_shader_uniform_block_t; + +typedef struct { + sg_shader_stage stage; + sg_view_type view_type; + sg_image_type image_type; + sg_pixel_format access_format; + sg_image_sample_type sample_type; + bool sbuf_readonly; + bool simg_writeonly; + bool multisampled; +} _sg_shader_view_t; + +typedef struct { + sg_shader_stage stage; + sg_sampler_type sampler_type; +} _sg_shader_sampler_t; + +typedef struct { + sg_shader_stage stage; + uint8_t view_slot; + uint8_t sampler_slot; +} _sg_shader_texture_sampler_t; + +typedef struct { + uint32_t required_bindings_and_uniforms; + bool is_compute; + _sg_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_shader_uniform_block_t uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + _sg_shader_view_t views[SG_MAX_VIEW_BINDSLOTS]; + _sg_shader_sampler_t samplers[SG_MAX_SAMPLER_BINDSLOTS]; + _sg_shader_texture_sampler_t texture_samplers[SG_MAX_TEXTURE_SAMPLER_PAIRS]; +} _sg_shader_common_t; + +typedef struct { + bool vertex_buffer_layout_active[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + bool use_instanced_draw; + bool is_compute; + uint32_t required_bindings_and_uniforms; + _sg_shader_ref_t shader; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_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; + sg_color blend_color; + bool alpha_to_coverage_enabled; +} _sg_pipeline_common_t; + +typedef struct { + _sg_buffer_ref_t ref; + int offset; +} _sg_buffer_view_common_t; + +typedef struct { + _sg_image_ref_t ref; + int mip_level; + int slice; + int mip_level_count; + int slice_count; +} _sg_image_view_common_t; + +typedef struct { + sg_view_type type; + _sg_buffer_view_common_t buf; + _sg_image_view_common_t img; +} _sg_view_common_t; + +#if defined(SOKOL_DUMMY_BACKEND) +typedef struct _sg_buffer_s { + _sg_slot_t slot; + _sg_buffer_common_t cmn; +} _sg_dummy_buffer_t; +typedef _sg_dummy_buffer_t _sg_buffer_t; + +typedef struct _sg_image_s { + _sg_slot_t slot; + _sg_image_common_t cmn; +} _sg_dummy_image_t; +typedef _sg_dummy_image_t _sg_image_t; + +typedef struct _sg_sampler_s { + _sg_slot_t slot; + _sg_sampler_common_t cmn; +} _sg_dummy_sampler_t; +typedef _sg_dummy_sampler_t _sg_sampler_t; + +typedef struct _sg_shader_s { + _sg_slot_t slot; + _sg_shader_common_t cmn; +} _sg_dummy_shader_t; +typedef _sg_dummy_shader_t _sg_shader_t; + +typedef struct _sg_pipeline_s { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; +} _sg_dummy_pipeline_t; +typedef _sg_dummy_pipeline_t _sg_pipeline_t; + +typedef struct _sg_view_s { + _sg_slot_t slot; + _sg_view_common_t cmn; +} _sg_dummy_view_t; +typedef _sg_dummy_view_t _sg_view_t; + +#elif defined(_SOKOL_ANY_GL) + +typedef enum { + _SG_GL_GPUDIRTY_VERTEXBUFFER = (1<<0), + _SG_GL_GPUDIRTY_INDEXBUFFER = (1<<1), + _SG_GL_GPUDIRTY_STORAGEBUFFER = (1<<2), + _SG_GL_GPUDIRTY_TEXTURE = (1<<3), + _SG_GL_GPUDIRTY_STORAGEIMAGE = (1<<4), + _SG_GL_GPUDIRTY_ATTACHMENT = (1<<5), + _SG_GL_GPUDIRTY_BUFFER_ALL = _SG_GL_GPUDIRTY_VERTEXBUFFER | _SG_GL_GPUDIRTY_INDEXBUFFER | _SG_GL_GPUDIRTY_STORAGEBUFFER, + _SG_GL_GPUDIRTY_IMAGE_ALL = _SG_GL_GPUDIRTY_TEXTURE | _SG_GL_GPUDIRTY_STORAGEIMAGE | _SG_GL_GPUDIRTY_ATTACHMENT, +} _sg_gl_gpudirty_t; + +typedef struct _sg_buffer_s { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + GLuint buf[SG_NUM_INFLIGHT_FRAMES]; + uint8_t gpu_dirty_flags; // combination of _sg_gl_gpudirty_t flags + bool injected; // if true, external buffers were injected with sg_buffer_desc.gl_buffers + } gl; +} _sg_gl_buffer_t; +typedef _sg_gl_buffer_t _sg_buffer_t; + +typedef struct _sg_image_s { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + GLenum target; + GLuint tex[SG_NUM_INFLIGHT_FRAMES]; + uint8_t gpu_dirty_flags; // combination of _sg_gl_gpudirty_flags + bool injected; // if true, external textures were injected with sg_image_desc.gl_textures + } gl; +} _sg_gl_image_t; +typedef _sg_gl_image_t _sg_image_t; + +typedef struct _sg_sampler_s { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + GLuint smp; + bool injected; // true if external sampler was injects in sg_sampler_desc.gl_sampler + } gl; +} _sg_gl_sampler_t; +typedef _sg_gl_sampler_t _sg_sampler_t; + +typedef struct { + GLint gl_loc; + sg_uniform_type type; + uint16_t count; + uint16_t offset; +} _sg_gl_uniform_t; + +typedef struct { + int num_uniforms; + _sg_gl_uniform_t uniforms[SG_MAX_UNIFORMBLOCK_MEMBERS]; +} _sg_gl_uniform_block_t; + +typedef struct { + _sg_str_t name; +} _sg_gl_shader_attr_t; + +typedef struct _sg_shader_s { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + GLuint prog; + _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_gl_uniform_block_t uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + uint8_t sbuf_binding[SG_MAX_VIEW_BINDSLOTS]; + uint8_t simg_binding[SG_MAX_VIEW_BINDSLOTS]; + int8_t tex_slot[SG_MAX_TEXTURE_SAMPLER_PAIRS]; // GL texture unit index + } gl; +} _sg_gl_shader_t; +typedef _sg_gl_shader_t _sg_shader_t; + +typedef struct { + int8_t vb_index; // -1 if attr is not enabled + int8_t divisor; // -1 if not initialized + uint8_t stride; + uint8_t size; + uint8_t normalized; + int offset; + GLenum type; + sg_shader_attr_base_type base_type; +} _sg_gl_attr_t; + +typedef struct _sg_pipeline_s { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + struct { + _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_depth_state depth; + sg_stencil_state stencil; + sg_primitive_type primitive_type; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + bool alpha_to_coverage_enabled; + } gl; +} _sg_gl_pipeline_t; +typedef _sg_gl_pipeline_t _sg_pipeline_t; + +typedef struct _sg_view_s { + _sg_slot_t slot; + _sg_view_common_t cmn; + struct { + GLuint tex_view[SG_NUM_INFLIGHT_FRAMES]; // only if sg_features.gl_texture_views + GLuint msaa_render_buffer; // only if !msaa_texture_bindings + GLuint msaa_resolve_frame_buffer; + } gl; +} _sg_gl_view_t; +typedef _sg_gl_view_t _sg_view_t; + +typedef struct { + _sg_gl_attr_t gl_attr; + GLuint gl_vbuf; +} _sg_gl_cache_attr_t; + +typedef struct { + GLenum target; + GLuint texture; + GLuint sampler; +} _sg_gl_cache_texture_sampler_bind_slot; + +#define _SG_GL_MAX_SBUF_BINDINGS (_SG_MAX_STORAGEBUFFER_BINDINGS_PER_STAGE) +#define _SG_GL_MAX_SIMG_BINDINGS (_SG_MAX_STORAGEIMAGE_BINDINGS_PER_STAGE) +#define _SG_GL_MAX_TEX_SMP_BINDINGS (SG_MAX_TEXTURE_SAMPLER_PAIRS) +typedef struct { + sg_depth_state depth; + sg_stencil_state stencil; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + bool polygon_offset_enabled; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + GLuint vertex_buffer; + GLuint index_buffer; + GLuint storage_buffer; // general bind point + GLuint storage_buffers[_SG_GL_MAX_SBUF_BINDINGS]; + int storage_buffer_offsets[_SG_GL_MAX_SBUF_BINDINGS]; + GLuint stored_vertex_buffer; + GLuint stored_index_buffer; + GLuint stored_storage_buffer; + GLuint prog; + _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_MAX_TEX_SMP_BINDINGS]; + _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler; + int cur_ib_offset; + GLenum cur_primitive_type; + GLenum cur_index_type; + GLenum cur_active_texture; + _sg_sref_t cur_pip; +} _sg_gl_cache_t; + +typedef struct { + bool valid; + GLuint vao; // global mutated vertex-array-object + GLuint fb; // global mutated framebuffer + _sg_gl_cache_t cache; + bool ext_anisotropic; + GLint max_anisotropy; + sg_store_action color_store_actions[SG_MAX_COLOR_ATTACHMENTS]; + sg_store_action depth_store_action; + sg_store_action stencil_store_action; + #if _SOKOL_USE_WIN32_GL_LOADER + HINSTANCE opengl32_dll; + #endif +} _sg_gl_backend_t; + +#elif defined(SOKOL_D3D11) + +typedef struct _sg_buffer_s { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + ID3D11Buffer* buf; + } d3d11; +} _sg_d3d11_buffer_t; +typedef _sg_d3d11_buffer_t _sg_buffer_t; + +typedef struct _sg_image_s { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + DXGI_FORMAT format; + ID3D11Texture2D* tex2d; + ID3D11Texture3D* tex3d; + ID3D11Resource* res; // either tex2d or tex3d + } d3d11; +} _sg_d3d11_image_t; +typedef _sg_d3d11_image_t _sg_image_t; + +typedef struct _sg_sampler_s { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_sampler_t; +typedef _sg_d3d11_sampler_t _sg_sampler_t; + +typedef struct { + _sg_str_t sem_name; + int sem_index; +} _sg_d3d11_shader_attr_t; + +#define _SG_D3D11_MAX_TEXTUREARRAY_LAYERS (2048) +#define _SG_D3D11_MAX_TEXTURE_SUBRESOURCES (SG_MAX_MIPMAPS * _SG_D3D11_MAX_TEXTUREARRAY_LAYERS) +#define _SG_D3D11_MAX_STAGE_UB_BINDINGS (_SG_MAX_UNIFORMBLOCK_BINDINGS_PER_STAGE) +#define _SG_D3D11_MAX_STAGE_SRV_BINDINGS (SG_MAX_VIEW_BINDSLOTS) +#define _SG_D3D11_MAX_STAGE_UAV_BINDINGS (SG_MAX_VIEW_BINDSLOTS) +#define _SG_D3D11_MAX_STAGE_SMP_BINDINGS (SG_MAX_SAMPLER_BINDSLOTS) + +typedef struct _sg_shader_s { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + ID3D11VertexShader* vs; + ID3D11PixelShader* fs; + ID3D11ComputeShader* cs; + void* vs_blob; + size_t vs_blob_length; + uint8_t ub_register_b_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + uint8_t view_register_t_n[SG_MAX_VIEW_BINDSLOTS]; + uint8_t view_register_u_n[SG_MAX_VIEW_BINDSLOTS]; + uint8_t smp_register_s_n[SG_MAX_SAMPLER_BINDSLOTS]; + ID3D11Buffer* all_cbufs[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + ID3D11Buffer* vs_cbufs[_SG_D3D11_MAX_STAGE_UB_BINDINGS]; + ID3D11Buffer* fs_cbufs[_SG_D3D11_MAX_STAGE_UB_BINDINGS]; + ID3D11Buffer* cs_cbufs[_SG_D3D11_MAX_STAGE_UB_BINDINGS]; + } d3d11; +} _sg_d3d11_shader_t; +typedef _sg_d3d11_shader_t _sg_shader_t; + +typedef struct _sg_pipeline_s { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + struct { + UINT stencil_ref; + UINT vb_strides[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + D3D_PRIMITIVE_TOPOLOGY topology; + DXGI_FORMAT index_format; + ID3D11InputLayout* il; + ID3D11RasterizerState* rs; + ID3D11DepthStencilState* dss; + ID3D11BlendState* bs; + } d3d11; +} _sg_d3d11_pipeline_t; +typedef _sg_d3d11_pipeline_t _sg_pipeline_t; + +typedef struct _sg_view_s { + _sg_slot_t slot; + _sg_view_common_t cmn; + struct { + ID3D11ShaderResourceView* srv; + ID3D11UnorderedAccessView* uav; + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + } d3d11; +} _sg_d3d11_view_t; +typedef _sg_d3d11_view_t _sg_view_t; + +typedef struct { + bool valid; + ID3D11Device* dev; + ID3D11DeviceContext* ctx; + struct { + ID3D11RenderTargetView* render_view; + ID3D11RenderTargetView* resolve_view; + } cur_swapchain; + // on-demand loaded d3dcompiler_47.dll handles + HINSTANCE d3dcompiler_dll; + bool d3dcompiler_dll_load_failed; + pD3DCompile D3DCompile_func; + // static bindings arrays + struct { + ID3D11Buffer* vbs[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + UINT vb_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + ID3D11ShaderResourceView* vs_srvs[_SG_D3D11_MAX_STAGE_SRV_BINDINGS]; + ID3D11ShaderResourceView* fs_srvs[_SG_D3D11_MAX_STAGE_SRV_BINDINGS]; + ID3D11ShaderResourceView* cs_srvs[_SG_D3D11_MAX_STAGE_SRV_BINDINGS]; + ID3D11UnorderedAccessView* cs_uavs[_SG_D3D11_MAX_STAGE_UAV_BINDINGS]; + ID3D11SamplerState* vs_smps[_SG_D3D11_MAX_STAGE_SMP_BINDINGS]; + ID3D11SamplerState* fs_smps[_SG_D3D11_MAX_STAGE_SMP_BINDINGS]; + ID3D11SamplerState* cs_smps[_SG_D3D11_MAX_STAGE_SMP_BINDINGS]; + } bnd; + // global subresourcedata array for texture updates + D3D11_SUBRESOURCE_DATA subres_data[_SG_D3D11_MAX_TEXTURE_SUBRESOURCES]; +} _sg_d3d11_backend_t; + +#elif defined(SOKOL_METAL) + +#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) +#define _SG_MTL_UB_ALIGN (256) +#else +#define _SG_MTL_UB_ALIGN (16) +#endif +#define _SG_MTL_INVALID_SLOT_INDEX (0) + +typedef struct { + uint32_t frame_index; // frame index at which it is safe to release this resource + int slot_index; +} _sg_mtl_release_item_t; + +typedef struct { + NSMutableArray* pool; + int num_slots; + int free_queue_top; + int* free_queue; + int release_queue_front; + int release_queue_back; + _sg_mtl_release_item_t* release_queue; +} _sg_mtl_idpool_t; + +typedef struct _sg_buffer_s { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + int buf[SG_NUM_INFLIGHT_FRAMES]; // index into _sg_mtl_pool + } mtl; +} _sg_mtl_buffer_t; +typedef _sg_mtl_buffer_t _sg_buffer_t; + +typedef struct _sg_image_s { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + int tex[SG_NUM_INFLIGHT_FRAMES]; + } mtl; +} _sg_mtl_image_t; +typedef _sg_mtl_image_t _sg_image_t; + +typedef struct _sg_sampler_s { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + int sampler_state; + } mtl; +} _sg_mtl_sampler_t; +typedef _sg_mtl_sampler_t _sg_sampler_t; + +typedef struct { + int mtl_lib; + int mtl_func; +} _sg_mtl_shader_func_t; + +typedef struct _sg_shader_s { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_mtl_shader_func_t vertex_func; + _sg_mtl_shader_func_t fragment_func; + _sg_mtl_shader_func_t compute_func; + MTLSize threads_per_threadgroup; + uint8_t ub_buffer_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + uint8_t view_buffer_texture_n[SG_MAX_VIEW_BINDSLOTS]; + uint8_t smp_sampler_n[SG_MAX_SAMPLER_BINDSLOTS]; + } mtl; +} _sg_mtl_shader_t; +typedef _sg_mtl_shader_t _sg_shader_t; + +typedef struct _sg_pipeline_s { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + struct { + MTLPrimitiveType prim_type; + int index_size; + MTLIndexType index_type; + MTLCullMode cull_mode; + MTLWinding winding; + uint32_t stencil_ref; + MTLSize threads_per_threadgroup; + int cps; // MTLComputePipelineState + int rps; // MTLRenderPipelineState + int dss; // MTLDepthStencilState + } mtl; +} _sg_mtl_pipeline_t; +typedef _sg_mtl_pipeline_t _sg_pipeline_t; + +typedef struct _sg_view_s { + _sg_slot_t slot; + _sg_view_common_t cmn; + struct { + int tex_view[SG_NUM_INFLIGHT_FRAMES]; + } mtl; +} _sg_mtl_view_t; +typedef _sg_mtl_view_t _sg_view_t; + +// resource binding state cache +// +// NOTE: reserved buffer bindslot ranges: +// - 0..<=7: uniform buffer bindings +// - 8..<=22: storage buffer bindings +// - 23..<=30: vertex buffer bindings +// +#define _SG_MTL_MAX_STAGE_BUFFER_BINDINGS (31) // see: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf +#define _SG_MTL_MAX_STAGE_UB_BINDINGS (_SG_MAX_UNIFORMBLOCK_BINDINGS_PER_STAGE) +#define _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS (_SG_MTL_MAX_STAGE_BUFFER_BINDINGS - SG_MAX_VERTEXBUFFER_BINDSLOTS) +#define _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS (SG_MAX_VIEW_BINDSLOTS) +#define _SG_MTL_MAX_STAGE_SAMPLER_BINDINGS (SG_MAX_SAMPLER_BINDSLOTS) + +typedef struct { + _sg_sref_t sref; + int active_slot; + int offset; +} _sg_mtl_cache_buf_t; + +typedef struct { + _sg_sref_t sref; + int active_slot; +} _sg_mtl_cache_tex_t; + +typedef enum { + _SG_MTL_CACHE_CMP_EQUAL = 0, + _SG_MTL_CACHE_CMP_SREF = (1<<1), + _SG_MTL_CACHE_CMP_OFFSET = (1<<2), + _SG_MTL_CACHE_CMP_ACTIVESLOT = (1<<3), +} _sg_mtl_cache_cmp_result_t; + +typedef struct { + _sg_sref_t cur_pip; + _sg_buffer_ref_t cur_ibuf; + int cur_ibuf_offset; + _sg_mtl_cache_buf_t cur_vsbufs[_SG_MTL_MAX_STAGE_BUFFER_BINDINGS]; + _sg_mtl_cache_buf_t cur_fsbufs[_SG_MTL_MAX_STAGE_BUFFER_BINDINGS]; + _sg_mtl_cache_buf_t cur_csbufs[_SG_MTL_MAX_STAGE_BUFFER_BINDINGS]; + _sg_mtl_cache_tex_t cur_vstexs[_SG_MTL_MAX_STAGE_TEXTURE_BINDINGS]; + _sg_mtl_cache_tex_t cur_fstexs[_SG_MTL_MAX_STAGE_TEXTURE_BINDINGS]; + _sg_mtl_cache_tex_t cur_cstexs[_SG_MTL_MAX_STAGE_TEXTURE_BINDINGS]; + _sg_sref_t cur_vssmps[_SG_MTL_MAX_STAGE_SAMPLER_BINDINGS]; + _sg_sref_t cur_fssmps[_SG_MTL_MAX_STAGE_SAMPLER_BINDINGS]; + _sg_sref_t cur_cssmps[_SG_MTL_MAX_STAGE_SAMPLER_BINDINGS]; +} _sg_mtl_cache_t; + +typedef struct { + bool valid; + bool use_shared_storage_mode; + uint32_t cur_frame_rotate_index; + int ub_size; + int cur_ub_offset; + uint8_t* cur_ub_base_ptr; + _sg_mtl_cache_t cache; + _sg_mtl_idpool_t idpool; + dispatch_semaphore_t sem; + id device; + id cmd_queue; + id cmd_buffer; + id render_cmd_encoder; + id compute_cmd_encoder; + id cur_drawable; + id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; +} _sg_mtl_backend_t; + +#elif defined(SOKOL_WGPU) + +#define _SG_WGPU_ROWPITCH_ALIGN (256) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) // also see WGPULimits.maxUniformBufferBindingSize +#define _SG_WGPU_MAX_BINDGROUPS (2) // 0: uniforms, 1: images, samplers, storage buffers, storage images +#define _SG_WGPU_UB_BINDGROUP_INDEX (0) +#define _SG_WGPU_VIEW_SMP_BINDGROUP_INDEX (1) +#define _SG_WGPU_MAX_UB_BINDGROUP_ENTRIES (SG_MAX_UNIFORMBLOCK_BINDSLOTS) +#define _SG_WGPU_MAX_UB_BINDGROUP_WGSL_SLOTS (2 * SG_MAX_UNIFORMBLOCK_BINDSLOTS) +#define _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES (SG_MAX_VIEW_BINDSLOTS + SG_MAX_SAMPLER_BINDSLOTS) +#define _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS (128) + +typedef struct _sg_buffer_s { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + WGPUBuffer buf; + } wgpu; +} _sg_wgpu_buffer_t; +typedef _sg_wgpu_buffer_t _sg_buffer_t; + +typedef struct _sg_image_s { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + WGPUTexture tex; + } wgpu; +} _sg_wgpu_image_t; +typedef _sg_wgpu_image_t _sg_image_t; + +typedef struct _sg_sampler_s { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + WGPUSampler smp; + } wgpu; +} _sg_wgpu_sampler_t; +typedef _sg_wgpu_sampler_t _sg_sampler_t; + +typedef struct { + WGPUShaderModule module; + _sg_str_t entry; +} _sg_wgpu_shader_func_t; + +typedef struct _sg_shader_s { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_wgpu_shader_func_t vertex_func; + _sg_wgpu_shader_func_t fragment_func; + _sg_wgpu_shader_func_t compute_func; + WGPUBindGroupLayout bgl_ub; + WGPUBindGroup bg_ub; + WGPUBindGroupLayout bgl_view_smp; + // a mapping of sokol-gfx bind slots to setBindGroup dynamic-offset-array indices + uint8_t ub_num_dynoffsets; + uint8_t ub_dynoffsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + // indexed by sokol-gfx bind slot: + uint8_t ub_grp0_bnd_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + uint8_t view_grp1_bnd_n[SG_MAX_VIEW_BINDSLOTS]; + uint8_t smp_grp1_bnd_n[SG_MAX_SAMPLER_BINDSLOTS]; + } wgpu; +} _sg_wgpu_shader_t; +typedef _sg_wgpu_shader_t _sg_shader_t; + +typedef struct _sg_pipeline_s { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + struct { + WGPURenderPipeline rpip; + WGPUComputePipeline cpip; + WGPUColor blend_color; + } wgpu; +} _sg_wgpu_pipeline_t; +typedef _sg_wgpu_pipeline_t _sg_pipeline_t; + +typedef struct _sg_view_s { + _sg_slot_t slot; + _sg_view_common_t cmn; + struct { + WGPUTextureView view; + } wgpu; +} _sg_wgpu_view_t; +typedef _sg_wgpu_view_t _sg_view_t; + +// a pool of per-frame uniform buffers +typedef struct { + uint32_t num_bytes; + uint32_t offset; // current offset into buf + uint8_t* staging; // intermediate buffer for uniform data updates + WGPUBuffer buf; // the GPU-side uniform buffer + bool dirty; + uint32_t bind_offsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; // NOTE: index is sokol-gfx ub slot index! +} _sg_wgpu_uniform_system_t; + +typedef struct { + uint32_t id; +} _sg_wgpu_bindgroup_handle_t; + +typedef enum { + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_NONE = 0, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_VIEW = 1, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER = 2, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE = 3, +} _sg_wgpu_bindgroups_cache_item_type_t; + +#define _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS (1 + _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES) +typedef struct { + uint64_t hash; + // the format of cache key items is BBTCCCCCIIIIIIII + // where + // - BB: 8 bits WGPU binding + // - T: 2 bits _sg_wgpu_bindgroups_cache_item_type_t + // - CCCCC: 22 bits slot.uninit_count + // - IIIIIIII: 32 bits slot.id + // + // where the item type is a per-resource-type bit pattern + uint64_t items[_SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS]; +} _sg_wgpu_bindgroups_cache_key_t; + +typedef struct { + uint32_t num; // must be 2^n + uint32_t index_mask; // mask to turn hash into valid index + _sg_wgpu_bindgroup_handle_t* items; +} _sg_wgpu_bindgroups_cache_t; + +typedef struct { + _sg_slot_t slot; + WGPUBindGroup bindgroup; + _sg_wgpu_bindgroups_cache_key_t key; +} _sg_wgpu_bindgroup_t; + +typedef struct { + _sg_pool_t pool; + _sg_wgpu_bindgroup_t* bindgroups; +} _sg_wgpu_bindgroups_pool_t; + +typedef struct { + struct { + sg_buffer buffer; + uint64_t offset; + } vbs[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + struct { + sg_buffer buffer; + uint64_t offset; + } ib; + _sg_wgpu_bindgroup_handle_t bg; +} _sg_wgpu_bindings_cache_t; + +// the WGPU backend state +typedef struct { + bool valid; + WGPUDevice dev; + WGPULimits limits; + WGPUQueue queue; + WGPUCommandEncoder cmd_enc; + WGPURenderPassEncoder rpass_enc; + WGPUComputePassEncoder cpass_enc; + _sg_wgpu_uniform_system_t uniform; + _sg_wgpu_bindings_cache_t bindings_cache; + _sg_wgpu_bindgroups_cache_t bindgroups_cache; + _sg_wgpu_bindgroups_pool_t bindgroups_pool; +} _sg_wgpu_backend_t; + +#elif defined(SOKOL_VULKAN) + +#define _SG_VK_MAX_UNIFORM_UPDATE_SIZE (1<<16) +#define _SG_VK_NUM_DESCRIPTORSETS (2) // 0: uniforms, 1: images, samplers, storage buffers, storage images +#define _SG_VK_UB_DESCRIPTORSET_INDEX (0) +#define _SG_VK_VIEW_SMP_DESCRIPTORSET_INDEX (1) +#define _SG_VK_MAX_UB_DESCRIPTORSET_ENTRIES (SG_MAX_UNIFORMBLOCK_BINDSLOTS) +#define _SG_VK_MAX_UB_DESCRIPTORSET_SLOTS (2 * SG_MAX_UNIFORMBLOCK_BINDSLOTS) +#define _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES (SG_MAX_VIEW_BINDSLOTS + SG_MAX_SAMPLER_BINDSLOTS) +#define _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS (128) +#define _SG_VK_MAX_DESCRIPTOR_DATA_SIZE (256) // FIXME: llvmpipe needs 280 bytes, do we need to care about that? + +typedef enum { + _SG_VK_MEMTYPE_STORAGE_BUFFER, + _SG_VK_MEMTYPE_GENERIC_BUFFER, + _SG_VK_MEMTYPE_IMAGE, + _SG_VK_MEMTYPE_STAGING_COPY, + _SG_VK_MEMTYPE_STAGING_STREAM, + _SG_VK_MEMTYPE_UNIFORMS, + _SG_VK_MEMTYPE_DESCRIPTORS, +} _sg_vk_memtype_t; + +typedef void (*_sg_vk_delete_queue_destructor_t)(void* obj); + +typedef struct { + _sg_vk_delete_queue_destructor_t destructor; + void* obj; +} _sg_vk_delete_queue_item_t; + +typedef struct { + uint32_t index; + uint32_t num; + _sg_vk_delete_queue_item_t* items; +} _sg_vk_delete_queue_t; + +typedef enum { + _SG_VK_ACCESS_NONE = (0), // initial state for new resources + _SG_VK_ACCESS_STAGING = (1<<0), + _SG_VK_ACCESS_VERTEXBUFFER = (1<<1), + _SG_VK_ACCESS_INDEXBUFFER = (1<<2), + _SG_VK_ACCESS_STORAGEBUFFER_RO = (1<<3), + _SG_VK_ACCESS_STORAGEBUFFER_RW = (1<<4), + _SG_VK_ACCESS_TEXTURE = (1<<5), + _SG_VK_ACCESS_STORAGEIMAGE = (1<<6), + _SG_VK_ACCESS_COLOR_ATTACHMENT = (1<<7), + _SG_VK_ACCESS_RESOLVE_ATTACHMENT = (1<<8), + _SG_VK_ACCESS_DEPTH_ATTACHMENT = (1<<9), + _SG_VK_ACCESS_STENCIL_ATTACHMENT = (1<<10), + _SG_VK_ACCESS_DISCARD = (1<<11), // in combination with attachments + _SG_VK_ACCESS_PRESENT = (1<<12), +} _sg_vk_access_bits_t; +typedef int _sg_vk_access_t; + +typedef struct _sg_buffer_s { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + VkBuffer buf; + VkDeviceMemory mem; + VkDeviceAddress dev_addr; // only valid for storage buffers + _sg_vk_access_t cur_access; + } vk; +} _sg_vk_buffer_t; +typedef _sg_vk_buffer_t _sg_buffer_t; + +typedef struct _sg_image_s { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + VkImage img; + VkDeviceMemory mem; + _sg_vk_access_t cur_access; + } vk; +} _sg_vk_image_t; +typedef _sg_vk_image_t _sg_image_t; + +typedef struct _sg_sampler_s { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + VkSampler smp; + size_t descriptor_size; + uint8_t descriptor_data[_SG_VK_MAX_DESCRIPTOR_DATA_SIZE]; + } vk; +} _sg_vk_sampler_t; +typedef _sg_vk_sampler_t _sg_sampler_t; + +typedef struct { + VkShaderModule module; + _sg_str_t entry; +} _sg_vk_shader_func_t; + +typedef struct _sg_shader_s { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_vk_shader_func_t vertex_func; + _sg_vk_shader_func_t fragment_func; + _sg_vk_shader_func_t compute_func; + VkDescriptorSetLayout ub_dsl; + VkDeviceSize ub_dset_size; + VkDescriptorSetLayout view_smp_dsl; + VkDeviceSize view_smp_dset_size; + VkPipelineLayout pip_layout; + // indexed by sokol-gfx bind-slot + uint8_t ub_set0_bnd_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + uint8_t view_set1_bnd_n[SG_MAX_VIEW_BINDSLOTS]; + uint8_t smp_set1_bnd_n[SG_MAX_SAMPLER_BINDSLOTS]; + // relative descriptor offsets to start of descriptor set in descriptor buffer + uint16_t ub_dset_offsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + uint16_t view_dset_offsets[SG_MAX_VIEW_BINDSLOTS]; + uint16_t smp_dset_offsets[SG_MAX_SAMPLER_BINDSLOTS]; + } vk; +} _sg_vk_shader_t; +typedef _sg_vk_shader_t _sg_shader_t; + +typedef struct _sg_pipeline_s { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + struct { + VkPipeline pip; + } vk; +} _sg_vk_pipeline_t; +typedef _sg_vk_pipeline_t _sg_pipeline_t; + +typedef struct _sg_view_s { + _sg_slot_t slot; + _sg_view_common_t cmn; + struct { + VkImageView img_view; + size_t descriptor_size; + uint8_t descriptor_data[_SG_VK_MAX_DESCRIPTOR_DATA_SIZE]; + } vk; +} _sg_vk_view_t; +typedef _sg_vk_view_t _sg_view_t; + +// a double-buffer cpu-write / gpu-read buffer +#define _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT (0xFFFFFFFF) +typedef struct { + uint32_t size; // buffer size + uint32_t align; // required buffer offset alignemnt + uint32_t offset; // current offset into buffer + VkBuffer cur_buf; // currently mapped buffer + void* cur_mem_ptr; // current pointer into currently mapped buffer + VkDeviceAddress cur_dev_addr; // current buffer device address (only valid for some buffer types) + bool overflown; // true when in overflown state + struct { + VkBuffer buf; + VkDeviceMemory mem; + VkDeviceAddress dev_addr; // only valid for some buffer types! + void* mem_ptr; + } slots[SG_NUM_INFLIGHT_FRAMES]; +} _sg_vk_shared_buffer_t; + +typedef struct { + bool valid; + VkInstance instance; + VkPhysicalDevice phys_dev; + VkDevice dev; + VkQueue queue; + uint32_t queue_family_index; + sg_vulkan_swapchain swapchain; + VkSemaphore present_complete_sem; + VkSemaphore render_finished_sem; + + // extension function pointers + struct { + PFN_vkSetDebugUtilsObjectNameEXT set_debug_utils_object_name_ext; + PFN_vkGetDescriptorSetLayoutSizeEXT get_descriptor_set_layout_size; + PFN_vkGetDescriptorSetLayoutBindingOffsetEXT get_descriptor_set_layout_binding_offset; + PFN_vkGetDescriptorEXT get_descriptor; + PFN_vkCmdBindDescriptorBuffersEXT cmd_bind_descriptor_buffers; + PFN_vkCmdSetDescriptorBufferOffsetsEXT cmd_set_descriptor_buffer_offsets; + } ext; + + uint32_t frame_slot; + struct { + VkCommandPool cmd_pool; + VkCommandBuffer cmd_buf; + VkCommandBuffer stream_cmd_buf; + struct { + VkFence fence; + VkCommandBuffer command_buffer; + VkCommandBuffer stream_command_buffer; + _sg_vk_delete_queue_t delete_queue; + } slot[SG_NUM_INFLIGHT_FRAMES]; + } frame; + // staging system + struct { + // staging system for immutable and dynamic resources, generally causes a stall + struct { + VkCommandPool cmd_pool; + VkCommandBuffer cmd_buf; + uint32_t size; + VkBuffer buf; + VkDeviceMemory mem; + } copy; + // staging buffer for per-frame streaming updates + _sg_vk_shared_buffer_t stream; + } stage; + // uniform update system + struct { + bool dirty; + _sg_vk_shared_buffer_t dbuf; // descriptor buffer + VkDescriptorAddressInfoEXT addr_info[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + VkDescriptorGetInfoEXT get_info[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; + size_t dset_cache_size; + uint8_t* dset_cache; + } uniforms; + // resource binding system (using descriptor buffers) + _sg_vk_shared_buffer_t bind; + // hazard tracking system for buffers and images + struct { + _sg_track_t buffers; + _sg_track_t images; + } track; + // device properties and features (initialized at startup) + VkPhysicalDeviceProperties2 dev_props; + VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_props; + VkPhysicalDeviceFeatures2 dev_features; +} _sg_vk_backend_t; + +#endif // SOKOL_VULKAN + +// this *MUST* remain 0 +#define _SG_INVALID_SLOT_INDEX (0) + +typedef struct _sg_pools_s { + _sg_pool_t buffer_pool; + _sg_pool_t image_pool; + _sg_pool_t sampler_pool; + _sg_pool_t shader_pool; + _sg_pool_t pipeline_pool; + _sg_pool_t view_pool; + _sg_buffer_t* buffers; + _sg_image_t* images; + _sg_sampler_t* samplers; + _sg_shader_t* shaders; + _sg_pipeline_t* pipelines; + _sg_view_t* views; +} _sg_pools_t; + +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; + +// resolved pass attachments struct +typedef struct { + bool empty; + int num_color_views; + _sg_view_t* color_views[SG_MAX_COLOR_ATTACHMENTS]; + _sg_view_t* resolve_views[SG_MAX_COLOR_ATTACHMENTS]; + _sg_view_t* ds_view; +} _sg_attachments_ptrs_t; + +// resolved resource bindings struct +typedef struct { + _sg_pipeline_t* pip; + int vb_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + int ib_offset; + _sg_buffer_t* vbs[SG_MAX_VERTEXBUFFER_BINDSLOTS]; + _sg_buffer_t* ib; + _sg_view_t* views[SG_MAX_VIEW_BINDSLOTS]; + _sg_sampler_t* smps[SG_MAX_SAMPLER_BINDSLOTS]; +} _sg_bindings_ptrs_t; + +typedef struct { + bool sample; + bool filter; + bool render; + bool blend; + bool msaa; + bool depth; + bool read; + bool write; +} _sg_pixelformat_info_t; + +typedef struct { + bool valid; + sg_desc desc; // original desc with default values patched in + uint32_t frame_index; + struct { + bool valid; + bool in_pass; + bool is_compute; + _sg_dimi_t dim; + sg_attachments atts; + sg_pass_action action; + struct { + sg_pixel_format color_fmt; + sg_pixel_format depth_fmt; + int sample_count; + } swapchain; + } cur_pass; + _sg_pipeline_ref_t cur_pip; + bool next_draw_valid; + bool use_indexed_draw; + bool use_instanced_draw; + uint32_t required_bindings_and_uniforms; // used to check that bindings and uniforms are applied after applying pipeline + uint32_t applied_bindings_and_uniforms; // bits 0..7: uniform blocks, bit 8: bindings + #if defined(SOKOL_DEBUG) + sg_log_item validate_error; + #endif + _sg_pools_t pools; + sg_backend backend; + sg_features features; + sg_limits limits; + _sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM]; + bool stats_enabled; + sg_stats stats; + #if defined(_SOKOL_ANY_GL) + _sg_gl_backend_t gl; + #elif defined(SOKOL_METAL) + _sg_mtl_backend_t mtl; + #elif defined(SOKOL_D3D11) + _sg_d3d11_backend_t d3d11; + #elif defined(SOKOL_WGPU) + _sg_wgpu_backend_t wgpu; + #elif defined(SOKOL_VULKAN) + _sg_vk_backend_t vk; + #endif + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks hooks; + #endif + _sg_commit_listeners_t commit_listeners; +} _sg_state_t; +static _sg_state_t _sg; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>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, (uint32_t)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 + +_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE uint32_t _sg_roundup_u32(uint32_t val, uint32_t round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE uint64_t _sg_roundup_u64(uint64_t val, uint64_t round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE bool _sg_multiple_u64(uint64_t val, uint64_t of) { + return (val & (of-1)) == 0; +} + +// 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_fn) { + ptr = _sg.desc.allocator.alloc_fn(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_fn) { + _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { + return 0 == str->buf[0]; +} + +_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { + return &str->buf[0]; +} + +_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); + #else + strncpy(dst->buf, src, _SG_STRING_SIZE); + #endif + dst->buf[_SG_STRING_SIZE-1] = 0; + } else { + _sg_clear(dst->buf, _SG_STRING_SIZE); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +_SOKOL_PRIVATE void _sg_pool_init(_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 + pool->size = num + 1; + 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*)_sg_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + 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; + } +} + +_SOKOL_PRIVATE void _sg_pool_discard(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sg_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sg_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SG_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sg_slot_reset(_sg_slot_t* slot) { + SOKOL_ASSERT(slot); + _sg_clear(slot, sizeof(_sg_slot_t)); +} + +_SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _sg_slot_t slot = buf->slot; + _sg_clear(buf, sizeof(*buf)); + buf->slot = slot; + buf->slot.uninit_count += 1; + buf->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { + SOKOL_ASSERT(img); + _sg_slot_t slot = img->slot; + _sg_clear(img, sizeof(*img)); + img->slot = slot; + img->slot.uninit_count += 1; + img->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _sg_slot_t slot = smp->slot; + _sg_clear(smp, sizeof(*smp)); + smp->slot = slot; + smp->slot.uninit_count += 1; + smp->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_slot_t slot = shd->slot; + _sg_clear(shd, sizeof(*shd)); + shd->slot = slot; + shd->slot.uninit_count += 1; + shd->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pipeline_to_alloc_state(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_slot_t slot = pip->slot; + _sg_clear(pip, sizeof(*pip)); + pip->slot = slot; + pip->slot.uninit_count += 1; + pip->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_view_to_alloc_state(_sg_view_t* view) { + SOKOL_ASSERT(view); + _sg_slot_t slot = view->slot; + _sg_clear(view, sizeof(*view)); + view->slot = slot; + view->slot.uninit_count += 1; + view->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { + SOKOL_ASSERT(p); + SOKOL_ASSERT(desc); + // note: the pools here will have an additional item, since slot 0 is reserved + SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); + _sg_pool_init(&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*) _sg_malloc_clear(buffer_pool_byte_size); + + SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); + _sg_pool_init(&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*) _sg_malloc_clear(image_pool_byte_size); + + SOKOL_ASSERT((desc->sampler_pool_size > 0) && (desc->sampler_pool_size < _SG_MAX_POOL_SIZE)); + _sg_pool_init(&p->sampler_pool, desc->sampler_pool_size); + size_t sampler_pool_byte_size = sizeof(_sg_sampler_t) * (size_t)p->sampler_pool.size; + p->samplers = (_sg_sampler_t*) _sg_malloc_clear(sampler_pool_byte_size); + + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); + _sg_pool_init(&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*) _sg_malloc_clear(shader_pool_byte_size); + + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); + _sg_pool_init(&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*) _sg_malloc_clear(pipeline_pool_byte_size); + + SOKOL_ASSERT((desc->view_pool_size > 0) && (desc->view_pool_size < _SG_MAX_POOL_SIZE)); + _sg_pool_init(&p->view_pool, desc->view_pool_size); + size_t view_pool_byte_size = sizeof(_sg_view_t) * (size_t)p->view_pool.size; + p->views = (_sg_view_t*) _sg_malloc_clear(view_pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { + SOKOL_ASSERT(p); + _sg_free(p->views); p->views = 0; + _sg_free(p->pipelines); p->pipelines = 0; + _sg_free(p->shaders); p->shaders = 0; + _sg_free(p->samplers); p->samplers = 0; + _sg_free(p->images); p->images = 0; + _sg_free(p->buffers); p->buffers = 0; + _sg_pool_discard(&p->view_pool); + _sg_pool_discard(&p->pipeline_pool); + _sg_pool_discard(&p->shader_pool); + _sg_pool_discard(&p->sampler_pool); + _sg_pool_discard(&p->image_pool); + _sg_pool_discard(&p->buffer_pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(slot->id == SG_INVALID_ID); + SOKOL_ASSERT(slot->state == SG_RESOURCESTATE_INITIAL); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { + int slot_index = (int) (id & _SG_SLOT_MASK); + SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +// returns pointer to resource by id without matching id check +_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(uint32_t buf_id) { + SOKOL_ASSERT(SG_INVALID_ID != buf_id); + int slot_index = _sg_slot_index(buf_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.buffer_pool.size)); + return &_sg.pools.buffers[slot_index]; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_image_at(uint32_t img_id) { + SOKOL_ASSERT(SG_INVALID_ID != img_id); + int slot_index = _sg_slot_index(img_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.image_pool.size)); + return &_sg.pools.images[slot_index]; +} + +_SOKOL_PRIVATE _sg_sampler_t* _sg_sampler_at(uint32_t smp_id) { + SOKOL_ASSERT(SG_INVALID_ID != smp_id); + int slot_index = _sg_slot_index(smp_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.sampler_pool.size)); + return &_sg.pools.samplers[slot_index]; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(uint32_t shd_id) { + SOKOL_ASSERT(SG_INVALID_ID != shd_id); + int slot_index = _sg_slot_index(shd_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.shader_pool.size)); + return &_sg.pools.shaders[slot_index]; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(uint32_t pip_id) { + SOKOL_ASSERT(SG_INVALID_ID != pip_id); + int slot_index = _sg_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.pipeline_pool.size)); + return &_sg.pools.pipelines[slot_index]; +} + +_SOKOL_PRIVATE _sg_view_t* _sg_view_at(uint32_t view_id) { + SOKOL_ASSERT(SG_INVALID_ID != view_id); + int slot_index = _sg_slot_index(view_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.view_pool.size)); + return &_sg.pools.views[slot_index]; +} + +// returns pointer to resource with matching id check, may return 0 +_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(uint32_t buf_id) { + if (SG_INVALID_ID != buf_id) { + _sg_buffer_t* buf = _sg_buffer_at(buf_id); + if (buf->slot.id == buf_id) { + return buf; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(uint32_t img_id) { + if (SG_INVALID_ID != img_id) { + _sg_image_t* img = _sg_image_at(img_id); + if (img->slot.id == img_id) { + return img; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_sampler_t* _sg_lookup_sampler(uint32_t smp_id) { + if (SG_INVALID_ID != smp_id) { + _sg_sampler_t* smp = _sg_sampler_at(smp_id); + if (smp->slot.id == smp_id) { + return smp; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(uint32_t shd_id) { + if (SG_INVALID_ID != shd_id) { + _sg_shader_t* shd = _sg_shader_at(shd_id); + if (shd->slot.id == shd_id) { + return shd; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(uint32_t pip_id) { + if (SG_INVALID_ID != pip_id) { + _sg_pipeline_t* pip = _sg_pipeline_at(pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_view_t* _sg_lookup_view(uint32_t view_id) { + if (SG_INVALID_ID != view_id) { + _sg_view_t* view = _sg_view_at(view_id); + if (view->slot.id == view_id) { + return view; + } + } + return 0; +} + +// ████████ ██████ █████ ██████ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ███████ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██████ ██ ██ +// +// >>track +_SOKOL_PRIVATE void _sg_track_init(_sg_track_t* track, int num_slots) { + SOKOL_ASSERT(track && (num_slots > 0)); + _sg_clear(track, sizeof(_sg_track_t)); + track->num_slots = num_slots; + track->slots = (uint32_t*)_sg_malloc_clear((size_t)num_slots * sizeof(uint32_t)); + track->occupy_num_bytes = _sg_roundup_u32((uint32_t)num_slots, 8) >> 3; + track->occupy_bits = (uint8_t*)_sg_malloc_clear(track->occupy_num_bytes); +} + +_SOKOL_PRIVATE void _sg_track_discard(_sg_track_t* track) { + SOKOL_ASSERT(track); + if (track->slots) { + _sg_free(track->slots); + track->slots = 0; + } + if (track->occupy_bits) { + _sg_free(track->occupy_bits); + track->occupy_num_bytes = 0; + track->occupy_bits = 0; + } + track->num_slots = 0; + track->cur_slot = 0; +} + +_SOKOL_PRIVATE void _sg_track_reset(_sg_track_t* track) { + SOKOL_ASSERT(track && track->slots && track->occupy_bits); + track->cur_slot = 0; + _sg_clear(track->occupy_bits, track->occupy_num_bytes); +} + +_SOKOL_PRIVATE int _sg_track_occupy_index(int slot_index) { + const int occupy_index = slot_index >> 3; + return occupy_index; +} + +_SOKOL_PRIVATE uint8_t _sg_track_occupy_mask(int slot_index) { + return (uint8_t)(1 << (slot_index & 7)); +} + +_SOKOL_PRIVATE void _sg_track_add(_sg_track_t* track, uint32_t id) { + SOKOL_ASSERT(track && track->slots && track->occupy_bits); + SOKOL_ASSERT(id != SG_INVALID_ID); + const int slot_index = _sg_slot_index(id); + const int occupy_index = _sg_track_occupy_index(slot_index); + SOKOL_ASSERT((uint32_t)occupy_index < track->occupy_num_bytes); + const uint8_t occupy_mask = _sg_track_occupy_mask(slot_index); + // don't record the same resource twice + if (0 == (track->occupy_bits[occupy_index] & occupy_mask)) { + SOKOL_ASSERT(track->cur_slot < track->num_slots); + track->slots[track->cur_slot++] = id; + track->occupy_bits[occupy_index] |= occupy_mask; + } +} + +_SOKOL_PRIVATE void _sg_track_remove(_sg_track_t* track, uint32_t id) { + SOKOL_ASSERT(track && track->slots && track->occupy_bits); + SOKOL_ASSERT(id != SG_INVALID_ID); + const int slot_index = _sg_slot_index(id); + const int occupy_index = _sg_track_occupy_index(slot_index); + const uint8_t occupy_mask = _sg_track_occupy_mask(slot_index); + if (track->occupy_bits[occupy_index] & occupy_mask) { + track->occupy_bits[occupy_index] &= ~occupy_mask; + // remove tracked id from the slots array + for (int i = 0; i < track->cur_slot; i++) { + if (id == track->slots[i]) { + SOKOL_ASSERT(track->cur_slot > 0); + track->slots[i] = track->slots[--track->cur_slot]; + break; + } + } + } +} + +// ██████ ███████ ███████ ███████ +// ██ ██ ██ ██ ██ +// ██████ █████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ███████ +// +// >>refs +_SOKOL_PRIVATE _sg_sref_t _sg_sref(const _sg_slot_t* slot) { + _SG_STRUCT(_sg_sref_t, sref); + if (slot) { + sref.id = slot->id; + sref.uninit_count = slot->uninit_count; + } + return sref; +} + +_SOKOL_PRIVATE bool _sg_sref_slot_eql(const _sg_sref_t* sref, const _sg_slot_t* slot) { + SOKOL_ASSERT(sref && slot); + return (sref->id == slot->id) && (sref->uninit_count == slot->uninit_count); +} + +_SOKOL_PRIVATE bool _sg_sref_sref_eql(const _sg_sref_t* sref0, const _sg_sref_t* sref1) { + SOKOL_ASSERT(sref0 && sref1); + return (sref0->id == sref1->id) && (sref0->uninit_count == sref1->uninit_count); +} + +_SOKOL_PRIVATE _sg_buffer_ref_t _sg_buffer_ref(_sg_buffer_t* buf_or_null) { + _SG_STRUCT(_sg_buffer_ref_t, ref); + if (buf_or_null) { + _sg_buffer_t* buf = buf_or_null; + SOKOL_ASSERT(buf->slot.id != SG_INVALID_ID); + ref.ptr = buf; + ref.sref = _sg_sref(&buf->slot); + } + return ref; +} + +_SOKOL_PRIVATE _sg_image_ref_t _sg_image_ref(_sg_image_t* img_or_null) { + _SG_STRUCT(_sg_image_ref_t, ref); + if (img_or_null) { + _sg_image_t* img = img_or_null; + SOKOL_ASSERT(img->slot.id != SG_INVALID_ID); + ref.ptr = img; + ref.sref = _sg_sref(&img->slot); + } + return ref; +} + +_SOKOL_PRIVATE _sg_sampler_ref_t _sg_sampler_ref(_sg_sampler_t* smp_or_null) { + _SG_STRUCT(_sg_sampler_ref_t, ref); + if (smp_or_null) { + _sg_sampler_t* smp = smp_or_null; + SOKOL_ASSERT(smp->slot.id != SG_INVALID_ID); + ref.ptr = smp; + ref.sref = _sg_sref(&smp->slot); + } + return ref; +} + +_SOKOL_PRIVATE _sg_shader_ref_t _sg_shader_ref(_sg_shader_t* shd_or_null) { + _SG_STRUCT(_sg_shader_ref_t, ref); + if (shd_or_null) { + _sg_shader_t* shd = shd_or_null; + SOKOL_ASSERT(shd->slot.id != SG_INVALID_ID); + ref.ptr = shd; + ref.sref = _sg_sref(&shd->slot); + } + return ref; +} + +_SOKOL_PRIVATE _sg_pipeline_ref_t _sg_pipeline_ref(_sg_pipeline_t* pip_or_null) { + _SG_STRUCT(_sg_pipeline_ref_t, ref); + if (pip_or_null) { + _sg_pipeline_t* pip = pip_or_null; + SOKOL_ASSERT(pip->slot.id != SG_INVALID_ID); + ref.ptr = pip; + ref.sref = _sg_sref(&pip->slot); + } + return ref; +} + +_SOKOL_PRIVATE _sg_view_ref_t _sg_view_ref(_sg_view_t* view_or_null) { + _SG_STRUCT(_sg_view_ref_t, ref); + if (view_or_null) { + _sg_view_t* view = view_or_null; + SOKOL_ASSERT(view->slot.id != SG_INVALID_ID); + ref.ptr = view; + ref.sref = _sg_sref(&view->slot); + } + return ref; +} + +#define _SG_IMPL_RES_EQL(NAME,REF,RES) _SOKOL_PRIVATE bool NAME(const REF* ref, const RES* res) { SOKOL_ASSERT(ref && res); return _sg_sref_slot_eql(&ref->sref, &res->slot); } +_SG_IMPL_RES_EQL(_sg_buffer_ref_eql, _sg_buffer_ref_t, _sg_buffer_t) +_SG_IMPL_RES_EQL(_sg_image_ref_eql, _sg_image_ref_t, _sg_image_t) +_SG_IMPL_RES_EQL(_sg_sampler_ref_eql, _sg_sampler_ref_t, _sg_sampler_t) +_SG_IMPL_RES_EQL(_sg_shader_ref_eql, _sg_shader_ref_t, _sg_shader_t) +_SG_IMPL_RES_EQL(_sg_pipeline_ref_eql, _sg_pipeline_ref_t, _sg_pipeline_t) +_SG_IMPL_RES_EQL(_sg_view_ref_eql, _sg_view_ref_t, _sg_view_t) + +#define _SG_IMPL_RES_NULL(NAME,REF) _SOKOL_PRIVATE bool NAME(const REF* ref) { SOKOL_ASSERT(ref); return SG_INVALID_ID == ref->sref.id; } +_SG_IMPL_RES_NULL(_sg_buffer_ref_null, _sg_buffer_ref_t) +_SG_IMPL_RES_NULL(_sg_image_ref_null, _sg_image_ref_t) +_SG_IMPL_RES_NULL(_sg_sampler_ref_null, _sg_sampler_ref_t) +_SG_IMPL_RES_NULL(_sg_shader_ref_null, _sg_shader_ref_t) +_SG_IMPL_RES_NULL(_sg_pipeline_ref_null, _sg_pipeline_ref_t) +_SG_IMPL_RES_NULL(_sg_view_ref_null, _sg_view_ref_t) + +#define _SG_IMPL_RES_ALIVE(NAME,REF) _SOKOL_PRIVATE bool NAME(const REF* ref) { SOKOL_ASSERT(ref); return ref->ptr && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot); } +_SG_IMPL_RES_ALIVE(_sg_buffer_ref_alive, _sg_buffer_ref_t) +_SG_IMPL_RES_ALIVE(_sg_image_ref_alive, _sg_image_ref_t) +_SG_IMPL_RES_ALIVE(_sg_sampler_ref_alive, _sg_sampler_ref_t) +_SG_IMPL_RES_ALIVE(_sg_shader_ref_alive, _sg_shader_ref_t) +_SG_IMPL_RES_ALIVE(_sg_pipeline_ref_alive, _sg_pipeline_ref_t) +_SG_IMPL_RES_ALIVE(_sg_view_ref_alive, _sg_view_ref_t) + +#define _SG_IMPL_RES_VALID(NAME,REF) _SOKOL_PRIVATE bool NAME(const REF* ref) { SOKOL_ASSERT(ref); return ref->ptr && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot) && (ref->ptr->slot.state == SG_RESOURCESTATE_VALID); } +_SG_IMPL_RES_VALID(_sg_buffer_ref_valid, _sg_buffer_ref_t) +_SG_IMPL_RES_VALID(_sg_image_ref_valid, _sg_image_ref_t) +_SG_IMPL_RES_VALID(_sg_sampler_ref_valid, _sg_sampler_ref_t) +_SG_IMPL_RES_VALID(_sg_shader_ref_valid, _sg_shader_ref_t) +_SG_IMPL_RES_VALID(_sg_pipeline_ref_valid, _sg_pipeline_ref_t) +_SG_IMPL_RES_VALID(_sg_view_ref_valid, _sg_view_ref_t) + +#define _SG_IMPL_RES_PTR(NAME,REF,RES) _SOKOL_PRIVATE RES* NAME(const REF* ref) { SOKOL_ASSERT(ref && ref->ptr && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot)); return ref->ptr; } +_SG_IMPL_RES_PTR(_sg_buffer_ref_ptr, _sg_buffer_ref_t, _sg_buffer_t) +_SG_IMPL_RES_PTR(_sg_image_ref_ptr, _sg_image_ref_t, _sg_image_t) +_SG_IMPL_RES_PTR(_sg_sampler_ref_ptr, _sg_sampler_ref_t, _sg_sampler_t) +_SG_IMPL_RES_PTR(_sg_shader_ref_ptr, _sg_shader_ref_t, _sg_shader_t) +_SG_IMPL_RES_PTR(_sg_pipeline_ref_ptr, _sg_pipeline_ref_t, _sg_pipeline_t) +_SG_IMPL_RES_PTR(_sg_view_ref_ptr, _sg_view_ref_t, _sg_view_t) + +#define _SG_IMPL_RES_PTR_OR_NULL(NAME,REF,RES) _SOKOL_PRIVATE RES* NAME(const REF* ref) { SOKOL_ASSERT(ref); if ((SG_INVALID_ID != ref->sref.id) && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot)) { return ref->ptr; } else { return 0; } } +_SG_IMPL_RES_PTR_OR_NULL(_sg_buffer_ref_ptr_or_null, _sg_buffer_ref_t, _sg_buffer_t) +_SG_IMPL_RES_PTR_OR_NULL(_sg_image_ref_ptr_or_null, _sg_image_ref_t, _sg_image_t) +_SG_IMPL_RES_PTR_OR_NULL(_sg_sampler_ref_ptr_or_null, _sg_sampler_ref_t, _sg_sampler_t) +_SG_IMPL_RES_PTR_OR_NULL(_sg_shader_ref_ptr_or_null, _sg_shader_ref_t, _sg_shader_t) +_SG_IMPL_RES_PTR_OR_NULL(_sg_pipeline_ref_ptr_or_null, _sg_pipeline_ref_t, _sg_pipeline_t) +_SG_IMPL_RES_PTR_OR_NULL(_sg_view_ref_ptr_or_null, _sg_view_ref_t, _sg_view_t) + +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers + +// helper macros +#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sg_min(a,b) (((a)<(b))?(a):(b)) +#define _sg_max(a,b) (((a)>(b))?(a):(b)) +#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))) +#define _sg_ispow2(val) ((val&(val-1))==0) +#define _sg_stats_add(key,val) {if(_sg.stats_enabled){ _sg.stats.cur_frame.key+=val;}} +#define _sg_stats_inc(key) {if(_sg.stats_enabled){ _sg.stats.cur_frame.key++;}} +#define _sg_resource_stats_inc(key) {if(_sg.stats_enabled){ _sg.stats.cur_frame.key++; _sg.stats.total.key++;}} + +_SOKOL_PRIVATE void _sg_update_alive_free_resource_stats(sg_total_resource_stats* stats, const _sg_pool_t* pool) { + SOKOL_ASSERT(stats && pool); + stats->alive = (uint32_t) ((pool->size - 1) - pool->queue_top); + stats->free = (uint32_t) pool->queue_top; +} + +_SOKOL_PRIVATE void _sg_update_stats(void) { + _sg.stats.cur_frame.frame_index = _sg.frame_index; + _sg.stats.prev_frame = _sg.stats.cur_frame; + _sg_clear(&_sg.stats.cur_frame, sizeof(_sg.stats.cur_frame)); +} + +_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); +} + +_SOKOL_PRIVATE _sg_recti_t _sg_clipi(int x, int y, int w, int h, int clip_width, int clip_height) { + x = _sg_min(_sg_max(0, x), clip_width-1); + y = _sg_min(_sg_max(0, y), clip_height-1); + if ((x + w) > clip_width) { + w = clip_width - x; + } + if ((y + h) > clip_height) { + h = clip_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + const _sg_recti_t res = { x, y, w, h }; + return res; +} + +// return size of a mipmap level +_SOKOL_PRIVATE int _sg_miplevel_dim(int base_dim, int mip_level) { + return _sg_max(base_dim >> mip_level, 1); +} + +_SOKOL_PRIVATE bool _sg_image_view_alive(const _sg_view_t* view) { + return view && _sg_image_ref_alive(&view->cmn.img.ref); +} + +_SOKOL_PRIVATE _sg_dimi_t _sg_image_view_dim(const _sg_view_t* view) { + SOKOL_ASSERT(view); + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + SOKOL_ASSERT((img->cmn.width > 0) && (img->cmn.height > 0)); + _SG_STRUCT(_sg_dimi_t, res); + res.width = _sg_miplevel_dim(img->cmn.width, view->cmn.img.mip_level); + res.height = _sg_miplevel_dim(img->cmn.height, view->cmn.img.mip_level); + return res; +} + +_SOKOL_PRIVATE bool _sg_attachments_empty(const sg_attachments* atts) { + SOKOL_ASSERT(atts); + for (size_t i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts->colors[i].id != SG_INVALID_ID) { + return false; + } + if (atts->resolves[i].id != SG_INVALID_ID) { + return false; + } + } + if (atts->depth_stencil.id != SG_INVALID_ID) { + return false; + } + return true; +} + +_SOKOL_PRIVATE _sg_attachments_ptrs_t _sg_attachments_ptrs(const sg_attachments* atts) { + SOKOL_ASSERT(atts); + _SG_STRUCT(_sg_attachments_ptrs_t, res); + res.empty = true; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts->colors[i].id != SG_INVALID_ID) { + res.empty = false; + res.num_color_views += 1; + res.color_views[i] = _sg_lookup_view(atts->colors[i].id); + } + if (atts->resolves[i].id != SG_INVALID_ID) { + SOKOL_ASSERT(atts->colors[i].id != SG_INVALID_ID); + res.empty = false; + res.resolve_views[i] = _sg_lookup_view(atts->resolves[i].id); + } + } + if (atts->depth_stencil.id != SG_INVALID_ID) { + res.empty = false; + res.ds_view = _sg_lookup_view(atts->depth_stencil.id); + } + return res; +} + +_SOKOL_PRIVATE _sg_dimi_t _sg_attachments_dim(const _sg_attachments_ptrs_t* atts_ptrs) { + if (atts_ptrs->ds_view) { + return _sg_image_view_dim(atts_ptrs->ds_view); + } else { + SOKOL_ASSERT(atts_ptrs->color_views[0]); + return _sg_image_view_dim(atts_ptrs->color_views[0]); + } +} + +_SOKOL_PRIVATE bool _sg_attachments_alive(const _sg_attachments_ptrs_t* atts_ptrs) { + for (int i = 0; i < atts_ptrs->num_color_views; i++) { + if (!_sg_image_view_alive(atts_ptrs->color_views[i])) { + return false; + } + if (atts_ptrs->resolve_views[i] && !_sg_image_view_alive(atts_ptrs->resolve_views[i])) { + return false; + } + } + if (atts_ptrs->ds_view && !_sg_image_view_alive(atts_ptrs->ds_view)) { + return false; + } + return true; +} + +_SOKOL_PRIVATE bool _sg_is_dualsource_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_SRC1_COLOR: + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: + case SG_BLENDFACTOR_SRC1_ALPHA: + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: + return true; + default: + return false; + } +} + +_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->update_frame_index = 0; + cmn->append_frame_index = 0; + cmn->num_slots = desc->usage.immutable ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; + cmn->usage = desc->usage; +} + +_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.immutable ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; + cmn->type = desc->type; + cmn->width = desc->width; + cmn->height = desc->height; + cmn->num_slices = desc->num_slices; + cmn->num_mipmaps = desc->num_mipmaps; + cmn->usage = desc->usage; + cmn->pixel_format = desc->pixel_format; + cmn->sample_count = desc->sample_count; +} + +_SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) { + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->mipmap_filter = desc->mipmap_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->min_lod = desc->min_lod; + cmn->max_lod = desc->max_lod; + cmn->border_color = desc->border_color; + cmn->compare = desc->compare; + cmn->max_anisotropy = desc->max_anisotropy; +} + +_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { + cmn->is_compute = desc->compute_func.source || desc->compute_func.bytecode.ptr; + for (size_t i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + cmn->attrs[i].base_type = desc->attrs[i].base_type; + } + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* src = &desc->uniform_blocks[i]; + _sg_shader_uniform_block_t* dst = &cmn->uniform_blocks[i]; + if (src->stage != SG_SHADERSTAGE_NONE) { + cmn->required_bindings_and_uniforms |= (1 << i); + dst->stage = src->stage; + dst->size = src->size; + } + } + const uint32_t required_bindings_flag = (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS); + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + _sg_shader_view_t* dst = &cmn->views[i]; + if (desc->views[i].texture.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_texture_view* src = &desc->views[i].texture; + dst->stage = src->stage; + dst->view_type = SG_VIEWTYPE_TEXTURE; + dst->image_type = src->image_type; + dst->sample_type = src->sample_type; + dst->multisampled = src->multisampled; + } else if (desc->views[i].storage_buffer.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_storage_buffer_view* src = &desc->views[i].storage_buffer; + cmn->required_bindings_and_uniforms |= required_bindings_flag; + dst->stage = src->stage; + dst->view_type = SG_VIEWTYPE_STORAGEBUFFER; + dst->sbuf_readonly = src->readonly; + } else if (desc->views[i].storage_image.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_storage_image_view* src = &desc->views[i].storage_image; + cmn->required_bindings_and_uniforms |= required_bindings_flag; + dst->stage = src->stage; + dst->view_type = SG_VIEWTYPE_STORAGEIMAGE; + dst->image_type = src->image_type; + dst->access_format = src->access_format; + dst->simg_writeonly = src->writeonly; + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const sg_shader_sampler* src = &desc->samplers[i]; + _sg_shader_sampler_t* dst = &cmn->samplers[i]; + if (src->stage != SG_SHADERSTAGE_NONE) { + cmn->required_bindings_and_uniforms |= required_bindings_flag; + dst->stage = src->stage; + dst->sampler_type = src->sampler_type; + } + } + for (size_t i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) { + const sg_shader_texture_sampler_pair* src = &desc->texture_sampler_pairs[i]; + _sg_shader_texture_sampler_t* dst = &cmn->texture_samplers[i]; + if (src->stage != SG_SHADERSTAGE_NONE) { + dst->stage = src->stage; + SOKOL_ASSERT((src->view_slot >= 0) && (src->view_slot < SG_MAX_VIEW_BINDSLOTS)); + SOKOL_ASSERT(cmn->views[src->view_slot].view_type == SG_VIEWTYPE_TEXTURE); + SOKOL_ASSERT(cmn->views[src->view_slot].stage == src->stage); + dst->view_slot = src->view_slot; + SOKOL_ASSERT((src->sampler_slot >= 0) && (src->sampler_slot < SG_MAX_SAMPLER_BINDSLOTS)); + SOKOL_ASSERT(desc->samplers[src->sampler_slot].stage == src->stage); + dst->sampler_slot = src->sampler_slot; + } + } +} + +_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc, _sg_shader_t* shd) { + SOKOL_ASSERT((desc->color_count >= 0) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + + // FIXME: most of this isn't needed for compute pipelines + + const uint32_t required_bindings_flag = (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS); + for (size_t attr_idx = 0; attr_idx < SG_MAX_VERTEX_ATTRIBUTES; attr_idx++) { + const sg_vertex_attr_state* attr_state = &desc->layout.attrs[attr_idx]; + if (attr_state->format != SG_VERTEXFORMAT_INVALID) { + SOKOL_ASSERT((attr_state->buffer_index >= 0) && (attr_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS)); + cmn->vertex_buffer_layout_active[attr_state->buffer_index] = true; + cmn->required_bindings_and_uniforms |= required_bindings_flag; + } + } + cmn->use_instanced_draw = false; + for (size_t vbuf_idx = 0; vbuf_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vbuf_idx++) { + const sg_vertex_buffer_layout_state* vbuf_state = &desc->layout.buffers[vbuf_idx]; + if (vbuf_state->step_func == SG_VERTEXSTEP_PER_INSTANCE) { + cmn->use_instanced_draw = true; + } + } + cmn->is_compute = desc->compute; + cmn->shader = _sg_shader_ref(shd); + 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->primitive_type = desc->primitive_type; + cmn->index_type = desc->index_type; + if (cmn->index_type != SG_INDEXTYPE_NONE) { + cmn->required_bindings_and_uniforms |= required_bindings_flag; + } + cmn->cull_mode = desc->cull_mode; + cmn->face_winding = desc->face_winding; + cmn->sample_count = desc->sample_count; + cmn->blend_color = desc->blend_color; + cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; +} + +_SOKOL_PRIVATE void _sg_buffer_view_common_init(_sg_buffer_view_common_t* cmn, const sg_buffer_view_desc* desc, _sg_buffer_t* buf) { + SOKOL_ASSERT(SG_RESOURCESTATE_VALID == buf->slot.state); + cmn->ref = _sg_buffer_ref(buf); + cmn->offset = desc->offset; +} + +_SOKOL_PRIVATE void _sg_texture_view_common_init(_sg_image_view_common_t* cmn, const sg_texture_view_desc* desc, _sg_image_t* img) { + SOKOL_ASSERT(SG_RESOURCESTATE_VALID == img->slot.state); + cmn->ref = _sg_image_ref(img); + cmn->mip_level = desc->mip_levels.base; + cmn->mip_level_count = _sg_def(desc->mip_levels.count, img->cmn.num_mipmaps - cmn->mip_level); + cmn->slice = desc->slices.base; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + cmn->slice_count = 1; + break; + case SG_IMAGETYPE_CUBE: + cmn->slice_count = 6; + break; + case SG_IMAGETYPE_3D: + cmn->slice_count = 1; + break; + case SG_IMAGETYPE_ARRAY: + cmn->slice_count = _sg_def(desc->slices.count, img->cmn.num_slices - cmn->slice); + break; + default: + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_image_view_common_init(_sg_image_view_common_t* cmn, const sg_image_view_desc* desc, _sg_image_t* img) { + SOKOL_ASSERT(SG_RESOURCESTATE_VALID == img->slot.state); + cmn->ref = _sg_image_ref(img); + cmn->mip_level = desc->mip_level; + cmn->mip_level_count = 1; + cmn->slice = desc->slice; + cmn->slice_count = 1; +} + +_SOKOL_PRIVATE void _sg_view_common_init(_sg_view_common_t* cmn, const sg_view_desc* desc, _sg_buffer_t* buf, _sg_image_t* img) { + if (desc->texture.image.id != SG_INVALID_ID) { + SOKOL_ASSERT(img); + cmn->type = SG_VIEWTYPE_TEXTURE; + _sg_texture_view_common_init(&cmn->img, &desc->texture, img); + } else if (desc->storage_buffer.buffer.id != SG_INVALID_ID) { + SOKOL_ASSERT(buf); + cmn->type = SG_VIEWTYPE_STORAGEBUFFER; + _sg_buffer_view_common_init(&cmn->buf, &desc->storage_buffer, buf); + } else if (desc->storage_image.image.id != SG_INVALID_ID) { + SOKOL_ASSERT(img); + cmn->type = SG_VIEWTYPE_STORAGEIMAGE; + _sg_image_view_common_init(&cmn->img, &desc->storage_image, img); + } else if (desc->color_attachment.image.id != SG_INVALID_ID) { + SOKOL_ASSERT(img); + cmn->type = SG_VIEWTYPE_COLORATTACHMENT; + _sg_image_view_common_init(&cmn->img, &desc->color_attachment, img); + } else if (desc->resolve_attachment.image.id != SG_INVALID_ID) { + SOKOL_ASSERT(img); + cmn->type = SG_VIEWTYPE_RESOLVEATTACHMENT; + _sg_image_view_common_init(&cmn->img, &desc->resolve_attachment, img); + } else if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + SOKOL_ASSERT(img); + cmn->type = SG_VIEWTYPE_DEPTHSTENCILATTACHMENT; + _sg_image_view_common_init(&cmn->img, &desc->depth_stencil_attachment, img); + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 4; + case SG_VERTEXFORMAT_FLOAT2: return 8; + case SG_VERTEXFORMAT_FLOAT3: return 12; + case SG_VERTEXFORMAT_FLOAT4: return 16; + case SG_VERTEXFORMAT_INT: return 4; + case SG_VERTEXFORMAT_INT2: return 8; + case SG_VERTEXFORMAT_INT3: return 12; + case SG_VERTEXFORMAT_INT4: return 16; + case SG_VERTEXFORMAT_UINT: return 4; + case SG_VERTEXFORMAT_UINT2: return 8; + case SG_VERTEXFORMAT_UINT3: return 12; + case SG_VERTEXFORMAT_UINT4: return 16; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 4; + case SG_VERTEXFORMAT_SHORT2N: return 4; + case SG_VERTEXFORMAT_USHORT2: return 4; + case SG_VERTEXFORMAT_USHORT2N: return 4; + case SG_VERTEXFORMAT_SHORT4: return 8; + case SG_VERTEXFORMAT_SHORT4N: return 8; + case SG_VERTEXFORMAT_USHORT4: 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; + return -1; + } +} + +_SOKOL_PRIVATE const char* _sg_vertexformat_to_string(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return "FLOAT"; + case SG_VERTEXFORMAT_FLOAT2: return "FLOAT2"; + case SG_VERTEXFORMAT_FLOAT3: return "FLOAT3"; + case SG_VERTEXFORMAT_FLOAT4: return "FLOAT4"; + case SG_VERTEXFORMAT_INT: return "INT"; + case SG_VERTEXFORMAT_INT2: return "INT2"; + case SG_VERTEXFORMAT_INT3: return "INT3"; + case SG_VERTEXFORMAT_INT4: return "INT4"; + case SG_VERTEXFORMAT_UINT: return "UINT"; + case SG_VERTEXFORMAT_UINT2: return "UINT2"; + case SG_VERTEXFORMAT_UINT3: return "UINT3"; + case SG_VERTEXFORMAT_UINT4: return "UINT4"; + case SG_VERTEXFORMAT_BYTE4: return "BYTE4"; + case SG_VERTEXFORMAT_BYTE4N: return "BYTE4N"; + case SG_VERTEXFORMAT_UBYTE4: return "UBYTE4"; + case SG_VERTEXFORMAT_UBYTE4N: return "UBYTE4N"; + case SG_VERTEXFORMAT_SHORT2: return "SHORT2"; + case SG_VERTEXFORMAT_SHORT2N: return "SHORT2N"; + case SG_VERTEXFORMAT_USHORT2: return "USHORT2"; + case SG_VERTEXFORMAT_USHORT2N: return "USHORT2N"; + case SG_VERTEXFORMAT_SHORT4: return "SHORT4"; + case SG_VERTEXFORMAT_SHORT4N: return "SHORT4N"; + case SG_VERTEXFORMAT_USHORT4: return "USHORT4"; + case SG_VERTEXFORMAT_USHORT4N: return "USHORT4N"; + case SG_VERTEXFORMAT_UINT10_N2: return "UINT10_N2"; + case SG_VERTEXFORMAT_HALF2: return "HALF2"; + case SG_VERTEXFORMAT_HALF4: return "HALF4"; + default: + SOKOL_UNREACHABLE; + return "INVALID"; + } +} + +_SOKOL_PRIVATE const char* _sg_shaderattrbasetype_to_string(sg_shader_attr_base_type b) { + switch (b) { + case SG_SHADERATTRBASETYPE_UNDEFINED: return "UNDEFINED"; + case SG_SHADERATTRBASETYPE_FLOAT: return "FLOAT"; + case SG_SHADERATTRBASETYPE_SINT: return "SINT"; + case SG_SHADERATTRBASETYPE_UINT: return "UINT"; + default: + SOKOL_UNREACHABLE; + return "INVALID"; + } +} + +_SOKOL_PRIVATE sg_shader_attr_base_type _sg_vertexformat_basetype(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + case SG_VERTEXFORMAT_HALF2: + case SG_VERTEXFORMAT_HALF4: + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_USHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return SG_SHADERATTRBASETYPE_FLOAT; + case SG_VERTEXFORMAT_INT: + case SG_VERTEXFORMAT_INT2: + case SG_VERTEXFORMAT_INT3: + case SG_VERTEXFORMAT_INT4: + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT4: + return SG_SHADERATTRBASETYPE_SINT; + case SG_VERTEXFORMAT_UINT: + case SG_VERTEXFORMAT_UINT2: + case SG_VERTEXFORMAT_UINT3: + case SG_VERTEXFORMAT_UINT4: + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_USHORT2: + case SG_VERTEXFORMAT_USHORT4: + return SG_SHADERATTRBASETYPE_UINT; + default: + SOKOL_UNREACHABLE; + return SG_SHADERATTRBASETYPE_UNDEFINED; + } +} + +_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; + } + } + } +} + +_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE bool _sg_is_valid_attachment_color_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; +} + +_SOKOL_PRIVATE bool _sg_is_valid_attachment_depth_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; +} + +_SOKOL_PRIVATE bool _sg_is_valid_storage_image_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].read || _sg.formats[fmt_index].write; +} + +_SOKOL_PRIVATE bool _sg_is_depth_or_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH == fmt) || (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + return 1; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + return 2; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16UI: + 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: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA16F: + return 8; + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + case SG_PIXELFORMAT_RGBA32F: + return 16; + case SG_PIXELFORMAT_DEPTH: + case SG_PIXELFORMAT_DEPTH_STENCIL: + return 4; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +// return the texture block width/height of an image format +_SOKOL_PRIVATE int _sg_block_dim(sg_pixel_format fmt) { + if (_sg_is_compressed_pixel_format(fmt)) { + return 4; + } else { + return 1; + } +} + +// return texture block size in bytes +_SOKOL_PRIVATE int _sg_block_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + return 8; + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return 16; + default: + return _sg_pixelformat_bytesize(fmt); + } +} + +/* return row pitch for an image + + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) { + const int block_dim = _sg_block_dim(fmt); + const int num_blocks_in_row = (width + (block_dim-1)) / block_dim; + const int block_num_bytes = _sg_block_bytesize(fmt); + int pitch = num_blocks_in_row * block_num_bytes; + pitch = (pitch < block_num_bytes) ? block_num_bytes : pitch; + pitch = _sg_roundup(pitch, row_align); + return pitch; +} + +// compute the number of rows in a surface depending on pixel format +_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { + const int block_dim = _sg_block_dim(fmt); + int num_rows = (height + (block_dim-1)) / block_dim; + if (num_rows < 1) { + num_rows = 1; + } + return num_rows; +} + +/* return pitch of a 2D subimage / texture slice + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) { + int num_rows = _sg_num_rows(fmt, height); + return num_rows * _sg_row_pitch(fmt, width, row_align); +} + +// capability table pixel format helper functions +_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sf(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; + pfi->depth = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfrm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; + pfi->msaa = true; +} +_SOKOL_PRIVATE void _sg_pixelformat_sbrm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sbr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_compute_all(_sg_pixelformat_info_t* pfi) { + pfi->read = true; + pfi->write = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_compute_writeonly(_sg_pixelformat_info_t* pfi) { + pfi->read = false; + pfi->write = true; +} + +_SOKOL_PRIVATE sg_pass_action _sg_pass_action_defaults(const sg_pass_action* action) { + SOKOL_ASSERT(action); + sg_pass_action res = *action; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (res.colors[i].load_action == _SG_LOADACTION_DEFAULT) { + res.colors[i].load_action = SG_LOADACTION_CLEAR; + res.colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED; + res.colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN; + res.colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE; + res.colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA; + } + if (res.colors[i].store_action == _SG_STOREACTION_DEFAULT) { + res.colors[i].store_action = SG_STOREACTION_STORE; + } + } + if (res.depth.load_action == _SG_LOADACTION_DEFAULT) { + res.depth.load_action = SG_LOADACTION_CLEAR; + res.depth.clear_value = SG_DEFAULT_CLEAR_DEPTH; + } + if (res.depth.store_action == _SG_STOREACTION_DEFAULT) { + res.depth.store_action = SG_STOREACTION_DONTCARE; + } + if (res.stencil.load_action == _SG_LOADACTION_DEFAULT) { + res.stencil.load_action = SG_LOADACTION_CLEAR; + res.stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; + } + if (res.stencil.store_action == _SG_STOREACTION_DEFAULT) { + res.stencil.store_action = SG_STOREACTION_DONTCARE; + } + return res; +} + +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>dummy backend +#if defined(SOKOL_DUMMY_BACKEND) + +_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + _SOKOL_UNUSED(desc); + _sg.backend = SG_BACKEND_DUMMY; + for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + _sg.formats[i].sample = true; + _sg.formats[i].filter = true; + _sg.formats[i].render = true; + _sg.formats[i].blend = true; + _sg.formats[i].msaa = true; + } + _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true; + _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; + _sg.limits.max_image_size_2d = 1024; + _sg.limits.max_image_size_cube = 1024; + _sg.limits.max_image_size_3d = 1024; + _sg.limits.max_image_size_array = 1024; + _sg.limits.max_image_array_layers = 1024; + _sg.limits.max_vertex_attrs = 16; + _sg.limits.max_color_attachments = SG_MAX_PORTABLE_COLOR_ATTACHMENTS; + _sg.limits.max_texture_bindings_per_stage = SG_MAX_PORTABLE_TEXTURE_BINDINGS_PER_STAGE; + _sg.limits.max_storage_buffer_bindings_per_stage = SG_MAX_PORTABLE_STORAGEBUFFER_BINDINGS_PER_STAGE; + _sg.limits.max_storage_image_bindings_per_stage = SG_MAX_PORTABLE_STORAGEIMAGE_BINDINGS_PER_STAGE; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { + // empty +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SOKOL_UNUSED(buf); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SOKOL_UNUSED(img); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SOKOL_UNUSED(smp); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SOKOL_UNUSED(smp); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + _SOKOL_UNUSED(shd); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SOKOL_UNUSED(shd); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + _SOKOL_UNUSED(pip); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && desc); + _SOKOL_UNUSED(view); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_view(_sg_view_t* view) { + SOKOL_ASSERT(view); + _SOKOL_UNUSED(view); +} + +_SOKOL_PRIVATE void _sg_dummy_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(pass && atts); + _SOKOL_UNUSED(pass); + _SOKOL_UNUSED(atts); +} + +_SOKOL_PRIVATE void _sg_dummy_end_pass(const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); +} + +_SOKOL_PRIVATE void _sg_dummy_commit(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE bool _sg_dummy_apply_bindings(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SOKOL_UNUSED(bnd); + return true; +} + +_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(int ub_slot, const sg_range* data) { + _SOKOL_UNUSED(ub_slot); + _SOKOL_UNUSED(data); +} + +_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); + _SOKOL_UNUSED(base_vertex); + _SOKOL_UNUSED(base_instance); +} + +_SOKOL_PRIVATE void _sg_dummy_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + _SOKOL_UNUSED(num_groups_x); + _SOKOL_UNUSED(num_groups_y); + _SOKOL_UNUSED(num_groups_z); +} + +_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } +} + +_SOKOL_PRIVATE bool _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + return true; +} + +_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _SOKOL_UNUSED(data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } +} + +// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>opengl backend +#elif defined(_SOKOL_ANY_GL) + +// optional GL loader for win32 +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + +#ifndef SG_GL_FUNCS_EXT +#define SG_GL_FUNCS_EXT +#endif + +// X Macro list of GL function names and signatures +#define _SG_GL_FUNCS \ + SG_GL_FUNCS_EXT \ + _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ + _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ + _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ + _SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ + _SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ + _SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ + _SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ + _SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ + _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(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)) \ + _SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ + _SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glDeleteShader, void, (GLuint shader)) \ + _SG_XMACRO(glDeleteProgram, void, (GLuint program)) \ + _SG_XMACRO(glCompileShader, void, (GLuint shader)) \ + _SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ + _SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ + _SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ + _SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ + _SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ + _SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ + _SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ + _SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * 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(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)) \ + _SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ + _SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ + _SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ + _SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ + _SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ + _SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ + _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ + _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ + _SG_XMACRO(glCreateProgram, GLuint, (void)) \ + _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ + _SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ + _SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ + _SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ + _SG_XMACRO(glVertexAttribIPointer, void, (GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer)) \ + _SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ + _SG_XMACRO(glDisable, void, (GLenum cap)) \ + _SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ + _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ + _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ + _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ + _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _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)) \ + _SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ + _SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ + _SG_XMACRO(glEnable, void, (GLenum cap)) \ + _SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ + _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ + _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ + _SG_XMACRO(glGetError, GLenum, (void)) \ + _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, 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(glReadBuffer, void, (GLenum src)) \ + _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(glPixelStorei, void, (GLenum pname, GLint param)) \ + _SG_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \ + _SG_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \ + _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ + _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ + _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ + _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) \ + _SG_XMACRO(glBindBufferRange, void, (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)) \ + _SG_XMACRO(glTexImage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \ + _SG_XMACRO(glTexImage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) \ + _SG_XMACRO(glDispatchCompute, void, (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z)) \ + _SG_XMACRO(glMemoryBarrier, void, (GLbitfield barriers)) \ + _SG_XMACRO(glBindImageTexture, void, (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format)) \ + _SG_XMACRO(glTexStorage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \ + _SG_XMACRO(glTexStorage2D, void, (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glTexStorage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) \ + _SG_XMACRO(glTexStorage3D, void, (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)) \ + _SG_XMACRO(glCompressedTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data)) \ + _SG_XMACRO(glCompressedTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data)) \ + _SG_XMACRO(glTextureView, void, (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)) \ + _SG_XMACRO(glDrawElementsBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex)) \ + _SG_XMACRO(glDrawElementsInstancedBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex)) \ + _SG_XMACRO(glDrawElementsInstancedBaseVertexBaseInstance, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance)) \ + _SG_XMACRO(glDrawArraysInstancedBaseInstance, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance)) + +// generate GL function pointer typedefs +#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// generate GL function pointers +#define _SG_XMACRO(name, ret, args) static PFN_ ## name name; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// helper function to lookup GL functions in GL DLL +typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR); +_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) { + void* proc_addr = (void*) wgl_getprocaddress(name); + if (0 == proc_addr) { + proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name); + } + SOKOL_ASSERT(proc_addr); + return proc_addr; +} + +// populate GL function pointers +_SOKOL_PRIVATE void _sg_gl_load_opengl(void) { + SOKOL_ASSERT(0 == _sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll"); + SOKOL_ASSERT(_sg.gl.opengl32_dll); + _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress"); + SOKOL_ASSERT(wgl_getprocaddress); + #define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress); + _SG_GL_FUNCS + #undef _SG_XMACRO +} + +_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { + SOKOL_ASSERT(_sg.gl.opengl32_dll); + FreeLibrary(_sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = 0; +} +#endif // _SOKOL_USE_WIN32_GL_LOADER + +//-- type translation ---------------------------------------------------------- +_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(const sg_buffer_usage* usg) { + // NOTE: the buffer target returned here is only used for the bind point + // to copy data into the buffer, expect for WebGL2, the bind point doesn't + // need to match the later usage of the buffer (but because of the WebGL2 + // restriction we cannot simply select a random bind point, because in WebGL2 + // a buffer cannot 'switch' bind points later. + if (usg->vertex_buffer) { + return GL_ARRAY_BUFFER; + } else if (usg->index_buffer) { + return GL_ELEMENT_ARRAY_BUFFER; + } else if (usg->storage_buffer) { + return GL_SHADER_STORAGE_BUFFER; + } else { + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t, int sample_count) { + #if defined(SOKOL_GLCORE) + const bool msaa = sample_count > 1; + if (msaa) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D_MULTISAMPLE; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_MULTISAMPLE_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } + } else { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } + } + #else + SOKOL_ASSERT(sample_count == 1); _SOKOL_UNUSED(sample_count); + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } + #endif +} + +_SOKOL_PRIVATE GLenum _sg_gl_buffer_usage(const sg_buffer_usage* usg) { + if (usg->immutable) { + return GL_STATIC_DRAW; + } else if (usg->dynamic_update) { + return GL_DYNAMIC_DRAW; + } else if (usg->stream_update) { + return GL_STREAM_DRAW; + } else { + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VERTEX: return GL_VERTEX_SHADER; + case SG_SHADERSTAGE_FRAGMENT: return GL_FRAGMENT_SHADER; + case SG_SHADERSTAGE_COMPUTE: return GL_COMPUTE_SHADER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 1; + case SG_VERTEXFORMAT_FLOAT2: return 2; + case SG_VERTEXFORMAT_FLOAT3: return 3; + case SG_VERTEXFORMAT_FLOAT4: return 4; + case SG_VERTEXFORMAT_INT: return 1; + case SG_VERTEXFORMAT_INT2: return 2; + case SG_VERTEXFORMAT_INT3: return 3; + case SG_VERTEXFORMAT_INT4: return 4; + case SG_VERTEXFORMAT_UINT: return 1; + case SG_VERTEXFORMAT_UINT2: return 2; + case SG_VERTEXFORMAT_UINT3: return 3; + case SG_VERTEXFORMAT_UINT4: return 4; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 2; + case SG_VERTEXFORMAT_SHORT2N: return 2; + case SG_VERTEXFORMAT_USHORT2: return 2; + case SG_VERTEXFORMAT_USHORT2N: return 2; + case SG_VERTEXFORMAT_SHORT4: return 4; + case SG_VERTEXFORMAT_SHORT4N: return 4; + case SG_VERTEXFORMAT_USHORT4: 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; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + return GL_FLOAT; + case SG_VERTEXFORMAT_INT: + case SG_VERTEXFORMAT_INT2: + case SG_VERTEXFORMAT_INT3: + case SG_VERTEXFORMAT_INT4: + return GL_INT; + case SG_VERTEXFORMAT_UINT: + case SG_VERTEXFORMAT_UINT2: + case SG_VERTEXFORMAT_UINT3: + case SG_VERTEXFORMAT_UINT4: + return GL_UNSIGNED_INT; + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_BYTE4N: + return GL_BYTE; + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_UBYTE4N: + return GL_UNSIGNED_BYTE; + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4: + case SG_VERTEXFORMAT_SHORT4N: + return GL_SHORT; + case SG_VERTEXFORMAT_USHORT2: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_USHORT4: + case SG_VERTEXFORMAT_USHORT4N: + 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; + } +} + +_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_USHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; + case SG_PRIMITIVETYPE_LINES: return GL_LINES; + case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; + case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { + switch (cmp) { + case SG_COMPAREFUNC_NEVER: return GL_NEVER; + case SG_COMPAREFUNC_LESS: return GL_LESS; + case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; + case SG_COMPAREFUNC_GREATER: return GL_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; + case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return GL_KEEP; + case SG_STENCILOP_ZERO: return GL_ZERO; + case SG_STENCILOP_REPLACE: return GL_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return GL_INCR; + case SG_STENCILOP_DECR_CLAMP: return GL_DECR; + case SG_STENCILOP_INVERT: return GL_INVERT; + case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; + case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return GL_ZERO; + case SG_BLENDFACTOR_ONE: return GL_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + #if defined(_SOKOL_GL_HAS_DUALSOURCEBLENDING) + case SG_BLENDFACTOR_SRC1_COLOR: return GL_SRC1_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return GL_ONE_MINUS_SRC1_COLOR; + case SG_BLENDFACTOR_SRC1_ALPHA: return GL_SRC1_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return GL_ONE_MINUS_SRC1_ALPHA; + #endif + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return GL_FUNC_ADD; + case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + case SG_BLENDOP_MIN: return GL_MIN; + case SG_BLENDOP_MAX: return GL_MAX; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) { + if (min_f == SG_FILTER_NEAREST) { + switch (mipmap_f) { + case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else if (min_f == SG_FILTER_LINEAR) { + switch (mipmap_f) { + case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else { + SOKOL_UNREACHABLE; return (GLenum)0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { + if (mag_f == SG_FILTER_NEAREST) { + return GL_NEAREST; + } else { + return GL_LINEAR; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { + switch (w) { + case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + #if defined(SOKOL_GLCORE) + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + #else + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; + #endif + case SG_WRAP_REPEAT: return GL_REPEAT; + case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8UI: + 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; + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8SI: + return GL_BYTE; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16UI: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16SI: + return GL_SHORT; + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA16F: + return GL_HALF_FLOAT; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RGBA32UI: + return GL_UNSIGNED_INT; + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_INT; + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA32F: + return GL_FLOAT; + case SG_PIXELFORMAT_RGB10A2: + 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; + case SG_PIXELFORMAT_DEPTH: + return GL_FLOAT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_UNSIGNED_INT_24_8; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_R32F: + return GL_RED; + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGB10A2: + return GL_RGBA; + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; + case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: + return GL_RGB; + case SG_PIXELFORMAT_DEPTH: + return GL_DEPTH_COMPONENT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_DEPTH_STENCIL; + case SG_PIXELFORMAT_BC1_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: + return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: + return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: + return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: + return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: + return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: + return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: + return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: + return GL_COMPRESSED_SRGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: + return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: + return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: + return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: + return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: + return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: + return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: + return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; + #endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; + #endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + 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; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; + #endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT32F; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { + switch (face_index) { + case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: SOKOL_UNREACHABLE; return 0; + } +} + +// see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml +_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _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]); + if (has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + } + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _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) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); +} + +// FIXME: OES_half_float_blend +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + } + } else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + } else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_astc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_compute(void) { + // using Vulkan's conservative default caps (see: https://github.com/gpuweb/gpuweb/issues/513) + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); +} + +_SOKOL_PRIVATE void _sg_gl_init_limits(void) { + _SG_GL_CHECK_ERROR(); + + GLint gl_int; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + + _sg.limits.max_image_size_2d = gl_int; + _sg.limits.max_image_size_array = gl_int; + + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_cube = gl_int; + + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_vertex_attrs = _sg_min(gl_int, SG_MAX_VERTEX_ATTRIBUTES); + + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_color_attachments = _sg_min(gl_int, SG_MAX_COLOR_ATTACHMENTS); + + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_texture_bindings_per_stage = _sg_min(gl_int, SG_MAX_VIEW_BINDSLOTS); + + #if defined(_SOKOL_GL_HAS_COMPUTE) + if (_sg.features.compute) { + glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min(gl_int, SG_MAX_VIEW_BINDSLOTS); + + glGetIntegerv(GL_MAX_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_storage_image_bindings_per_stage = _sg_min(gl_int, SG_MAX_VIEW_BINDSLOTS); + } + #endif + + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_vertex_uniform_components = gl_int; + + if (_sg.gl.ext_anisotropic) { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_anisotropy = gl_int; + } else { + _sg.gl.max_anisotropy = 1; + } + + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_combined_texture_image_units = gl_int; +} + +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { + _sg.backend = SG_BACKEND_GLCORE; + + GLint major_version = 0; + GLint minor_version = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + const int version = major_version * 100 + minor_version * 10; + _sg.features.origin_top_left = false; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = true; + _sg.features.compute = version >= 430; + _sg.features.gl_texture_views = version >= 430; + #if defined(__APPLE__) + _sg.features.msaa_texture_bindings = false; + #else + _sg.features.msaa_texture_bindings = true; + #endif + _sg.features.draw_base_vertex = version >= 320; + _sg.features.draw_base_instance = version >= 420; + _sg.features.dual_source_blending = version >= 330; + + // scan extensions + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 + bool has_etc2 = false; + bool has_astc = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } else if (strstr(ext, "_ES3_compatibility")) { + has_etc2 = true; + } else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } else if (strstr(ext, "_texture_compression_astc_ldr")) { + has_astc = true; + } + } + } + + // limits + _sg_gl_init_limits(); + + // pixel formats + const bool has_bgra = false; // not a bug + const bool has_colorbuffer_float = true; + const bool has_colorbuffer_half_float = true; + const bool has_texture_float_linear = true; // FIXME??? + const bool has_float_blend = true; + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } + if (_sg.features.compute) { + _sg_gl_init_pixelformats_compute(); + } +} +#endif + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { + _sg.backend = SG_BACKEND_GLES3; + + GLint major_version = 0; + GLint minor_version = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + const int version = major_version * 100 + minor_version * 10; + _sg.features.origin_top_left = false; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + #if defined(_SOKOL_GL_HAS_COLORMASKI) + _sg.features.mrt_independent_write_mask = version >= 320; + #else + _sg.features.mrt_independent_write_mask = false; + #endif + _sg.features.compute = version >= 310; + _sg.features.msaa_texture_bindings = false; + _sg.features.gl_texture_views = version >= 430; + #if defined(__EMSCRIPTEN__) + _sg.features.separate_buffer_types = true; + #else + _sg.features.separate_buffer_types = false; + #endif + _sg.features.draw_base_vertex = version >= 320; + _sg.features.draw_base_instance = false; + _sg.features.dual_source_blending = false; + + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 + #if defined(__EMSCRIPTEN__) + bool has_etc2 = false; + #else + bool has_etc2 = true; + #endif + bool has_astc = false; + bool has_colorbuffer_float = false; + bool has_colorbuffer_half_float = false; + bool has_texture_float_linear = false; + bool has_float_blend = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_compressed_texture_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } else if (strstr(ext, "_compressed_texture_etc")) { + has_etc2 = true; + } else if (strstr(ext, "_compressed_texture_astc")) { + has_astc = true; + } else if (strstr(ext, "_color_buffer_float")) { + has_colorbuffer_float = true; + } else if (strstr(ext, "_color_buffer_half_float")) { + has_colorbuffer_half_float = true; + } else if (strstr(ext, "_texture_float_linear")) { + has_texture_float_linear = true; + } else if (strstr(ext, "_float_blend")) { + has_float_blend = true; + } else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* on WebGL2, color_buffer_float also includes 16-bit formats + see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float + */ + #if defined(__EMSCRIPTEN__) + if (!has_colorbuffer_half_float && has_colorbuffer_float) { + has_colorbuffer_half_float = has_colorbuffer_float; + } + #endif + + // limits + _sg_gl_init_limits(); + + // pixel formats + const bool has_bgra = false; // not a bug + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } + if (_sg.features.compute) { + _sg_gl_init_pixelformats_compute(); + } +} +#endif + +//-- state cache implementation ------------------------------------------------ +_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { + if (force || (_sg.gl.cache.vertex_buffer != 0)) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg.gl.cache.vertex_buffer = 0; + _sg_stats_inc(gl.num_bind_buffer); + } + if (force || (_sg.gl.cache.index_buffer != 0)) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg.gl.cache.index_buffer = 0; + _sg_stats_inc(gl.num_bind_buffer); + } + if (force || (_sg.gl.cache.storage_buffer != 0)) { + if (_sg.features.compute) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + _sg.gl.cache.storage_buffer = 0; + _sg_stats_inc(gl.num_bind_buffer); + } + for (int i = 0; i < _SG_GL_MAX_SBUF_BINDINGS; i++) { + if (force || (_sg.gl.cache.storage_buffers[i] != 0)) { + if (_sg.features.compute && (i < _sg.limits.max_storage_buffer_bindings_per_stage)) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, (GLuint)i, 0); + } + _sg.gl.cache.storage_buffers[i] = 0; + _sg_stats_inc(gl.num_bind_buffer); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target) || (GL_SHADER_STORAGE_BUFFER == target)); + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.vertex_buffer != buffer) { + _sg.gl.cache.vertex_buffer = buffer; + glBindBuffer(target, buffer); + _sg_stats_inc(gl.num_bind_buffer); + } + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + if (_sg.gl.cache.index_buffer != buffer) { + _sg.gl.cache.index_buffer = buffer; + glBindBuffer(target, buffer); + _sg_stats_inc(gl.num_bind_buffer); + } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.storage_buffer != buffer) { + _sg.gl.cache.storage_buffer = buffer; + if (_sg.features.compute) { + glBindBuffer(target, buffer); + } + _sg_stats_inc(gl.num_bind_buffer); + } + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_storage_buffer(uint8_t glsl_binding_n, GLuint buffer, int offset, int buf_size) { + SOKOL_ASSERT(glsl_binding_n < _SG_GL_MAX_SBUF_BINDINGS); + SOKOL_ASSERT(offset < buf_size); + const bool buf_neql = _sg.gl.cache.storage_buffers[glsl_binding_n] != buffer; + const bool off_neql = _sg.gl.cache.storage_buffer_offsets[glsl_binding_n] != offset; + if (buf_neql || off_neql) { + _sg.gl.cache.storage_buffers[glsl_binding_n] = buffer; + _sg.gl.cache.storage_buffer_offsets[glsl_binding_n] = offset; + _sg.gl.cache.storage_buffer = buffer; // not a bug + if (_sg.features.compute) { + SOKOL_ASSERT(glsl_binding_n < _sg.limits.max_storage_buffer_bindings_per_stage); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, glsl_binding_n, buffer, offset, buf_size - offset); + } + _sg_stats_inc(gl.num_bind_buffer); + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } else if (target == GL_SHADER_STORAGE_BUFFER) { + _sg.gl.cache.stored_storage_buffer = _sg.gl.cache.storage_buffer; + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_vertex_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + _sg.gl.cache.stored_vertex_buffer = 0; + } + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_index_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + _sg.gl.cache.stored_index_buffer = 0; + } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.stored_storage_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_storage_buffer); + _sg.gl.cache.stored_storage_buffer = 0; + } + } else { + SOKOL_UNREACHABLE; + } +} + +// called from _sg_gl_discard_buffer() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { + if (buf == _sg.gl.cache.vertex_buffer) { + _sg.gl.cache.vertex_buffer = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg_stats_inc(gl.num_bind_buffer); + } + if (buf == _sg.gl.cache.index_buffer) { + _sg.gl.cache.index_buffer = 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg_stats_inc(gl.num_bind_buffer); + } + if (buf == _sg.gl.cache.storage_buffer) { + _sg.gl.cache.storage_buffer = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _sg_stats_inc(gl.num_bind_buffer); + } + for (int i = 0; i < _SG_GL_MAX_SBUF_BINDINGS; i++) { + if (buf == _sg.gl.cache.storage_buffers[i]) { + _sg.gl.cache.storage_buffers[i] = 0; + _sg.gl.cache.storage_buffer = 0; // not a bug! + if (_sg.features.compute && (i < _sg.limits.max_storage_buffer_bindings_per_stage)) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, (GLuint)i, 0); + } + _sg_stats_inc(gl.num_bind_buffer); + } + } + if (buf == _sg.gl.cache.stored_vertex_buffer) { + _sg.gl.cache.stored_vertex_buffer = 0; + } + if (buf == _sg.gl.cache.stored_index_buffer) { + _sg.gl.cache.stored_index_buffer = 0; + } + if (buf == _sg.gl.cache.stored_storage_buffer) { + _sg.gl.cache.stored_storage_buffer = 0; + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { + _sg.gl.cache.attrs[i].gl_vbuf = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + _SG_GL_CHECK_ERROR(); + if (_sg.gl.cache.cur_active_texture != texture) { + _sg.gl.cache.cur_active_texture = texture; + glActiveTexture(texture); + _sg_stats_inc(gl.num_active_texture); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; (i < _SG_GL_MAX_TEX_SMP_BINDINGS) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) { + GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_unit); + _sg_stats_inc(gl.num_active_texture); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + _sg_stats_add(gl.num_bind_texture, 4); + glBindSampler((GLuint)i, 0); + _sg_stats_inc(gl.num_bind_sampler); + _sg.gl.cache.texture_samplers[i].target = 0; + _sg.gl.cache.texture_samplers[i].texture = 0; + _sg.gl.cache.texture_samplers[i].sampler = 0; + _sg.gl.cache.cur_active_texture = gl_texture_unit; + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int8_t gl_tex_slot, GLenum target, GLuint texture, GLuint sampler) { + /* it's valid to call this function with target=0 and/or texture=0 + target=0 will unbind the previous binding, texture=0 will clear + the new binding + */ + SOKOL_ASSERT((gl_tex_slot >= 0) && (gl_tex_slot < _SG_GL_MAX_TEX_SMP_BINDINGS)); + if (gl_tex_slot >= _sg.limits.gl_max_combined_texture_image_units) { + return; + } + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[gl_tex_slot]; + if ((slot->target != target) || (slot->texture != texture) || (slot->sampler != sampler)) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + gl_tex_slot)); + // if the target has changed, clear the previous binding on that target + if ((target != slot->target) && (slot->target != 0)) { + glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_inc(gl.num_bind_texture); + } + // apply new binding (can be 0 to unbind) + if (target != 0) { + glBindTexture(target, texture); + _SG_GL_CHECK_ERROR(); + _sg_stats_inc(gl.num_bind_texture); + } + // apply new sampler (can be 0 to unbind) + glBindSampler((GLuint)gl_tex_slot, sampler); + _SG_GL_CHECK_ERROR(); + _sg_stats_inc(gl.num_bind_sampler); + + slot->target = target; + slot->texture = texture; + slot->sampler = sampler; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_sampler_binding(int8_t gl_tex_slot) { + SOKOL_ASSERT((gl_tex_slot >= 0) && (gl_tex_slot < _SG_GL_MAX_TEX_SMP_BINDINGS)); + _sg.gl.cache.stored_texture_sampler = _sg.gl.cache.texture_samplers[gl_tex_slot]; +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_sampler_binding(int8_t gl_tex_slot) { + SOKOL_ASSERT((gl_tex_slot >= 0) && (gl_tex_slot < _SG_GL_MAX_TEX_SMP_BINDINGS)); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.stored_texture_sampler; + if (slot->texture != 0) { + // we only care about restoring valid ids + SOKOL_ASSERT(slot->target != 0); + _sg_gl_cache_bind_texture_sampler(gl_tex_slot, slot->target, slot->texture, slot->sampler); + slot->target = 0; + slot->texture = 0; + slot->sampler = 0; + } +} + +// called from _sg_gl_discard_texture() and _sg_gl_discard_sampler() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint smp) { + _SG_GL_CHECK_ERROR(); + for (size_t i = 0; i < _SG_GL_MAX_TEX_SMP_BINDINGS; i++) { + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[i]; + if ((0 != slot->target) && ((tex == slot->texture) || (smp == slot->sampler))) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); + glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_inc(gl.num_bind_texture); + glBindSampler((GLuint)i, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_inc(gl.num_bind_sampler); + slot->target = 0; + slot->texture = 0; + slot->sampler = 0; + } + } + if ((tex == _sg.gl.cache.stored_texture_sampler.texture) || (smp == _sg.gl.cache.stored_texture_sampler.sampler)) { + _sg.gl.cache.stored_texture_sampler.target = 0; + _sg.gl.cache.stored_texture_sampler.texture = 0; + _sg.gl.cache.stored_texture_sampler.sampler = 0; + } +} + +// 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; + glUseProgram(0); + _sg_stats_inc(gl.num_use_program); + } +} + +// called from _sg_gl_discard_pipeline() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { + if (_sg_sref_slot_eql(&_sg.gl.cache.cur_pip, &pip->slot)) { + _sg.gl.cache.cur_pip = _sg_sref(0); + } +} + +_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { + _SG_GL_CHECK_ERROR(); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + _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_sampler_bindings(true); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; + attr->vb_index = -1; + attr->divisor = -1; + glDisableVertexAttribArray((GLuint)i); + _SG_GL_CHECK_ERROR(); + _sg_stats_inc(gl.num_disable_vertex_attrib_array); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + + // shader program + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); + + // depth and stencil state + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + _sg_stats_add(gl.num_render_state, 7); + + // blend state + _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; + _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + _sg_stats_add(gl.num_render_state, 4); + + // standalone state + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; + _sg.gl.cache.face_winding = SG_FACEWINDING_CW; + _sg.gl.cache.sample_count = 1; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + _sg_stats_add(gl.num_render_state, 10); + #if defined(SOKOL_GLCORE) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + _sg_stats_add(gl.num_render_state, 2); + #endif +} + +_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { + _SOKOL_UNUSED(desc); + + // assumes that _sg.gl is already zero-initialized + _sg.gl.valid = true; + + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_load_opengl(); + #endif + + // clear initial GL error state + #if defined(SOKOL_DEBUG) + while (glGetError() != GL_NO_ERROR); + #endif + #if defined(SOKOL_GLCORE) + _sg_gl_init_caps_glcore(); + #elif defined(SOKOL_GLES3) + _sg_gl_init_caps_gles3(); + #endif + + // create and bind global vertex array object which will be mutated as needed + glGenVertexArrays(1, &_sg.gl.vao); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + + // create global framebuffer object which will be mutated as needed + glGenFramebuffers(1, &_sg.gl.fb); + _SG_GL_CHECK_ERROR(); + + // incoming texture data is generally expected to be packed tightly + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + #if defined(SOKOL_GLCORE) + // enable seamless cubemap sampling (only desktop GL) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + #endif + _sg_gl_reset_state_cache(); +} + +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + if (_sg.gl.fb) { + glDeleteFramebuffers(1, &_sg.gl.fb); + } + if (_sg.gl.vao) { + glDeleteVertexArrays(1, &_sg.gl.vao); + } + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_unload_opengl(); + #endif + _sg.gl.valid = false; +} + +//-- GL backend resource creation and destruction ------------------------------ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SG_GL_CHECK_ERROR(); + buf->gl.injected = (0 != desc->gl_buffers[0]); + const GLenum gl_target = _sg_gl_buffer_target(&buf->cmn.usage); + const GLenum gl_usage = _sg_gl_buffer_usage(&buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + GLuint gl_buf = 0; + if (buf->gl.injected) { + SOKOL_ASSERT(desc->gl_buffers[slot]); + gl_buf = desc->gl_buffers[slot]; + } else { + glGenBuffers(1, &gl_buf); + SOKOL_ASSERT(gl_buf); + _sg_gl_cache_store_buffer_binding(gl_target); + _sg_gl_cache_bind_buffer(gl_target, gl_buf); + glBufferData(gl_target, buf->cmn.size, 0, gl_usage); + if (desc->data.ptr) { + glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr); + } + _sg_gl_cache_restore_buffer_binding(gl_target); + } + buf->gl.buf[slot] = gl_buf; + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_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++) { + if (buf->gl.buf[slot]) { + _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); + if (!buf->gl.injected) { + glDeleteBuffers(1, &buf->gl.buf[slot]); + } + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].sample; +} + +_SOKOL_PRIVATE void _sg_gl_texstorage(const _sg_image_t* img) { + const GLenum tgt = img->gl.target; + const int num_mips = img->cmn.num_mipmaps; + #if defined(_SOKOL_GL_HAS_TEXSTORAGE) + const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + const bool msaa = img->cmn.sample_count > 1; + const int w = img->cmn.width; + const int h = img->cmn.height; + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + #if defined(SOKOL_GLCORE) + if (msaa) { + glTexStorage2DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, GL_TRUE); + } else { + glTexStorage2D(tgt, num_mips, ifmt, w, h); + } + #else + SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa); + glTexStorage2D(tgt, num_mips, ifmt, w, h); + #endif + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + const int depth = img->cmn.num_slices; + #if defined(SOKOL_GLCORE) + if (msaa) { + // NOTE: MSAA works only for array textures, not 3D textures + glTexStorage3DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, depth, GL_TRUE); + } else { + glTexStorage3D(tgt, num_mips, ifmt, w, h, depth); + } + #else + SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa); + glTexStorage3D(tgt, num_mips, ifmt, w, h, depth); + #endif + } + #else + glTexParameteri(tgt, GL_TEXTURE_MAX_LEVEL, num_mips - 1); + #endif + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_texsubimage(const _sg_image_t* img, GLenum tgt, int mip_index, int w, int h, int depth, const GLvoid* data_ptr, GLsizei data_size) { + SOKOL_ASSERT(data_ptr && (data_size > 0)); + SOKOL_ASSERT(img->cmn.sample_count == 1); + const bool compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (compressed) { + const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + glCompressedTexSubImage2D(tgt, mip_index, 0, 0, w, h, ifmt, data_size, data_ptr); + } else { + const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format); + const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format); + glTexSubImage2D(tgt, mip_index, 0, 0, w, h, fmt, type, data_ptr); + } + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + if (compressed) { + const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + glCompressedTexSubImage3D(tgt, mip_index, 0, 0, 0, w, h, depth, ifmt, data_size, data_ptr); + } else { + const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format); + const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format); + glTexSubImage3D(tgt, mip_index, 0, 0, 0, w, h, depth, fmt, type, data_ptr); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_teximage(const _sg_image_t* img, GLenum tgt, int mip_index, int w, int h, int depth, const GLvoid* data_ptr, GLsizei data_size) { + #if defined(_SOKOL_GL_HAS_TEXSTORAGE) + if (data_ptr == 0) { + return; + } + _sg_gl_texsubimage(img, tgt, mip_index, w, h, depth, data_ptr, data_size); + #else + const bool compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + const bool msaa = img->cmn.sample_count > 1; + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (compressed) { + SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa); + glCompressedTexImage2D(tgt, mip_index, ifmt, w, h, 0, data_size, data_ptr); + } else { + const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format); + const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format); + #if defined(SOKOL_GLCORE) && !defined(__APPLE__) + if (msaa) { + glTexImage2DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, GL_TRUE); + } else { + glTexImage2D(tgt, mip_index, (GLint)ifmt, w, h, 0, fmt, type, data_ptr); + } + #else + SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa); + glTexImage2D(tgt, mip_index, (GLint)ifmt, w, h, 0, fmt, type, data_ptr); + #endif + } + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + if (compressed) { + SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa); + glCompressedTexImage3D(tgt, mip_index, ifmt, w, h, depth, 0, data_size, data_ptr); + } else { + const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format); + const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format); + #if defined(SOKOL_GLCORE) && !defined(__APPLE__) + if (msaa) { + // NOTE: MSAA works only for array textures, not 3D textures + glTexImage3DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, depth, GL_TRUE); + } else { + glTexImage3D(tgt, mip_index, (GLint)ifmt, w, h, depth, 0, fmt, type, data_ptr); + } + #else + SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa); + glTexImage3D(tgt, mip_index, (GLint)ifmt, w, h, depth, 0, fmt, type, data_ptr); + #endif + } + } + #endif + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SG_GL_CHECK_ERROR(); + img->gl.injected = (0 != desc->gl_textures[0]); + + // check if texture format is support + if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { + _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED); + return SG_RESOURCESTATE_FAILED; + } + + if (img->gl.injected) { + img->gl.target = _sg_gl_texture_target(img->cmn.type, img->cmn.sample_count); + // inject externally GL textures + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; + } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; + } + } else { + // on platforms that don't support MSAA texture bindings, no actual GL + // texture object is created, instead only attachment view object can be built + const bool msaa = img->cmn.sample_count > 1; + if (msaa && !_sg.features.msaa_texture_bindings) { + if (img->cmn.usage.color_attachment || img->cmn.usage.depth_stencil_attachment) { + return SG_RESOURCESTATE_VALID; + } else { + return SG_RESOURCESTATE_FAILED; + } + } + img->gl.target = _sg_gl_texture_target(img->cmn.type, img->cmn.sample_count); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + SOKOL_ASSERT(img->gl.tex[slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); + _sg_gl_texstorage(img); + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const GLvoid* data_ptr = desc->data.mip_levels[mip_index].ptr; + const GLsizei data_size = (GLsizei)desc->data.mip_levels[mip_index].size; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const int mip_depth = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + // NOTE: surf_ptr is allowed to be null here + const uint8_t* surf_ptr = (const uint8_t*) data_ptr; + for (int i = 0; i < 6; i++) { + const GLenum gl_img_target = _sg_gl_cubeface_target(i); + _sg_gl_teximage(img, gl_img_target, mip_index, mip_width, mip_height, mip_depth, surf_ptr, surf_pitch); + if (data_ptr) { + SOKOL_ASSERT((6 * surf_pitch) <= data_size); + surf_ptr += surf_pitch; + } + } + } else { + _sg_gl_teximage(img, img->gl.target, mip_index, mip_width, mip_height, mip_depth, data_ptr, data_size); + } + } + _sg_gl_cache_restore_texture_sampler_binding(0); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_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++) { + if (img->gl.tex[slot]) { + _sg_gl_cache_invalidate_texture_sampler(img->gl.tex[slot], 0); + if (!img->gl.injected) { + glDeleteTextures(1, &img->gl.tex[slot]); + } + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SG_GL_CHECK_ERROR(); + smp->gl.injected = (0 != desc->gl_sampler); + if (smp->gl.injected) { + smp->gl.smp = (GLuint) desc->gl_sampler; + } else { + glGenSamplers(1, &smp->gl.smp); + SOKOL_ASSERT(smp->gl.smp); + + const GLenum gl_min_filter = _sg_gl_min_filter(smp->cmn.min_filter, smp->cmn.mipmap_filter); + const GLenum gl_mag_filter = _sg_gl_mag_filter(smp->cmn.mag_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + // GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MIN_LOD, min_lod); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MAX_LOD, max_lod); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w)); + #if defined(SOKOL_GLCORE) + float border[4]; + switch (smp->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glSamplerParameterfv(smp->gl.smp, GL_TEXTURE_BORDER_COLOR, border); + #endif + if (smp->cmn.compare != SG_COMPAREFUNC_NEVER) { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_FUNC, (GLint)_sg_gl_compare_func(smp->cmn.compare)); + } else { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + if (_sg.gl.ext_anisotropic && (smp->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) smp->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_invalidate_texture_sampler(0, smp->gl.smp); + if (!smp->gl.injected) { + glDeleteSamplers(1, &smp->gl.smp); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { + SOKOL_ASSERT(src); + _SG_GL_CHECK_ERROR(); + GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); + glShaderSource(gl_shd, 1, &src, 0); + glCompileShader(gl_shd); + GLint compile_status = 0; + glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + // compilation failed, log error and delete shader + GLint log_len = 0; + glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); + glGetShaderInfoLog(gl_shd, log_len, &log_len, 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; + } + _SG_GL_CHECK_ERROR(); + return gl_shd; +} + +// NOTE: this is an out-of-range check for GLSL bindslots that's also active in release mode +_SOKOL_PRIVATE bool _sg_gl_ensure_glsl_bindslot_ranges(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); _SOKOL_UNUSED(desc); + #if defined(_SOKOL_GL_HAS_COMPUTE) + SOKOL_ASSERT(_sg.limits.max_storage_buffer_bindings_per_stage <= _SG_GL_MAX_SBUF_BINDINGS); + SOKOL_ASSERT(_sg.limits.max_storage_image_bindings_per_stage <= _SG_GL_MAX_SIMG_BINDINGS); + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_buffer.glsl_binding_n >= _sg.limits.max_storage_buffer_bindings_per_stage) { + _SG_ERROR(GL_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE); + return false; + } + } + if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_image.glsl_binding_n >= _sg.limits.max_storage_image_bindings_per_stage) { + _SG_ERROR(GL_STORAGEIMAGE_GLSL_BINDING_OUT_OF_RANGE); + return false; + } + } + } + #endif + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->gl.prog); + _SG_GL_CHECK_ERROR(); + + // perform a fatal range-check on GLSL bindslots that's also active + // in release mode to avoid potential out-of-bounds array accesses + if (!_sg_gl_ensure_glsl_bindslot_ranges(desc)) { + return SG_RESOURCESTATE_FAILED; + } + + // copy the optional vertex attribute names over + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].glsl_name); + } + + const bool has_vs = desc->vertex_func.source; + const bool has_fs = desc->fragment_func.source; + const bool has_cs = desc->compute_func.source; + SOKOL_ASSERT((has_vs && has_fs) || has_cs); + GLuint gl_prog = glCreateProgram(); + if (has_vs && has_fs) { + GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VERTEX, desc->vertex_func.source); + GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FRAGMENT, desc->fragment_func.source); + if (!(gl_vs && gl_fs)) { + glDeleteProgram(gl_prog); + if (gl_vs) { glDeleteShader(gl_vs); } + if (gl_fs) { glDeleteShader(gl_fs); } + return SG_RESOURCESTATE_FAILED; + } + glAttachShader(gl_prog, gl_vs); + glAttachShader(gl_prog, gl_fs); + glLinkProgram(gl_prog); + glDeleteShader(gl_vs); + glDeleteShader(gl_fs); + _SG_GL_CHECK_ERROR(); + } else if (has_cs) { + GLuint gl_cs = _sg_gl_compile_shader(SG_SHADERSTAGE_COMPUTE, desc->compute_func.source); + if (!gl_cs) { + glDeleteProgram(gl_prog); + return SG_RESOURCESTATE_FAILED; + } + glAttachShader(gl_prog, gl_cs); + glLinkProgram(gl_prog); + glDeleteShader(gl_cs); + _SG_GL_CHECK_ERROR(); + } else { + SOKOL_UNREACHABLE; + } + GLint link_status; + glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLint log_len = 0; + glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); + glGetProgramInfoLog(gl_prog, log_len, &log_len, 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; + } + shd->gl.prog = gl_prog; + + // resolve uniforms + _SG_GL_CHECK_ERROR(); + for (size_t ub_index = 0; ub_index < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_index++) { + const sg_shader_uniform_block* ub_desc = &desc->uniform_blocks[ub_index]; + if (ub_desc->stage == SG_SHADERSTAGE_NONE) { + continue; + } + SOKOL_ASSERT(ub_desc->size > 0); + _sg_gl_uniform_block_t* ub = &shd->gl.uniform_blocks[ub_index]; + SOKOL_ASSERT(ub->num_uniforms == 0); + uint32_t cur_uniform_offset = 0; + for (int u_index = 0; u_index < SG_MAX_UNIFORMBLOCK_MEMBERS; u_index++) { + const sg_glsl_shader_uniform* u_desc = &ub_desc->glsl_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; + SOKOL_ASSERT(u_desc->glsl_name); + u->gl_loc = glGetUniformLocation(gl_prog, u_desc->glsl_name); + if (u->gl_loc == -1) { + _SG_WARN(GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER, u_desc->glsl_name); + } + cur_uniform_offset += u_size; + 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); + } + + // copy resource bindslot mappings + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + SOKOL_ASSERT(0 == shd->gl.sbuf_binding[i]); + SOKOL_ASSERT(0 == shd->gl.simg_binding[i]); + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + shd->gl.sbuf_binding[i] = view->storage_buffer.glsl_binding_n; + } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + shd->gl.simg_binding[i] = view->storage_image.glsl_binding_n; + } + } + + // record image sampler location in shader program + _SG_GL_CHECK_ERROR(); + GLuint cur_prog = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); + glUseProgram(gl_prog); + GLint gl_tex_slot = 0; + for (size_t tex_smp_index = 0; tex_smp_index < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_index++) { + const sg_shader_texture_sampler_pair* tex_smp_desc = &desc->texture_sampler_pairs[tex_smp_index]; + if (tex_smp_desc->stage == SG_SHADERSTAGE_NONE) { + continue; + } + SOKOL_ASSERT(tex_smp_desc->glsl_name); + GLint gl_loc = glGetUniformLocation(gl_prog, tex_smp_desc->glsl_name); + if (gl_loc != -1) { + glUniform1i(gl_loc, gl_tex_slot); + shd->gl.tex_slot[tex_smp_index] = (int8_t)gl_tex_slot++; + } else { + shd->gl.tex_slot[tex_smp_index] = -1; + _SG_WARN(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER, tex_smp_desc->glsl_name); + } + } + + // it's legal to call glUseProgram with 0 + glUseProgram(cur_prog); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SG_GL_CHECK_ERROR(); + if (shd->gl.prog) { + _sg_gl_cache_invalidate_program(shd->gl.prog); + glDeleteProgram(shd->gl.prog); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + SOKOL_ASSERT(_sg.limits.max_vertex_attrs <= SG_MAX_VERTEX_ATTRIBUTES); + if (pip->cmn.is_compute) { + // shortcut for compute pipelines + return SG_RESOURCESTATE_VALID; + } + pip->gl.primitive_type = desc->primitive_type; + pip->gl.depth = desc->depth; + pip->gl.stencil = desc->stencil; + // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4) + pip->gl.blend = desc->colors[0].blend; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + pip->gl.color_write_mask[i] = desc->colors[i].write_mask; + } + pip->gl.cull_mode = desc->cull_mode; + pip->gl.face_winding = desc->face_winding; + pip->gl.sample_count = desc->sample_count; + pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; + + // NOTE: GLSL compilers may remove unused vertex attributes so we can't rely + // on the 'prepopulated' vertex_buffer_layout_active[] state and need to + // fill this array from scratch with the actual info after GLSL compilation + for (int i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + pip->cmn.vertex_buffer_layout_active[i] = false; + } + + // resolve vertex attributes + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(shd->gl.prog); + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + pip->gl.attrs[attr_index].vb_index = -1; + } + for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; + GLint attr_loc = attr_index; + if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { + attr_loc = glGetAttribLocation(shd->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + if (attr_loc != -1) { + SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); + _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; + SOKOL_ASSERT(gl_attr->vb_index == -1); + gl_attr->vb_index = (int8_t) a_state->buffer_index; + if (step_func == SG_VERTEXSTEP_PER_VERTEX) { + gl_attr->divisor = 0; + } else { + gl_attr->divisor = (int8_t) step_rate; + } + SOKOL_ASSERT(l_state->stride > 0); + gl_attr->stride = (uint8_t) l_state->stride; + gl_attr->offset = a_state->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_state->format); + gl_attr->type = _sg_gl_vertexformat_type(a_state->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_state->format); + gl_attr->base_type = _sg_vertexformat_basetype(a_state->format); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } else { + _SG_WARN(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_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_gl_cache_invalidate_pipeline(pip); +} + +_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_view_t* view, GLenum gl_att_type) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + const GLuint gl_tex = img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const GLuint gl_target = img->gl.target; + SOKOL_ASSERT(gl_target); + const int mip_level = view->cmn.img.mip_level; + const int slice = view->cmn.img.slice; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice); + break; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_image_t* ds_img) { + if (_sg_is_depth_stencil_format(ds_img->cmn.pixel_format)) { + return GL_DEPTH_STENCIL_ATTACHMENT; + } else { + return GL_DEPTH_ATTACHMENT; + } +} + +_SOKOL_PRIVATE bool _sg_gl_check_framebuffer_status(void) { + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } + return false; + } + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && desc); + _SOKOL_UNUSED(desc); + _SG_GL_CHECK_ERROR(); + if ((view->cmn.type == SG_VIEWTYPE_TEXTURE) && (_sg.features.gl_texture_views)) { + #if defined(_SOKOL_GL_HAS_TEXVIEWS) + if (_sg.features.gl_texture_views) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(img->gl.tex[slot] != 0); + const GLuint min_level = (GLuint)view->cmn.img.mip_level; + const GLuint num_levels = (GLuint)view->cmn.img.mip_level_count; + const GLuint min_layer = (GLuint)view->cmn.img.slice; + const GLuint num_layers = (GLuint)view->cmn.img.slice_count; + const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + glGenTextures(1, &view->gl.tex_view[slot]); + glTextureView(view->gl.tex_view[slot], img->gl.target, img->gl.tex[slot], ifmt, min_level, num_levels, min_layer, num_layers); + } + } + #endif + } else if ((view->cmn.type == SG_VIEWTYPE_COLORATTACHMENT) || (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT)) { + // create MSAA render buffer if MSAA textures are not supported + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + const bool msaa = img->cmn.sample_count > 1; + if (msaa && !_sg.features.msaa_texture_bindings) { + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + glGenRenderbuffers(1, &view->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, view->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + } + } else if (view->cmn.type == SG_VIEWTYPE_RESOLVEATTACHMENT) { + // store current framebuffer binding (restored at end of block) + GLuint gl_orig_fb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); + + // create MSAA resolve framebuffer + glGenFramebuffers(1, &view->gl.msaa_resolve_frame_buffer); + glBindFramebuffer(GL_FRAMEBUFFER, view->gl.msaa_resolve_frame_buffer); + _sg_gl_fb_attach_texture(view, GL_COLOR_ATTACHMENT0); + if (!_sg_gl_check_framebuffer_status()) { + return SG_RESOURCESTATE_FAILED; + } + // setup color attachments for the framebuffer + static const GLenum gl_draw_buf = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &gl_draw_buf); + // bind original framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_view(_sg_view_t* view) { + SOKOL_ASSERT(view); + _SG_GL_CHECK_ERROR(); + for (size_t slot = 0; slot < SG_NUM_INFLIGHT_FRAMES; slot++) { + if (0 != view->gl.tex_view[slot]) { + // NOTE: cache invalidation also works as expected without + // GL texture view support, in that case the view's texture object + // will simply remain bound until the sg_image object is discarded + _sg_gl_cache_invalidate_texture_sampler(view->gl.tex_view[slot], 0); + glDeleteTextures(1, &view->gl.tex_view[slot]); + } + } + if (view->gl.msaa_render_buffer) { + glDeleteRenderbuffers(1, &view->gl.msaa_render_buffer); + } + if (view->gl.msaa_resolve_frame_buffer) { + glDeleteFramebuffers(1, &view->gl.msaa_resolve_frame_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +#if defined(_SOKOL_GL_HAS_COMPUTE) +_SOKOL_PRIVATE void _sg_gl_handle_memory_barriers(const _sg_shader_t* shd, const _sg_bindings_ptrs_t* bnd, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT((shd && bnd && atts == 0) || (atts && shd == 0 && bnd == 0)); + if (!_sg.features.compute) { + return; + } + GLbitfield gl_barrier_bits = 0; + + // if vertex-, index- or storage-buffer bindings have been written + // by a compute shader before, a barrier must be issued + if (bnd) { + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + _sg_buffer_t* buf = bnd->vbs[i]; + if (!buf) { + continue; + } + if (buf->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_VERTEXBUFFER) { + gl_barrier_bits |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; + buf->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_VERTEXBUFFER; + } + } + if (bnd->ib) { + _sg_buffer_t* buf = bnd->ib; + if (buf->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_INDEXBUFFER) { + gl_barrier_bits |= GL_ELEMENT_ARRAY_BARRIER_BIT; + buf->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_INDEXBUFFER; + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_view_t* view = bnd->views[i]; + if (!view) { + continue; + } + if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + if (buf->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_STORAGEBUFFER) { + gl_barrier_bits |= GL_SHADER_STORAGE_BARRIER_BIT; + buf->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_STORAGEBUFFER; + } + } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) { + _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + if (img->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_TEXTURE) { + gl_barrier_bits |= GL_TEXTURE_FETCH_BARRIER_BIT; + img->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_TEXTURE; + } + } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) { + _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + if (img->gl.gpu_dirty_flags &= _SG_GL_GPUDIRTY_STORAGEIMAGE) { + gl_barrier_bits |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + img->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_STORAGEIMAGE; + } + } else { + SOKOL_UNREACHABLE; + } + } + } + if (atts) { + for (int i = 0; i < atts->num_color_views; i++) { + const _sg_view_t* view = atts->color_views[i]; + SOKOL_ASSERT(view); + _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + if (img->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_ATTACHMENT) { + gl_barrier_bits |= GL_FRAMEBUFFER_BARRIER_BIT; + img->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_ATTACHMENT; + } + } + } + if (0 != gl_barrier_bits) { + glMemoryBarrier(gl_barrier_bits); + _sg_stats_inc(gl.num_memory_barriers); + } + + // mark resources as dirty which will be written by compute shaders + // (don't merge this into the above loop, this would mess up the + // dirty flags if the same resource is bound multiple times) + if (bnd) { + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_view_t* view = bnd->views[i]; + if (!view) { + continue; + } + if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + if (!shd->cmn.views[i].sbuf_readonly) { + _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + buf->gl.gpu_dirty_flags = _SG_GL_GPUDIRTY_BUFFER_ALL; + } + } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) { + // NOTE: storage image bindings are always written, otherwise + // they would be texture bindings! + _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + img->gl.gpu_dirty_flags = _SG_GL_GPUDIRTY_IMAGE_ALL; + } + } + } +} +#endif + +_SOKOL_PRIVATE void _sg_gl_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(pass && atts); + _SG_GL_CHECK_ERROR(); + + // early out if this a compute pass + if (pass->compute) { + return; + } + + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + const bool is_swapchain_pass = atts->empty; + const bool is_offscreen_pass = !atts->empty; + + // 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 (is_offscreen_pass) { + + // offscreen pass, mutate the global offscreen framebuffer object + #if defined(SOKOL_GLCORE) + glEnable(GL_FRAMEBUFFER_SRGB); + #endif + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.fb); + for (int i = 0; i < atts->num_color_views; i++) { + const _sg_view_t* view = atts->color_views[i]; + const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + if (view->gl.msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att_type, GL_RENDERBUFFER, view->gl.msaa_render_buffer); + } else { + _sg_gl_fb_attach_texture(view, gl_att_type); + } + } + // explicitly detach unused color attachments + for (int i = atts->num_color_views; i < _sg.limits.max_color_attachments; i++) { + const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att_type, GL_RENDERBUFFER, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, GL_TEXTURE_2D, 0, 0); + } + if (atts->ds_view) { + // When switching between depth-only and depth-stencil attachments, + // explicitly detach BOTH attachment types first. Some GL drivers + // fail with GL_FRAMEBUFFER_UNSUPPORTED if both attachment types + // are bound to the same FBO. + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + const _sg_view_t* view = atts->ds_view; + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(img); + if (view->gl.msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att_type, GL_RENDERBUFFER, view->gl.msaa_render_buffer); + } else { + _sg_gl_fb_attach_texture(view, gl_att_type); + } + } else { + // explicitly detach depth-stencil attachment if not used in this pass + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + } + if (!_sg_gl_check_framebuffer_status()) { + _sg.cur_pass.valid = false; + return; + } + GLenum gl_draw_bufs[SG_MAX_COLOR_ATTACHMENTS]; + SOKOL_ASSERT(_sg.limits.max_color_attachments <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < _sg.limits.max_color_attachments; i++) { + if (i < atts->num_color_views) { + gl_draw_bufs[i] = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + } else { + gl_draw_bufs[i] = GL_NONE; + } + } + glDrawBuffers(_sg.limits.max_color_attachments, gl_draw_bufs); + + #if defined(_SOKOL_GL_HAS_COMPUTE) + _sg_gl_handle_memory_barriers(0, 0, atts); + _SG_GL_CHECK_ERROR(); + #endif + + } else { + // swapchain pass + #if defined(SOKOL_GLCORE) + glDisable(GL_FRAMEBUFFER_SRGB); + #endif + // NOTE: on some platforms, the default framebuffer of a context + // is null, so we can't actually assert here that the + // framebuffer has been provided + glBindFramebuffer(GL_FRAMEBUFFER, swapchain->gl.framebuffer); + } + glViewport(0, 0, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height); + glScissor(0, 0, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height); + + // number of color attachments + const int num_color_atts = is_offscreen_pass ? atts->num_color_views : 1; + + // clear color and depth-stencil attachments if needed + bool clear_any_color = false; + for (int i = 0; i < num_color_atts; i++) { + if (SG_LOADACTION_CLEAR == action->colors[i].load_action) { + clear_any_color = true; + break; + } + } + const bool clear_depth = (action->depth.load_action == SG_LOADACTION_CLEAR); + const bool clear_stencil = (action->stencil.load_action == SG_LOADACTION_CLEAR); + + bool need_pip_cache_flush = false; + if (clear_any_color) { + bool need_color_mask_flush = false; + // NOTE: not a bug to iterate over all possible color attachments + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) { + need_pip_cache_flush = true; + need_color_mask_flush = true; + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + } + if (need_color_mask_flush) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + if (clear_depth) { + if (!_sg.gl.cache.depth.write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } + } + if (clear_stencil) { + if (_sg.gl.cache.stencil.write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.stencil.write_mask = 0xFF; + glStencilMask(0xFF); + } + } + if (need_pip_cache_flush) { + // we messed with the state cache directly, need to clear cached + // pipeline to force re-evaluation in next sg_apply_pipeline() + _sg.gl.cache.cur_pip = _sg_sref(0); + } + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r); + } + } + if (is_swapchain_pass || atts->ds_view) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value); + } else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.clear_value); + } else if (clear_stencil) { + GLint val = (GLint) action->stencil.clear_value; + glClearBufferiv(GL_STENCIL, 0, &val); + } + } + // keep store actions for end-pass + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.color_store_actions[i] = action->colors[i].store_action; + } + _sg.gl.depth_store_action = action->depth.store_action; + _sg.gl.stencil_store_action = action->stencil.store_action; + + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_end_render_pass(const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(atts); + if (!atts->empty) { + bool fb_read_bound = false; + bool fb_draw_bound = false; + const int num_color_atts = atts->num_color_views; + for (int i = 0; i < num_color_atts; i++) { + // perform MSAA resolve if needed + const _sg_view_t* rsv_view = atts->resolve_views[i]; + if (rsv_view && rsv_view->gl.msaa_resolve_frame_buffer) { + if (!fb_read_bound) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, _sg.gl.fb); + fb_read_bound = true; + } + const _sg_image_t* rsv_img = _sg_image_ref_ptr(&rsv_view->cmn.img.ref); + const int w = rsv_img->cmn.width; + const int h = rsv_img->cmn.height; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rsv_view->gl.msaa_resolve_frame_buffer); + glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + i)); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + fb_draw_bound = true; + } + } + + // invalidate framebuffers + _SOKOL_UNUSED(fb_draw_bound); + #if defined(SOKOL_GLES3) + // need to restore framebuffer binding before invalidate if the MSAA resolve had changed the binding + if (fb_draw_bound) { + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.fb); + } + GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 }; + int att_index = 0; + for (int i = 0; i < num_color_atts; i++) { + if (_sg.gl.color_store_actions[i] == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + } + } + if (!atts->ds_view) { + if (_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT; + } + if (_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT; + } + } + if (att_index > 0) { + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, att_index, invalidate_atts); + } + #endif + } +} + +_SOKOL_PRIVATE void _sg_gl_end_pass(const _sg_attachments_ptrs_t* atts) { + _SG_GL_CHECK_ERROR(); + if (!_sg.cur_pass.is_compute) { + _sg_gl_end_render_pass(atts); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + y = origin_top_left ? (_sg.cur_pass.dim.height - (y+h)) : y; + glViewport(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + y = origin_top_left ? (_sg.cur_pass.dim.height - (y+h)) : y; + glScissor(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_render_pipeline_state(_sg_pipeline_t* pip) { + // update render pipeline state + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); + + // update depth state + { + const sg_depth_state* state_ds = &pip->gl.depth; + sg_depth_state* cache_ds = &_sg.gl.cache.depth; + if (state_ds->compare != cache_ds->compare) { + cache_ds->compare = state_ds->compare; + glDepthFunc(_sg_gl_compare_func(state_ds->compare)); + _sg_stats_inc(gl.num_render_state); + } + if (state_ds->write_enabled != cache_ds->write_enabled) { + cache_ds->write_enabled = state_ds->write_enabled; + glDepthMask(state_ds->write_enabled); + _sg_stats_inc(gl.num_render_state); + } + if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) || + !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f)) + { + /* according to ANGLE's D3D11 backend: + D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor + D3D11 DepthBias ==> GL polygonOffsetUnits + DepthBiasClamp has no meaning on GL + */ + cache_ds->bias = state_ds->bias; + cache_ds->bias_slope_scale = state_ds->bias_slope_scale; + glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias); + _sg_stats_inc(gl.num_render_state); + bool po_enabled = true; + if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) && + _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f)) + { + po_enabled = false; + } + if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { + _sg.gl.cache.polygon_offset_enabled = po_enabled; + if (po_enabled) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + _sg_stats_inc(gl.num_render_state); + } + } + } + + // update stencil state + { + const sg_stencil_state* state_ss = &pip->gl.stencil; + sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; + if (state_ss->enabled != cache_ss->enabled) { + cache_ss->enabled = state_ss->enabled; + if (state_ss->enabled) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + _sg_stats_inc(gl.num_render_state); + } + if (state_ss->write_mask != cache_ss->write_mask) { + cache_ss->write_mask = state_ss->write_mask; + glStencilMask(state_ss->write_mask); + _sg_stats_inc(gl.num_render_state); + } + for (int i = 0; i < 2; i++) { + const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; + sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back; + GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; + if ((state_sfs->compare != cache_sfs->compare) || + (state_ss->read_mask != cache_ss->read_mask) || + (state_ss->ref != cache_ss->ref)) + { + cache_sfs->compare = state_sfs->compare; + glStencilFuncSeparate(gl_face, + _sg_gl_compare_func(state_sfs->compare), + state_ss->ref, + state_ss->read_mask); + _sg_stats_inc(gl.num_render_state); + } + if ((state_sfs->fail_op != cache_sfs->fail_op) || + (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || + (state_sfs->pass_op != cache_sfs->pass_op)) + { + cache_sfs->fail_op = state_sfs->fail_op; + cache_sfs->depth_fail_op = state_sfs->depth_fail_op; + cache_sfs->pass_op = state_sfs->pass_op; + glStencilOpSeparate(gl_face, + _sg_gl_stencil_op(state_sfs->fail_op), + _sg_gl_stencil_op(state_sfs->depth_fail_op), + _sg_gl_stencil_op(state_sfs->pass_op)); + _sg_stats_inc(gl.num_render_state); + } + } + cache_ss->read_mask = state_ss->read_mask; + cache_ss->ref = state_ss->ref; + } + + if (pip->cmn.color_count > 0) { + // update blend state + // FIXME: separate blend state per color attachment + const sg_blend_state* state_bs = &pip->gl.blend; + sg_blend_state* cache_bs = &_sg.gl.cache.blend; + if (state_bs->enabled != cache_bs->enabled) { + cache_bs->enabled = state_bs->enabled; + if (state_bs->enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + _sg_stats_inc(gl.num_render_state); + } + if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || + (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || + (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) || + (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha)) + { + cache_bs->src_factor_rgb = state_bs->src_factor_rgb; + cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb; + cache_bs->src_factor_alpha = state_bs->src_factor_alpha; + cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha; + glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb), + _sg_gl_blend_factor(state_bs->dst_factor_rgb), + _sg_gl_blend_factor(state_bs->src_factor_alpha), + _sg_gl_blend_factor(state_bs->dst_factor_alpha)); + _sg_stats_inc(gl.num_render_state); + } + if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) { + cache_bs->op_rgb = state_bs->op_rgb; + cache_bs->op_alpha = state_bs->op_alpha; + glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); + _sg_stats_inc(gl.num_render_state); + } + + // standalone color target state + 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; + if (_sg.features.mrt_independent_write_mask) { + #if defined(_SOKOL_GL_HAS_COLORMASKI) + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + #else + // can't happen + SOKOL_ASSERT(false); + #endif + } else if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } + _sg_stats_inc(gl.num_render_state); + } + } + + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + _sg_stats_inc(gl.num_render_state); + } + } // pip->cmn.color_count > 0 + + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { + _sg.gl.cache.cull_mode = pip->gl.cull_mode; + if (SG_CULLMODE_NONE == pip->gl.cull_mode) { + glDisable(GL_CULL_FACE); + _sg_stats_inc(gl.num_render_state); + } else { + glEnable(GL_CULL_FACE); + GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; + glCullFace(gl_mode); + _sg_stats_add(gl.num_render_state, 2); + } + } + if (pip->gl.face_winding != _sg.gl.cache.face_winding) { + _sg.gl.cache.face_winding = pip->gl.face_winding; + GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW; + glFrontFace(gl_winding); + _sg_stats_inc(gl.num_render_state); + } + if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) { + _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; + if (pip->gl.alpha_to_coverage_enabled) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + _sg_stats_inc(gl.num_render_state); + } + #ifdef SOKOL_GLCORE + if (pip->gl.sample_count != _sg.gl.cache.sample_count) { + _sg.gl.cache.sample_count = pip->gl.sample_count; + if (pip->gl.sample_count > 1) { + glEnable(GL_MULTISAMPLE); + } else { + glDisable(GL_MULTISAMPLE); + } + _sg_stats_inc(gl.num_render_state); + } + #endif +} + +_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SG_GL_CHECK_ERROR(); + if (!_sg_sref_slot_eql(&_sg.gl.cache.cur_pip, &pip->slot)) { + _sg.gl.cache.cur_pip = _sg_sref(&pip->slot); + + // bind shader program + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + if (shd->gl.prog != _sg.gl.cache.prog) { + _sg.gl.cache.prog = shd->gl.prog; + glUseProgram(shd->gl.prog); + _sg_stats_inc(gl.num_use_program); + } + + if (!pip->cmn.is_compute) { + _sg_gl_apply_render_pipeline_state(pip); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SG_GL_CHECK_ERROR(); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + + // bind combined texture-samplers + _SG_GL_CHECK_ERROR(); + for (size_t tex_smp_index = 0; tex_smp_index < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_index++) { + const _sg_shader_texture_sampler_t* tex_smp = &shd->cmn.texture_samplers[tex_smp_index]; + if (tex_smp->stage == SG_SHADERSTAGE_NONE) { + continue; + } + const int8_t gl_tex_slot = (GLint)shd->gl.tex_slot[tex_smp_index]; + if (gl_tex_slot != -1) { + SOKOL_ASSERT(tex_smp->view_slot < SG_MAX_VIEW_BINDSLOTS); + SOKOL_ASSERT(tex_smp->sampler_slot < SG_MAX_SAMPLER_BINDSLOTS); + const _sg_view_t* view = bnd->views[tex_smp->view_slot]; + const _sg_sampler_t* smp = bnd->smps[tex_smp->sampler_slot]; + SOKOL_ASSERT(view); + SOKOL_ASSERT(smp); + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + const GLenum gl_tgt = img->gl.target; + const GLuint gl_smp = smp->gl.smp; + GLuint gl_tex; + if (_sg.features.gl_texture_views) { + gl_tex = view->gl.tex_view[img->cmn.active_slot]; + } else { + gl_tex = img->gl.tex[img->cmn.active_slot]; + } + _sg_gl_cache_bind_texture_sampler(gl_tex_slot, gl_tgt, gl_tex, gl_smp); + } + } + _SG_GL_CHECK_ERROR(); + + // bind storage buffer and images + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const _sg_view_t* view = bnd->views[i]; + if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + const _sg_buffer_t* sbuf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + const uint8_t gl_binding = shd->gl.sbuf_binding[i]; + GLuint gl_sbuf = sbuf->gl.buf[sbuf->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(gl_binding, gl_sbuf, view->cmn.buf.offset, sbuf->cmn.size); + } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) { + #if defined(_SOKOL_GL_HAS_COMPUTE) + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + const uint8_t gl_unit = shd->gl.simg_binding[i]; + SOKOL_ASSERT((int)gl_unit < _sg.limits.max_storage_image_bindings_per_stage); + GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; + GLint level = (GLint)view->cmn.img.mip_level; + GLint layer = (GLint)view->cmn.img.slice; + GLboolean layered = shd->cmn.views[i].image_type != SG_IMAGETYPE_2D; + GLenum access = shd->cmn.views[i].simg_writeonly ? GL_WRITE_ONLY : GL_READ_WRITE; + GLenum format = _sg_gl_teximage_internal_format(shd->cmn.views[i].access_format); + // NOTE: we specifically don't go through the GL cache since storage images + // are not supported on WebGL2, and on native platforms call caching isn't + // worth the hassle + glBindImageTexture(gl_unit, gl_tex, level, layered, layer, access, format); + _sg_stats_inc(gl.num_bind_image_texture); + #endif + } + } + _SG_GL_CHECK_ERROR(); + + if (!bnd->pip->cmn.is_compute) { + // index buffer (can be 0) + const GLuint gl_ib = bnd->ib ? bnd->ib->gl.buf[bnd->ib->cmn.active_slot] : 0; + _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); + _sg.gl.cache.cur_ib_offset = bnd->ib_offset; + + // vertex attributes + for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { + _sg_gl_attr_t* attr = &bnd->pip->gl.attrs[attr_index]; + _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; + bool cache_attr_dirty = false; + int vb_offset = 0; + GLuint gl_vb = 0; + if (attr->vb_index >= 0) { + // attribute is enabled + SOKOL_ASSERT(attr->vb_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + _sg_buffer_t* vb = bnd->vbs[attr->vb_index]; + SOKOL_ASSERT(vb); + gl_vb = vb->gl.buf[vb->cmn.active_slot]; + vb_offset = bnd->vb_offsets[attr->vb_index] + attr->offset; + if ((gl_vb != cache_attr->gl_vbuf) || + (attr->size != cache_attr->gl_attr.size) || + (attr->type != cache_attr->gl_attr.type) || + (attr->normalized != cache_attr->gl_attr.normalized) || + (attr->base_type != cache_attr->gl_attr.base_type) || + (attr->stride != cache_attr->gl_attr.stride) || + (vb_offset != cache_attr->gl_attr.offset) || + (cache_attr->gl_attr.divisor != attr->divisor)) + { + _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); + if (attr->base_type == SG_SHADERATTRBASETYPE_FLOAT) { + glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); + } else { + glVertexAttribIPointer(attr_index, attr->size, attr->type, attr->stride, (const GLvoid*)(GLintptr)vb_offset); + } + _sg_stats_inc(gl.num_vertex_attrib_pointer); + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); + _sg_stats_inc(gl.num_vertex_attrib_divisor); + cache_attr_dirty = true; + } + if (cache_attr->gl_attr.vb_index == -1) { + glEnableVertexAttribArray(attr_index); + _sg_stats_inc(gl.num_enable_vertex_attrib_array); + cache_attr_dirty = true; + } + } else { + // attribute is disabled + if (cache_attr->gl_attr.vb_index != -1) { + glDisableVertexAttribArray(attr_index); + _sg_stats_inc(gl.num_disable_vertex_attrib_array); + cache_attr_dirty = true; + } + } + if (cache_attr_dirty) { + cache_attr->gl_attr = *attr; + cache_attr->gl_attr.offset = vb_offset; + cache_attr->gl_vbuf = gl_vb; + } + } + _SG_GL_CHECK_ERROR(); + } + + // take care of storage resource memory barriers (this needs to happen after the bindings are set) + #if defined(_SOKOL_GL_HAS_COMPUTE) + _sg_gl_handle_memory_barriers(shd, bnd, 0); + _SG_GL_CHECK_ERROR(); + #endif + + return true; +} + +_SOKOL_PRIVATE void _sg_gl_apply_uniforms(int ub_slot, const sg_range* data) { + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(SG_SHADERSTAGE_NONE != shd->cmn.uniform_blocks[ub_slot].stage); + SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size); + const _sg_gl_uniform_block_t* gl_ub = &shd->gl.uniform_blocks[ub_slot]; + for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { + const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; + SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); + if (u->gl_loc == -1) { + continue; + } + _sg_stats_inc(gl.num_uniform); + 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, fptr); + break; + case SG_UNIFORMTYPE_FLOAT2: + glUniform2fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT3: + glUniform3fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT4: + 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, fptr); + break; + default: + SOKOL_UNREACHABLE; + break; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + const GLenum p_type = _sg.gl.cache.cur_primitive_type; + const bool use_instanced_draw = (num_instances > 1) || _sg.use_instanced_draw; + if (_sg.use_indexed_draw) { + // indexed rendering + const GLenum i_type = _sg.gl.cache.cur_index_type; + 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 (use_instanced_draw) { + if ((base_vertex == 0) && (base_instance == 0)) { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } else if ((base_vertex != 0) && (base_instance == 0) && _sg.features.draw_base_vertex) { + #if defined(_SOKOL_GL_HAS_BASEVERTEX) + glDrawElementsInstancedBaseVertex(p_type, num_elements, i_type, indices, num_instances, base_vertex); + #endif + } else if ((base_instance != 0) && _sg.features.draw_base_instance) { + #if defined(_SOKOL_GL_HAS_BASEINSTANCE) + glDrawElementsInstancedBaseVertexBaseInstance(p_type, num_elements, i_type, indices, num_instances, base_vertex, (GLuint)base_instance); + #endif + } + } else { + if (base_vertex == 0) { + glDrawElements(p_type, num_elements, i_type, indices); + } else if (_sg.features.draw_base_vertex) { + #if defined(_SOKOL_GL_HAS_BASEVERTEX) + glDrawElementsBaseVertex(p_type, num_elements, i_type, indices, base_vertex); + #endif + } + } + } else { + // non-indexed rendering + if (use_instanced_draw) { + if (base_instance == 0) { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } else if (_sg.features.draw_base_instance) { + #if defined(_SOKOL_GL_HAS_BASEINSTANCE) + glDrawArraysInstancedBaseInstance(p_type, base_element, num_elements, num_instances, (GLuint)base_instance); + #endif + } + } else { + glDrawArrays(p_type, base_element, num_elements); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + #if defined(_SOKOL_GL_HAS_COMPUTE) + if (!_sg.features.compute) { + return; + } + glDispatchCompute((GLuint)num_groups_x, (GLuint)num_groups_y, (GLuint)num_groups_z); + #else + (void)num_groups_x; (void)num_groups_y; (void)num_groups_z; + #endif +} + +_SOKOL_PRIVATE void _sg_gl_commit(void) { + // "soft" clear bindings (only those that are actually bound) + _sg_gl_cache_clear_buffer_bindings(false); + _sg_gl_cache_clear_texture_sampler_bindings(false); +} + +_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + // only one update per buffer per frame allowed + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + GLenum gl_tgt = _sg_gl_buffer_target(&buf->cmn.usage); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + GLenum gl_tgt = _sg_gl_buffer_target(&buf->cmn.usage); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + // only one update per image per frame allowed + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], 0); + const int num_mips = img->cmn.num_mipmaps; + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const GLvoid* data_ptr = data->mip_levels[mip_index].ptr; + const GLsizei data_size = (GLsizei)data->mip_levels[mip_index].size; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const int mip_depth = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + SOKOL_ASSERT((6 * surf_pitch) <= data_size); + const uint8_t* surf_ptr = (const uint8_t*) data_ptr; + for (int i = 0; i < 6; i++) { + const GLenum gl_img_target = _sg_gl_cubeface_target(i); + _sg_gl_texsubimage(img, gl_img_target, mip_index, mip_width, mip_height, mip_depth, surf_ptr, surf_pitch); + surf_ptr += surf_pitch; + } + } else { + _sg_gl_texsubimage(img, img->gl.target, mip_index, mip_width, mip_height, mip_depth, data_ptr, data_size); + } + } + _sg_gl_cache_restore_texture_sampler_binding(0); +} + +// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>d3d11 backend +#elif defined(SOKOL_D3D11) + +#if defined(__cplusplus) +#define _sg_d3d11_AddRef(self) (self)->AddRef() +#else +#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self) +#endif + +#if defined(__cplusplus) +#define _sg_d3d11_Release(self) (self)->Release() +#else +#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) +#endif + +// NOTE: This needs to be a macro since we can't use the polymorphism in C. It's called on many kinds of resources. +// NOTE: Based on microsoft docs, it's fine to call this with pData=NULL if DataSize is also zero. +#if defined(__cplusplus) +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->SetPrivateData(guid, DataSize, pData) +#else +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->lpVtbl->SetPrivateData(self, guid, DataSize, pData) +#endif + +#if defined(__cplusplus) +#define _sg_win32_refguid(guid) guid +#else +#define _sg_win32_refguid(guid) &guid +#endif + +static const GUID _sg_d3d11_WKPDID_D3DDebugObjectName = { 0x429b8c22,0x9188,0x4b0c, {0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00} }; + +#if defined(SOKOL_DEBUG) +#define _sg_d3d11_setlabel(self, label) _sg_d3d11_SetPrivateData(self, _sg_win32_refguid(_sg_d3d11_WKPDID_D3DDebugObjectName), label ? (UINT)strlen(label) : 0, label) +#else +#define _sg_d3d11_setlabel(self, label) +#endif + + +//-- D3D11 C/C++ wrappers ------------------------------------------------------ +static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { + #if defined(__cplusplus) + return self->CheckFormatSupport(Format, pFormatSupport); + #else + return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport); + #endif +} + +static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) { + #if defined(__cplusplus) + self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView); + #else + self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) { + #if defined(__cplusplus) + self->RSSetState(pRasterizerState); + #else + self->lpVtbl->RSSetState(self, pRasterizerState); + #endif +} + +static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { + #if defined(__cplusplus) + self->OMSetDepthStencilState(pDepthStencilState, StencilRef); + #else + self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef); + #endif +} + +static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { + #if defined(__cplusplus) + self->OMSetBlendState(pBlendState, BlendFactor, SampleMask); + #else + self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask); + #endif +} + +static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { + #if defined(__cplusplus) + self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #else + self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #endif +} + +static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { + #if defined(__cplusplus) + self->IASetIndexBuffer(pIndexBuffer, Format, Offset); + #else + self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset); + #endif +} + +static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) { + #if defined(__cplusplus) + self->IASetInputLayout(pInputLayout); + #else + self->lpVtbl->IASetInputLayout(self, pInputLayout); + #endif +} + +static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_CSSetShader(ID3D11DeviceContext* self, ID3D11ComputeShader* pComputeShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->CSSetShader(pComputeShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->CSSetShader(self, pComputeShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_CSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->CSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->CSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_CSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->CSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->CSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline void _sg_d3d11_CSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->CSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->CSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline void _sg_d3d11_CSSetUnorderedAccessViews(ID3D11DeviceContext* self, UINT StartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts) { + #if defined(__cplusplus) + self->CSSetUnorderedAccessViews(StartSlot, NumUAVs, ppUnorderedAccessViews, pUAVInitialCounts); + #else + self->lpVtbl->CSSetUnorderedAccessViews(self, StartSlot, NumUAVs, ppUnorderedAccessViews, pUAVInitialCounts); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { + #if defined(__cplusplus) + return self->CreateBuffer(pDesc, pInitialData, ppBuffer); + #else + return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { + #if defined(__cplusplus) + return self->CreateShaderResourceView(pResource, pDesc, ppSRView); + #else + return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView); + #endif +} + +static inline HRESULT _sg_d3d11_CreateUnorderedAccessView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAVView) { + #if defined(__cplusplus) + return self->CreateUnorderedAccessView(pResource, pDesc, ppUAVView); + #else + return self->lpVtbl->CreateUnorderedAccessView(self, pResource, pDesc, ppUAVView); + #endif +} + +static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) { + #if defined(__cplusplus) + self->GetResource(ppResource); + #else + self->lpVtbl->GetResource(self, ppResource); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { + #if defined(__cplusplus) + return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D); + #else + return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { + #if defined(__cplusplus) + return self->CreateSamplerState(pSamplerDesc, ppSamplerState); + #else + return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState); + #endif +} + +static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferPointer(); + #else + return self->lpVtbl->GetBufferPointer(self); + #endif +} + +static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferSize(); + #else + return self->lpVtbl->GetBufferSize(self); + #endif +} + +static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { + #if defined(__cplusplus) + return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #else + return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { + #if defined(__cplusplus) + return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #else + return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreateComputeShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11ComputeShader** ppComputeShader) { + #if defined(__cplusplus) + return self->CreateComputeShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppComputeShader); + #else + return self->lpVtbl->CreateComputeShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppComputeShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) { + #if defined(__cplusplus) + return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #else + return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { + #if defined(__cplusplus) + return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState); + #else + return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { + #if defined(__cplusplus) + return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState); + #else + return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { + #if defined(__cplusplus) + return self->CreateBlendState(pBlendStateDesc, ppBlendState); + #else + return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { + #if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) { + #if defined(__cplusplus) + self->RSSetViewports(NumViewports, pViewports); + #else + self->lpVtbl->RSSetViewports(self, NumViewports, pViewports); + #endif +} + +static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) { + #if defined(__cplusplus) + self->RSSetScissorRects(NumRects, pRects); + #else + self->lpVtbl->RSSetScissorRects(self, NumRects, pRects); + #endif +} + +static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { + #if defined(__cplusplus) + self->ClearRenderTargetView(pRenderTargetView, ColorRGBA); + #else + self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA); + #endif +} + +static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { + #if defined(__cplusplus) + self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil); + #else + self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil); + #endif +} + +static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { + #if defined(__cplusplus) + self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #else + self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #endif +} + +static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) { + #if defined(__cplusplus) + self->IASetPrimitiveTopology(Topology); + #else + self->lpVtbl->IASetPrimitiveTopology(self, Topology); + #endif +} + +static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { + #if defined(__cplusplus) + self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #else + self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #endif +} + +static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { + #if defined(__cplusplus) + self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); + #else + self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #endif +} + +static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) { + #if defined(__cplusplus) + self->Draw(VertexCount, StartVertexLocation); + #else + self->lpVtbl->Draw(self, VertexCount, StartVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #endif +} + +static inline void _sg_d3d11_Dispatch(ID3D11DeviceContext* self, UINT ThreadGroupCountX, UINT ThreadGroupCountY, UINT ThreadGroupCountZ) { + #if defined(__cplusplus) + self->Dispatch(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ); + #else + self->lpVtbl->Dispatch(self, ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ); + #endif +} + +static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { + #if defined(__cplusplus) + return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource); + #else + return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource); + #endif +} + +static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) { + #if defined(__cplusplus) + self->Unmap(pResource, Subresource); + #else + self->lpVtbl->Unmap(self, pResource, Subresource); + #endif +} + +static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { + #if defined(__cplusplus) + self->ClearState(); + #else + self->lpVtbl->ClearState(self); + #endif +} + +static inline D3D_FEATURE_LEVEL _sg_d3d11_GetFeatureLevel(ID3D11Device* self) { + #if defined(__cplusplus) + return self->GetFeatureLevel(); + #else + return self->lpVtbl->GetFeatureLevel(self); + #endif +} + +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_image_usage(const sg_image_usage* usg) { + if (usg->immutable) { + if (usg->color_attachment || + usg->resolve_attachment || + usg->depth_stencil_attachment || + usg->storage_image) + { + return D3D11_USAGE_DEFAULT; + } else { + return D3D11_USAGE_IMMUTABLE; + } + } else { + return D3D11_USAGE_DYNAMIC; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_image_bind_flags(const sg_image_usage* usg) { + UINT res = D3D11_BIND_SHADER_RESOURCE; + if (usg->color_attachment) { + res |= D3D11_BIND_RENDER_TARGET; + } + if (usg->depth_stencil_attachment) { + res |= D3D11_BIND_DEPTH_STENCIL; + } + if (usg->storage_image) { + res |= D3D11_BIND_UNORDERED_ACCESS; + } + return res; +} + +_SOKOL_PRIVATE UINT _sg_d3d11_image_cpu_access_flags(const sg_image_usage* usg) { + if (usg->color_attachment || + usg->resolve_attachment || + usg->depth_stencil_attachment || + usg->storage_image || + usg->immutable) + { + return 0; + } else { + return D3D11_CPU_ACCESS_WRITE; + } +} + +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_buffer_usage(const sg_buffer_usage* usg) { + if (usg->immutable) { + return usg->storage_buffer ? D3D11_USAGE_DEFAULT : D3D11_USAGE_IMMUTABLE; + } else { + return D3D11_USAGE_DYNAMIC; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_bind_flags(const sg_buffer_usage* usg) { + UINT res = 0; + if (usg->vertex_buffer) { + res |= D3D11_BIND_VERTEX_BUFFER; + } + if (usg->index_buffer) { + res |= D3D11_BIND_INDEX_BUFFER; + } + if (usg->storage_buffer) { + res |= D3D11_BIND_SHADER_RESOURCE; + if (usg->immutable) { + res |= D3D11_BIND_UNORDERED_ACCESS; + } + } + return res; +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_misc_flags(const sg_buffer_usage* usg) { + return usg->storage_buffer ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0; +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_cpu_access_flags(const sg_buffer_usage* usg) { + return usg->immutable ? 0 : D3D11_CPU_ACCESS_WRITE; +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; + 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; + case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS; + case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_R24G8_TYPELESS; + case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC3_SRGBA: return DXGI_FORMAT_BC3_UNORM_SRGB; + case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; + case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; + case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; + case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; + case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; + case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; + case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; + case SG_PIXELFORMAT_BC7_SRGBA: return DXGI_FORMAT_BC7_UNORM_SRGB; + default: return DXGI_FORMAT_UNKNOWN; + }; +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_D32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_D24_UNORM_S8_UINT; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_uav_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { + switch (prim_type) { + case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { + switch (index_type) { + case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; + case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; + case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, sg_filter mipmap_f, bool comparison, uint32_t max_anisotropy) { + uint32_t d3d11_filter = 0; + if (max_anisotropy > 1) { + // D3D11_FILTER_ANISOTROPIC = 0x55, + d3d11_filter |= 0x55; + } else { + // D3D11_FILTER_MIN_MAG_MIP_POINT = 0, + // D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1, + // D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4, + // D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5, + // D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10, + // D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11, + // D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14, + // D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15, + if (mipmap_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x01; + } + if (mag_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x04; + } + if (min_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x10; + } + } + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT = 0x80, + // D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 0x81, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x84, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 0x85, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 0x90, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x91, + // D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 0x94, + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR = 0x95, + // D3D11_FILTER_COMPARISON_ANISOTROPIC = 0xd5, + if (comparison) { + d3d11_filter |= 0x80; + } + return (D3D11_FILTER)d3d11_filter; +} + +_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; + case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; + case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER; + case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; + default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; + case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; + case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; + case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_VERTEXFORMAT_INT: return DXGI_FORMAT_R32_SINT; + case SG_VERTEXFORMAT_INT2: return DXGI_FORMAT_R32G32_SINT; + case SG_VERTEXFORMAT_INT3: return DXGI_FORMAT_R32G32B32_SINT; + case SG_VERTEXFORMAT_INT4: return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_VERTEXFORMAT_UINT: return DXGI_FORMAT_R32_UINT; + case SG_VERTEXFORMAT_UINT2: return DXGI_FORMAT_R32G32_UINT; + case SG_VERTEXFORMAT_UINT3: return DXGI_FORMAT_R32G32B32_UINT; + case SG_VERTEXFORMAT_UINT4: return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_USHORT2: return DXGI_FORMAT_R16G16_UINT; + case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM; + case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_VERTEXFORMAT_USHORT4: return DXGI_FORMAT_R16G16B16A16_UINT; + 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; + } +} + +_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; + case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; + default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; + } +} + +_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return D3D11_CULL_NONE; + case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; + case SG_CULLMODE_BACK: return D3D11_CULL_BACK; + default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; + } +} + +_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; + case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; + case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; + case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; + default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; + } +} + +_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; + case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; + case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; + case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; + default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; + case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; + case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_SRC1_COLOR: return D3D11_BLEND_SRC1_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return D3D11_BLEND_INV_SRC1_COLOR; + case SG_BLENDFACTOR_SRC1_ALPHA: return D3D11_BLEND_SRC1_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return D3D11_BLEND_INV_SRC1_ALPHA; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; + case SG_BLENDOP_MIN: return D3D11_BLEND_OP_MIN; + case SG_BLENDOP_MAX: return D3D11_BLEND_OP_MAX; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; + } +} + +_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { + UINT8 res = 0; + if (m & SG_COLORMASK_R) { + res |= D3D11_COLOR_WRITE_ENABLE_RED; + } + if (m & SG_COLORMASK_G) { + res |= D3D11_COLOR_WRITE_ENABLE_GREEN; + } + if (m & SG_COLORMASK_B) { + res |= D3D11_COLOR_WRITE_ENABLE_BLUE; + } + if (m & SG_COLORMASK_A) { + res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + } + return res; +} + +_SOKOL_PRIVATE UINT _sg_d3d11_dxgi_fmt_caps(DXGI_FORMAT dxgi_fmt) { + UINT dxgi_fmt_caps = 0; + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + return dxgi_fmt_caps; +} + +// see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware +_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { + _sg.backend = SG_BACKEND_D3D11; + + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.compute = true; + _sg.features.msaa_texture_bindings = true; + _sg.features.draw_base_vertex = true; + _sg.features.draw_base_instance = true; + _sg.features.dual_source_blending = true; + + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = _SG_D3D11_MAX_TEXTUREARRAY_LAYERS; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + _sg.limits.max_color_attachments = _sg_min(8, SG_MAX_COLOR_ATTACHMENTS); + _sg.limits.max_texture_bindings_per_stage = _sg_min(128, SG_MAX_VIEW_BINDSLOTS); + _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min(64, SG_MAX_VIEW_BINDSLOTS); + if (_sg_d3d11_GetFeatureLevel(_sg.d3d11.dev) >= D3D_FEATURE_LEVEL_11_1) { + _sg.limits.d3d11_max_unordered_access_views = _sg_min(64, SG_MAX_VIEW_BINDSLOTS); + } else { + _sg.limits.d3d11_max_unordered_access_views = _sg_min(8, SG_MAX_VIEW_BINDSLOTS); + } + _sg.limits.max_storage_image_bindings_per_stage = _sg.limits.d3d11_max_unordered_access_views; + + // see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support + for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { + const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt)); + const UINT rtv_uav_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_uav_pixel_format((sg_pixel_format)fmt)); + const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt)); + _sg_pixelformat_info_t* info = &_sg.formats[fmt]; + const bool render = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + const bool depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); + info->filter = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); + info->render = render || depth; + if (depth) { + info->blend = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + } else { + info->blend = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + } + info->depth = depth; + info->read = info->write = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { + // assume _sg.d3d11 already is zero-initialized + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.d3d11.device); + SOKOL_ASSERT(desc->environment.d3d11.device_context); + _sg.d3d11.valid = true; + _sg.d3d11.dev = (ID3D11Device*) desc->environment.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->environment.d3d11.device_context; + _sg_d3d11_init_caps(); + if (_sg_d3d11_GetFeatureLevel(_sg.d3d11.dev) == D3D_FEATURE_LEVEL_11_0) { + _SG_WARN(D3D11_FEATURE_LEVEL_0_DETECTED); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { + SOKOL_ASSERT(_sg.d3d11.valid); + _sg.d3d11.valid = false; +} + +_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { + // clear all the device context state, so that resource refs don't keep stuck in the d3d device context + _sg_d3d11_ClearState(_sg.d3d11.ctx); +} + +_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { + // there's currently no state cache in the D3D11 backend, so this is a no-op +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(!buf->d3d11.buf); + const bool injected = (0 != desc->d3d11_buffer); + if (injected) { + buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; + _sg_d3d11_AddRef(buf->d3d11.buf); + } else { + _SG_STRUCT(D3D11_BUFFER_DESC, d3d11_buf_desc); + d3d11_buf_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_buf_desc.Usage = _sg_d3d11_buffer_usage(&buf->cmn.usage); + d3d11_buf_desc.BindFlags = _sg_d3d11_buffer_bind_flags(&buf->cmn.usage); + d3d11_buf_desc.CPUAccessFlags = _sg_d3d11_buffer_cpu_access_flags(&buf->cmn.usage); + d3d11_buf_desc.MiscFlags = _sg_d3d11_buffer_misc_flags(&buf->cmn.usage); + D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; + _SG_STRUCT(D3D11_SUBRESOURCE_DATA, init_data); + if (desc->data.ptr) { + init_data.pSysMem = desc->data.ptr; + init_data_ptr = &init_data; + } + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_buf_desc, init_data_ptr, &buf->d3d11.buf); + if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { + _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(buf->d3d11.buf, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->d3d11.buf) { + _sg_d3d11_Release(buf->d3d11.buf); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { + const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices; + int subres_index = 0; + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < _SG_D3D11_MAX_TEXTURE_SUBRESOURCES); + D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const sg_range* miplevel_data = &(data->mip_levels[mip_index]); + const size_t slice_size = miplevel_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* ptr = (const uint8_t*) miplevel_data->ptr; + subres_data->pSysMem = ptr + slice_offset; + subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { + subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } else { + subres_data->SysMemSlicePitch = 0; + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res)); + HRESULT hr; + + const bool injected = (0 != desc->d3d11_texture); + const bool msaa = (img->cmn.sample_count > 1); + SOKOL_ASSERT(!(msaa && (img->cmn.type == SG_IMAGETYPE_CUBE))); + img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + + // prepare initial content pointers + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && desc->data.mip_levels[0].ptr) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + // 2D-, cube- or array-texture + // first check for injected texture and/or resource view + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex2d); + } else { + // if not injected, create 2D texture + _SG_STRUCT(D3D11_TEXTURE2D_DESC, 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; + d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; + d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.BindFlags = _sg_d3d11_image_bind_flags(&img->cmn.usage); + d3d11_tex_desc.Usage = _sg_d3d11_image_usage(&img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_image_cpu_access_flags(&img->cmn.usage); + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + 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); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label); + } + SOKOL_ASSERT(img->d3d11.tex2d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; + _sg_d3d11_AddRef(img->d3d11.res); + } else { + // 3D texture - same procedure, first check if injected, than create non-injected + if (injected) { + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex3d); + } else { + // not injected, create 3d texture + _SG_STRUCT(D3D11_TEXTURE3D_DESC, 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; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.BindFlags = _sg_d3d11_image_bind_flags(&img->cmn.usage); + d3d11_tex_desc.Usage = _sg_d3d11_image_usage(&img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_image_cpu_access_flags(&img->cmn.usage); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _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); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.tex3d, desc->label); + } + SOKOL_ASSERT(img->d3d11.tex3d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d; + _sg_d3d11_AddRef(img->d3d11.res); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.res) { + _sg_d3d11_Release(img->d3d11.res); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(0 == smp->d3d11.smp); + const bool injected = (0 != desc->d3d11_sampler); + if (injected) { + smp->d3d11.smp = (ID3D11SamplerState*)desc->d3d11_sampler; + _sg_d3d11_AddRef(smp->d3d11.smp); + } else { + _SG_STRUCT(D3D11_SAMPLER_DESC, d3d11_smp_desc); + d3d11_smp_desc.Filter = _sg_d3d11_filter(desc->min_filter, desc->mag_filter, desc->mipmap_filter, desc->compare != SG_COMPAREFUNC_NEVER, desc->max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(desc->wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(desc->wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(desc->wrap_w); + d3d11_smp_desc.MipLODBias = 0.0f; // FIXME? + switch (desc->border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + // all 0.0f + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + for (int i = 0; i < 4; i++) { + d3d11_smp_desc.BorderColor[i] = 1.0f; + } + break; + default: + // opaque black + d3d11_smp_desc.BorderColor[3] = 1.0f; + break; + } + d3d11_smp_desc.MaxAnisotropy = desc->max_anisotropy; + d3d11_smp_desc.ComparisonFunc = _sg_d3d11_compare_func(desc->compare); + d3d11_smp_desc.MinLOD = desc->min_lod; + d3d11_smp_desc.MaxLOD = desc->max_lod; + HRESULT hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &smp->d3d11.smp); + if (!(SUCCEEDED(hr) && smp->d3d11.smp)) { + _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(smp->d3d11.smp, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->d3d11.smp) { + _sg_d3d11_Release(smp->d3d11.smp); + } +} + +_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + // don't attempt to load missing DLL in the future + _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; + } + // look up function pointers + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; +} + +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_function* shd_func) { + if (!_sg_d3d11_load_d3dcompiler_dll()) { + return NULL; + } + SOKOL_ASSERT(shd_func->d3d11_target); + UINT flags1 = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; + if (_sg.desc.d3d11.shader_debugging) { + flags1 |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; + } else { + flags1 |= D3DCOMPILE_OPTIMIZATION_LEVEL3; + } + ID3DBlob* output = NULL; + ID3DBlob* errors_or_warnings = NULL; + HRESULT hr = _sg.d3d11.D3DCompile_func( + shd_func->source, // pSrcData + strlen(shd_func->source), // SrcDataSize + shd_func->d3d11_filepath, // pSourceName + NULL, // pDefines + D3D_COMPILE_STANDARD_FILE_INCLUDE, // pInclude + shd_func->entry ? shd_func->entry : "main", // pEntryPoint + shd_func->d3d11_target, // pTarget + flags1, // Flags1 + 0, // Flags2 + &output, // ppCode + &errors_or_warnings); // ppErrorMsgs + if (FAILED(hr)) { + _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED); + } + if (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)) { + // just in case, usually output is NULL here + if (output) { + _sg_d3d11_Release(output); + output = NULL; + } + } + return output; +} + +// NOTE: this is an out-of-range check for HLSL bindslots that's also active in release mode +_SOKOL_PRIVATE bool _sg_d3d11_ensure_hlsl_bindslot_ranges(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* ub = &desc->uniform_blocks[i]; + if (ub->stage != SG_SHADERSTAGE_NONE) { + if (ub->hlsl_register_b_n >= _SG_D3D11_MAX_STAGE_UB_BINDINGS) { + _SG_ERROR(D3D11_UNIFORMBLOCK_HLSL_REGISTER_B_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + if (view->texture.stage != SG_SHADERSTAGE_NONE) { + if (view->texture.hlsl_register_t_n >= _SG_D3D11_MAX_STAGE_SRV_BINDINGS) { + _SG_ERROR(D3D11_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE); + return false; + } + } + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_buffer.hlsl_register_t_n >= _SG_D3D11_MAX_STAGE_SRV_BINDINGS) { + _SG_ERROR(D3D11_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE); + return false; + } + if (view->storage_buffer.hlsl_register_u_n >= _SG_D3D11_MAX_STAGE_UAV_BINDINGS) { + _SG_ERROR(D3D11_STORAGEBUFFER_HLSL_REGISTER_U_OUT_OF_RANGE); + return false; + } + } + if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_image.hlsl_register_u_n >= _SG_D3D11_MAX_STAGE_UAV_BINDINGS) { + _SG_ERROR(D3D11_STORAGEIMAGE_HLSL_REGISTER_U_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const sg_shader_sampler* smp = &desc->samplers[i]; + if (smp->stage != SG_SHADERSTAGE_NONE) { + if (smp->hlsl_register_s_n >= _SG_D3D11_MAX_STAGE_SMP_BINDINGS) { + _SG_ERROR(D3D11_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE); + return false; + } + } + } + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.cs && !shd->d3d11.vs_blob); + HRESULT hr; + + // perform a range-check on HLSL bindslots that's also active in release + // mode to avoid potential out-of-bounds array accesses + if (!_sg_d3d11_ensure_hlsl_bindslot_ranges(desc)) { + return SG_RESOURCESTATE_FAILED; + } + + // copy vertex attribute semantic names and indices + for (size_t i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].hlsl_sem_name); + shd->d3d11.attrs[i].sem_index = desc->attrs[i].hlsl_sem_index; + } + + // copy HLSL bind slots + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + SOKOL_ASSERT(0 == shd->d3d11.ub_register_b_n[i]); + shd->d3d11.ub_register_b_n[i] = desc->uniform_blocks[i].hlsl_register_b_n; + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + SOKOL_ASSERT((0 == shd->d3d11.view_register_t_n[i]) && (0 == shd->d3d11.view_register_u_n[i])); + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + shd->d3d11.view_register_t_n[i] = view->storage_buffer.hlsl_register_t_n; + shd->d3d11.view_register_u_n[i] = view->storage_buffer.hlsl_register_u_n; + } else if (view->texture.stage != SG_SHADERSTAGE_NONE) { + shd->d3d11.view_register_t_n[i] = view->texture.hlsl_register_t_n; + } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + shd->d3d11.view_register_u_n[i] = view->storage_image.hlsl_register_u_n; + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + SOKOL_ASSERT(0 == shd->d3d11.smp_register_s_n[i]); + shd->d3d11.smp_register_s_n[i] = desc->samplers[i].hlsl_register_s_n; + } + + // create a D3D constant buffer for each uniform block + for (size_t ub_index = 0; ub_index < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_index++) { + const sg_shader_stage stage = desc->uniform_blocks[ub_index].stage; + if (stage == SG_SHADERSTAGE_NONE) { + continue; + } + const _sg_shader_uniform_block_t* ub = &shd->cmn.uniform_blocks[ub_index]; + ID3D11Buffer* cbuf = 0; + _SG_STRUCT(D3D11_BUFFER_DESC, 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, &cbuf); + if (!(SUCCEEDED(hr) && cbuf)) { + _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(cbuf, desc->label); + shd->d3d11.all_cbufs[ub_index] = cbuf; + + const uint8_t d3d11_slot = shd->d3d11.ub_register_b_n[ub_index]; + SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_UB_BINDINGS); + if (stage == SG_SHADERSTAGE_VERTEX) { + SOKOL_ASSERT(0 == shd->d3d11.vs_cbufs[d3d11_slot]); + shd->d3d11.vs_cbufs[d3d11_slot] = cbuf; + } else if (stage == SG_SHADERSTAGE_FRAGMENT) { + SOKOL_ASSERT(0 == shd->d3d11.fs_cbufs[d3d11_slot]); + shd->d3d11.fs_cbufs[d3d11_slot] = cbuf; + } else if (stage == SG_SHADERSTAGE_COMPUTE) { + SOKOL_ASSERT(0 == shd->d3d11.cs_cbufs[d3d11_slot]); + shd->d3d11.cs_cbufs[d3d11_slot] = cbuf; + } else { + SOKOL_UNREACHABLE; + } + } + + // create shader functions + const bool has_vs = desc->vertex_func.bytecode.ptr || desc->vertex_func.source; + const bool has_fs = desc->fragment_func.bytecode.ptr || desc->fragment_func.source; + const bool has_cs = desc->compute_func.bytecode.ptr || desc->compute_func.source; + bool vs_valid = false; bool fs_valid = false; bool cs_valid = false; + if (has_vs) { + const void* vs_ptr = 0; SIZE_T vs_length = 0; + ID3DBlob* vs_blob = 0; + if (desc->vertex_func.bytecode.ptr) { + SOKOL_ASSERT(desc->vertex_func.bytecode.size > 0); + vs_ptr = desc->vertex_func.bytecode.ptr; + vs_length = desc->vertex_func.bytecode.size; + } else { + SOKOL_ASSERT(desc->vertex_func.source); + vs_blob = _sg_d3d11_compile_shader(&desc->vertex_func); + if (vs_blob) { + vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob); + vs_length = _sg_d3d11_GetBufferSize(vs_blob); + } + } + if (vs_ptr && (vs_length > 0)) { + hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); + vs_valid = SUCCEEDED(hr) && shd->d3d11.vs; + } + // set label, and need to store a copy of the vertex shader blob for the pipeline creation + if (vs_valid) { + _sg_d3d11_setlabel(shd->d3d11.vs, desc->label); + shd->d3d11.vs_blob_length = 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); + } + if (vs_blob) { + _sg_d3d11_Release(vs_blob); + } + } + if (has_fs) { + const void* fs_ptr = 0; SIZE_T fs_length = 0; + ID3DBlob* fs_blob = 0; + if (desc->fragment_func.bytecode.ptr) { + SOKOL_ASSERT(desc->fragment_func.bytecode.size > 0); + fs_ptr = desc->fragment_func.bytecode.ptr; + fs_length = desc->fragment_func.bytecode.size; + } else { + SOKOL_ASSERT(desc->fragment_func.source); + fs_blob = _sg_d3d11_compile_shader(&desc->fragment_func); + if (fs_blob) { + fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob); + fs_length = _sg_d3d11_GetBufferSize(fs_blob); + } + } + if (fs_ptr && (fs_length > 0)) { + hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); + fs_valid = SUCCEEDED(hr) && shd->d3d11.fs; + } + if (fs_valid) { + _sg_d3d11_setlabel(shd->d3d11.fs, desc->label); + } + if (fs_blob) { + _sg_d3d11_Release(fs_blob); + } + } + if (has_cs) { + const void* cs_ptr = 0; SIZE_T cs_length = 0; + ID3DBlob* cs_blob = 0; + if (desc->compute_func.bytecode.ptr) { + SOKOL_ASSERT(desc->compute_func.bytecode.size > 0); + cs_ptr = desc->compute_func.bytecode.ptr; + cs_length = desc->compute_func.bytecode.size; + } else { + SOKOL_ASSERT(desc->compute_func.source); + cs_blob = _sg_d3d11_compile_shader(&desc->compute_func); + if (cs_blob) { + cs_ptr = _sg_d3d11_GetBufferPointer(cs_blob); + cs_length = _sg_d3d11_GetBufferSize(cs_blob); + } + } + if (cs_ptr && (cs_length > 0)) { + hr = _sg_d3d11_CreateComputeShader(_sg.d3d11.dev, cs_ptr, cs_length, NULL, &shd->d3d11.cs); + cs_valid = SUCCEEDED(hr) && shd->d3d11.cs; + } + if (cs_blob) { + _sg_d3d11_Release(cs_blob); + } + } + if ((vs_valid && fs_valid) || cs_valid) { + return SG_RESOURCESTATE_VALID; + } else { + return SG_RESOURCESTATE_FAILED; + } +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->d3d11.vs) { + _sg_d3d11_Release(shd->d3d11.vs); + } + if (shd->d3d11.fs) { + _sg_d3d11_Release(shd->d3d11.fs); + } + if (shd->d3d11.cs) { + _sg_d3d11_Release(shd->d3d11.cs); + } + if (shd->d3d11.vs_blob) { + _sg_free(shd->d3d11.vs_blob); + } + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + if (shd->d3d11.all_cbufs[i]) { + _sg_d3d11_Release(shd->d3d11.all_cbufs[i]); + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + + // if this is a compute pipeline, we're done here + if (pip->cmn.is_compute) { + return SG_RESOURCESTATE_VALID; + } + + // a render pipeline... + SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); + + pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); + pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); + pip->d3d11.stencil_ref = desc->stencil.ref; + + // create input layout object + HRESULT hr; + _SG_STRUCT(D3D11_INPUT_ELEMENT_DESC, d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]); + size_t attr_index = 0; + for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[a_state->buffer_index]); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; + D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; + d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); + d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_state->format); + d3d11_comp->InputSlot = (UINT)a_state->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_state->offset; + d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); + if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { + d3d11_comp->InstanceDataStepRate = (UINT)step_rate; + } + } + for (size_t layout_index = 0; layout_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_state->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_state->stride; + } else { + pip->d3d11.vb_strides[layout_index] = 0; + } + } + if (attr_index > 0) { + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, // pInputElementDesc + (UINT)attr_index, // NumElements + shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature + shd->d3d11.vs_blob_length, // BytecodeLength + &pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.il, desc->label); + } + + // create rasterizer state + _SG_STRUCT(D3D11_RASTERIZER_DESC, 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.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); + if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { + _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.rs, desc->label); + + // create depth-stencil state + _SG_STRUCT(D3D11_DEPTH_STENCIL_DESC, 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); + dss_desc.StencilEnable = desc->stencil.enabled; + dss_desc.StencilReadMask = desc->stencil.read_mask; + dss_desc.StencilWriteMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); + dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); + dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); + dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare); + const sg_stencil_face_state* sb = &desc->stencil.back; + dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); + dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); + 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); + if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { + _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.dss, desc->label); + + // create blend state + _SG_STRUCT(D3D11_BLEND_DESC, bs_desc); + bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; + bs_desc.IndependentBlendEnable = TRUE; + { + size_t i = 0; + for (i = 0; i < (size_t)desc->color_count; i++) { + const sg_blend_state* src = &desc->colors[i].blend; + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = src->enabled; + dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb); + dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb); + dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb); + dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha); + dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha); + dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha); + dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask); + } + for (; i < 8; i++) { + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = FALSE; + dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE; + dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO; + dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD; + dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + } + } + hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); + if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { + _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.bs, desc->label); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip->d3d11.il) { + _sg_d3d11_Release(pip->d3d11.il); + } + if (pip->d3d11.rs) { + _sg_d3d11_Release(pip->d3d11.rs); + } + if (pip->d3d11.dss) { + _sg_d3d11_Release(pip->d3d11.dss); + } + if (pip->d3d11.bs) { + _sg_d3d11_Release(pip->d3d11.bs); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && desc); + _SOKOL_UNUSED(desc); + HRESULT hr; + if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + SOKOL_ASSERT(buf->d3d11.buf); + const UINT size = (UINT) buf->cmn.size; + SOKOL_ASSERT(_sg_multiple_u64(size, 4)); + const UINT offset = (UINT) view->cmn.buf.offset; + SOKOL_ASSERT(_sg_multiple_u64(offset, 4)); + SOKOL_ASSERT(offset < size); + const UINT first_element = offset / 4; + const UINT num_elements = (size - offset) / 4; + _SG_STRUCT(D3D11_SHADER_RESOURCE_VIEW_DESC, d3d11_srv_desc); + d3d11_srv_desc.Format = DXGI_FORMAT_R32_TYPELESS; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + d3d11_srv_desc.BufferEx.FirstElement = first_element; + d3d11_srv_desc.BufferEx.NumElements = num_elements; + d3d11_srv_desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + SOKOL_ASSERT(!view->d3d11.srv); + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_srv_desc, &view->d3d11.srv); + if (!(SUCCEEDED(hr) && view->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_BUFFER_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(view->d3d11.srv, desc->label); + if (buf->cmn.usage.immutable) { + _SG_STRUCT(D3D11_UNORDERED_ACCESS_VIEW_DESC, d3d11_uav_desc); + d3d11_uav_desc.Format = DXGI_FORMAT_R32_TYPELESS; + d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + d3d11_uav_desc.Buffer.FirstElement = first_element; + d3d11_uav_desc.Buffer.NumElements = num_elements; + d3d11_uav_desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; + SOKOL_ASSERT(!view->d3d11.uav); + hr = _sg_d3d11_CreateUnorderedAccessView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_uav_desc, &view->d3d11.uav); + if (!(SUCCEEDED(hr) && view->d3d11.uav)) { + _SG_ERROR(D3D11_CREATE_BUFFER_UAV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(view->d3d11.uav, desc->label); + } + } else { + // it's an image view + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + SOKOL_ASSERT(img->d3d11.res); + const bool msaa = img->cmn.sample_count > 1; + SOKOL_ASSERT(view->cmn.img.mip_level_count >= 1); + SOKOL_ASSERT(view->cmn.img.slice_count >= 1); + const UINT mip_level = (UINT)view->cmn.img.mip_level; + const UINT mip_count = (UINT)view->cmn.img.mip_level_count; + const UINT slice = (UINT)view->cmn.img.slice; + const UINT slice_count = (UINT)view->cmn.img.slice_count; + + if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) { + SOKOL_ASSERT(!msaa); + _SG_STRUCT(D3D11_UNORDERED_ACCESS_VIEW_DESC, d3d11_uav_desc); + d3d11_uav_desc.Format = _sg_d3d11_rtv_uav_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + d3d11_uav_desc.Texture2D.MipSlice = mip_level; + break; + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; + d3d11_uav_desc.Texture2DArray.MipSlice = mip_level; + d3d11_uav_desc.Texture2DArray.FirstArraySlice = slice; + d3d11_uav_desc.Texture2DArray.ArraySize = 1; + break; + case SG_IMAGETYPE_3D: + d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; + d3d11_uav_desc.Texture3D.MipSlice = mip_level; + d3d11_uav_desc.Texture3D.FirstWSlice = slice; + d3d11_uav_desc.Texture3D.WSize = 1; + break; + default: SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateUnorderedAccessView(_sg.d3d11.dev, img->d3d11.res, &d3d11_uav_desc, &view->d3d11.uav); + if (!(SUCCEEDED(hr) && view->d3d11.uav)) { + _SG_ERROR(D3D11_CREATE_UAV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(view->d3d11.uav, desc->label); + + } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) { + + _SG_STRUCT(D3D11_SHADER_RESOURCE_VIEW_DESC, d3d11_srv_desc); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + if (msaa) { + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MostDetailedMip = mip_level; + d3d11_srv_desc.Texture2D.MipLevels = mip_count; + } + break; + case SG_IMAGETYPE_CUBE: + SOKOL_ASSERT(!msaa); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MostDetailedMip = mip_level; + d3d11_srv_desc.TextureCube.MipLevels = mip_count; + break; + case SG_IMAGETYPE_ARRAY: + if (msaa) { + // NOTE: _sg_validate_image_desc() currently disallows MSAA array textures + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_srv_desc.Texture2DMSArray.FirstArraySlice = slice; + d3d11_srv_desc.Texture2DMSArray.ArraySize = slice_count; + } else { + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MostDetailedMip = mip_level; + d3d11_srv_desc.Texture2DArray.MipLevels = mip_count; + d3d11_srv_desc.Texture2DArray.FirstArraySlice = slice; + d3d11_srv_desc.Texture2DArray.ArraySize = slice_count; + } + break; + case SG_IMAGETYPE_3D: + SOKOL_ASSERT(!msaa); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MostDetailedMip = mip_level; + d3d11_srv_desc.Texture3D.MipLevels = mip_count; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &view->d3d11.srv); + if (!(SUCCEEDED(hr) && view->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(view->d3d11.srv, desc->label); + + } else if (view->cmn.type == SG_VIEWTYPE_COLORATTACHMENT) { + + _SG_STRUCT(D3D11_RENDER_TARGET_VIEW_DESC, d3d11_rtv_desc); + d3d11_rtv_desc.Format = _sg_d3d11_rtv_uav_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = mip_level; + } + break; + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = slice; + d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + break; + case SG_IMAGETYPE_3D: + SOKOL_ASSERT(!msaa); + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + break; + default: SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, img->d3d11.res, &d3d11_rtv_desc, &view->d3d11.rtv); + if (!(SUCCEEDED(hr) && view->d3d11.rtv)) { + _SG_ERROR(D3D11_CREATE_RTV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(view->d3d11.rtv, desc->label); + + } else if (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT) { + + SOKOL_ASSERT(img->cmn.type != SG_IMAGETYPE_3D); + _SG_STRUCT(D3D11_DEPTH_STENCIL_VIEW_DESC, d3d11_dsv_desc); + d3d11_dsv_desc.Format = _sg_d3d11_dsv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + d3d11_dsv_desc.Texture2D.MipSlice = mip_level; + } + break; + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_dsv_desc.Texture2DMSArray.FirstArraySlice = slice; + d3d11_dsv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + d3d11_dsv_desc.Texture2DArray.MipSlice = mip_level; + d3d11_dsv_desc.Texture2DArray.FirstArraySlice = slice; + d3d11_dsv_desc.Texture2DArray.ArraySize = 1; + } + break; + default: SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, img->d3d11.res, &d3d11_dsv_desc, &view->d3d11.dsv); + if (!(SUCCEEDED(hr) && view->d3d11.dsv)) { + _SG_ERROR(D3D11_CREATE_DSV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(view->d3d11.dsv, desc->label); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_view(_sg_view_t* view) { + SOKOL_ASSERT(view); + if (view->d3d11.srv) { + _sg_d3d11_Release(view->d3d11.srv); + } + if (view->d3d11.uav) { + _sg_d3d11_Release(view->d3d11.uav); + } + if (view->d3d11.rtv) { + _sg_d3d11_Release(view->d3d11.rtv); + } + if (view->d3d11.dsv) { + _sg_d3d11_Release(view->d3d11.dsv); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(_sg.d3d11.ctx && pass && atts); + if (_sg.cur_pass.is_compute) { + // nothing to do in compute passes + return; + } + int num_rtvs = 0; + ID3D11RenderTargetView* rtvs[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + ID3D11DepthStencilView* dsv = 0; + _sg.d3d11.cur_swapchain.render_view = 0; + _sg.d3d11.cur_swapchain.resolve_view = 0; + if (!atts->empty) { + SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS); + num_rtvs = atts->num_color_views; + for (int i = 0; i < num_rtvs; i++) { + SOKOL_ASSERT(atts->color_views[i]); + SOKOL_ASSERT(atts->color_views[i]->d3d11.rtv); + rtvs[i] = atts->color_views[i]->d3d11.rtv; + } + if (atts->ds_view) { + SOKOL_ASSERT(atts->ds_view->d3d11.dsv); + dsv = atts->ds_view->d3d11.dsv; + } + } else { + // NOTE: swapchain depth-stencil-view is optional + const sg_swapchain* swapchain = &pass->swapchain; + SOKOL_ASSERT(swapchain->d3d11.render_view); + num_rtvs = 1; + rtvs[0] = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + dsv = (ID3D11DepthStencilView*) swapchain->d3d11.depth_stencil_view; + _sg.d3d11.cur_swapchain.render_view = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + _sg.d3d11.cur_swapchain.resolve_view = (ID3D11RenderTargetView*) swapchain->d3d11.resolve_view; + } + // apply the render-target- and depth-stencil-views + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, rtvs, dsv); + _sg_stats_inc(d3d11.pass.num_om_set_render_targets); + + // set viewport and scissor rect to cover whole screen + _SG_STRUCT(D3D11_VIEWPORT, vp); + vp.Width = (FLOAT) _sg.cur_pass.dim.width; + vp.Height = (FLOAT) _sg.cur_pass.dim.height; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); + D3D11_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = _sg.cur_pass.dim.width; + rect.bottom = _sg.cur_pass.dim.height; + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); + + // perform clear action + const sg_pass_action* action = &pass->action; + for (size_t i = 0; i < (size_t)num_rtvs; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], (float*)&action->colors[i].clear_value); + _sg_stats_inc(d3d11.pass.num_clear_render_target_view); + } + } + UINT ds_flags = 0; + if (action->depth.load_action == SG_LOADACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_DEPTH; + } + if (action->stencil.load_action == SG_LOADACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_STENCIL; + } + if ((0 != ds_flags) && dsv) { + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); + _sg_stats_inc(d3d11.pass.num_clear_depth_stencil_view); + } +} + +// D3D11CalcSubresource only exists for C++ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_d3d11_end_pass(const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(_sg.d3d11.ctx && atts); + + if (!_sg.cur_pass.is_compute) { + // need to resolve MSAA render attachments into texture? + if (!atts->empty) { + // ...for offscreen pass... + for (int i = 0; i < atts->num_color_views; i++) { + const _sg_view_t* resolve_view = atts->resolve_views[i]; + if (resolve_view) { + const _sg_image_t* resolve_img = _sg_image_ref_ptr(&resolve_view->cmn.img.ref); + const _sg_view_t* color_view = atts->color_views[i]; + SOKOL_ASSERT(color_view); + const _sg_image_t* color_img = _sg_image_ref_ptr(&color_view->cmn.img.ref); + SOKOL_ASSERT(color_img->cmn.sample_count > 1); + SOKOL_ASSERT(resolve_img->cmn.sample_count == 1); + const UINT src_subres = _sg_d3d11_calcsubresource( + (UINT)color_view->cmn.img.mip_level, + (UINT)color_view->cmn.img.slice, + (UINT)color_img->cmn.num_mipmaps); + const UINT dst_subres = _sg_d3d11_calcsubresource( + (UINT)resolve_view->cmn.img.mip_level, + (UINT)resolve_view->cmn.img.slice, + (UINT)resolve_img->cmn.num_mipmaps); + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, + resolve_img->d3d11.res, + dst_subres, + color_img->d3d11.res, + src_subres, + color_img->d3d11.format); + _sg_stats_inc(d3d11.pass.num_resolve_subresource); + } + } + } else { + // ...for swapchain pass... + if (_sg.d3d11.cur_swapchain.resolve_view) { + SOKOL_ASSERT(_sg.d3d11.cur_swapchain.render_view); + SOKOL_ASSERT(_sg.cur_pass.swapchain.sample_count > 1); + SOKOL_ASSERT(_sg.cur_pass.swapchain.color_fmt > SG_PIXELFORMAT_NONE); + ID3D11Resource* d3d11_render_res = 0; + ID3D11Resource* d3d11_resolve_res = 0; + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_swapchain.render_view, &d3d11_render_res); + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_swapchain.resolve_view, &d3d11_resolve_res); + SOKOL_ASSERT(d3d11_render_res); + SOKOL_ASSERT(d3d11_resolve_res); + const sg_pixel_format color_fmt = _sg.cur_pass.swapchain.color_fmt; + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, d3d11_resolve_res, 0, d3d11_render_res, 0, _sg_d3d11_rtv_uav_pixel_format(color_fmt)); + _sg_d3d11_Release(d3d11_render_res); + _sg_d3d11_Release(d3d11_resolve_res); + _sg_stats_inc(d3d11.pass.num_resolve_subresource); + } + } + } + _sg.d3d11.cur_swapchain.render_view = 0; + _sg.d3d11.cur_swapchain.resolve_view = 0; + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + D3D11_VIEWPORT vp; + vp.TopLeftX = (FLOAT) x; + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h))); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + D3D11_RECT rect; + rect.left = x; + rect.top = (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h))); + rect.right = x + w; + rect.bottom = origin_top_left ? (y + h) : (_sg.cur_pass.dim.height - y); + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + if (pip->cmn.is_compute) { + // a compute pipeline + SOKOL_ASSERT(shd->d3d11.cs); + _sg_d3d11_CSSetShader(_sg.d3d11.ctx, shd->d3d11.cs, NULL, 0); + _sg_d3d11_CSSetConstantBuffers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_UB_BINDINGS, shd->d3d11.cs_cbufs); + _sg_stats_inc(d3d11.pipeline.num_cs_set_shader); + _sg_stats_inc(d3d11.pipeline.num_cs_set_constant_buffers); + } else { + // a render pipeline + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss); + SOKOL_ASSERT(shd->d3d11.vs); + SOKOL_ASSERT(shd->d3d11.fs); + + _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); + _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF); + _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); + _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); + _sg_d3d11_VSSetShader(_sg.d3d11.ctx, shd->d3d11.vs, NULL, 0); + _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_UB_BINDINGS, shd->d3d11.vs_cbufs); + _sg_d3d11_PSSetShader(_sg.d3d11.ctx, shd->d3d11.fs, NULL, 0); + _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_UB_BINDINGS, shd->d3d11.fs_cbufs); + _sg_stats_inc(d3d11.pipeline.num_rs_set_state); + _sg_stats_inc(d3d11.pipeline.num_om_set_depth_stencil_state); + _sg_stats_inc(d3d11.pipeline.num_om_set_blend_state); + _sg_stats_inc(d3d11.pipeline.num_ia_set_primitive_topology); + _sg_stats_inc(d3d11.pipeline.num_ia_set_input_layout); + _sg_stats_inc(d3d11.pipeline.num_vs_set_shader); + _sg_stats_inc(d3d11.pipeline.num_vs_set_constant_buffers); + _sg_stats_inc(d3d11.pipeline.num_ps_set_shader); + _sg_stats_inc(d3d11.pipeline.num_ps_set_constant_buffers); + } +} + +_SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + const bool is_compute = bnd->pip->cmn.is_compute; + + if (is_compute) { + _sg_clear(&_sg.d3d11.bnd.cs_srvs, sizeof(_sg.d3d11.bnd.cs_srvs)); + _sg_clear(&_sg.d3d11.bnd.cs_uavs, sizeof(_sg.d3d11.bnd.cs_uavs)); + _sg_clear(&_sg.d3d11.bnd.cs_smps, sizeof(_sg.d3d11.bnd.cs_smps)); + } else { + _sg_clear(&_sg.d3d11.bnd.vbs, sizeof(_sg.d3d11.bnd.vbs)); + _sg_clear(&_sg.d3d11.bnd.vb_offsets, sizeof(_sg.d3d11.bnd.vb_offsets)); + _sg_clear(&_sg.d3d11.bnd.vs_srvs, sizeof(_sg.d3d11.bnd.vs_srvs)); + _sg_clear(&_sg.d3d11.bnd.fs_srvs, sizeof(_sg.d3d11.bnd.fs_srvs)); + _sg_clear(&_sg.d3d11.bnd.vs_smps, sizeof(_sg.d3d11.bnd.vs_smps)); + _sg_clear(&_sg.d3d11.bnd.fs_smps, sizeof(_sg.d3d11.bnd.fs_smps)); + } + + // gather all the D3D11 resources into arrays + ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0; + + if (is_compute) { + // on D3D11 we need to break a chicken-egg-situation where a resource + // may still be set as shader resource view, but is going to be set + // as unordered-access-view, so first clear all shader resource view bindings + _sg_d3d11_CSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.cs_srvs); + } else { + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + const _sg_buffer_t* vb = bnd->vbs[i]; + if (vb == 0) { + continue; + } + SOKOL_ASSERT(vb->d3d11.buf); + _sg.d3d11.bnd.vbs[i] = vb->d3d11.buf; + _sg.d3d11.bnd.vb_offsets[i] = (UINT)bnd->vb_offsets[i]; + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_view_t* view = bnd->views[i]; + if (0 == view) { + continue; + } + const _sg_shader_view_t* shd_view = &shd->cmn.views[i]; + const sg_shader_stage stage = shd_view->stage; + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) + || (stage == SG_SHADERSTAGE_FRAGMENT) + || (stage == SG_SHADERSTAGE_COMPUTE)); + SOKOL_ASSERT((shd_view->view_type == SG_VIEWTYPE_TEXTURE) + || (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER) + || (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE)); + if (shd_view->view_type == SG_VIEWTYPE_TEXTURE) { + const uint8_t d3d11_slot = shd->d3d11.view_register_t_n[i]; + SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SRV_BINDINGS); + ID3D11ShaderResourceView* d3d11_srv = view->d3d11.srv; + SOKOL_ASSERT(d3d11_srv); + switch (stage) { + case SG_SHADERSTAGE_VERTEX: _sg.d3d11.bnd.vs_srvs[d3d11_slot] = d3d11_srv; break; + case SG_SHADERSTAGE_FRAGMENT: _sg.d3d11.bnd.fs_srvs[d3d11_slot] = d3d11_srv; break; + case SG_SHADERSTAGE_COMPUTE: _sg.d3d11.bnd.cs_srvs[d3d11_slot] = d3d11_srv; break; + default: SOKOL_UNREACHABLE; + } + } else if (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER) { + if (shd->cmn.views[i].sbuf_readonly) { + const uint8_t d3d11_slot = shd->d3d11.view_register_t_n[i]; + SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SRV_BINDINGS); + ID3D11ShaderResourceView* d3d11_srv = view->d3d11.srv; + SOKOL_ASSERT(d3d11_srv); + switch (stage) { + case SG_SHADERSTAGE_VERTEX: _sg.d3d11.bnd.vs_srvs[d3d11_slot] = d3d11_srv; break; + case SG_SHADERSTAGE_FRAGMENT: _sg.d3d11.bnd.fs_srvs[d3d11_slot] = d3d11_srv; break; + case SG_SHADERSTAGE_COMPUTE: _sg.d3d11.bnd.cs_srvs[d3d11_slot] = d3d11_srv; break; + default: SOKOL_UNREACHABLE; + } + } else { + SOKOL_ASSERT(stage == SG_SHADERSTAGE_COMPUTE); + const uint8_t d3d11_slot = shd->d3d11.view_register_u_n[i]; + SOKOL_ASSERT(d3d11_slot < _sg.limits.d3d11_max_unordered_access_views); + ID3D11UnorderedAccessView* d3d11_uav = view->d3d11.uav; + SOKOL_ASSERT(d3d11_uav); + _sg.d3d11.bnd.cs_uavs[d3d11_slot] = d3d11_uav; + } + } else if (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE) { + SOKOL_ASSERT(stage == SG_SHADERSTAGE_COMPUTE); + const uint8_t d3d11_slot = shd->d3d11.view_register_u_n[i]; + SOKOL_ASSERT(d3d11_slot < _sg.limits.d3d11_max_unordered_access_views); + ID3D11UnorderedAccessView* d3d11_uav = view->d3d11.uav; + SOKOL_ASSERT(d3d11_uav); + _sg.d3d11.bnd.cs_uavs[d3d11_slot] = d3d11_uav; + } else SOKOL_UNREACHABLE; + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const _sg_sampler_t* smp = bnd->smps[i]; + if (smp == 0) { + continue; + } + const sg_shader_stage stage = shd->cmn.samplers[i].stage; + SOKOL_ASSERT(stage != SG_SHADERSTAGE_NONE); + const uint8_t d3d11_slot = shd->d3d11.smp_register_s_n[i]; + SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SMP_BINDINGS); + SOKOL_ASSERT(smp->d3d11.smp); + ID3D11SamplerState* d3d11_smp = smp->d3d11.smp; + switch (stage) { + case SG_SHADERSTAGE_VERTEX: _sg.d3d11.bnd.vs_smps[d3d11_slot] = d3d11_smp; break; + case SG_SHADERSTAGE_FRAGMENT: _sg.d3d11.bnd.fs_smps[d3d11_slot] = d3d11_smp; break; + case SG_SHADERSTAGE_COMPUTE: _sg.d3d11.bnd.cs_smps[d3d11_slot] = d3d11_smp; break; + default: SOKOL_UNREACHABLE; + } + } + if (is_compute) { + SOKOL_ASSERT(_sg.limits.d3d11_max_unordered_access_views <= _SG_D3D11_MAX_STAGE_UAV_BINDINGS); + _sg_d3d11_CSSetUnorderedAccessViews(_sg.d3d11.ctx, 0, _sg.limits.d3d11_max_unordered_access_views, _sg.d3d11.bnd.cs_uavs, NULL); + _sg_d3d11_CSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.cs_srvs); + _sg_d3d11_CSSetSamplers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SMP_BINDINGS, _sg.d3d11.bnd.cs_smps); + _sg_stats_inc(d3d11.bindings.num_cs_set_shader_resources); + _sg_stats_inc(d3d11.bindings.num_cs_set_samplers); + _sg_stats_inc(d3d11.bindings.num_cs_set_unordered_access_views); + } else { + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEXBUFFER_BINDSLOTS, _sg.d3d11.bnd.vbs, bnd->pip->d3d11.vb_strides, _sg.d3d11.bnd.vb_offsets); + _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, bnd->pip->d3d11.index_format, (UINT)bnd->ib_offset); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.vs_srvs); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.fs_srvs); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SMP_BINDINGS, _sg.d3d11.bnd.vs_smps); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SMP_BINDINGS, _sg.d3d11.bnd.fs_smps); + _sg_stats_inc(d3d11.bindings.num_ia_set_vertex_buffers); + _sg_stats_inc(d3d11.bindings.num_ia_set_index_buffer); + _sg_stats_inc(d3d11.bindings.num_vs_set_shader_resources); + _sg_stats_inc(d3d11.bindings.num_ps_set_shader_resources); + _sg_stats_inc(d3d11.bindings.num_vs_set_samplers); + _sg_stats_inc(d3d11.bindings.num_ps_set_samplers); + } + return true; +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(int ub_slot, const sg_range* data) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size); + + ID3D11Buffer* cbuf = shd->d3d11.all_cbufs[ub_slot]; + SOKOL_ASSERT(cbuf); + _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cbuf, 0, NULL, data->ptr, 0, 0); + _sg_stats_inc(d3d11.uniforms.num_update_subresource); +} + +_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + const bool use_instanced_draw = (num_instances > 1) || (_sg.use_instanced_draw); + if (_sg.use_indexed_draw) { + if (use_instanced_draw) { + _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, + (UINT)num_elements, + (UINT)num_instances, + (UINT)base_element, + base_vertex, + (UINT)base_instance); + _sg_stats_inc(d3d11.draw.num_draw_indexed_instanced); + } else { + _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, base_vertex); + _sg_stats_inc(d3d11.draw.num_draw_indexed); + } + } else { + if (use_instanced_draw) { + _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, + (UINT)num_elements, + (UINT)num_instances, + (UINT)base_element, + (UINT)base_instance); + _sg_stats_inc(d3d11.draw.num_draw_instanced); + } else { + _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + _sg_stats_inc(d3d11.draw.num_draw); + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + _sg_d3d11_Dispatch(_sg.d3d11.ctx, (UINT)num_groups_x, (UINT)num_groups_y, (UINT)num_groups_z); +} + +_SOKOL_PRIVATE void _sg_d3d11_commit(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + 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); + _sg_stats_inc(d3d11.num_map); + if (SUCCEEDED(hr)) { + memcpy(d3d11_msr.pData, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + _sg_stats_inc(d3d11.num_unmap); + } else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + 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); + _sg_stats_inc(d3d11.num_map); + 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); + _sg_stats_inc(d3d11.num_unmap); + } else { + _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); + } +} + +// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources +// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource +_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11.res); + const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices; + const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices : 1; + UINT subres_index = 0; + HRESULT hr; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < _SG_D3D11_MAX_TEXTURE_SUBRESOURCES); + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + const sg_range* miplevel_data = &(data->mip_levels[mip_index]); + const size_t slice_size = miplevel_data->size / (size_t)num_slices; + SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices)); + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* slice_ptr = ((const uint8_t*)miplevel_data->ptr) + slice_offset; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_inc(d3d11.num_map); + if (SUCCEEDED(hr)) { + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData; + for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) { + if (src_row_pitch == (int)d3d11_msr.RowPitch) { + const size_t copy_size = slice_size / (size_t)num_depth_slices; + SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size); + memcpy(dst_ptr, src_ptr, copy_size); + } else { + SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_row_ptr = src_ptr; + uint8_t* dst_row_ptr = dst_ptr; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch); + src_row_ptr += src_row_pitch; + dst_row_ptr += d3d11_msr.RowPitch; + } + } + src_ptr += src_depth_pitch; + dst_ptr += d3d11_msr.DepthPitch; + } + _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); + _sg_stats_inc(d3d11.num_unmap); + } else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); + } + } + } +} + +// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>metal backend +#elif defined(SOKOL_METAL) + +#if __has_feature(objc_arc) +#define _SG_OBJC_RETAIN(obj) { } +#define _SG_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SG_OBJC_RETAIN(obj) { [obj retain]; } +#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_load_action a) { + switch (a) { + case SG_LOADACTION_CLEAR: return MTLLoadActionClear; + case SG_LOADACTION_LOAD: return MTLLoadActionLoad; + case SG_LOADACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resolve) { + switch (a) { + case SG_STOREACTION_STORE: + if (resolve) { + return MTLStoreActionStoreAndMultisampleResolve; + } else { + return MTLStoreActionStore; + } + break; + case SG_STOREACTION_DONTCARE: + if (resolve) { + return MTLStoreActionMultisampleResolve; + } else { + return MTLStoreActionDontCare; + } + break; + default: SOKOL_UNREACHABLE; return (MTLStoreAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) { + #if defined(_SG_TARGET_MACOS) + if (_sg.mtl.use_shared_storage_mode) { + return MTLResourceStorageModeShared; + } else { + return MTLResourceStorageModeManaged; + } + #else + // MTLResourceStorageModeManaged is not even defined on iOS SDK + return MTLResourceStorageModeShared; + #endif +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(const sg_buffer_usage* usage) { + if (usage->immutable) { + return _sg_mtl_resource_options_storage_mode_managed_or_shared(); + } else { + return MTLResourceCPUCacheModeWriteCombined | _sg_mtl_resource_options_storage_mode_managed_or_shared(); + } +} + +_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; + case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; + } +} + +_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; + case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; + case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; + case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; + case SG_VERTEXFORMAT_INT: return MTLVertexFormatInt; + case SG_VERTEXFORMAT_INT2: return MTLVertexFormatInt2; + case SG_VERTEXFORMAT_INT3: return MTLVertexFormatInt3; + case SG_VERTEXFORMAT_INT4: return MTLVertexFormatInt4; + case SG_VERTEXFORMAT_UINT: return MTLVertexFormatUInt; + case SG_VERTEXFORMAT_UINT2: return MTLVertexFormatUInt2; + case SG_VERTEXFORMAT_UINT3: return MTLVertexFormatUInt3; + case SG_VERTEXFORMAT_UINT4: return MTLVertexFormatUInt4; + case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; + case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; + case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; + case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; + case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; + case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; + case SG_VERTEXFORMAT_USHORT2: return MTLVertexFormatUShort2; + case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized; + case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; + case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; + case SG_VERTEXFORMAT_USHORT4: return MTLVertexFormatUShort4; + 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; + } +} + +_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; + case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; + case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; + default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm; + case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm; + case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint; + case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint; + case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm; + case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm; + case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint; + case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint; + case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; + case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm; + case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm; + case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint; + case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint; + case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint; + case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint; + case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; + case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm; + case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm; + case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint; + 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; + case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm; + case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm; + case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8; + #if defined(_SG_TARGET_MACOS) + case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; + case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; + case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; + case SG_PIXELFORMAT_BC3_SRGBA: return MTLPixelFormatBC3_RGBA_sRGB; + case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; + case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return MTLPixelFormatBC7_RGBAUnorm_sRGB; + #else + case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB; + case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; + case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return MTLPixelFormatEAC_RGBA8_sRGB; + case SG_PIXELFORMAT_EAC_R11: return MTLPixelFormatEAC_R11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return MTLPixelFormatEAC_R11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return MTLPixelFormatASTC_4x4_LDR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return MTLPixelFormatASTC_4x4_sRGB; + #endif + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { + MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; + if (m & SG_COLORMASK_R) { + mtl_mask |= MTLColorWriteMaskRed; + } + if (m & SG_COLORMASK_G) { + mtl_mask |= MTLColorWriteMaskGreen; + } + if (m & SG_COLORMASK_B) { + mtl_mask |= MTLColorWriteMaskBlue; + } + if (m & SG_COLORMASK_A) { + mtl_mask |= MTLColorWriteMaskAlpha; + } + return mtl_mask; +} + +_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return MTLBlendOperationAdd; + case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + case SG_BLENDOP_MIN: return MTLBlendOperationMin; + case SG_BLENDOP_MAX: return MTLBlendOperationMax; + default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; + } +} + +_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; + case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + case SG_BLENDFACTOR_SRC1_COLOR: return MTLBlendFactorSource1Color; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return MTLBlendFactorOneMinusSource1Color; + case SG_BLENDFACTOR_SRC1_ALPHA: return MTLBlendFactorSource1Alpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return MTLBlendFactorOneMinusSource1Alpha; + default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; + } +} + +_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; + case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; + case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; + case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; + case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; + case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; + default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; + } +} + +_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; + case SG_STENCILOP_ZERO: return MTLStencilOperationZero; + case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; + case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; + case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; + case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; + case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; + default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; + } +} + +_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return MTLCullModeNone; + case SG_CULLMODE_FRONT: return MTLCullModeFront; + case SG_CULLMODE_BACK: return MTLCullModeBack; + default: SOKOL_UNREACHABLE; return (MTLCullMode)0; + } +} + +_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { + switch (w) { + case SG_FACEWINDING_CW: return MTLWindingClockwise; + case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; + default: SOKOL_UNREACHABLE; return (MTLWinding)0; + } +} + +_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; + case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; + default: SOKOL_UNREACHABLE; return (MTLIndexType)0; + } +} + +_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return 2; + case SG_INDEXTYPE_UINT32: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t, bool msaa) { + switch (t) { + case SG_IMAGETYPE_2D: return msaa ? MTLTextureType2DMultisample : MTLTextureType2D; + case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; + case SG_IMAGETYPE_3D: return MTLTextureType3D; + // NOTE: MTLTextureType2DMultisampleArray requires macOS 10.14+, iOS 14.0+ + case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; + default: SOKOL_UNREACHABLE; return (MTLTextureType)0; + } +} + +_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + // border color feature available + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } + } + } + // fallthrough: clamp to border no supported + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } +} + +_SOKOL_PRIVATE API_AVAILABLE(ios(14.0), macos(12.0)) MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { + switch (c) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; + case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; + case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite; + default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return MTLSamplerMinMagFilterNearest; + case SG_FILTER_LINEAR: + return MTLSamplerMinMagFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return MTLSamplerMipFilterNearest; + case SG_FILTER_LINEAR: + return MTLSamplerMipFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; + } +} + +_SOKOL_PRIVATE size_t _sg_mtl_vertexbuffer_bindslot(size_t sokol_bindslot) { + return sokol_bindslot + _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS; +} + +//-- a pool for all Metal resource objects, with deferred release queue --------- +_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { + _sg.mtl.idpool.num_slots = 2 * + ( + 2 * desc->buffer_pool_size + + 2 * desc->image_pool_size + + 1 * desc->sampler_pool_size + + 6 * desc->shader_pool_size + + 3 * desc->pipeline_pool_size + + 1 * desc->view_pool_size + + 128 + ); + _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; + _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); + NSNull* null = [NSNull null]; + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + [_sg.mtl.idpool.pool addObject:null]; + } + 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*)_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; + } + // a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index + _sg.mtl.idpool.release_queue_front = 0; + _sg.mtl.idpool.release_queue_back = 0; + _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; + } +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { + _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); +} + +// get a new free resource pool slot +_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); + const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + return slot_index; +} + +// put a free resource pool slot back into the free-queue +_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; +} + +// add an MTLResource to the pool, return pool index or 0 if input was 'nil' +_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { + if (nil == res) { + return _SG_MTL_INVALID_SLOT_INDEX; + } + _sg_stats_inc(metal.idpool.num_added); + 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; +} + +/* mark an MTLResource for release, this will put the resource into the + deferred-release queue, and the resource will then be released N frames later, + the special pool index 0 will be ignored (this means that a nil + value was provided to _sg_mtl_add_resource() +*/ +_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) { + if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { + return; + } + _sg_stats_inc(metal.idpool.num_released); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + int release_index = _sg.mtl.idpool.release_queue_front++; + if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_front = 0; + } + // release queue full? + SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); + SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); + const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; + _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; + _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; +} + +// run garbage-collection pass on all resources in the release-queue +_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { + while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { + if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { + // don't need to check further, release-items past this are too young + break; + } + _sg_stats_inc(metal.idpool.num_garbage_collected); + // 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.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 + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + _sg.mtl.idpool.release_queue_back++; + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_back = 0; + } + } +} + +_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { + return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; +} + +_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { + _sg_clear(&_sg.mtl.cache, sizeof(_sg.mtl.cache)); +} + +// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf +_SOKOL_PRIVATE void _sg_mtl_init_caps(void) { + #if defined(_SG_TARGET_MACOS) + _sg.backend = SG_BACKEND_METAL_MACOS; + #elif defined(_SG_TARGET_IOS) + #if defined(_SG_TARGET_IOS_SIMULATOR) + _sg.backend = SG_BACKEND_METAL_SIMULATOR; + #else + _sg.backend = SG_BACKEND_METAL_IOS; + #endif + #endif + _sg.features.origin_top_left = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.compute = true; + _sg.features.msaa_texture_bindings = true; + _sg.features.draw_base_vertex = true; + _sg.features.draw_base_instance = true; + _sg.features.dual_source_blending = true; + + _sg.features.image_clamp_to_border = false; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) + if (@available(macOS 12.0, iOS 14.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2]; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160000) + if (!_sg.features.image_clamp_to_border) { + if (@available(macOS 13.0, iOS 16.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3]; + } + } + #endif + } + #endif + + #if defined(_SG_TARGET_MACOS) + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_texture_bindings_per_stage = _sg_min(128, SG_MAX_VIEW_BINDSLOTS); + #else + // FIXME: newer iOS devices support 16k textures + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_texture_bindings_per_stage = _sg_min(96, SG_MAX_VIEW_BINDSLOTS); // since iPhone8 + #endif + _sg.limits.max_storage_image_bindings_per_stage = _sg.limits.max_texture_bindings_per_stage; // shared with texture bindings + _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min(_SG_MTL_MAX_STAGE_BUFFER_BINDINGS - (SG_MAX_VERTEXBUFFER_BINDSLOTS + SG_MAX_UNIFORMBLOCK_BINDSLOTS), SG_MAX_VIEW_BINDSLOTS); + _sg.limits.max_color_attachments = _sg_min(8, SG_MAX_COLOR_ATTACHMENTS); + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _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]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _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 + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #endif + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + #else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + #endif + + // compute shader access (see: https://github.com/gpuweb/gpuweb/issues/513) + // for now let's use the same conservative set on all backends even though + // some backends are less restrictive + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); +} + +//-- main Metal backend state and functions ------------------------------------ +_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { + // assume already zero-initialized + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.metal.device); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg_mtl_init_pool(desc); + _sg_mtl_clear_state_cache(); + _sg.mtl.valid = true; + _sg.mtl.ub_size = desc->uniform_buffer_size; + _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg.mtl.device = (__bridge id) desc->environment.metal.device; + _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; + + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg.mtl.uniform_buffers[i] = [_sg.mtl.device + newBufferWithLength:(NSUInteger)_sg.mtl.ub_size + options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared + ]; + #if defined(SOKOL_DEBUG) + _sg.mtl.uniform_buffers[i].label = [NSString stringWithFormat:@"sg-uniform-buffer.%d", i]; + #endif + } + + if (desc->metal.force_managed_storage_mode) { + _sg.mtl.use_shared_storage_mode = false; + } else if (@available(macOS 10.15, iOS 13.0, *)) { + // on Intel Macs, always use managed resources even though the + // device says it supports unified memory (because of texture restrictions) + const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1]; + if (!is_apple_gpu) { + _sg.mtl.use_shared_storage_mode = false; + } else { + _sg.mtl.use_shared_storage_mode = true; + } + } else { + #if defined(_SG_TARGET_MACOS) + _sg.mtl.use_shared_storage_mode = false; + #else + _sg.mtl.use_shared_storage_mode = true; + #endif + } + _sg_mtl_init_caps(); +} + +_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { + SOKOL_ASSERT(_sg.mtl.valid); + // wait for the last frame to finish + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + } + // semaphore must be "relinquished" before destruction + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_signal(_sg.mtl.sem); + } + _sg_mtl_garbage_collect(_sg.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_destroy_pool(); + _sg.mtl.valid = false; + + _SG_OBJC_RELEASE(_sg.mtl.sem); + _SG_OBJC_RELEASE(_sg.mtl.device); + _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); + } + // NOTE: MTLCommandBuffer, MTLRenderCommandEncoder and MTLComputeCommandEncoder are auto-released + _sg.mtl.cmd_buffer = nil; + _sg.mtl.render_cmd_encoder = nil; + _sg.mtl.compute_cmd_encoder = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { + _sg_mtl_clear_state_cache(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(buf->cmn.size > 0); + const bool injected = (0 != desc->mtl_buffers[0]); + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(&buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + id mtl_buf; + if (injected) { + SOKOL_ASSERT(desc->mtl_buffers[slot]); + mtl_buf = (__bridge id) desc->mtl_buffers[slot]; + } else { + if (desc->data.ptr) { + SOKOL_ASSERT(desc->data.size > 0); + mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; + } else { + mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; + } + if (nil == mtl_buf) { + _SG_ERROR(METAL_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_buf.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); + _SG_OBJC_RELEASE(mtl_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_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' + _sg_mtl_release_resource(_sg.frame_index, buf->mtl.buf[slot]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { + const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices; + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->mip_levels[mip_index].ptr); + SOKOL_ASSERT(data->mip_levels[mip_index].size > 0); + const uint8_t* data_ptr = (const uint8_t*)data->mip_levels[mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + int bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + /* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion + + "Supply a nonzero value only when you copy data to a MTLTextureType3D type texture" + */ + MTLRegion region; + int bytes_per_image; + if (img->cmn.type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); + bytes_per_image = bytes_per_slice; + // FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this + } else { + region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); + bytes_per_image = 0; + } + + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + const int slice_offset = slice_index * bytes_per_slice; + SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->mip_levels[mip_index].size); + [mtl_tex replaceRegion:region + mipmapLevel:(NSUInteger)mip_index + slice:(NSUInteger)slice_index + withBytes:data_ptr + slice_offset + bytesPerRow:(NSUInteger)bytes_per_row + bytesPerImage:(NSUInteger)bytes_per_image]; + } + } +} + +_SOKOL_PRIVATE bool _sg_mtl_init_texdesc(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type, img->cmn.sample_count > 1); + mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); + if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { + _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED); + return false; + } + mtl_desc.width = (NSUInteger)img->cmn.width; + mtl_desc.height = (NSUInteger)img->cmn.height; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mtl_desc.depth = (NSUInteger)img->cmn.num_slices; + } else { + mtl_desc.depth = 1; + } + mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->cmn.type) { + mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; + } else { + mtl_desc.arrayLength = 1; + } + mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; + + const sg_image_usage* usg = &img->cmn.usage; + const bool any_attachment = usg->color_attachment || usg->resolve_attachment || usg->depth_stencil_attachment; + MTLTextureUsage mtl_tex_usage = MTLTextureUsageShaderRead; + if (any_attachment) { + mtl_tex_usage |= MTLTextureUsageRenderTarget; + } + if (img->cmn.usage.storage_image) { + mtl_tex_usage |= MTLTextureUsageShaderWrite; + } + mtl_desc.usage = mtl_tex_usage; + + MTLResourceOptions mtl_res_options = 0; + if (any_attachment || img->cmn.usage.storage_image) { + mtl_res_options |= MTLResourceStorageModePrivate; + } else { + mtl_res_options |= _sg_mtl_resource_options_storage_mode_managed_or_shared(); + if (!img->cmn.usage.immutable) { + mtl_res_options |= MTLResourceCPUCacheModeWriteCombined; + } + } + mtl_desc.resourceOptions = mtl_res_options; + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + const bool injected = (0 != desc->mtl_textures[0]); + + // first initialize all Metal resource pool slots to 'empty' + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + img->mtl.tex[i] = _sg_mtl_add_resource(nil); + } + + // initialize a Metal texture descriptor + MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; + if (!_sg_mtl_init_texdesc(mtl_desc, img)) { + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_FAILED; + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id mtl_tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + mtl_tex = (__bridge id) desc->mtl_textures[slot]; + } else { + mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if (nil == mtl_tex) { + _SG_OBJC_RELEASE(mtl_desc); + _SG_ERROR(METAL_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (desc->data.mip_levels[0].ptr) { + _sg_mtl_copy_image_data(img, mtl_tex, &desc->data); + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_tex.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex); + _SG_OBJC_RELEASE(mtl_tex); + } + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_VALID; +} + +_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++) { + _sg_mtl_release_resource(_sg.frame_index, img->mtl.tex[slot]); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + id mtl_smp; + const bool injected = (0 != desc->mtl_sampler); + if (injected) { + SOKOL_ASSERT(desc->mtl_sampler); + mtl_smp = (__bridge id) desc->mtl_sampler; + } else { + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(desc->wrap_v); + mtl_desc.rAddressMode = _sg_mtl_address_mode(desc->wrap_w); + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + mtl_desc.borderColor = _sg_mtl_border_color(desc->border_color); + } + } + mtl_desc.minFilter = _sg_mtl_minmag_filter(desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mipmap_filter(desc->mipmap_filter); + mtl_desc.lodMinClamp = desc->min_lod; + mtl_desc.lodMaxClamp = desc->max_lod; + // FIXME: lodAverage? + mtl_desc.maxAnisotropy = desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare); + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_desc.label = [NSString stringWithUTF8String:desc->label]; + } + #endif + mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + if (nil == mtl_smp) { + _SG_ERROR(METAL_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp); + _SG_OBJC_RELEASE(mtl_smp); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, smp->mtl.sampler_state); +} + +_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { + NSError* err = NULL; + id lib = [_sg.mtl.device + newLibraryWithSource:[NSString stringWithUTF8String:src] + options:nil + error:&err + ]; + if (err) { + _SG_ERROR(METAL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) { + NSError* err = NULL; + 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) { + _SG_ERROR(METAL_SHADER_CREATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); + } + _SG_OBJC_RELEASE(lib_data); + return lib; +} + +_SOKOL_PRIVATE bool _sg_mtl_create_shader_func(const sg_shader_function* func, const char* label, const char* label_ext, _sg_mtl_shader_func_t* res) { + SOKOL_ASSERT(res->mtl_lib == _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(res->mtl_func == _SG_MTL_INVALID_SLOT_INDEX); + id mtl_lib = nil; + if (func->bytecode.ptr) { + SOKOL_ASSERT(func->bytecode.size > 0); + mtl_lib = _sg_mtl_library_from_bytecode(func->bytecode.ptr, func->bytecode.size); + } else if (func->source) { + mtl_lib = _sg_mtl_compile_library(func->source); + } + if (mtl_lib == nil) { + return false; + } + #if defined(SOKOL_DEBUG) + if (label) { + SOKOL_ASSERT(label_ext); + mtl_lib.label = [NSString stringWithFormat:@"%s.%s", label, label_ext]; + } + #else + _SOKOL_UNUSED(label); + _SOKOL_UNUSED(label_ext); + #endif + SOKOL_ASSERT(func->entry); + id mtl_func = [mtl_lib newFunctionWithName:[NSString stringWithUTF8String:func->entry]]; + if (mtl_func == nil) { + _SG_ERROR(METAL_SHADER_ENTRY_NOT_FOUND); + _SG_OBJC_RELEASE(mtl_lib); + return false; + } + res->mtl_lib = _sg_mtl_add_resource(mtl_lib); + res->mtl_func = _sg_mtl_add_resource(mtl_func); + _SG_OBJC_RELEASE(mtl_lib); + _SG_OBJC_RELEASE(mtl_func); + return true; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_shader_func(const _sg_mtl_shader_func_t* func) { + // it is valid to call _sg_mtl_release_resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, func->mtl_func); + _sg_mtl_release_resource(_sg.frame_index, func->mtl_lib); +} + +// NOTE: this is an out-of-range check for MSL bindslots that's also active in release mode +_SOKOL_PRIVATE bool _sg_mtl_ensure_msl_bindslot_ranges(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* ub = &desc->uniform_blocks[i]; + if (ub->stage != SG_SHADERSTAGE_NONE) { + if (ub->msl_buffer_n >= _SG_MTL_MAX_STAGE_UB_BINDINGS) { + _SG_ERROR(METAL_UNIFORMBLOCK_MSL_BUFFER_SLOT_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + if (view->texture.stage != SG_SHADERSTAGE_NONE) { + if (view->texture.msl_texture_n >= _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS) { + _SG_ERROR(METAL_IMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE); + return false; + } + } + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + if ((view->storage_buffer.msl_buffer_n < _SG_MTL_MAX_STAGE_UB_BINDINGS) || + (view->storage_buffer.msl_buffer_n >= _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS)) + { + _SG_ERROR(METAL_STORAGEBUFFER_MSL_BUFFER_SLOT_OUT_OF_RANGE); + return false; + } + } + if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_image.msl_texture_n >= _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS) { + _SG_ERROR(METAL_STORAGEIMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const sg_shader_sampler* smp = &desc->samplers[i]; + if (smp->stage != SG_SHADERSTAGE_NONE) { + if (smp->msl_sampler_n >= _SG_MTL_MAX_STAGE_SAMPLER_BINDINGS) { + _SG_ERROR(METAL_SAMPLER_MSL_SAMPLER_SLOT_OUT_OF_RANGE); + return false; + } + } + } + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + + // do a MSL bindslot range check also in release mode, and if that fails, + // also fail shader creation + if (!_sg_mtl_ensure_msl_bindslot_ranges(desc)) { + return SG_RESOURCESTATE_FAILED; + } + + shd->mtl.threads_per_threadgroup = MTLSizeMake( + (NSUInteger)desc->mtl_threads_per_threadgroup.x, + (NSUInteger)desc->mtl_threads_per_threadgroup.y, + (NSUInteger)desc->mtl_threads_per_threadgroup.z); + + // copy resource bindslot mappings + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + shd->mtl.ub_buffer_n[i] = desc->uniform_blocks[i].msl_buffer_n; + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + SOKOL_ASSERT(0 == shd->mtl.view_buffer_texture_n[i]); + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + shd->mtl.view_buffer_texture_n[i] = view->storage_buffer.msl_buffer_n; + } else if (view->texture.stage != SG_SHADERSTAGE_NONE) { + shd->mtl.view_buffer_texture_n[i] = view->texture.msl_texture_n; + } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + shd->mtl.view_buffer_texture_n[i] = view->storage_image.msl_texture_n; + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + shd->mtl.smp_sampler_n[i] = desc->samplers[i].msl_sampler_n; + } + + // create metal library and function objects + bool shd_valid = true; + if (desc->vertex_func.source || desc->vertex_func.bytecode.ptr) { + shd_valid &= _sg_mtl_create_shader_func(&desc->vertex_func, desc->label, "vs", &shd->mtl.vertex_func); + } + if (desc->fragment_func.source || desc->fragment_func.bytecode.ptr) { + shd_valid &= _sg_mtl_create_shader_func(&desc->fragment_func, desc->label, "fs", &shd->mtl.fragment_func); + } + if (desc->compute_func.source || desc->compute_func.bytecode.ptr) { + shd_valid &= _sg_mtl_create_shader_func(&desc->compute_func, desc->label, "cs", &shd->mtl.compute_func); + } + if (!shd_valid) { + _sg_mtl_discard_shader_func(&shd->mtl.vertex_func); + _sg_mtl_discard_shader_func(&shd->mtl.fragment_func); + _sg_mtl_discard_shader_func(&shd->mtl.compute_func); + } + return shd_valid ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_mtl_discard_shader_func(&shd->mtl.vertex_func); + _sg_mtl_discard_shader_func(&shd->mtl.fragment_func); + _sg_mtl_discard_shader_func(&shd->mtl.compute_func); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + if (pip->cmn.is_compute) { + NSError* err = NULL; + MTLComputePipelineDescriptor* cp_desc = [[MTLComputePipelineDescriptor alloc] init]; + cp_desc.computeFunction = _sg_mtl_id(shd->mtl.compute_func.mtl_func); + cp_desc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_shader_view_t* view = &shd->cmn.views[i]; + if (view->view_type != SG_VIEWTYPE_STORAGEBUFFER) { + continue; + } + if (!view->sbuf_readonly) { + continue; + } + SOKOL_ASSERT(view->stage == SG_SHADERSTAGE_COMPUTE); + const NSUInteger mtl_slot = shd->mtl.view_buffer_texture_n[i]; + SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_BUFFER_BINDINGS); + cp_desc.buffers[mtl_slot].mutability = MTLMutabilityImmutable; + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + cp_desc.label = [NSString stringWithFormat:@"%s", desc->label]; + } + #endif + id mtl_cps = [_sg.mtl.device + newComputePipelineStateWithDescriptor:cp_desc + options:MTLPipelineOptionNone + reflection:nil + error:&err]; + _SG_OBJC_RELEASE(cp_desc); + if (nil == mtl_cps) { + SOKOL_ASSERT(err); + _SG_ERROR(METAL_CREATE_CPS_FAILED); + _SG_LOGMSG(METAL_CREATE_CPS_OUTPUT, [err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.cps = _sg_mtl_add_resource(mtl_cps); + _SG_OBJC_RELEASE(mtl_cps); + pip->mtl.threads_per_threadgroup = shd->mtl.threads_per_threadgroup; + } else { + sg_primitive_type prim_type = desc->primitive_type; + pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); + pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); + if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); + } + pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode); + pip->mtl.winding = _sg_mtl_winding(desc->face_winding); + pip->mtl.stencil_ref = desc->stencil.ref; + + // create vertex-descriptor + MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; + for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[a_state->buffer_index]); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_state->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_state->offset; + vtx_desc.attributes[attr_index].bufferIndex = _sg_mtl_vertexbuffer_bindslot((size_t)a_state->buffer_index); + } + for (NSUInteger layout_index = 0; layout_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + const NSUInteger mtl_vb_slot = _sg_mtl_vertexbuffer_bindslot(layout_index); + SOKOL_ASSERT(l_state->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate; + } + } + + // render-pipeline descriptor + MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; + rp_desc.vertexDescriptor = vtx_desc; + SOKOL_ASSERT(shd->mtl.vertex_func.mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.vertex_func.mtl_func); + SOKOL_ASSERT(shd->mtl.fragment_func.mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.fragment_func.mtl_func); + rp_desc.rasterSampleCount = (NSUInteger)desc->sample_count; + rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + rp_desc.alphaToOneEnabled = NO; + rp_desc.rasterizationEnabled = YES; + rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { + rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + } + for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + const sg_color_target_state* cs = &desc->colors[i]; + rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); + rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); + rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; + rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha); + rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb); + rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha); + rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb); + rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); + rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); + } + // Set buffer mutability for all buffers (vertex buffers and storage buffers). + // For vertex buffer it is guaranteed that neither the GPU nor CPU will update their content + // as long as it is in flight (since dynamic buffers are double-buffered, and vertex-buffers + // are not updated by the GPU). + // For storage buffer the same double-buffering applies, and if they are applied + // to the vertex- or fragment-stage must be declared as readonly in the shader. + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + if (pip->cmn.vertex_buffer_layout_active[i]) { + const NSUInteger mtl_slot = _sg_mtl_vertexbuffer_bindslot(i); + rp_desc.vertexBuffers[mtl_slot].mutability = MTLMutabilityImmutable; + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_shader_view_t* view = &shd->cmn.views[i]; + if (view->view_type != SG_VIEWTYPE_STORAGEBUFFER) { + continue; + } + const sg_shader_stage stage = view->stage; + SOKOL_ASSERT(view->stage != SG_SHADERSTAGE_COMPUTE); + SOKOL_ASSERT(view->sbuf_readonly); + const NSUInteger mtl_slot = shd->mtl.view_buffer_texture_n[i]; + SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_BUFFER_BINDINGS); + if (stage == SG_SHADERSTAGE_VERTEX) { + rp_desc.vertexBuffers[mtl_slot].mutability = MTLMutabilityImmutable; + } else if (stage == SG_SHADERSTAGE_FRAGMENT) { + rp_desc.fragmentBuffers[mtl_slot].mutability = MTLMutabilityImmutable; + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + rp_desc.label = [NSString stringWithFormat:@"%s", desc->label]; + } + #endif + NSError* err = NULL; + id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + _SG_OBJC_RELEASE(rp_desc); + if (nil == mtl_rps) { + SOKOL_ASSERT(err); + _SG_ERROR(METAL_CREATE_RPS_FAILED); + _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + _SG_OBJC_RELEASE(mtl_rps); + + // depth-stencil-state + MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; + ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + if (desc->stencil.enabled) { + const sg_stencil_face_state* sb = &desc->stencil.back; + ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); + ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); + ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); + ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare); + ds_desc.backFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); + ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); + ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); + ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare); + ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + ds_desc.label = [NSString stringWithFormat:@"%s.dss", desc->label]; + } + #endif + id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; + _SG_OBJC_RELEASE(ds_desc); + if (nil == mtl_dss) { + _SG_ERROR(METAL_CREATE_DSS_FAILED); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); + _SG_OBJC_RELEASE(mtl_dss); + } + return SG_RESOURCESTATE_VALID; +} + +_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.frame_index, pip->mtl.cps); + _sg_mtl_release_resource(_sg.frame_index, pip->mtl.rps); + _sg_mtl_release_resource(_sg.frame_index, pip->mtl.dss); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && desc); + _SOKOL_UNUSED(desc); + if ((SG_VIEWTYPE_TEXTURE == view->cmn.type) || (SG_VIEWTYPE_STORAGEIMAGE == view->cmn.type)) { + const _sg_image_view_common_t* cmn = &view->cmn.img; + const _sg_image_t* img = _sg_image_ref_ptr(&cmn->ref); + SOKOL_ASSERT(cmn->mip_level_count >= 1); + SOKOL_ASSERT(cmn->slice_count >= 1); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(img->mtl.tex[slot] != _SG_MTL_INVALID_SLOT_INDEX); + id mtl_tex_view = [_sg_mtl_id(img->mtl.tex[slot]) + newTextureViewWithPixelFormat: _sg_mtl_pixel_format(img->cmn.pixel_format) + textureType: _sg_mtl_texture_type(img->cmn.type, img->cmn.sample_count > 1) + levels: NSMakeRange((NSUInteger)cmn->mip_level, (NSUInteger)cmn->mip_level_count) + slices: NSMakeRange((NSUInteger)cmn->slice, (NSUInteger)cmn->slice_count)]; + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_tex_view.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + view->mtl.tex_view[slot] = _sg_mtl_add_resource(mtl_tex_view); + _SG_OBJC_RELEASE(mtl_tex_view); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_view(_sg_view_t* view) { + SOKOL_ASSERT(view); + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + // it's valid to call _sg_mtl_release_resource with a null handle + _sg_mtl_release_resource(_sg.frame_index, view->mtl.tex_view[i]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { + // In the Metal backend, uniform buffer bindings happen once in sg_begin_pass() and + // remain valid for the entire pass. Only binding offsets will be updated + // in sg_apply_uniforms() + if (_sg.cur_pass.is_compute) { + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + for (size_t slot = 0; slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS; slot++) { + [_sg.mtl.compute_cmd_encoder + setBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + } + } else { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + for (size_t slot = 0; slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS; slot++) { + [_sg.mtl.render_cmd_encoder + setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + [_sg.mtl.render_cmd_encoder + setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + } + } +} + +_SOKOL_PRIVATE void _sg_mtl_begin_compute_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); (void)pass; + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder); + + _sg.mtl.compute_cmd_encoder = [_sg.mtl.cmd_buffer computeCommandEncoder]; + if (nil == _sg.mtl.compute_cmd_encoder) { + _sg.cur_pass.valid = false; + return; + } + + #if defined(SOKOL_DEBUG) + if (pass->label) { + _sg.mtl.compute_cmd_encoder.label = [NSString stringWithUTF8String:pass->label]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_begin_render_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(pass && atts); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder); + + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + MTLRenderPassDescriptor* pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + SOKOL_ASSERT(pass_desc); + if (!atts->empty) { + // setup pass descriptor for offscreen rendering + for (NSUInteger i = 0; i < (NSUInteger)atts->num_color_views; i++) { + const _sg_view_t* color_view = atts->color_views[i]; + SOKOL_ASSERT(color_view); + const _sg_view_t* resolve_view = atts->resolve_views[i]; + const _sg_image_t* color_img = _sg_image_ref_ptr(&color_view->cmn.img.ref); + SOKOL_ASSERT(color_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(color_img->cmn.active_slot == 0); + SOKOL_ASSERT(color_img->mtl.tex[0] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].load_action); + pass_desc.colorAttachments[i].storeAction = _sg_mtl_store_action(action->colors[i].store_action, resolve_view != 0); + sg_color c = action->colors[i].clear_value; + pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(color_img->mtl.tex[0]); + pass_desc.colorAttachments[i].level = (NSUInteger)color_view->cmn.img.mip_level; + switch (color_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)color_view->cmn.img.slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)color_view->cmn.img.slice; + break; + default: break; + } + if (resolve_view) { + const _sg_image_t* resolve_img = _sg_image_ref_ptr(&resolve_view->cmn.img.ref); + SOKOL_ASSERT(resolve_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(resolve_img->cmn.active_slot == 0); + SOKOL_ASSERT(resolve_img->mtl.tex[0] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(resolve_img->mtl.tex[0]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)resolve_view->cmn.img.mip_level; + switch (resolve_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)resolve_view->cmn.img.slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)resolve_view->cmn.img.slice; + break; + default: break; + } + } + } + if (atts->ds_view) { + const _sg_view_t* ds_view = atts->ds_view; + const _sg_image_t* ds_img = _sg_image_ref_ptr(&ds_view->cmn.img.ref); + SOKOL_ASSERT(ds_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(ds_img->cmn.active_slot == 0); + SOKOL_ASSERT(ds_img->mtl.tex[0] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_img->mtl.tex[0]); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + switch (ds_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.depthAttachment.slice = (NSUInteger)ds_view->cmn.img.slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.depthAttachment.resolveDepthPlane = (NSUInteger)ds_view->cmn.img.slice; + break; + default: break; + } + if (_sg_is_depth_stencil_format(ds_img->cmn.pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_img->mtl.tex[0]); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + switch (ds_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.stencilAttachment.slice = (NSUInteger)ds_view->cmn.img.slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.stencilAttachment.resolveDepthPlane = (NSUInteger)ds_view->cmn.img.slice; + break; + default: break; + } + } + } + } else { + // setup pass descriptor for swapchain rendering + // + // NOTE: at least in macOS Sonoma this no longer seems to be the case, the + // current drawable is also valid in a minimized window + // === + // an MTKView current_drawable will not be valid if window is minimized, don't do any rendering in this case + if (0 == swapchain->metal.current_drawable) { + _sg.cur_pass.valid = false; + return; + } + // pin the swapchain resources into memory so that they outlive their command buffer + // (this is necessary because the command buffer doesn't retain references) + int pass_desc_ref = _sg_mtl_add_resource(pass_desc); + _sg_mtl_release_resource(_sg.frame_index, pass_desc_ref); + + _sg.mtl.cur_drawable = (__bridge id) swapchain->metal.current_drawable; + if (swapchain->sample_count > 1) { + // multi-sampling: render into msaa texture, resolve into drawable texture + id msaa_tex = (__bridge id) swapchain->metal.msaa_color_texture; + SOKOL_ASSERT(msaa_tex != nil); + pass_desc.colorAttachments[0].texture = msaa_tex; + pass_desc.colorAttachments[0].resolveTexture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve; + } else { + // non-msaa: render into current_drawable + pass_desc.colorAttachments[0].texture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore; + } + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action); + const sg_color c = action->colors[0].clear_value; + pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + + // optional depth-stencil texture + if (swapchain->metal.depth_stencil_texture) { + id ds_tex = (__bridge id) swapchain->metal.depth_stencil_texture; + SOKOL_ASSERT(ds_tex != nil); + pass_desc.depthAttachment.texture = ds_tex; + pass_desc.depthAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + if (_sg_is_depth_stencil_format(swapchain->depth_format)) { + pass_desc.stencilAttachment.texture = ds_tex; + pass_desc.stencilAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + } + } + } + + // NOTE: at least in macOS Sonoma, the following is no longer the case, a valid + // render command encoder is also returned in a minimized window + // === + // create a render command encoder, this might return nil if window is minimized + _sg.mtl.render_cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (nil == _sg.mtl.render_cmd_encoder) { + _sg.cur_pass.valid = false; + return; + } + + #if defined(SOKOL_DEBUG) + if (pass->label) { + _sg.mtl.render_cmd_encoder.label = [NSString stringWithUTF8String:pass->label]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(pass && atts); + SOKOL_ASSERT(_sg.mtl.cmd_queue); + SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.cur_drawable); + _sg_mtl_clear_state_cache(); + + // if this is the first pass in the frame, create one command buffer and blit-cmd-encoder for the entire frame + if (nil == _sg.mtl.cmd_buffer) { + // block until the oldest frame in flight has finished + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + if (_sg.desc.metal.use_command_buffer_with_retained_references) { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; + } else { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + } + [_sg.mtl.cmd_buffer enqueue]; + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buf) { + // NOTE: this code is called on a different thread! + _SOKOL_UNUSED(cmd_buf); + dispatch_semaphore_signal(_sg.mtl.sem); + }]; + } + + // if this is first pass in frame, get uniform buffer base pointer + if (0 == _sg.mtl.cur_ub_base_ptr) { + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + } + + if (pass->compute) { + _sg_mtl_begin_compute_pass(pass); + } else { + _sg_mtl_begin_render_pass(pass, atts); + } + + // bind uniform buffers, those bindings remain valid for the entire pass + if (_sg.cur_pass.valid) { + _sg_mtl_bind_uniform_buffers(); + } +} + +_SOKOL_PRIVATE void _sg_mtl_end_pass(const _sg_attachments_ptrs_t* atts) { + _SOKOL_UNUSED(atts); + if (nil != _sg.mtl.render_cmd_encoder) { + [_sg.mtl.render_cmd_encoder endEncoding]; + // NOTE: MTLRenderCommandEncoder is autoreleased + _sg.mtl.render_cmd_encoder = nil; + } + if (nil != _sg.mtl.compute_cmd_encoder) { + [_sg.mtl.compute_cmd_encoder endEncoding]; + // NOTE: MTLComputeCommandEncoder is autoreleased + _sg.mtl.compute_cmd_encoder = nil; + } + // if this is a swapchain pass, present the drawable + if (nil != _sg.mtl.cur_drawable) { + [_sg.mtl.cmd_buffer presentDrawable:_sg.mtl.cur_drawable]; + _sg.mtl.cur_drawable = nil; + } +} + +_SOKOL_PRIVATE void _sg_mtl_commit(void) { + SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + + // commit the frame's command buffer + [_sg.mtl.cmd_buffer commit]; + + // garbage-collect resources pending for release + _sg_mtl_garbage_collect(_sg.frame_index); + + // rotate uniform buffer slot + if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { + _sg.mtl.cur_frame_rotate_index = 0; + } + _sg.mtl.cur_ub_offset = 0; + _sg.mtl.cur_ub_base_ptr = 0; + // NOTE: MTLCommandBuffer is autoreleased + _sg.mtl.cmd_buffer = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.dim.height > 0); + MTLViewport vp; + vp.originX = (double) x; + vp.originY = (double) (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h))); + vp.width = (double) w; + vp.height = (double) h; + vp.znear = 0.0; + vp.zfar = 1.0; + [_sg.mtl.render_cmd_encoder setViewport:vp]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.dim.width > 0); + SOKOL_ASSERT(_sg.cur_pass.dim.height > 0); + // clip against framebuffer rect + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height); + MTLScissorRect r; + r.x = (NSUInteger)clip.x; + r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.cur_pass.dim.height - (clip.y + clip.h))); + r.width = (NSUInteger)clip.w; + r.height = (NSUInteger)clip.h; + [_sg.mtl.render_cmd_encoder setScissorRect:r]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_pip, &pip->slot)) { + _sg.mtl.cache.cur_pip = _sg_sref(&pip->slot); + if (pip->cmn.is_compute) { + SOKOL_ASSERT(_sg.cur_pass.is_compute); + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + SOKOL_ASSERT(pip->mtl.cps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.compute_cmd_encoder setComputePipelineState:_sg_mtl_id(pip->mtl.cps)]; + } else { + SOKOL_ASSERT(!_sg.cur_pass.is_compute); + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + sg_color c = pip->cmn.blend_color; + [_sg.mtl.render_cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; + _sg_stats_inc(metal.pipeline.num_set_blend_color); + [_sg.mtl.render_cmd_encoder setCullMode:pip->mtl.cull_mode]; + _sg_stats_inc(metal.pipeline.num_set_cull_mode); + [_sg.mtl.render_cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + _sg_stats_inc(metal.pipeline.num_set_front_facing_winding); + [_sg.mtl.render_cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + _sg_stats_inc(metal.pipeline.num_set_stencil_reference_value); + [_sg.mtl.render_cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp]; + _sg_stats_inc(metal.pipeline.num_set_depth_bias); + SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.render_cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + _sg_stats_inc(metal.pipeline.num_set_render_pipeline_state); + SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.render_cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; + _sg_stats_inc(metal.pipeline.num_set_depth_stencil_state); + } + } +} + +_SOKOL_PRIVATE int _sg_mtl_cache_buf_cmp(const _sg_mtl_cache_buf_t* item, const _sg_slot_t* slot, int active_slot, int offset) { + int res = _SG_MTL_CACHE_CMP_EQUAL; + if (!_sg_sref_slot_eql(&item->sref, slot)) { + res |= _SG_MTL_CACHE_CMP_SREF; + } + if (item->active_slot != active_slot) { + res |= _SG_MTL_CACHE_CMP_ACTIVESLOT; + } + if (item->offset != offset) { + res |= _SG_MTL_CACHE_CMP_OFFSET; + } + return res; +} + +_SOKOL_PRIVATE void _sg_mtl_cache_buf_upd(_sg_mtl_cache_buf_t* item, const _sg_slot_t* slot, int active_slot, int offset) { + item->sref = _sg_sref(slot); + item->offset = offset; + item->active_slot = active_slot; +} + +_SOKOL_PRIVATE int _sg_mtl_cache_tex_cmp(const _sg_mtl_cache_tex_t* item, const _sg_slot_t* slot, int active_slot) { + int res = _SG_MTL_CACHE_CMP_EQUAL; + if (!_sg_sref_slot_eql(&item->sref, slot)) { + res |= _SG_MTL_CACHE_CMP_SREF; + } + if (item->active_slot != active_slot) { + res |= _SG_MTL_CACHE_CMP_ACTIVESLOT; + } + return res; +} + +_SOKOL_PRIVATE void _sg_mtl_cache_tex_upd(_sg_mtl_cache_tex_t* item, const _sg_slot_t* slot, int active_slot) { + item->sref = _sg_sref(slot); + item->active_slot = active_slot; +} + + +_SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + + // don't set vertex- and index-buffers in compute passes + if (!_sg.cur_pass.is_compute) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + // store index buffer binding, this will be needed later in sg_draw() + _sg.mtl.cache.cur_ibuf = _sg_buffer_ref(bnd->ib); + _sg.mtl.cache.cur_ibuf_offset = bnd->ib_offset; + if (bnd->ib) { + SOKOL_ASSERT(bnd->pip->cmn.index_type != SG_INDEXTYPE_NONE); + } else { + SOKOL_ASSERT(bnd->pip->cmn.index_type == SG_INDEXTYPE_NONE); + } + // apply vertex buffers + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + const _sg_buffer_t* vb = bnd->vbs[i]; + if (vb == 0) { + continue; + } + const NSUInteger mtl_slot = _sg_mtl_vertexbuffer_bindslot(i); + SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_BUFFER_BINDINGS); + const int active_slot = vb->cmn.active_slot; + SOKOL_ASSERT(vb->mtl.buf[active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const int offset = bnd->vb_offsets[i]; + _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_vsbufs[i]; + const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &vb->slot, active_slot, offset); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_buf_upd(cache_item, &vb->slot, active_slot, offset); + if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) { + // only vertex buffer offset has changed + [_sg.mtl.render_cmd_encoder setVertexBufferOffset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_vertex_buffer_offset); + } else { + [_sg.mtl.render_cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_vertex_buffer); + } + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_buffer); + } + } + } + + // apply view bindings (textures, storage images, storage buffers) + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_view_t* view = bnd->views[i]; + if (0 == view) { + continue; + } + const _sg_shader_view_t* shd_view = &shd->cmn.views[i]; + const sg_shader_stage stage = shd_view->stage; + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) + || (stage == SG_SHADERSTAGE_FRAGMENT) + || (stage == SG_SHADERSTAGE_COMPUTE)); + SOKOL_ASSERT((shd_view->view_type == SG_VIEWTYPE_TEXTURE) + || (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER) + || (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE)); + const NSUInteger mtl_slot = shd->mtl.view_buffer_texture_n[i]; + + // same handling for textures and storage images + if ((shd_view->view_type == SG_VIEWTYPE_TEXTURE) || (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE)) { + SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS); + const int active_slot = _sg_image_ref_ptr(&view->cmn.img.ref)->cmn.active_slot; + SOKOL_ASSERT(view->mtl.tex_view[active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + if (stage == SG_SHADERSTAGE_VERTEX) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + _sg_mtl_cache_tex_t* cache_item = &_sg.mtl.cache.cur_vstexs[mtl_slot]; + const int cmp = _sg_mtl_cache_tex_cmp(cache_item, &view->slot, active_slot); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_tex_upd(cache_item, &view->slot, active_slot); + [_sg.mtl.render_cmd_encoder setVertexTexture:_sg_mtl_id(view->mtl.tex_view[active_slot]) atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_vertex_texture); + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_texture); + } + } else if (stage == SG_SHADERSTAGE_FRAGMENT) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + _sg_mtl_cache_tex_t* cache_item = &_sg.mtl.cache.cur_fstexs[mtl_slot]; + const int cmp = _sg_mtl_cache_tex_cmp(cache_item, &view->slot, active_slot); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_tex_upd(cache_item, &view->slot, active_slot); + [_sg.mtl.render_cmd_encoder setFragmentTexture:_sg_mtl_id(view->mtl.tex_view[active_slot]) atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_fragment_texture); + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_fragment_texture); + } + } else if (stage == SG_SHADERSTAGE_COMPUTE) { + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + _sg_mtl_cache_tex_t* cache_item = &_sg.mtl.cache.cur_cstexs[mtl_slot]; + const int cmp = _sg_mtl_cache_tex_cmp(cache_item, &view->slot, active_slot); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_tex_upd(cache_item, &view->slot, active_slot); + [_sg.mtl.compute_cmd_encoder setTexture:_sg_mtl_id(view->mtl.tex_view[active_slot]) atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_compute_texture); + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_compute_texture); + } + } else SOKOL_UNREACHABLE; + } else if (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER) { + SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS); + const _sg_buffer_t* sbuf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + const int active_slot = sbuf->cmn.active_slot; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const int offset = view->cmn.buf.offset; + if (stage == SG_SHADERSTAGE_VERTEX) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_vsbufs[mtl_slot]; + const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &sbuf->slot, active_slot, offset); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_buf_upd(cache_item, &sbuf->slot, active_slot, offset); + if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) { + // only offset has changed + [_sg.mtl.render_cmd_encoder setVertexBufferOffset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_vertex_buffer_offset); + } else { + [_sg.mtl.render_cmd_encoder setVertexBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_vertex_buffer); + } + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_buffer); + } + } else if (stage == SG_SHADERSTAGE_FRAGMENT) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_fsbufs[mtl_slot]; + const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &sbuf->slot, active_slot, offset); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_buf_upd(cache_item, &sbuf->slot, active_slot, offset); + if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) { + // only offset has changed + [_sg.mtl.render_cmd_encoder setFragmentBufferOffset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_fragment_buffer_offset); + } else { + [_sg.mtl.render_cmd_encoder setFragmentBuffer:_sg_mtl_id(sbuf->mtl.buf[active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_fragment_buffer); + } + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_fragment_buffer); + } + } else if (stage == SG_SHADERSTAGE_COMPUTE) { + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_csbufs[mtl_slot]; + const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &sbuf->slot, active_slot, offset); + if (cmp != _SG_MTL_CACHE_CMP_EQUAL) { + _sg_mtl_cache_buf_upd(cache_item, &sbuf->slot, active_slot, offset); + if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) { + // only offset has changed + [_sg.mtl.compute_cmd_encoder setBufferOffset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_compute_buffer_offset); + } else { + [_sg.mtl.compute_cmd_encoder setBuffer:_sg_mtl_id(sbuf->mtl.buf[active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_compute_buffer); + } + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_compute_buffer); + } + } + } else SOKOL_UNREACHABLE; + } + + // apply sampler bindings + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const _sg_sampler_t* smp = bnd->smps[i]; + if (smp == 0) { + continue; + } + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + const sg_shader_stage stage = shd->cmn.samplers[i].stage; + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) || (stage == SG_SHADERSTAGE_FRAGMENT) || (stage == SG_SHADERSTAGE_COMPUTE)); + const NSUInteger mtl_slot = shd->mtl.smp_sampler_n[i]; + SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_SAMPLER_BINDINGS); + if (stage == SG_SHADERSTAGE_VERTEX) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_vssmps[mtl_slot], &smp->slot)) { + _sg.mtl.cache.cur_vssmps[mtl_slot] = _sg_sref(&smp->slot); + [_sg.mtl.render_cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_vertex_sampler_state); + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_sampler_state); + } + } else if (stage == SG_SHADERSTAGE_FRAGMENT) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_fssmps[mtl_slot], &smp->slot)) { + _sg.mtl.cache.cur_fssmps[mtl_slot] = _sg_sref(&smp->slot); + [_sg.mtl.render_cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_fragment_sampler_state); + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_fragment_sampler_state); + } + } else if (stage == SG_SHADERSTAGE_COMPUTE) { + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_cssmps[mtl_slot], &smp->slot)) { + _sg.mtl.cache.cur_cssmps[mtl_slot] = _sg_sref(&smp->slot); + [_sg.mtl.compute_cmd_encoder setSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:mtl_slot]; + _sg_stats_inc(metal.bindings.num_set_compute_sampler_state); + } else { + _sg_stats_inc(metal.bindings.num_skip_redundant_compute_sampler_state); + } + } else SOKOL_UNREACHABLE; + } + return true; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(int ub_slot, const sg_range* data) { + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + SOKOL_ASSERT(pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size); + + const sg_shader_stage stage = shd->cmn.uniform_blocks[ub_slot].stage; + const NSUInteger mtl_slot = shd->mtl.ub_buffer_n[ub_slot]; + + // 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]; + memcpy(dst, data->ptr, data->size); + if (stage == SG_SHADERSTAGE_VERTEX) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + [_sg.mtl.render_cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:mtl_slot]; + _sg_stats_inc(metal.uniforms.num_set_vertex_buffer_offset); + } else if (stage == SG_SHADERSTAGE_FRAGMENT) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + [_sg.mtl.render_cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:mtl_slot]; + _sg_stats_inc(metal.uniforms.num_set_fragment_buffer_offset); + } else if (stage == SG_SHADERSTAGE_COMPUTE) { + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + [_sg.mtl.compute_cmd_encoder setBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:mtl_slot]; + _sg_stats_inc(metal.uniforms.num_set_compute_buffer_offset); + } else { + SOKOL_UNREACHABLE; + } + _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); +} + +_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + SOKOL_ASSERT(pip); + if (_sg.use_indexed_draw) { + // indexed rendering + const _sg_buffer_t* ib = _sg_buffer_ref_ptr(&_sg.mtl.cache.cur_ibuf); + SOKOL_ASSERT(ib && (ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX)); + const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.cache.cur_ibuf_offset + base_element * pip->mtl.index_size); + [_sg.mtl.render_cmd_encoder drawIndexedPrimitives:pip->mtl.prim_type + indexCount:(NSUInteger)num_elements + indexType:pip->mtl.index_type + indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) + indexBufferOffset:index_buffer_offset + instanceCount:(NSUInteger)num_instances + baseVertex:base_vertex + baseInstance:(NSUInteger)base_instance]; + } else { + // non-indexed rendering + [_sg.mtl.render_cmd_encoder drawPrimitives:pip->mtl.prim_type + vertexStart:(NSUInteger)base_element + vertexCount:(NSUInteger)num_elements + instanceCount:(NSUInteger)num_instances + baseInstance:(NSUInteger)base_instance]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder); + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + SOKOL_ASSERT(pip); + const MTLSize thread_groups = MTLSizeMake( + (NSUInteger)num_groups_x, + (NSUInteger)num_groups_y, + (NSUInteger)num_groups_z); + const MTLSize threads_per_threadgroup = pip->mtl.threads_per_threadgroup; + [_sg.mtl.compute_cmd_encoder dispatchThreadgroups:thread_groups threadsPerThreadgroup:threads_per_threadgroup]; +} + +_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + void* dst_ptr = [mtl_buf contents]; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; + dst_ptr += buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); + _sg_mtl_copy_image_data(img, mtl_tex, data); +} + +_SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) { + SOKOL_ASSERT(name); + if (_sg.mtl.render_cmd_encoder) { + [_sg.mtl.render_cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]]; + } else if (_sg.mtl.compute_cmd_encoder) { + [_sg.mtl.compute_cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { + if (_sg.mtl.render_cmd_encoder) { + [_sg.mtl.render_cmd_encoder popDebugGroup]; + } else if (_sg.mtl.compute_cmd_encoder) { + [_sg.mtl.compute_cmd_encoder popDebugGroup]; + } +} + +// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>webgpu +// >>wgpu +#elif defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUStringView _sg_wgpu_stringview(const char* str) { + WGPUStringView res; + if (str) { + res.data = str; + res.length = strlen(str); + } else { + res.data = 0; + res.length = 0; + } + return res; +} + +_SOKOL_PRIVATE WGPUOptionalBool _sg_wgpu_optional_bool(bool b) { + return b ? WGPUOptionalBool_True : WGPUOptionalBool_False; +} + +_SOKOL_PRIVATE WGPUBufferUsage _sg_wgpu_buffer_usage(const sg_buffer_usage* usg) { + int res = 0; + if (usg->vertex_buffer) { + res |= (int)WGPUBufferUsage_Vertex; + } + if (usg->index_buffer) { + res |= (int)WGPUBufferUsage_Index; + } + if (usg->storage_buffer) { + res |= (int)WGPUBufferUsage_Storage; + } + if (!usg->immutable) { + res |= (int)WGPUBufferUsage_CopyDst; + } + return (WGPUBufferUsage)res; +} + +_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(WGPUTextureView view, sg_load_action a) { + if (0 == view) { + return WGPULoadOp_Undefined; + } else switch (a) { + case SG_LOADACTION_CLEAR: + case SG_LOADACTION_DONTCARE: + return WGPULoadOp_Clear; + case SG_LOADACTION_LOAD: + return WGPULoadOp_Load; + default: + SOKOL_UNREACHABLE; + return WGPULoadOp_Force32; + } +} + +_SOKOL_PRIVATE WGPUStoreOp _sg_wgpu_store_op(WGPUTextureView view, sg_store_action a) { + if (0 == view) { + return WGPUStoreOp_Undefined; + } else switch (a) { + case SG_STOREACTION_STORE: + return WGPUStoreOp_Store; + case SG_STOREACTION_DONTCARE: + return WGPUStoreOp_Discard; + default: + SOKOL_UNREACHABLE; + return WGPUStoreOp_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_texture_view_dimension(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_attachment_view_dimension(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_2DArray; // not a bug + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_2D; // not a bug + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_texture_dimension(sg_image_type t) { + if (SG_IMAGETYPE_3D == t) { + return WGPUTextureDimension_3D; + } else { + return WGPUTextureDimension_2D; + } +} + +_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t, bool msaa) { + switch (t) { + case SG_IMAGESAMPLETYPE_FLOAT: return msaa ? WGPUTextureSampleType_UnfilterableFloat : WGPUTextureSampleType_Float; + case SG_IMAGESAMPLETYPE_DEPTH: return WGPUTextureSampleType_Depth; + case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureSampleType_Sint; + case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureSampleType_Uint; + case SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT: return WGPUTextureSampleType_UnfilterableFloat; + default: SOKOL_UNREACHABLE; return WGPUTextureSampleType_Force32; + } +} + +_SOKOL_PRIVATE WGPUSamplerBindingType _sg_wgpu_sampler_binding_type(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FILTERING: return WGPUSamplerBindingType_Filtering; + case SG_SAMPLERTYPE_COMPARISON: return WGPUSamplerBindingType_Comparison; + case SG_SAMPLERTYPE_NONFILTERING: return WGPUSamplerBindingType_NonFiltering; + default: SOKOL_UNREACHABLE; return WGPUSamplerBindingType_Force32; + } +} + +_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: + return WGPUAddressMode_Repeat; + case SG_WRAP_CLAMP_TO_EDGE: + case SG_WRAP_CLAMP_TO_BORDER: + return WGPUAddressMode_ClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: + return WGPUAddressMode_MirrorRepeat; + default: + SOKOL_UNREACHABLE; + return WGPUAddressMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return WGPUFilterMode_Nearest; + case SG_FILTER_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return WGPUMipmapFilterMode_Nearest; + case SG_FILTER_LINEAR: + return WGPUMipmapFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUMipmapFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { + // NOTE: there's no WGPUIndexFormat_None + return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_stripindexformat(sg_primitive_type prim_type, sg_index_type idx_type) { + if (idx_type == SG_INDEXTYPE_NONE) { + return WGPUIndexFormat_Undefined; + } else if ((prim_type == SG_PRIMITIVETYPE_LINE_STRIP) || (prim_type == SG_PRIMITIVETYPE_TRIANGLE_STRIP)) { + return _sg_wgpu_indexformat(idx_type); + } else { + return WGPUIndexFormat_Undefined; + } +} + +_SOKOL_PRIVATE WGPUVertexStepMode _sg_wgpu_stepmode(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUVertexStepMode_Vertex : WGPUVertexStepMode_Instance; +} + +_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float32; + case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float32x2; + case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float32x3; + case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float32x4; + case SG_VERTEXFORMAT_INT: return WGPUVertexFormat_Sint32; + case SG_VERTEXFORMAT_INT2: return WGPUVertexFormat_Sint32x2; + case SG_VERTEXFORMAT_INT3: return WGPUVertexFormat_Sint32x3; + case SG_VERTEXFORMAT_INT4: return WGPUVertexFormat_Sint32x4; + case SG_VERTEXFORMAT_UINT: return WGPUVertexFormat_Uint32; + case SG_VERTEXFORMAT_UINT2: return WGPUVertexFormat_Uint32x2; + case SG_VERTEXFORMAT_UINT3: return WGPUVertexFormat_Uint32x3; + case SG_VERTEXFORMAT_UINT4: return WGPUVertexFormat_Uint32x4; + case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Sint8x4; + case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Snorm8x4; + case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_Uint8x4; + case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_Unorm8x4; + case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Sint16x2; + case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Snorm16x2; + case SG_VERTEXFORMAT_USHORT2: return WGPUVertexFormat_Uint16x2; + case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_Unorm16x2; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Sint16x4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Snorm16x4; + case SG_VERTEXFORMAT_USHORT4: return WGPUVertexFormat_Uint16x4; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_Unorm16x4; + case SG_VERTEXFORMAT_UINT10_N2: return WGPUVertexFormat_Unorm10_10_10_2; + case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Float16x2; + case SG_VERTEXFORMAT_HALF4: return WGPUVertexFormat_Float16x4; + default: + SOKOL_UNREACHABLE; + return WGPUVertexFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; + case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; + case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; + default: + SOKOL_UNREACHABLE; + return WGPUPrimitiveTopology_Force32; + } +} + +_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; +} + +_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return WGPUCullMode_None; + case SG_CULLMODE_FRONT: return WGPUCullMode_Front; + case SG_CULLMODE_BACK: return WGPUCullMode_Back; + default: + SOKOL_UNREACHABLE; + return WGPUCullMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { + switch (p) { + case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; + case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; + case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; + case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; + case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; + case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; + case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; + case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; + case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; + case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; + case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; + case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; + case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; + case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; + case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; + case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; + case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; + case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; + case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return WGPUTextureFormat_RGBA8UnormSrgb; + case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Ufloat; + case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat; + case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; + case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; + case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; + case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth32FloatStencil8; + case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; + case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; + case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC3_SRGBA: return WGPUTextureFormat_BC3RGBAUnormSrgb; + case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; + case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return WGPUTextureFormat_BC7RGBAUnormSrgb; + case SG_PIXELFORMAT_ETC2_RGB8: return WGPUTextureFormat_ETC2RGB8Unorm; + case SG_PIXELFORMAT_ETC2_RGB8A1: return WGPUTextureFormat_ETC2RGB8A1Unorm; + case SG_PIXELFORMAT_ETC2_RGBA8: return WGPUTextureFormat_ETC2RGBA8Unorm; + case SG_PIXELFORMAT_ETC2_SRGB8: return WGPUTextureFormat_ETC2RGB8UnormSrgb; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return WGPUTextureFormat_ETC2RGBA8UnormSrgb; + case SG_PIXELFORMAT_EAC_R11: return WGPUTextureFormat_EACR11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return WGPUTextureFormat_EACRG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return WGPUTextureFormat_EACRG11Snorm; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return WGPUTextureFormat_ASTC4x4Unorm; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return WGPUTextureFormat_ASTC4x4UnormSrgb; + // NOT SUPPORTED + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + return WGPUTextureFormat_Undefined; + + default: + SOKOL_UNREACHABLE; + return WGPUTextureFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; + case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; + case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; + case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; + case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; + default: + SOKOL_UNREACHABLE; + return WGPUCompareFunction_Force32; + } +} + +_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; + case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; + case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; + case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; + case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; + case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; + case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; + default: + SOKOL_UNREACHABLE; + return WGPUStencilOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; + case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; + case SG_BLENDOP_MIN: return WGPUBlendOperation_Min; + case SG_BLENDOP_MAX: return WGPUBlendOperation_Max; + default: + SOKOL_UNREACHABLE; + return WGPUBlendOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; + case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; + case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_Src; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrc; + case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; + case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_Dst; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDst; + case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusConstant; + // FIXME: separate blend alpha value not supported? + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusConstant; + case SG_BLENDFACTOR_SRC1_COLOR: return WGPUBlendFactor_Src1 ; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return WGPUBlendFactor_OneMinusSrc1; + case SG_BLENDFACTOR_SRC1_ALPHA: return WGPUBlendFactor_Src1Alpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return WGPUBlendFactor_OneMinusSrc1Alpha; + default: + SOKOL_UNREACHABLE; + return WGPUBlendFactor_Force32; + } +} + +_SOKOL_PRIVATE WGPUColorWriteMask _sg_wgpu_colorwritemask(sg_color_mask m) { + int res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= (int)WGPUColorWriteMask_Red; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= (int)WGPUColorWriteMask_Green; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= (int)WGPUColorWriteMask_Blue; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= (int)WGPUColorWriteMask_Alpha; + } + return (WGPUColorWriteMask)res; +} + +_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VERTEX: return WGPUShaderStage_Vertex; + case SG_SHADERSTAGE_FRAGMENT: return WGPUShaderStage_Fragment; + case SG_SHADERSTAGE_COMPUTE: return WGPUShaderStage_Compute; + default: SOKOL_UNREACHABLE; return WGPUShaderStage_None; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { + _sg.backend = SG_BACKEND_WGPU; + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.compute = true; + _sg.features.msaa_texture_bindings = true; + _sg.features.draw_base_vertex = true; + _sg.features.draw_base_instance = true; + _sg.features.dual_source_blending = wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_DualSourceBlending); + + wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); + + const WGPULimits* l = &_sg.wgpu.limits; + _sg.limits.max_image_size_2d = (int) l->maxTextureDimension2D; + _sg.limits.max_image_size_cube = (int) l->maxTextureDimension2D; // not a bug, see: https://github.com/gpuweb/gpuweb/issues/1327 + _sg.limits.max_image_size_3d = (int) l->maxTextureDimension3D; + _sg.limits.max_image_size_array = (int) l->maxTextureDimension2D; + _sg.limits.max_image_array_layers = (int) l->maxTextureArrayLayers; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + _sg.limits.max_color_attachments = _sg_min((int)l->maxColorAttachments, SG_MAX_COLOR_ATTACHMENTS); + _sg.limits.max_texture_bindings_per_stage = _sg_min((int)l->maxSampledTexturesPerShaderStage, SG_MAX_VIEW_BINDSLOTS); + _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min((int)l->maxStorageBuffersPerShaderStage, SG_MAX_VIEW_BINDSLOTS); + _sg.limits.max_storage_image_bindings_per_stage = _sg_min((int)l->maxStorageTexturesPerShaderStage, SG_MAX_VIEW_BINDSLOTS); + + // NOTE: no WGPUTextureFormat_R16Unorm + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + + // FIXME: can be made renderable via extension + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + + // NOTE: msaa rendering is possible in WebGPU, but no resolve + // which is a combination that's not currently supported in sokol-gfx + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Filterable)) { + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionBC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + } + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionETC2)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + } + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionASTC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } + + // see: https://github.com/gpuweb/gpuweb/issues/513 + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_system_init(const sg_desc* desc) { + SOKOL_ASSERT(0 == _sg.wgpu.uniform.staging); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.buf); + + // Add the max-uniform-update size (64 KB) to the requested buffer size, + // this is to prevent validation errors in the WebGPU implementation + // if the entire buffer size is used per frame. 64 KB is the allowed + // max uniform update size on NVIDIA + // + // FIXME: is this still needed? + _sg.wgpu.uniform.num_bytes = (uint32_t)(desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + _sg.wgpu.uniform.staging = (uint8_t*)_sg_malloc(_sg.wgpu.uniform.num_bytes); + + _SG_STRUCT(WGPUBufferDescriptor, ub_desc); + ub_desc.size = _sg.wgpu.uniform.num_bytes; + ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; + _sg.wgpu.uniform.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.buf); +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_system_discard(void) { + if (_sg.wgpu.uniform.buf) { + wgpuBufferRelease(_sg.wgpu.uniform.buf); + _sg.wgpu.uniform.buf = 0; + } + if (_sg.wgpu.uniform.staging) { + _sg_free(_sg.wgpu.uniform.staging); + _sg.wgpu.uniform.staging = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_system_set_bindgroup(void) { + SOKOL_ASSERT(_sg.wgpu.uniform.dirty); + _sg.wgpu.uniform.dirty = false; + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + // NOTE: dynamic offsets must be in binding order, not in BindGroupEntry order + SOKOL_ASSERT(shd->wgpu.ub_num_dynoffsets < SG_MAX_UNIFORMBLOCK_BINDSLOTS); + _SG_STRUCT(uint32_t, dyn_offsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS]); + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + uint8_t dynoffset_index = shd->wgpu.ub_dynoffsets[i]; + SOKOL_ASSERT(dynoffset_index < shd->wgpu.ub_num_dynoffsets); + dyn_offsets[dynoffset_index] = _sg.wgpu.uniform.bind_offsets[i]; + } + if (_sg.cur_pass.is_compute) { + SOKOL_ASSERT(_sg.wgpu.cpass_enc); + wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, + _SG_WGPU_UB_BINDGROUP_INDEX, + shd->wgpu.bg_ub, + shd->wgpu.ub_num_dynoffsets, + dyn_offsets); + } else { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, + _SG_WGPU_UB_BINDGROUP_INDEX, + shd->wgpu.bg_ub, + shd->wgpu.ub_num_dynoffsets, + dyn_offsets); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_system_on_apply_pipeline(void) { + _sg.wgpu.uniform.dirty = false; +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_system_on_commit(void) { + wgpuQueueWriteBuffer(_sg.wgpu.queue, _sg.wgpu.uniform.buf, 0, _sg.wgpu.uniform.staging, _sg.wgpu.uniform.offset); + _sg_stats_add(wgpu.uniforms.size_write_buffer, _sg.wgpu.uniform.offset); + _sg.wgpu.uniform.offset = 0; + _sg_clear(_sg.wgpu.uniform.bind_offsets, sizeof(_sg.wgpu.uniform.bind_offsets)); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_init(const sg_desc* desc) { + SOKOL_ASSERT((desc->wgpu.bindgroups_cache_size > 0) && (desc->wgpu.bindgroups_cache_size < _SG_MAX_POOL_SIZE)); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + SOKOL_ASSERT(0 == p->bindgroups); + const int pool_size = desc->wgpu.bindgroups_cache_size; + _sg_pool_init(&p->pool, pool_size); + size_t pool_byte_size = sizeof(_sg_wgpu_bindgroup_t) * (size_t)p->pool.size; + p->bindgroups = (_sg_wgpu_bindgroup_t*) _sg_malloc_clear(pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_discard(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + SOKOL_ASSERT(p->bindgroups); + _sg_free(p->bindgroups); p->bindgroups = 0; + _sg_pool_discard(&p->pool); +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_bindgroup_at(uint32_t bg_id) { + SOKOL_ASSERT(SG_INVALID_ID != bg_id); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + int slot_index = _sg_slot_index(bg_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->bindgroups[slot_index]; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_lookup_bindgroup(uint32_t bg_id) { + if (SG_INVALID_ID != bg_id) { + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id); + if (bg->slot.id == bg_id) { + return bg; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_handle_t _sg_wgpu_alloc_bindgroup(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + _sg_wgpu_bindgroup_handle_t res; + int slot_index = _sg_pool_alloc_index(&p->pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&p->pool, &p->bindgroups[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(WGPU_BINDGROUPS_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_dealloc_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC) && (bg->slot.id != SG_INVALID_ID)); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + _sg_pool_free_index(&p->pool, _sg_slot_index(bg->slot.id)); + _sg_slot_reset(&bg->slot); +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_bindgroup_to_alloc_state(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_slot_t slot = bg->slot; + _sg_clear(bg, sizeof(_sg_wgpu_bindgroup_t)); + bg->slot = slot; + bg->slot.state = SG_RESOURCESTATE_ALLOC; +} + +// MurmurHash64B (see: https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L142) +_SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) { + const uint32_t m = 0x5bd1e995; + const int r = 24; + uint32_t h1 = (uint32_t)seed ^ (uint32_t)len; + uint32_t h2 = (uint32_t)(seed >> 32); + const uint32_t * data = (const uint32_t *)key; + while (len >= 8) { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + uint32_t k2 = *data++; + k2 *= m; k2 ^= k2 >> r; k2 *= m; + h2 *= m; h2 ^= k2; + len -= 4; + } + if (len >= 4) { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + } + switch(len) { + case 3: h2 ^= (uint32_t)(((unsigned char*)data)[2] << 16); + // fall through + case 2: h2 ^= (uint32_t)(((unsigned char*)data)[1] << 8); + // fall through + case 1: h2 ^= ((unsigned char*)data)[0]; + // fall through + h2 *= m; + }; + h1 ^= h2 >> 18; h1 *= m; + h2 ^= h1 >> 22; h2 *= m; + h1 ^= h2 >> 17; h1 *= m; + h2 ^= h1 >> 19; h2 *= m; + uint64_t h = h1; + h = (h << 32) | h2; + return h; +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_item(_sg_wgpu_bindgroups_cache_item_type_t type, uint8_t wgpu_binding, uint32_t id, uint32_t uninit_count) { + const uint64_t bb = wgpu_binding; + const uint64_t t = type & 3; + const uint64_t ccccc = uninit_count & ((1 << 22) - 1); + const uint64_t iiiiiiii = id; + return (bb << 56) | (t << 54) | (ccccc << 32) | iiiiiiii; +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_pip_item(const _sg_slot_t* slot) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, 0xFF, slot->id, slot->uninit_count); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_view_item(uint8_t wgpu_binding, const _sg_slot_t* slot) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_VIEW, wgpu_binding, slot->id, slot->uninit_count); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sampler_item(uint8_t wgpu_binding, const _sg_slot_t* slot) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, wgpu_binding, slot->id, slot->uninit_count); +} + +_SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + + _sg_clear(key->items, sizeof(key->items)); + key->items[0] = _sg_wgpu_bindgroups_cache_pip_item(&bnd->pip->slot); + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + SOKOL_ASSERT(bnd->views[i]); + const size_t item_idx = i + 1; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + SOKOL_ASSERT(0 == key->items[item_idx]); + const uint8_t wgpu_binding = shd->wgpu.view_grp1_bnd_n[i]; + key->items[item_idx] = _sg_wgpu_bindgroups_cache_view_item(wgpu_binding, &bnd->views[i]->slot); + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + SOKOL_ASSERT(bnd->smps[i]); + const size_t item_idx = i + 1 + SG_MAX_VIEW_BINDSLOTS; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + SOKOL_ASSERT(0 == key->items[item_idx]); + const uint8_t wgpu_binding = shd->wgpu.smp_grp1_bnd_n[i]; + key->items[item_idx] = _sg_wgpu_bindgroups_cache_sampler_item(wgpu_binding, &bnd->smps[i]->slot); + } + key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321); +} + +_SOKOL_PRIVATE bool _sg_wgpu_compare_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* k0, _sg_wgpu_bindgroups_cache_key_t* k1) { + SOKOL_ASSERT(k0 && k1); + if (k0->hash != k1->hash) { + return false; + } + if (memcmp(&k0->items, &k1->items, sizeof(k0->items)) != 0) { + _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch); + return false; + } + return true; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(bnd->pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + _sg_stats_inc(wgpu.bindings.num_create_bindgroup); + _sg_wgpu_bindgroup_handle_t bg_id = _sg_wgpu_alloc_bindgroup(); + if (bg_id.id == SG_INVALID_ID) { + return 0; + } + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id.id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC)); + + // create wgpu bindgroup object (also see _sg_wgpu_create_shader()) + WGPUBindGroupLayout bgl = shd->wgpu.bgl_view_smp; + SOKOL_ASSERT(bgl); + _SG_STRUCT(WGPUBindGroupEntry, bg_entries[_SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES]); + size_t bgl_index = 0; + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const _sg_view_t* view = bnd->views[i]; + SOKOL_ASSERT(view); + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES); + WGPUBindGroupEntry* bg_entry = &bg_entries[bgl_index]; + bg_entry->binding = shd->wgpu.view_grp1_bnd_n[i]; + if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + SOKOL_ASSERT(buf->wgpu.buf); + SOKOL_ASSERT(view->cmn.buf.offset < buf->cmn.size); + bg_entry->buffer = buf->wgpu.buf; + bg_entry->offset = (uint64_t)view->cmn.buf.offset; + bg_entry->size = (uint64_t)(buf->cmn.size - view->cmn.buf.offset); + } else { + SOKOL_ASSERT(view->wgpu.view); + bg_entry->textureView = view->wgpu.view; + } + bgl_index += 1; + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + SOKOL_ASSERT(bnd->smps[i]); + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES); + WGPUBindGroupEntry* bg_entry = &bg_entries[bgl_index]; + bg_entry->binding = shd->wgpu.smp_grp1_bnd_n[i]; + bg_entry->sampler = bnd->smps[i]->wgpu.smp; + bgl_index += 1; + } + _SG_STRUCT(WGPUBindGroupDescriptor, bg_desc); + bg_desc.layout = bgl; + bg_desc.entryCount = bgl_index; + bg_desc.entries = bg_entries; + bg->bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + if (bg->bindgroup == 0) { + _SG_ERROR(WGPU_CREATEBINDGROUP_FAILED); + bg->slot.state = SG_RESOURCESTATE_FAILED; + return bg; + } + _sg_wgpu_init_bindgroups_cache_key(&bg->key, bnd); + bg->slot.state = SG_RESOURCESTATE_VALID; + return bg; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_stats_inc(wgpu.bindings.num_discard_bindgroup); + if (bg->slot.state == SG_RESOURCESTATE_VALID) { + if (bg->bindgroup) { + wgpuBindGroupRelease(bg->bindgroup); + bg->bindgroup = 0; + } + _sg_wgpu_reset_bindgroup_to_alloc_state(bg); + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (bg->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_wgpu_dealloc_bindgroup(bg); + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_INITIAL); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_all_bindgroups(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + for (int i = 0; i < p->pool.size; i++) { + sg_resource_state state = p->bindgroups[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_wgpu_discard_bindgroup(&p->bindgroups[i]); + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_init(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.num == 0); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.index_mask == 0); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items == 0); + const int num = desc->wgpu.bindgroups_cache_size; + if (num <= 1) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE); + } + if (!_sg_ispow2(num)) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_POW2); + } + _sg.wgpu.bindgroups_cache.num = (uint32_t)desc->wgpu.bindgroups_cache_size; + _sg.wgpu.bindgroups_cache.index_mask = _sg.wgpu.bindgroups_cache.num - 1; + size_t size_in_bytes = sizeof(_sg_wgpu_bindgroup_handle_t) * (size_t)num; + _sg.wgpu.bindgroups_cache.items = (_sg_wgpu_bindgroup_handle_t*)_sg_malloc_clear(size_in_bytes); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_discard(void) { + if (_sg.wgpu.bindgroups_cache.items) { + _sg_free(_sg.wgpu.bindgroups_cache.items); + _sg.wgpu.bindgroups_cache.items = 0; + } + _sg.wgpu.bindgroups_cache.num = 0; + _sg.wgpu.bindgroups_cache.index_mask = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_set(uint64_t hash, uint32_t bg_id) { + uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask; + SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + _sg.wgpu.bindgroups_cache.items[index].id = bg_id; +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) { + uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask; + SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + return _sg.wgpu.bindgroups_cache.items[index].id; +} + +// called from wgpu resource destroy functions to also invalidate any +// bindgroups cache slot and bindgroup referencing that resource +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_invalidate(_sg_wgpu_bindgroups_cache_item_type_t type, const _sg_slot_t* slot) { + const uint64_t key_mask = _sg_wgpu_bindgroups_cache_item(type, 0xFF, 0xFFFFFFFF, 0xFFFFFFFF); + const uint64_t key_item = _sg_wgpu_bindgroups_cache_item(type, 0, slot->id, slot->uninit_count) & key_mask; + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + for (uint32_t cache_item_idx = 0; cache_item_idx < _sg.wgpu.bindgroups_cache.num; cache_item_idx++) { + const uint32_t bg_id = _sg.wgpu.bindgroups_cache.items[cache_item_idx].id; + if (bg_id != SG_INVALID_ID) { + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_lookup_bindgroup(bg_id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID)); + // check if resource is in bindgroup, if yes discard bindgroup and invalidate cache slot + bool invalidate_cache_item = false; + for (int key_item_idx = 0; key_item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS; key_item_idx++) { + if ((bg->key.items[key_item_idx] & key_mask) == key_item) { + invalidate_cache_item = true; + break; + } + } + if (invalidate_cache_item) { + _sg_wgpu_discard_bindgroup(bg); bg = 0; + _sg_wgpu_bindgroups_cache_set(cache_item_idx, SG_INVALID_ID); + _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_invalidates); + } + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) { + memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache)); +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_vb_dirty(size_t index, const _sg_buffer_t* vb, uint64_t offset) { + SOKOL_ASSERT(index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + if (vb) { + return (_sg.wgpu.bindings_cache.vbs[index].buffer.id != vb->slot.id) + || (_sg.wgpu.bindings_cache.vbs[index].offset != offset); + } else { + return _sg.wgpu.bindings_cache.vbs[index].buffer.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_vb_update(size_t index, const _sg_buffer_t* vb, uint64_t offset) { + SOKOL_ASSERT(index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + if (vb) { + _sg.wgpu.bindings_cache.vbs[index].buffer.id = vb->slot.id; + _sg.wgpu.bindings_cache.vbs[index].offset = offset; + } else { + _sg.wgpu.bindings_cache.vbs[index].buffer.id = SG_INVALID_ID; + _sg.wgpu.bindings_cache.vbs[index].offset = 0; + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_ib_dirty(const _sg_buffer_t* ib, uint64_t offset) { + if (ib) { + return (_sg.wgpu.bindings_cache.ib.buffer.id != ib->slot.id) + || (_sg.wgpu.bindings_cache.ib.offset != offset); + } else { + return _sg.wgpu.bindings_cache.ib.buffer.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_ib_update(const _sg_buffer_t* ib, uint64_t offset) { + if (ib) { + _sg.wgpu.bindings_cache.ib.buffer.id = ib->slot.id; + _sg.wgpu.bindings_cache.ib.offset = offset; + } else { + _sg.wgpu.bindings_cache.ib.buffer.id = SG_INVALID_ID; + _sg.wgpu.bindings_cache.ib.offset = 0; + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_bg_dirty(const _sg_wgpu_bindgroup_t* bg) { + if (bg) { + return _sg.wgpu.bindings_cache.bg.id != bg->slot.id; + } else { + return _sg.wgpu.bindings_cache.bg.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t* bg) { + if (bg) { + _sg.wgpu.bindings_cache.bg.id = bg->slot.id; + } else { + _sg.wgpu.bindings_cache.bg.id = SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(uint32_t bg_idx, _sg_wgpu_bindgroup_t* bg) { + if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { + _sg_wgpu_bindings_cache_bg_update(bg); + _sg_stats_inc(wgpu.bindings.num_set_bindgroup); + if (_sg.cur_pass.is_compute) { + SOKOL_ASSERT(_sg.wgpu.cpass_enc); + if (bg) { + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(bg->bindgroup); + wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, bg_idx, bg->bindgroup, 0, 0); + } else { + wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, bg_idx, 0, 0, 0); + } + } else { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + if (bg) { + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(bg->bindgroup); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, bg_idx, bg->bindgroup, 0, 0); + } else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, bg_idx, 0, 0, 0); + } + } + } else { + _sg_stats_inc(wgpu.bindings.num_skip_redundant_bindgroup); + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings_bindgroup(_sg_bindings_ptrs_t* bnd) { + if (!_sg.desc.wgpu.disable_bindgroups_cache) { + _sg_wgpu_bindgroup_t* bg = 0; + _sg_wgpu_bindgroups_cache_key_t key; + _sg_wgpu_init_bindgroups_cache_key(&key, bnd); + uint32_t bg_id = _sg_wgpu_bindgroups_cache_get(key.hash); + if (bg_id != SG_INVALID_ID) { + // potential cache hit + bg = _sg_wgpu_lookup_bindgroup(bg_id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID)); + if (!_sg_wgpu_compare_bindgroups_cache_key(&key, &bg->key)) { + // cache collision, need to delete cached bindgroup + _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_collisions); + _sg_wgpu_discard_bindgroup(bg); + _sg_wgpu_bindgroups_cache_set(key.hash, SG_INVALID_ID); + bg = 0; + } else { + _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_hits); + } + } else { + _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_misses); + } + if (bg == 0) { + // either no cache entry yet, or cache collision, create new bindgroup and store in cache + bg = _sg_wgpu_create_bindgroup(bnd); + _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id); + } + if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) { + _sg_wgpu_set_bindgroup(_SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, bg); + } else { + return false; + } + } else { + // bindgroups cache disabled, create and destroy bindgroup on the fly (expensive!) + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd); + if (bg) { + if (bg->slot.state == SG_RESOURCESTATE_VALID) { + _sg_wgpu_set_bindgroup(_SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, bg); + } + _sg_wgpu_discard_bindgroup(bg); + } else { + return false; + } + } + return true; +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + const _sg_buffer_t* ib = bnd->ib; + uint64_t offset = (uint64_t)bnd->ib_offset; + if (_sg_wgpu_bindings_cache_ib_dirty(ib, offset)) { + _sg_wgpu_bindings_cache_ib_update(ib, offset); + if (ib) { + const WGPUIndexFormat format = _sg_wgpu_indexformat(bnd->pip->cmn.index_type); + const uint64_t buf_size = (uint64_t)ib->cmn.size; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.rpass_enc, ib->wgpu.buf, format, offset, max_bytes); + /* + NOTE: as per webgpu spec setIndexBuffer does not accept a null pointer + } else { + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.rpass_enc, 0, WGPUIndexFormat_Undefined, 0, 0); + */ + } + _sg_stats_inc(wgpu.bindings.num_set_index_buffer); + } else { + _sg_stats_inc(wgpu.bindings.num_skip_redundant_index_buffer); + } + return true; +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_vertex_buffers(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + for (uint32_t slot = 0; slot < SG_MAX_VERTEXBUFFER_BINDSLOTS; slot++) { + const _sg_buffer_t* vb = bnd->vbs[slot]; + const uint64_t offset = (uint64_t)bnd->vb_offsets[slot]; + if (_sg_wgpu_bindings_cache_vb_dirty(slot, vb, offset)) { + _sg_wgpu_bindings_cache_vb_update(slot, vb, offset); + if (vb) { + const uint64_t buf_size = (uint64_t)vb->cmn.size; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.rpass_enc, slot, vb->wgpu.buf, offset, max_bytes); + } else { + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.rpass_enc, slot, 0, 0, 0); + } + _sg_stats_inc(wgpu.bindings.num_set_vertex_buffer); + } else { + _sg_stats_inc(wgpu.bindings.num_skip_redundant_vertex_buffer); + } + } + return true; +} + +_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.wgpu.device); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg.wgpu.valid = true; + _sg.wgpu.dev = (WGPUDevice) desc->environment.wgpu.device; + _sg.wgpu.queue = wgpuDeviceGetQueue(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.queue); + + _sg_wgpu_init_caps(); + _sg_wgpu_uniform_system_init(desc); + _sg_wgpu_bindgroups_pool_init(desc); + _sg_wgpu_bindgroups_cache_init(desc); + _sg_wgpu_bindings_cache_clear(); +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { + SOKOL_ASSERT(_sg.wgpu.valid); + _sg.wgpu.valid = false; + _sg_wgpu_discard_all_bindgroups(); + _sg_wgpu_bindgroups_cache_discard(); + _sg_wgpu_bindgroups_pool_discard(); + _sg_wgpu_uniform_system_discard(); + // the command encoder is usually released in sg_commit() + if (_sg.wgpu.cmd_enc) { + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); _sg.wgpu.cmd_enc = 0; + } + wgpuQueueRelease(_sg.wgpu.queue); _sg.wgpu.queue = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { + _sg_wgpu_bindings_cache_clear(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(buf->cmn.size > 0); + const bool injected = (0 != desc->wgpu_buffer); + if (injected) { + buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; + wgpuBufferAddRef(buf->wgpu.buf); + } else { + // buffer mapping size must be multiple of 4, so round up buffer size (only a problem + // with index buffers containing odd number of indices) + const uint64_t wgpu_buf_size = _sg_roundup_u64((uint64_t)buf->cmn.size, 4); + const bool map_at_creation = buf->cmn.usage.immutable && (desc->data.ptr); + + _SG_STRUCT(WGPUBufferDescriptor, wgpu_buf_desc); + wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(&buf->cmn.usage); + wgpu_buf_desc.size = wgpu_buf_size; + wgpu_buf_desc.mappedAtCreation = map_at_creation; + wgpu_buf_desc.label = _sg_wgpu_stringview(desc->label); + buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + if (0 == buf->wgpu.buf) { + _SG_ERROR(WGPU_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (map_at_creation) { + SOKOL_ASSERT(desc->data.ptr && (desc->data.size > 0)); + SOKOL_ASSERT(desc->data.size <= (size_t)buf->cmn.size); + // FIXME: inefficient on WASM + void* ptr = wgpuBufferGetMappedRange(buf->wgpu.buf, 0, wgpu_buf_size); + SOKOL_ASSERT(ptr); + memcpy(ptr, desc->data.ptr, desc->data.size); + wgpuBufferUnmap(buf->wgpu.buf); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->wgpu.buf) { + wgpuBufferRelease(buf->wgpu.buf); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_buffer_data(const _sg_buffer_t* buf, uint64_t offset, const sg_range* data) { + SOKOL_ASSERT((offset + data->size) <= (size_t)buf->cmn.size); + // WebGPU's write-buffer requires the size to be a multiple of four, so we may need to split the copy + // operation into two writeBuffer calls + uint64_t clamped_size = data->size & ~3UL; + uint64_t extra_size = data->size & 3UL; + SOKOL_ASSERT(extra_size < 4); + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, offset, data->ptr, clamped_size); + if (extra_size > 0) { + const uint64_t extra_src_offset = clamped_size; + const uint64_t extra_dst_offset = offset + clamped_size; + uint8_t extra_data[4] = { 0 }; + const uint8_t* extra_src_ptr = ((uint8_t*)data->ptr) + extra_src_offset; + for (size_t i = 0; i < extra_size; i++) { + extra_data[i] = extra_src_ptr[i]; + } + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, extra_dst_offset, extra_data, 4); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, const sg_image_data* data) { + _SG_STRUCT(WGPUTexelCopyBufferLayout, wgpu_layout); + _SG_STRUCT(WGPUTexelCopyTextureInfo, wgpu_copy_tex); + wgpu_copy_tex.texture = img->wgpu.tex; + wgpu_copy_tex.aspect = WGPUTextureAspect_All; + _SG_STRUCT(WGPUExtent3D, wgpu_extent); + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + wgpu_copy_tex.mipLevel = (uint32_t)mip_index; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + int mip_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + const int row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int num_rows = _sg_num_rows(img->cmn.pixel_format, mip_height); + if (_sg_is_compressed_pixel_format(img->cmn.pixel_format)) { + mip_width = _sg_roundup(mip_width, 4); + mip_height = _sg_roundup(mip_height, 4); + } + wgpu_layout.bytesPerRow = (uint32_t)row_pitch; + wgpu_layout.rowsPerImage = (uint32_t)num_rows; + wgpu_extent.width = (uint32_t)mip_width; + wgpu_extent.height = (uint32_t)mip_height; + wgpu_extent.depthOrArrayLayers = (uint32_t)mip_slices; + const sg_range* mip_data = &data->mip_levels[mip_index]; + wgpuQueueWriteTexture(_sg.wgpu.queue, &wgpu_copy_tex, mip_data->ptr, mip_data->size, &wgpu_layout, &wgpu_extent); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + const bool injected = (0 != desc->wgpu_texture); + if (injected) { + img->wgpu.tex = (WGPUTexture)desc->wgpu_texture; + wgpuTextureAddRef(img->wgpu.tex); + } else { + _SG_STRUCT(WGPUTextureDescriptor, wgpu_tex_desc); + wgpu_tex_desc.label = _sg_wgpu_stringview(desc->label); + wgpu_tex_desc.usage = WGPUTextureUsage_TextureBinding|WGPUTextureUsage_CopyDst; + if (desc->usage.color_attachment || desc->usage.resolve_attachment || desc->usage.depth_stencil_attachment) { + wgpu_tex_desc.usage |= WGPUTextureUsage_RenderAttachment; + } + if (desc->usage.storage_image) { + wgpu_tex_desc.usage |= WGPUTextureUsage_StorageBinding; + } + wgpu_tex_desc.dimension = _sg_wgpu_texture_dimension(img->cmn.type); + wgpu_tex_desc.size.width = (uint32_t) img->cmn.width; + wgpu_tex_desc.size.height = (uint32_t) img->cmn.height; + wgpu_tex_desc.size.depthOrArrayLayers = (uint32_t) img->cmn.num_slices; + wgpu_tex_desc.format = _sg_wgpu_textureformat(img->cmn.pixel_format); + wgpu_tex_desc.mipLevelCount = (uint32_t) img->cmn.num_mipmaps; + wgpu_tex_desc.sampleCount = (uint32_t) img->cmn.sample_count; + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + if (0 == img->wgpu.tex) { + _SG_ERROR(WGPU_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (desc->data.mip_levels[0].ptr) { + _sg_wgpu_copy_image_data(img, &desc->data); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->wgpu.tex) { + wgpuTextureRelease(img->wgpu.tex); + img->wgpu.tex = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(_sg.wgpu.dev); + const bool injected = (0 != desc->wgpu_sampler); + if (injected) { + smp->wgpu.smp = (WGPUSampler) desc->wgpu_sampler; + wgpuSamplerAddRef(smp->wgpu.smp); + } else { + _SG_STRUCT(WGPUSamplerDescriptor, wgpu_desc); + wgpu_desc.label = _sg_wgpu_stringview(desc->label); + wgpu_desc.addressModeU = _sg_wgpu_sampler_address_mode(desc->wrap_u); + wgpu_desc.addressModeV = _sg_wgpu_sampler_address_mode(desc->wrap_v); + wgpu_desc.addressModeW = _sg_wgpu_sampler_address_mode(desc->wrap_w); + wgpu_desc.magFilter = _sg_wgpu_sampler_minmag_filter(desc->mag_filter); + wgpu_desc.minFilter = _sg_wgpu_sampler_minmag_filter(desc->min_filter); + wgpu_desc.mipmapFilter = _sg_wgpu_sampler_mipmap_filter(desc->mipmap_filter); + wgpu_desc.lodMinClamp = desc->min_lod; + wgpu_desc.lodMaxClamp = desc->max_lod; + wgpu_desc.compare = _sg_wgpu_comparefunc(desc->compare); + if (wgpu_desc.compare == WGPUCompareFunction_Never) { + wgpu_desc.compare = WGPUCompareFunction_Undefined; + } + wgpu_desc.maxAnisotropy = (uint16_t)desc->max_anisotropy; + smp->wgpu.smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &wgpu_desc); + if (0 == smp->wgpu.smp) { + _SG_ERROR(WGPU_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, &smp->slot); + if (smp->wgpu.smp) { + wgpuSamplerRelease(smp->wgpu.smp); + smp->wgpu.smp = 0; + } +} + +_SOKOL_PRIVATE _sg_wgpu_shader_func_t _sg_wgpu_create_shader_func(const sg_shader_function* func, const char* label) { + SOKOL_ASSERT(func); + SOKOL_ASSERT(func->source); + SOKOL_ASSERT(func->entry); + + _SG_STRUCT(_sg_wgpu_shader_func_t, res); + _sg_strcpy(&res.entry, func->entry); + + _SG_STRUCT(WGPUShaderSourceWGSL, wgpu_shdsrc_wgsl); + wgpu_shdsrc_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL; + wgpu_shdsrc_wgsl.code = _sg_wgpu_stringview(func->source); + + _SG_STRUCT(WGPUShaderModuleDescriptor, wgpu_shdmod_desc); + wgpu_shdmod_desc.nextInChain = &wgpu_shdsrc_wgsl.chain; + wgpu_shdmod_desc.label = _sg_wgpu_stringview(label); + + // NOTE: if compilation fails we won't actually find out in this call since + // it always returns a valid module handle, and the GetCompilationInfo() call + // is asynchronous + res.module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); + if (0 == res.module) { + _SG_ERROR(WGPU_CREATE_SHADER_MODULE_FAILED); + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_shader_func(_sg_wgpu_shader_func_t* func) { + if (func->module) { + wgpuShaderModuleRelease(func->module); + func->module = 0; + } +} + +typedef struct { uint8_t sokol_slot, wgpu_slot; } _sg_wgpu_dynoffset_mapping_t; + +_SOKOL_PRIVATE int _sg_wgpu_dynoffset_cmp(const void* a, const void* b) { + const _sg_wgpu_dynoffset_mapping_t* aa = (const _sg_wgpu_dynoffset_mapping_t*)a; + const _sg_wgpu_dynoffset_mapping_t* bb = (const _sg_wgpu_dynoffset_mapping_t*)b; + if (aa->wgpu_slot < bb->wgpu_slot) return -1; + else if (aa->wgpu_slot > bb->wgpu_slot) return 1; + return 0; +} + +// NOTE: this is an out-of-range check for WGSL bindslots that's also active in release mode +_SOKOL_PRIVATE bool _sg_wgpu_ensure_wgsl_bindslot_ranges(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* ub = &desc->uniform_blocks[i]; + if (ub->stage != SG_SHADERSTAGE_NONE) { + if (ub->wgsl_group0_binding_n >= _SG_WGPU_MAX_UB_BINDGROUP_WGSL_SLOTS) { + _SG_ERROR(WGPU_UNIFORMBLOCK_WGSL_GROUP0_BINDING_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + if (view->texture.stage != SG_SHADERSTAGE_NONE) { + if (view->texture.wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) { + _SG_ERROR(WGPU_TEXTURE_WGSL_GROUP1_BINDING_OUT_OF_RANGE); + return false; + } + } + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_buffer.wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) { + _SG_ERROR(WGPU_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE); + return false; + } + } + if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_image.wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) { + _SG_ERROR(WGPU_STORAGEIMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const sg_shader_sampler* smp = &desc->samplers[i]; + if (smp->stage != SG_SHADERSTAGE_NONE) { + if (smp->wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) { + _SG_ERROR(WGPU_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE); + return false; + } + } + } + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(shd->wgpu.vertex_func.module == 0); + SOKOL_ASSERT(shd->wgpu.fragment_func.module == 0); + SOKOL_ASSERT(shd->wgpu.compute_func.module == 0); + SOKOL_ASSERT(shd->wgpu.bgl_ub == 0); + SOKOL_ASSERT(shd->wgpu.bg_ub == 0); + SOKOL_ASSERT(shd->wgpu.bgl_view_smp == 0); + + // do a release-mode bounds-check on wgsl bindslots, even though out-of-range + // bindslots can't cause out-of-bounds accesses in the wgpu backend, this + // is done to be consistent with the other backends + if (!_sg_wgpu_ensure_wgsl_bindslot_ranges(desc)) { + return SG_RESOURCESTATE_FAILED; + } + + // build shader modules + bool shd_valid = true; + if (desc->vertex_func.source) { + shd->wgpu.vertex_func = _sg_wgpu_create_shader_func(&desc->vertex_func, desc->label); + shd_valid &= shd->wgpu.vertex_func.module != 0; + } + if (desc->fragment_func.source) { + shd->wgpu.fragment_func = _sg_wgpu_create_shader_func(&desc->fragment_func, desc->label); + shd_valid &= shd->wgpu.fragment_func.module != 0; + } + if (desc->compute_func.source) { + shd->wgpu.compute_func = _sg_wgpu_create_shader_func(&desc->compute_func, desc->label); + shd_valid &= shd->wgpu.compute_func.module != 0; + } + if (!shd_valid) { + _sg_wgpu_discard_shader_func(&shd->wgpu.vertex_func); + _sg_wgpu_discard_shader_func(&shd->wgpu.fragment_func); + _sg_wgpu_discard_shader_func(&shd->wgpu.compute_func); + return SG_RESOURCESTATE_FAILED; + } + + // create bind group layout and bind group for uniform blocks + // NOTE also need to create a mapping of sokol ub bind slots to array indices + // for the dynamic offsets array in the setBindGroup call + SOKOL_ASSERT(_SG_WGPU_MAX_UB_BINDGROUP_ENTRIES <= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES); + _SG_STRUCT(WGPUBindGroupLayoutEntry, bgl_entries[_SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES]); + _SG_STRUCT(WGPUBindGroupLayoutDescriptor, bgl_desc); + _SG_STRUCT(WGPUBindGroupEntry, bg_entries[_SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES]); + _SG_STRUCT(WGPUBindGroupDescriptor, bg_desc); + _SG_STRUCT(_sg_wgpu_dynoffset_mapping_t, dynoffset_map[SG_MAX_UNIFORMBLOCK_BINDSLOTS]); + size_t bgl_index = 0; + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + shd->wgpu.ub_grp0_bnd_n[i] = desc->uniform_blocks[i].wgsl_group0_binding_n; + WGPUBindGroupEntry* bg_entry = &bg_entries[bgl_index]; + WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index]; + bgl_entry->binding = shd->wgpu.ub_grp0_bnd_n[i]; + bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.uniform_blocks[i].stage); + bgl_entry->buffer.type = WGPUBufferBindingType_Uniform; + bgl_entry->buffer.hasDynamicOffset = true; + bg_entry->binding = bgl_entry->binding; + bg_entry->buffer = _sg.wgpu.uniform.buf; + bg_entry->size = _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + dynoffset_map[i].sokol_slot = (uint8_t)i; + dynoffset_map[i].wgpu_slot = (uint8_t)bgl_entry->binding; + bgl_index += 1; + } + bgl_desc.entryCount = bgl_index; + bgl_desc.entries = bgl_entries; + shd->wgpu.bgl_ub = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + SOKOL_ASSERT(shd->wgpu.bgl_ub); + bg_desc.layout = shd->wgpu.bgl_ub; + bg_desc.entryCount = bgl_index; + bg_desc.entries = bg_entries; + shd->wgpu.bg_ub = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(shd->wgpu.bg_ub); + + // sort the dynoffset_map by wgpu bindings, this is because the + // dynamic offsets of the WebGPU setBindGroup call must be in + // 'binding order', not 'bindgroup entry order' + qsort(dynoffset_map, bgl_index, sizeof(_sg_wgpu_dynoffset_mapping_t), _sg_wgpu_dynoffset_cmp); + shd->wgpu.ub_num_dynoffsets = (uint8_t)bgl_index; + for (uint8_t i = 0; i < bgl_index; i++) { + const uint8_t sokol_slot = dynoffset_map[i].sokol_slot; + shd->wgpu.ub_dynoffsets[sokol_slot] = i; + } + + // create bind group layout for textures, storage buffers/images and samplers + _sg_clear(bgl_entries, sizeof(bgl_entries)); + _sg_clear(&bgl_desc, sizeof(bgl_desc)); + bgl_index = 0; + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index]; + bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.views[i].stage); + if (shd->cmn.views[i].view_type == SG_VIEWTYPE_TEXTURE) { + shd->wgpu.view_grp1_bnd_n[i] = desc->views[i].texture.wgsl_group1_binding_n; + const bool msaa = shd->cmn.views[i].multisampled; + bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(shd->cmn.views[i].image_type); + bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(shd->cmn.views[i].sample_type, msaa); + bgl_entry->texture.multisampled = msaa; + } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) { + shd->wgpu.view_grp1_bnd_n[i] = desc->views[i].storage_buffer.wgsl_group1_binding_n; + if (shd->cmn.views[i].sbuf_readonly) { + bgl_entry->buffer.type = WGPUBufferBindingType_ReadOnlyStorage; + } else { + bgl_entry->buffer.type = WGPUBufferBindingType_Storage; + } + } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEIMAGE) { + shd->wgpu.view_grp1_bnd_n[i] = desc->views[i].storage_image.wgsl_group1_binding_n; + if (shd->cmn.views[i].simg_writeonly) { + bgl_entry->storageTexture.access = WGPUStorageTextureAccess_WriteOnly; + } else { + bgl_entry->storageTexture.access = WGPUStorageTextureAccess_ReadWrite; + } + bgl_entry->storageTexture.format = _sg_wgpu_textureformat(shd->cmn.views[i].access_format); + bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(shd->cmn.views[i].image_type); + } else { + SOKOL_UNREACHABLE; + } + bgl_entry->binding = shd->wgpu.view_grp1_bnd_n[i]; + bgl_index += 1; + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + shd->wgpu.smp_grp1_bnd_n[i] = desc->samplers[i].wgsl_group1_binding_n; + WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index]; + bgl_entry->binding = shd->wgpu.smp_grp1_bnd_n[i]; + bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.samplers[i].stage); + bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(shd->cmn.samplers[i].sampler_type); + bgl_index += 1; + } + bgl_desc.entryCount = bgl_index; + bgl_desc.entries = bgl_entries; + shd->wgpu.bgl_view_smp = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + if (shd->wgpu.bgl_view_smp == 0) { + _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_wgpu_discard_shader_func(&shd->wgpu.vertex_func); + _sg_wgpu_discard_shader_func(&shd->wgpu.fragment_func); + _sg_wgpu_discard_shader_func(&shd->wgpu.compute_func); + if (shd->wgpu.bgl_ub) { + wgpuBindGroupLayoutRelease(shd->wgpu.bgl_ub); + shd->wgpu.bgl_ub = 0; + } + if (shd->wgpu.bg_ub) { + wgpuBindGroupRelease(shd->wgpu.bg_ub); + shd->wgpu.bg_ub = 0; + } + if (shd->wgpu.bgl_view_smp) { + wgpuBindGroupLayoutRelease(shd->wgpu.bgl_view_smp); + shd->wgpu.bgl_view_smp = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(shd->wgpu.bgl_ub); + SOKOL_ASSERT(shd->wgpu.bgl_view_smp); + + pip->wgpu.blend_color.r = (double) desc->blend_color.r; + pip->wgpu.blend_color.g = (double) desc->blend_color.g; + pip->wgpu.blend_color.b = (double) desc->blend_color.b; + pip->wgpu.blend_color.a = (double) desc->blend_color.a; + + // - @group(0) for uniform blocks + // - @group(1) for all image, sampler and storagebuffer resources + size_t num_bgls = 2; + _SG_STRUCT(WGPUBindGroupLayout, wgpu_bgl[_SG_WGPU_MAX_BINDGROUPS]); + wgpu_bgl[_SG_WGPU_UB_BINDGROUP_INDEX ] = shd->wgpu.bgl_ub; + wgpu_bgl[_SG_WGPU_VIEW_SMP_BINDGROUP_INDEX] = shd->wgpu.bgl_view_smp; + _SG_STRUCT(WGPUPipelineLayoutDescriptor, wgpu_pl_desc); + wgpu_pl_desc.bindGroupLayoutCount = num_bgls; + wgpu_pl_desc.bindGroupLayouts = &wgpu_bgl[0]; + const WGPUPipelineLayout wgpu_pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &wgpu_pl_desc); + if (0 == wgpu_pip_layout) { + _SG_ERROR(WGPU_CREATE_PIPELINE_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(wgpu_pip_layout); + + if (pip->cmn.is_compute) { + _SG_STRUCT(WGPUComputePipelineDescriptor, wgpu_pip_desc); + wgpu_pip_desc.label = _sg_wgpu_stringview(desc->label); + wgpu_pip_desc.layout = wgpu_pip_layout; + wgpu_pip_desc.compute.module = shd->wgpu.compute_func.module; + wgpu_pip_desc.compute.entryPoint = _sg_wgpu_stringview(shd->wgpu.compute_func.entry.buf); + pip->wgpu.cpip = wgpuDeviceCreateComputePipeline(_sg.wgpu.dev, &wgpu_pip_desc); + wgpuPipelineLayoutRelease(wgpu_pip_layout); + if (0 == pip->wgpu.cpip) { + _SG_ERROR(WGPU_CREATE_COMPUTE_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } else { + _SG_STRUCT(WGPUVertexBufferLayout, wgpu_vb_layouts[SG_MAX_VERTEXBUFFER_BINDSLOTS]); + _SG_STRUCT(WGPUVertexAttribute, wgpu_vtx_attrs[SG_MAX_VERTEXBUFFER_BINDSLOTS][SG_MAX_VERTEX_ATTRIBUTES]); + int wgpu_vb_num = 0; + for (int vb_idx = 0; vb_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vb_idx++, wgpu_vb_num++) { + const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vb_idx]; + if (0 == vbl_state->stride) { + break; + } + wgpu_vb_layouts[vb_idx].arrayStride = (uint64_t)vbl_state->stride; + wgpu_vb_layouts[vb_idx].stepMode = _sg_wgpu_stepmode(vbl_state->step_func); + wgpu_vb_layouts[vb_idx].attributes = &wgpu_vtx_attrs[vb_idx][0]; + } + for (int va_idx = 0; va_idx < SG_MAX_VERTEX_ATTRIBUTES; va_idx++) { + const sg_vertex_attr_state* va_state = &desc->layout.attrs[va_idx]; + if (SG_VERTEXFORMAT_INVALID == va_state->format) { + break; + } + const int vb_idx = va_state->buffer_index; + SOKOL_ASSERT(vb_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS); + SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[vb_idx]); + const size_t wgpu_attr_idx = wgpu_vb_layouts[vb_idx].attributeCount; + wgpu_vb_layouts[vb_idx].attributeCount += 1; + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].format = _sg_wgpu_vertexformat(va_state->format); + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].offset = (uint64_t)va_state->offset; + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].shaderLocation = (uint32_t)va_idx; + } + + _SG_STRUCT(WGPURenderPipelineDescriptor, wgpu_pip_desc); + _SG_STRUCT(WGPUDepthStencilState, wgpu_ds_state); + _SG_STRUCT(WGPUFragmentState, wgpu_frag_state); + _SG_STRUCT(WGPUColorTargetState, wgpu_ctgt_state[SG_MAX_COLOR_ATTACHMENTS]); + _SG_STRUCT(WGPUBlendState, wgpu_blend_state[SG_MAX_COLOR_ATTACHMENTS]); + wgpu_pip_desc.label = _sg_wgpu_stringview(desc->label); + wgpu_pip_desc.layout = wgpu_pip_layout; + wgpu_pip_desc.vertex.module = shd->wgpu.vertex_func.module; + wgpu_pip_desc.vertex.entryPoint = _sg_wgpu_stringview(shd->wgpu.vertex_func.entry.buf); + wgpu_pip_desc.vertex.bufferCount = (size_t)wgpu_vb_num; + wgpu_pip_desc.vertex.buffers = &wgpu_vb_layouts[0]; + wgpu_pip_desc.primitive.topology = _sg_wgpu_topology(desc->primitive_type); + wgpu_pip_desc.primitive.stripIndexFormat = _sg_wgpu_stripindexformat(desc->primitive_type, desc->index_type); + wgpu_pip_desc.primitive.frontFace = _sg_wgpu_frontface(desc->face_winding); + wgpu_pip_desc.primitive.cullMode = _sg_wgpu_cullmode(desc->cull_mode); + if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { + wgpu_ds_state.format = _sg_wgpu_textureformat(desc->depth.pixel_format); + wgpu_ds_state.depthWriteEnabled = _sg_wgpu_optional_bool(desc->depth.write_enabled); + wgpu_ds_state.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); + wgpu_ds_state.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); + wgpu_ds_state.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); + wgpu_ds_state.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); + wgpu_ds_state.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); + wgpu_ds_state.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); + wgpu_ds_state.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); + wgpu_ds_state.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); + wgpu_ds_state.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); + wgpu_ds_state.stencilReadMask = desc->stencil.read_mask; + wgpu_ds_state.stencilWriteMask = desc->stencil.write_mask; + wgpu_ds_state.depthBias = (int32_t)desc->depth.bias; + wgpu_ds_state.depthBiasSlopeScale = desc->depth.bias_slope_scale; + wgpu_ds_state.depthBiasClamp = desc->depth.bias_clamp; + wgpu_pip_desc.depthStencil = &wgpu_ds_state; + } + wgpu_pip_desc.multisample.count = (uint32_t)desc->sample_count; + wgpu_pip_desc.multisample.mask = 0xFFFFFFFF; + wgpu_pip_desc.multisample.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + if (desc->color_count > 0) { + wgpu_frag_state.module = shd->wgpu.fragment_func.module; + wgpu_frag_state.entryPoint = _sg_wgpu_stringview(shd->wgpu.fragment_func.entry.buf); + wgpu_frag_state.targetCount = (size_t)desc->color_count; + wgpu_frag_state.targets = &wgpu_ctgt_state[0]; + for (int i = 0; i < desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + wgpu_ctgt_state[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); + wgpu_ctgt_state[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); + if (desc->colors[i].blend.enabled) { + wgpu_ctgt_state[i].blend = &wgpu_blend_state[i]; + wgpu_blend_state[i].color.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); + wgpu_blend_state[i].color.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); + wgpu_blend_state[i].color.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); + wgpu_blend_state[i].alpha.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); + wgpu_blend_state[i].alpha.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); + wgpu_blend_state[i].alpha.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); + } + } + wgpu_pip_desc.fragment = &wgpu_frag_state; + } + pip->wgpu.rpip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &wgpu_pip_desc); + wgpuPipelineLayoutRelease(wgpu_pip_layout); + if (0 == pip->wgpu.rpip) { + _SG_ERROR(WGPU_CREATE_RENDER_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, &pip->slot); + if (pip->wgpu.rpip) { + wgpuRenderPipelineRelease(pip->wgpu.rpip); + pip->wgpu.rpip = 0; + } + if (pip->wgpu.cpip) { + wgpuComputePipelineRelease(pip->wgpu.cpip); + pip->wgpu.cpip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && desc); + if (view->cmn.type != SG_VIEWTYPE_STORAGEBUFFER) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + SOKOL_ASSERT(img->wgpu.tex); + SOKOL_ASSERT(view->cmn.img.mip_level_count >= 1); + SOKOL_ASSERT(view->cmn.img.slice_count >= 1); + _SG_STRUCT(WGPUTextureViewDescriptor, wgpu_texview_desc); + wgpu_texview_desc.label = _sg_wgpu_stringview(desc->label); + wgpu_texview_desc.baseMipLevel = (uint32_t)view->cmn.img.mip_level; + wgpu_texview_desc.mipLevelCount = (uint32_t)view->cmn.img.mip_level_count; + wgpu_texview_desc.baseArrayLayer = (uint32_t)view->cmn.img.slice; + wgpu_texview_desc.arrayLayerCount = (uint32_t)view->cmn.img.slice_count; + if (view->cmn.type == SG_VIEWTYPE_TEXTURE) { + wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type); + } else { + wgpu_texview_desc.dimension = _sg_wgpu_attachment_view_dimension(img->cmn.type); + } + if (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT) { + wgpu_texview_desc.aspect = WGPUTextureAspect_All; + } else if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + wgpu_texview_desc.aspect = WGPUTextureAspect_DepthOnly; + } else { + wgpu_texview_desc.aspect = WGPUTextureAspect_All; + } + view->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc); + if (0 == view->wgpu.view) { + _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_view(_sg_view_t* view) { + SOKOL_ASSERT(view); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_VIEW, &view->slot); + if (view->wgpu.view) { + wgpuTextureViewRelease(view->wgpu.view); + view->wgpu.view = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_color_att(WGPURenderPassColorAttachment* wgpu_att, const sg_color_attachment_action* action, WGPUTextureView color_view, WGPUTextureView resolve_view) { + wgpu_att->depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + wgpu_att->view = color_view; + wgpu_att->resolveTarget = resolve_view; + wgpu_att->loadOp = _sg_wgpu_load_op(color_view, action->load_action); + wgpu_att->storeOp = _sg_wgpu_store_op(color_view, action->store_action); + wgpu_att->clearValue.r = action->clear_value.r; + wgpu_att->clearValue.g = action->clear_value.g; + wgpu_att->clearValue.b = action->clear_value.b; + wgpu_att->clearValue.a = action->clear_value.a; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_ds_att(WGPURenderPassDepthStencilAttachment* wgpu_att, const sg_pass_action* action, sg_pixel_format fmt, WGPUTextureView view) { + wgpu_att->view = view; + wgpu_att->depthLoadOp = _sg_wgpu_load_op(view, action->depth.load_action); + wgpu_att->depthStoreOp = _sg_wgpu_store_op(view, action->depth.store_action); + wgpu_att->depthClearValue = action->depth.clear_value; + wgpu_att->depthReadOnly = false; + if (_sg_is_depth_stencil_format(fmt)) { + wgpu_att->stencilLoadOp = _sg_wgpu_load_op(view, action->stencil.load_action); + wgpu_att->stencilStoreOp = _sg_wgpu_store_op(view, action->stencil.store_action); + } else { + wgpu_att->stencilLoadOp = WGPULoadOp_Undefined; + wgpu_att->stencilStoreOp = WGPUStoreOp_Undefined; + } + wgpu_att->stencilClearValue = action->stencil.clear_value; + wgpu_att->stencilReadOnly = false; +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_compute_pass(const sg_pass* pass) { + _SG_STRUCT(WGPUComputePassDescriptor, wgpu_pass_desc); + wgpu_pass_desc.label = _sg_wgpu_stringview(pass->label); + _sg.wgpu.cpass_enc = wgpuCommandEncoderBeginComputePass(_sg.wgpu.cmd_enc, &wgpu_pass_desc); + SOKOL_ASSERT(_sg.wgpu.cpass_enc); + // clear initial bindings + wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, _SG_WGPU_UB_BINDGROUP_INDEX, 0, 0, 0); + wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, _SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, 0, 0, 0); + _sg_stats_inc(wgpu.bindings.num_set_bindgroup); +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_render_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + _SG_STRUCT(WGPURenderPassDescriptor, wgpu_pass_desc); + _SG_STRUCT(WGPURenderPassColorAttachment, wgpu_color_att[SG_MAX_COLOR_ATTACHMENTS]); + _SG_STRUCT(WGPURenderPassDepthStencilAttachment, wgpu_ds_att); + wgpu_pass_desc.label = _sg_wgpu_stringview(pass->label); + if (!atts->empty) { + SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < atts->num_color_views; i++) { + SOKOL_ASSERT(atts->color_views[i]); + WGPUTextureView wgpu_color_view = atts->color_views[i]->wgpu.view; + WGPUTextureView wgpu_resolve_view = 0; + if (atts->resolve_views[i]) { + wgpu_resolve_view = atts->resolve_views[i]->wgpu.view; + } + _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], wgpu_color_view, wgpu_resolve_view); + } + wgpu_pass_desc.colorAttachmentCount = (size_t)atts->num_color_views; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; + if (atts->ds_view) { + const _sg_image_t* img = _sg_image_ref_ptr(&atts->ds_view->cmn.img.ref); + WGPUTextureView wgpu_ds_view = atts->ds_view->wgpu.view; + SOKOL_ASSERT(wgpu_ds_view); + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, img->cmn.pixel_format, wgpu_ds_view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; + } + } else { + WGPUTextureView wgpu_color_view = (WGPUTextureView) swapchain->wgpu.render_view; + WGPUTextureView wgpu_resolve_view = (WGPUTextureView) swapchain->wgpu.resolve_view; + WGPUTextureView wgpu_depth_stencil_view = (WGPUTextureView) swapchain->wgpu.depth_stencil_view; + _sg_wgpu_init_color_att(&wgpu_color_att[0], &action->colors[0], wgpu_color_view, wgpu_resolve_view); + wgpu_pass_desc.colorAttachmentCount = 1; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; + if (wgpu_depth_stencil_view) { + SOKOL_ASSERT(swapchain->depth_format > SG_PIXELFORMAT_NONE); + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, swapchain->depth_format, wgpu_depth_stencil_view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; + } + } + _sg.wgpu.rpass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.cmd_enc, &wgpu_pass_desc); + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, _SG_WGPU_UB_BINDGROUP_INDEX, 0, 0, 0); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, _SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, 0, 0, 0); + _sg_stats_inc(wgpu.bindings.num_set_bindgroup); +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(pass && atts); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(0 == _sg.wgpu.rpass_enc); + SOKOL_ASSERT(0 == _sg.wgpu.cpass_enc); + + // first pass in the frame? create command encoder + if (0 == _sg.wgpu.cmd_enc) { + _SG_STRUCT(WGPUCommandEncoderDescriptor, cmd_enc_desc); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + } + + _sg_wgpu_bindings_cache_clear(); + if (pass->compute) { + _sg_wgpu_begin_compute_pass(pass); + } else { + _sg_wgpu_begin_render_pass(pass, atts); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_end_pass(const _sg_attachments_ptrs_t* atts) { + _SOKOL_UNUSED(atts); + if (_sg.wgpu.rpass_enc) { + wgpuRenderPassEncoderEnd(_sg.wgpu.rpass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.rpass_enc); + _sg.wgpu.rpass_enc = 0; + } + if (_sg.wgpu.cpass_enc) { + wgpuComputePassEncoderEnd(_sg.wgpu.cpass_enc); + wgpuComputePassEncoderRelease(_sg.wgpu.cpass_enc); + _sg.wgpu.cpass_enc = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_commit(void) { + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + + _sg_wgpu_uniform_system_on_commit(); + + _SG_STRUCT(WGPUCommandBufferDescriptor, cmd_buf_desc); + WGPUCommandBuffer wgpu_cmd_buf = wgpuCommandEncoderFinish(_sg.wgpu.cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(wgpu_cmd_buf); + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); + _sg.wgpu.cmd_enc = 0; + + wgpuQueueSubmit(_sg.wgpu.queue, 1, &wgpu_cmd_buf); + wgpuCommandBufferRelease(wgpu_cmd_buf); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + float xf = (float) x; + float yf = (float) (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h))); + float wf = (float) w; + float hf = (float) h; + wgpuRenderPassEncoderSetViewport(_sg.wgpu.rpass_enc, xf, yf, wf, hf, 0.0f, 1.0f); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height); + uint32_t sx = (uint32_t) clip.x; + uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.cur_pass.dim.height - (clip.y + clip.h))); + uint32_t sw = (uint32_t) clip.w; + uint32_t sh = (uint32_t) clip.h; + wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.rpass_enc, sx, sy, sw, sh); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_wgpu_uniform_system_on_apply_pipeline(); + if (pip->cmn.is_compute) { + SOKOL_ASSERT(_sg.cur_pass.is_compute); + SOKOL_ASSERT(pip->wgpu.cpip); + SOKOL_ASSERT(_sg.wgpu.cpass_enc); + wgpuComputePassEncoderSetPipeline(_sg.wgpu.cpass_enc, pip->wgpu.cpip); + } else { + SOKOL_ASSERT(!_sg.cur_pass.is_compute); + SOKOL_ASSERT(pip->wgpu.rpip); + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + wgpuRenderPassEncoderSetPipeline(_sg.wgpu.rpass_enc, pip->wgpu.rpip); + wgpuRenderPassEncoderSetBlendConstant(_sg.wgpu.rpass_enc, &pip->wgpu.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.rpass_enc, pip->cmn.stencil.ref); + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd); + bool retval = true; + if (!_sg.cur_pass.is_compute) { + retval &= _sg_wgpu_apply_index_buffer(bnd); + retval &= _sg_wgpu_apply_vertex_buffers(bnd); + } + retval &= _sg_wgpu_apply_bindings_bindgroup(bnd); + return retval; +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(int ub_slot, const sg_range* data) { + const uint32_t alignment = _sg.wgpu.limits.minUniformBufferOffsetAlignment; + SOKOL_ASSERT(_sg.wgpu.uniform.staging); + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + SOKOL_ASSERT((_sg.wgpu.uniform.offset + data->size) <= _sg.wgpu.uniform.num_bytes); + SOKOL_ASSERT((_sg.wgpu.uniform.offset & (alignment - 1)) == 0); + SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + + _sg_stats_inc(wgpu.uniforms.num_set_bindgroup); + memcpy(_sg.wgpu.uniform.staging + _sg.wgpu.uniform.offset, data->ptr, data->size); + _sg.wgpu.uniform.bind_offsets[ub_slot] = _sg.wgpu.uniform.offset; + _sg.wgpu.uniform.offset = _sg_roundup_u32(_sg.wgpu.uniform.offset + (uint32_t)data->size, alignment); + _sg.wgpu.uniform.dirty = true; +} + +_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + SOKOL_ASSERT(_sg.wgpu.rpass_enc); + if (_sg.wgpu.uniform.dirty) { + _sg_wgpu_uniform_system_set_bindgroup(); + } + if (_sg.use_indexed_draw) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.rpass_enc, + (uint32_t)num_elements, + (uint32_t)num_instances, + (uint32_t)base_element, + base_vertex, + (uint32_t)base_instance); + } else { + wgpuRenderPassEncoderDraw(_sg.wgpu.rpass_enc, + (uint32_t)num_elements, + (uint32_t)num_instances, + (uint32_t)base_element, + (uint32_t)base_instance); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + SOKOL_ASSERT(_sg.wgpu.cpass_enc); + if (_sg.wgpu.uniform.dirty) { + _sg_wgpu_uniform_system_set_bindgroup(); + } + wgpuComputePassEncoderDispatchWorkgroups(_sg.wgpu.cpass_enc, + (uint32_t)num_groups_x, + (uint32_t)num_groups_y, + (uint32_t)num_groups_z); +} + +_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _sg_wgpu_copy_buffer_data(buf, 0, data); +} + +_SOKOL_PRIVATE void _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(new_frame); + _sg_wgpu_copy_buffer_data(buf, (uint64_t)buf->cmn.append_pos, data); +} + +_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _sg_wgpu_copy_image_data(img, data); +} + +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ █████ ███████ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██████ ███████ ██ ██ ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>vulkan +// >>vk +#elif defined(SOKOL_VULKAN) + +_SOKOL_PRIVATE void _sg_vk_set_object_label(VkObjectType obj_type, uint64_t obj_handle, const char* label) { + #if defined(SOKOL_DEBUG) + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.ext.set_debug_utils_object_name_ext); + SOKOL_ASSERT(obj_handle != 0); + if (label) { + _SG_STRUCT(VkDebugUtilsObjectNameInfoEXT, name_info); + name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + name_info.objectType = obj_type; + name_info.objectHandle = obj_handle, + name_info.pObjectName = label; + VkResult res = _sg.vk.ext.set_debug_utils_object_name_ext(_sg.vk.dev, &name_info); + SOKOL_ASSERT(res == VK_SUCCESS); + } + #else + _SOKOL_UNUSED(obj_type); + _SOKOL_UNUSED(obj_handle); + _SOKOL_UNUSED(label); + #endif +} + +_SOKOL_PRIVATE bool _sg_vk_is_read_access(_sg_vk_access_t access) { + _sg_vk_access_t read_bits = + _SG_VK_ACCESS_VERTEXBUFFER | + _SG_VK_ACCESS_INDEXBUFFER | + _SG_VK_ACCESS_STORAGEBUFFER_RO | + _SG_VK_ACCESS_TEXTURE | + _SG_VK_ACCESS_PRESENT; + return 0 == (access & ~read_bits); +} + +_SOKOL_PRIVATE VkPipelineStageFlags2 _sg_vk_stage_mask(_sg_vk_access_t access, bool is_dst_access) { + access &= ~_SG_VK_ACCESS_DISCARD; + if (is_dst_access) { + SOKOL_ASSERT(access != _SG_VK_ACCESS_NONE); + } + VkPipelineStageFlags2 f = 0; + if (access == _SG_VK_ACCESS_NONE) { + return VK_PIPELINE_STAGE_2_NONE; + } + if (access & _SG_VK_ACCESS_PRESENT) { + return VK_PIPELINE_STAGE_2_NONE; + } + if (access & _SG_VK_ACCESS_STAGING) { + f |= VK_PIPELINE_STAGE_2_COPY_BIT; + } + if (access & _SG_VK_ACCESS_VERTEXBUFFER) { + f |= VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT; + } + if (access & _SG_VK_ACCESS_INDEXBUFFER) { + f |= VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT; + } + if (access & (_SG_VK_ACCESS_STORAGEBUFFER_RO|_SG_VK_ACCESS_TEXTURE)) { + f |= VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; + } + if (access & _SG_VK_ACCESS_STORAGEBUFFER_RW) { + f |= VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; + } + if (access & _SG_VK_ACCESS_STORAGEIMAGE) { + f |= VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; + } + if (access & _SG_VK_ACCESS_COLOR_ATTACHMENT) { + f |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + } + if (access & _SG_VK_ACCESS_RESOLVE_ATTACHMENT) { + f |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + } + if (access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT)) { + f |= VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT|VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; + } + SOKOL_ASSERT(f != 0); + return f; +} + +// return pipeline stages on 'before' side of a barrier +_SOKOL_PRIVATE VkPipelineStageFlags2 _sg_vk_src_stage_mask(_sg_vk_access_t access) { + return _sg_vk_stage_mask(access, false); +} + +// return pipeline stage on 'after side' of a barrier +_SOKOL_PRIVATE VkPipelineStageFlags2 _sg_vk_dst_stage_mask(_sg_vk_access_t access) { + return _sg_vk_stage_mask(access, true); +} + +_SOKOL_PRIVATE VkAccessFlags2 _sg_vk_access_mask(_sg_vk_access_t access, bool is_dst_access) { + access &= ~_SG_VK_ACCESS_DISCARD; + if (access == _SG_VK_ACCESS_NONE) { + return VK_ACCESS_2_NONE; + } + if (access & _SG_VK_ACCESS_PRESENT) { + return VK_ACCESS_2_NONE; + } + VkAccessFlags2 f = VK_ACCESS_2_NONE; + if (is_dst_access) { + // NOTE: read bits don't make sense for src-mask + if (access & _SG_VK_ACCESS_VERTEXBUFFER) { + f |= VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT; + } + if (access & _SG_VK_ACCESS_INDEXBUFFER) { + f |= VK_ACCESS_2_INDEX_READ_BIT; + } + if (access & _SG_VK_ACCESS_STORAGEBUFFER_RO) { + f |= VK_ACCESS_2_SHADER_STORAGE_READ_BIT; + } + if (access & _SG_VK_ACCESS_TEXTURE) { + f |= VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; + } + } + if (access & _SG_VK_ACCESS_STAGING) { + f |= VK_ACCESS_2_TRANSFER_WRITE_BIT; + } + if (access & _SG_VK_ACCESS_STORAGEBUFFER_RW) { + f |= VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT; + } + if (access & _SG_VK_ACCESS_STORAGEIMAGE) { + f |= VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT; + } + if (access & _SG_VK_ACCESS_COLOR_ATTACHMENT) { + f |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + } + if (access & _SG_VK_ACCESS_RESOLVE_ATTACHMENT) { + f |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + } + if (access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT | _SG_VK_ACCESS_STENCIL_ATTACHMENT)) { + f |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + if (is_dst_access) { + f |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + } + } + return f; +} + +_SOKOL_PRIVATE VkAccessFlags2 _sg_vk_src_access_mask(_sg_vk_access_t access) { + return _sg_vk_access_mask(access, false); +} + +_SOKOL_PRIVATE VkAccessFlags2 _sg_vk_dst_access_mask(_sg_vk_access_t access) { + return _sg_vk_access_mask(access, true); +} + +_SOKOL_PRIVATE VkImageLayout _sg_vk_image_layout(_sg_vk_access_t access) { + // NOTE: "image layout transitions with VK_IMAGE_LAYOUT_UNDEFINED allow + // the implementation to discard the image subresource range" + if (access & _SG_VK_ACCESS_DISCARD) { + return VK_IMAGE_LAYOUT_UNDEFINED; + } + switch (access) { + case _SG_VK_ACCESS_NONE: + return VK_IMAGE_LAYOUT_UNDEFINED; + case _SG_VK_ACCESS_STAGING: + return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + case _SG_VK_ACCESS_TEXTURE: + return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + case _SG_VK_ACCESS_STORAGEIMAGE: + return VK_IMAGE_LAYOUT_GENERAL; + case _SG_VK_ACCESS_COLOR_ATTACHMENT: + case _SG_VK_ACCESS_RESOLVE_ATTACHMENT: + case _SG_VK_ACCESS_DEPTH_ATTACHMENT: + case _SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT: + return VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL; + case _SG_VK_ACCESS_PRESENT: + return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + default: + SOKOL_UNREACHABLE; + return VK_IMAGE_LAYOUT_UNDEFINED; + } +} + +_SOKOL_PRIVATE void _sg_vk_swapchain_beginpass_barrier(VkCommandBuffer cmd_buf, VkImage vkimg, _sg_vk_access_t pass_access) { + SOKOL_ASSERT(cmd_buf); + _SG_STRUCT(VkImageMemoryBarrier2, barrier); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.srcStageMask = _sg_vk_src_stage_mask(pass_access); + barrier.srcAccessMask = _sg_vk_src_access_mask(pass_access); + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.dstStageMask = _sg_vk_dst_stage_mask(pass_access); + barrier.dstAccessMask = _sg_vk_dst_access_mask(pass_access); + barrier.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = vkimg; + if (0 != (pass_access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT))) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (0 != (pass_access & _SG_VK_ACCESS_STENCIL_ATTACHMENT)) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + _SG_STRUCT(VkDependencyInfo, dep_info); + dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + dep_info.imageMemoryBarrierCount = 1; + dep_info.pImageMemoryBarriers = &barrier; + vkCmdPipelineBarrier2(cmd_buf, &dep_info); + _sg_stats_inc(vk.num_cmd_pipeline_barrier); +} + +_SOKOL_PRIVATE void _sg_vk_swapchain_endpass_barrier(VkCommandBuffer cmd_buf, VkImage vkimg, _sg_vk_access_t pass_access, bool present) { + SOKOL_ASSERT(cmd_buf); + _SG_STRUCT(VkImageMemoryBarrier2, barrier); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.srcStageMask = _sg_vk_src_stage_mask(pass_access); + barrier.srcAccessMask = _sg_vk_src_access_mask(pass_access); + barrier.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_NONE; + barrier.dstAccessMask = VK_ACCESS_2_NONE; + if (present) { + barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } else { + barrier.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL; + } + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = vkimg; + if (0 != (pass_access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT))) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (0 != (pass_access & _SG_VK_ACCESS_STENCIL_ATTACHMENT)) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + _SG_STRUCT(VkDependencyInfo, dep_info); + dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + dep_info.imageMemoryBarrierCount = 1; + dep_info.pImageMemoryBarriers = &barrier; + vkCmdPipelineBarrier2(cmd_buf, &dep_info); + _sg_stats_inc(vk.num_cmd_pipeline_barrier); +} + +_SOKOL_PRIVATE void _sg_vk_image_barrier(VkCommandBuffer cmd_buf, _sg_image_t* img, _sg_vk_access_t new_access) { + SOKOL_ASSERT(cmd_buf && img && img->vk.img); + if (_sg_vk_is_read_access(img->vk.cur_access) && _sg_vk_is_read_access(new_access)) { + return; + } + _SG_STRUCT(VkImageMemoryBarrier2, barrier); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.srcStageMask = _sg_vk_src_stage_mask(img->vk.cur_access); + barrier.srcAccessMask = _sg_vk_src_access_mask(img->vk.cur_access); + barrier.oldLayout = _sg_vk_image_layout(img->vk.cur_access); + barrier.dstStageMask = _sg_vk_dst_stage_mask(new_access); + barrier.dstAccessMask = _sg_vk_dst_access_mask(new_access); + barrier.newLayout = _sg_vk_image_layout(new_access); + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = img->vk.img; + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + _SG_STRUCT(VkDependencyInfo, dep_info); + dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + dep_info.imageMemoryBarrierCount = 1; + dep_info.pImageMemoryBarriers = &barrier; + vkCmdPipelineBarrier2(cmd_buf, &dep_info); + _sg_stats_inc(vk.num_cmd_pipeline_barrier); + img->vk.cur_access = new_access; +} + +_SOKOL_PRIVATE void _sg_vk_buffer_barrier(VkCommandBuffer cmd_buf, _sg_buffer_t* buf, _sg_vk_access_t new_access) { + SOKOL_ASSERT(cmd_buf && buf && buf->vk.buf); + if (_sg_vk_is_read_access(buf->vk.cur_access) && _sg_vk_is_read_access(new_access)) { + return; + } + _SG_STRUCT(VkBufferMemoryBarrier2, barrier); + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; + barrier.srcStageMask = _sg_vk_src_stage_mask(buf->vk.cur_access); + barrier.srcAccessMask = _sg_vk_src_access_mask(buf->vk.cur_access); + barrier.dstStageMask = _sg_vk_dst_stage_mask(new_access); + barrier.dstAccessMask = _sg_vk_dst_access_mask(new_access); + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.buffer = buf->vk.buf; + barrier.offset = 0; + barrier.size = VK_WHOLE_SIZE; + _SG_STRUCT(VkDependencyInfo, dep_info); + dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + dep_info.bufferMemoryBarrierCount = 1; + dep_info.pBufferMemoryBarriers = &barrier; + vkCmdPipelineBarrier2(cmd_buf, &dep_info); + _sg_stats_inc(vk.num_cmd_pipeline_barrier); + buf->vk.cur_access = new_access; +} + +_SOKOL_PRIVATE void _sg_vk_barrier_on_begin_pass(VkCommandBuffer cmd_buf, const sg_pass* pass, const _sg_attachments_ptrs_t* atts, bool is_compute_pass) { + SOKOL_ASSERT(cmd_buf); + if (is_compute_pass) { + SOKOL_ASSERT(0 == _sg.vk.track.buffers.cur_slot); + SOKOL_ASSERT(0 == _sg.vk.track.images.cur_slot); + } else { + const bool is_swapchain_pass = atts->empty; + if (is_swapchain_pass) { + const sg_vulkan_swapchain* vk_swapchain = &pass->swapchain.vulkan; + SOKOL_ASSERT(vk_swapchain->render_image); + VkImage vk_color_image = (VkImage)vk_swapchain->render_image; + _sg_vk_swapchain_beginpass_barrier(cmd_buf, vk_color_image, _SG_VK_ACCESS_COLOR_ATTACHMENT); + if (_sg.cur_pass.swapchain.sample_count > 1) { + VkImage vk_resolve_image = (VkImage)vk_swapchain->resolve_image; + SOKOL_ASSERT(vk_resolve_image); + _sg_vk_swapchain_beginpass_barrier(cmd_buf, vk_resolve_image, _SG_VK_ACCESS_RESOLVE_ATTACHMENT); + } + if (vk_swapchain->depth_stencil_image) { + VkImage vk_ds_image = (VkImage)vk_swapchain->depth_stencil_image; + const bool has_stencil = _sg_is_depth_stencil_format(_sg.cur_pass.swapchain.depth_fmt); + _sg_vk_access_t access = _SG_VK_ACCESS_DEPTH_ATTACHMENT; + if (has_stencil) { + access |= _SG_VK_ACCESS_STENCIL_ATTACHMENT; + } + _sg_vk_swapchain_beginpass_barrier(cmd_buf, vk_ds_image, access); + } + } else { + SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < atts->num_color_views; i++) { + SOKOL_ASSERT(atts->color_views[i]); + _sg_image_t* color_image = _sg_image_ref_ptr(&atts->color_views[i]->cmn.img.ref); + if (pass->action.colors[i].load_action != SG_LOADACTION_LOAD) { + // don't need to preserve image content for clear and dontcare + color_image->vk.cur_access |= _SG_VK_ACCESS_DISCARD; + } + _sg_vk_image_barrier(cmd_buf, color_image, _SG_VK_ACCESS_COLOR_ATTACHMENT); + if (atts->resolve_views[i]) { + _sg_image_t* resolve_image = _sg_image_ref_ptr(&atts->resolve_views[i]->cmn.img.ref); + // never need to preserve content for resolve image + resolve_image->vk.cur_access |= _SG_VK_ACCESS_DISCARD; + _sg_vk_image_barrier(cmd_buf, resolve_image, _SG_VK_ACCESS_RESOLVE_ATTACHMENT); + } + } + if (atts->ds_view) { + _sg_image_t* ds_image = _sg_image_ref_ptr(&atts->ds_view->cmn.img.ref); + const bool has_stencil = _sg_is_depth_stencil_format(ds_image->cmn.pixel_format); + if ((pass->action.depth.load_action != SG_LOADACTION_LOAD) && + (pass->action.stencil.load_action != SG_LOADACTION_LOAD)) + { + // don't need to preserve image content for clear and dontcare + ds_image->vk.cur_access |= _SG_VK_ACCESS_DISCARD; + } + _sg_vk_access_t dst_access = _SG_VK_ACCESS_DEPTH_ATTACHMENT; + if (has_stencil) { + dst_access |= _SG_VK_ACCESS_STENCIL_ATTACHMENT; + } + _sg_vk_image_barrier(cmd_buf, ds_image, dst_access); + } + } + } +} + +_SOKOL_PRIVATE void _sg_vk_barrier_on_apply_bindings(VkCommandBuffer cmd_buf, const _sg_bindings_ptrs_t* bnd, bool is_compute_pass) { + SOKOL_ASSERT(bnd); + if (is_compute_pass) { + SOKOL_ASSERT(bnd->pip); + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_view_t* view = bnd->views[i]; + if (0 == view) { + continue; + } else if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + _sg_vk_access_t new_access = shd->cmn.views[i].sbuf_readonly + ? _SG_VK_ACCESS_STORAGEBUFFER_RO + : _SG_VK_ACCESS_STORAGEBUFFER_RW; + _sg_vk_buffer_barrier(cmd_buf, buf, new_access); + _sg_track_add(&_sg.vk.track.buffers, buf->slot.id); + } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) { + _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_STORAGEIMAGE); + _sg_track_add(&_sg.vk.track.images, img->slot.id); + } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) { + _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); + _sg_track_add(&_sg.vk.track.images, img->slot.id); + } else { + SOKOL_UNREACHABLE; + } + } + } else { + // no transitions allowed in render passes, but check if resources are in + // correct access state + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + if (bnd->vbs[i]) { + SOKOL_ASSERT(0 != (bnd->vbs[i]->vk.cur_access & _SG_VK_ACCESS_VERTEXBUFFER)); + } + } + if (bnd->ib) { + SOKOL_ASSERT(0 != (bnd->ib->vk.cur_access & _SG_VK_ACCESS_INDEXBUFFER)); + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const _sg_view_t* view = bnd->views[i]; + if (0 == view) { + continue; + } + else if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + _SOKOL_UNUSED(buf); + SOKOL_ASSERT(0 != (buf->vk.cur_access & _SG_VK_ACCESS_STORAGEBUFFER_RO)); + } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + _SOKOL_UNUSED(img); + SOKOL_ASSERT(0 != (img->vk.cur_access & _SG_VK_ACCESS_TEXTURE)); + } else { + SOKOL_UNREACHABLE; + } + } + } +} + +_SOKOL_PRIVATE void _sg_vk_barrier_on_end_pass(VkCommandBuffer cmd_buf, const _sg_attachments_ptrs_t* atts, bool is_compute_pass) { + SOKOL_ASSERT(cmd_buf); + if (is_compute_pass) { + // transition all tracked buffers into vertex+index+sbuf-ro access + const _sg_vk_access_t new_buf_access = _SG_VK_ACCESS_VERTEXBUFFER|_SG_VK_ACCESS_INDEXBUFFER|_SG_VK_ACCESS_STORAGEBUFFER_RO; + for (int i = 0; i < _sg.vk.track.buffers.cur_slot; i++) { + const uint32_t buf_id = _sg.vk.track.buffers.slots[i]; + _sg_buffer_t* buf = _sg_lookup_buffer(buf_id); + if (buf) { + _sg_vk_buffer_barrier(cmd_buf, buf, new_buf_access); + } + } + _sg_track_reset(&_sg.vk.track.buffers); + + // transition all tracked images into texture access + const _sg_vk_access_t new_img_access = _SG_VK_ACCESS_TEXTURE; + for (int i = 0; i < _sg.vk.track.images.cur_slot; i++) { + const uint32_t img_id = _sg.vk.track.images.slots[i]; + _sg_image_t* img = _sg_lookup_image(img_id); + if (img) { + _sg_vk_image_barrier(cmd_buf, img, new_img_access); + } + } + _sg_track_reset(&_sg.vk.track.images); + } else { + const bool is_swapchain_pass = atts->empty; + if (is_swapchain_pass) { + SOKOL_ASSERT(_sg.vk.swapchain.render_image); + VkImage present_image = _sg.vk.swapchain.resolve_image + ? (VkImage)_sg.vk.swapchain.resolve_image + : (VkImage)_sg.vk.swapchain.render_image; + _sg_vk_swapchain_endpass_barrier(cmd_buf, present_image, _SG_VK_ACCESS_COLOR_ATTACHMENT, true); + } else { + for (int i = 0; i < atts->num_color_views; i++) { + if (_sg.cur_pass.action.colors[i].store_action == SG_STOREACTION_STORE) { + SOKOL_ASSERT(atts->color_views[i]); + _sg_image_t* img = _sg_image_ref_ptr(&atts->color_views[i]->cmn.img.ref); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); + } + if (atts->resolve_views[i]) { + _sg_image_t* img = _sg_image_ref_ptr(&atts->resolve_views[i]->cmn.img.ref); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); + } + } + if (atts->ds_view) { + _sg_image_t* img = _sg_image_ref_ptr(&atts->ds_view->cmn.img.ref); + if (_sg.cur_pass.action.depth.store_action == SG_STOREACTION_STORE) { + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); + } + } + } + } +} + +_SOKOL_PRIVATE int _sg_vk_mem_find_memory_type_index(uint32_t type_filter, VkMemoryPropertyFlags props) { + SOKOL_ASSERT(_sg.vk.phys_dev); + _SG_STRUCT(VkPhysicalDeviceMemoryProperties, mem_props); + vkGetPhysicalDeviceMemoryProperties(_sg.vk.phys_dev, &mem_props); + for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { + if ((type_filter & (1 << i)) && ((mem_props.memoryTypes[i].propertyFlags & props) == props)) { + return (int)i; + } + } + return -1; +} + +_SOKOL_PRIVATE VkDeviceMemory _sg_vk_mem_alloc_device_memory(_sg_vk_memtype_t mem_type, const VkMemoryRequirements* mem_reqs) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(mem_reqs); + + VkMemoryPropertyFlags mem_prop_flags = 0; + VkMemoryAllocateFlags mem_alloc_flags = 0; + switch (mem_type) { + case _SG_VK_MEMTYPE_GENERIC_BUFFER: + mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case _SG_VK_MEMTYPE_STORAGE_BUFFER: + mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + mem_alloc_flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + break; + case _SG_VK_MEMTYPE_IMAGE: + mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case _SG_VK_MEMTYPE_STAGING_COPY: + mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case _SG_VK_MEMTYPE_STAGING_STREAM: + mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case _SG_VK_MEMTYPE_UNIFORMS: + mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + mem_alloc_flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + break; + case _SG_VK_MEMTYPE_DESCRIPTORS: + mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + mem_alloc_flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + break; + default: + SOKOL_UNREACHABLE; + break; + } + + int mem_type_index = _sg_vk_mem_find_memory_type_index(mem_reqs->memoryTypeBits, mem_prop_flags); + if (-1 == mem_type_index) { + _SG_ERROR(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE); + return 0; + } + _SG_STRUCT(VkMemoryAllocateFlagsInfo, flags_info); + flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + flags_info.flags = mem_alloc_flags; + _SG_STRUCT(VkMemoryAllocateInfo, alloc_info); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = &flags_info; + alloc_info.allocationSize = mem_reqs->size; + alloc_info.memoryTypeIndex = (uint32_t) mem_type_index; + VkDeviceMemory vk_dev_mem = 0; + VkResult res = vkAllocateMemory(_sg.vk.dev, &alloc_info, 0, &vk_dev_mem); + _sg_stats_inc(vk.num_allocate_memory); + _sg_stats_add(vk.size_allocate_memory, (uint32_t)mem_reqs->size); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_ALLOCATE_MEMORY_FAILED); + return 0; + } + SOKOL_ASSERT(vk_dev_mem); + return vk_dev_mem; +} + +_SOKOL_PRIVATE void _sg_vk_mem_free_device_memory(VkDeviceMemory vk_dev_mem) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(vk_dev_mem); + vkFreeMemory(_sg.vk.dev, vk_dev_mem, 0); + _sg_stats_inc(vk.num_free_memory); +} + +_SOKOL_PRIVATE bool _sg_vk_mem_alloc_buffer_device_memory(_sg_buffer_t* buf) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(buf); + SOKOL_ASSERT(buf->vk.buf); + SOKOL_ASSERT(0 == buf->vk.mem); + _SG_STRUCT(VkMemoryRequirements, mem_reqs); + vkGetBufferMemoryRequirements(_sg.vk.dev, buf->vk.buf, &mem_reqs); + _sg_vk_memtype_t mem_type = buf->cmn.usage.storage_buffer + ? _SG_VK_MEMTYPE_STORAGE_BUFFER + : _SG_VK_MEMTYPE_GENERIC_BUFFER; + buf->vk.mem = _sg_vk_mem_alloc_device_memory(mem_type, &mem_reqs); + if (0 == buf->vk.mem) { + _SG_ERROR(VULKAN_ALLOC_BUFFER_DEVICE_MEMORY_FAILED); + return false; + } + return true; +} + +_SOKOL_PRIVATE bool _sg_vk_mem_alloc_image_device_memory(_sg_image_t* img) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(img); + SOKOL_ASSERT(img->vk.img); + SOKOL_ASSERT(0 == img->vk.mem); + _SG_STRUCT(VkMemoryRequirements, mem_reqs); + vkGetImageMemoryRequirements(_sg.vk.dev, img->vk.img, &mem_reqs); + img->vk.mem = _sg_vk_mem_alloc_device_memory(_SG_VK_MEMTYPE_IMAGE, &mem_reqs); + if (0 == img->vk.mem) { + _SG_ERROR(VULKAN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED); + return false; + } + return true; +} + +_SOKOL_PRIVATE void _sg_vk_create_delete_queues(void) { + const uint32_t num_items = (uint32_t) + (2 * _sg.desc.buffer_pool_size + + 2 * _sg.desc.image_pool_size + + 1 * _sg.desc.sampler_pool_size + + 5 * _sg.desc.shader_pool_size + + 2 * _sg.desc.pipeline_pool_size + + 1 * _sg.desc.view_pool_size + + 256); + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg_vk_delete_queue_t* queue = &_sg.vk.frame.slot[i].delete_queue; + SOKOL_ASSERT(0 == queue->items); + SOKOL_ASSERT(0 == queue->index); + queue->num = num_items; + const size_t pool_size = num_items * sizeof(_sg_vk_delete_queue_item_t); + queue->items = (_sg_vk_delete_queue_item_t*)_sg_malloc(pool_size); + } +} + +_SOKOL_PRIVATE void _sg_vk_delete_queue_collect_items(_sg_vk_delete_queue_t* queue) { + SOKOL_ASSERT(queue && queue->items); + for (uint32_t i = 0; i < queue->index; i++) { + _sg_vk_delete_queue_item_t* item = &queue->items[i]; + SOKOL_ASSERT(item->destructor && item->obj); + item->destructor(item->obj); + item->destructor = 0; + item->obj = 0; + } + _sg_stats_add(vk.num_delete_queue_collected, queue->index); + queue->index = 0; +} + +_SOKOL_PRIVATE void _sg_vk_destroy_delete_queues(void) { + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg_vk_delete_queue_t* queue = &_sg.vk.frame.slot[i].delete_queue; + SOKOL_ASSERT(queue->items); + _sg_vk_delete_queue_collect_items(queue); + _sg_free(queue->items); + SOKOL_ASSERT(queue->index == 0); + queue->items = 0; + queue->num = 0; + } +} + +_SOKOL_PRIVATE _sg_vk_delete_queue_t* _sg_vk_cur_delete_queue(void) { + return &_sg.vk.frame.slot[_sg.vk.frame_slot].delete_queue; +} + +_SOKOL_PRIVATE void _sg_vk_delete_queue_collect(void) { + _sg_vk_delete_queue_t* queue = _sg_vk_cur_delete_queue(); + _sg_vk_delete_queue_collect_items(queue); +} + +_SOKOL_PRIVATE void _sg_vk_delete_queue_add(_sg_vk_delete_queue_destructor_t destructor, void* obj) { + SOKOL_ASSERT(destructor && obj); + _sg_vk_delete_queue_t* queue = _sg_vk_cur_delete_queue(); + SOKOL_ASSERT(queue->items); + if (queue->index >= queue->num) { + _SG_PANIC(VULKAN_DELETE_QUEUE_EXHAUSTED); + } + queue->items[queue->index].destructor = destructor; + queue->items[queue->index].obj = obj; + queue->index += 1; + _sg_stats_inc(vk.num_delete_queue_added); +} + +// double-buffer system for any non-blocking CPU => GPU data +_SOKOL_PRIVATE void _sg_vk_shared_buffer_init(_sg_vk_shared_buffer_t* shbuf, uint32_t size, uint32_t align, _sg_vk_memtype_t mem_type, const char* label) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(shbuf && (size > 0) && (align > 0)); + SOKOL_ASSERT(0 == shbuf->size); + SOKOL_ASSERT(0 == shbuf->offset); + SOKOL_ASSERT(0 == shbuf->cur_buf); + SOKOL_ASSERT(false == shbuf->overflown); + VkResult res; + VkBufferUsageFlags vk_usage = 0; + bool want_device_address = false; + switch (mem_type) { + case _SG_VK_MEMTYPE_STAGING_STREAM: + vk_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + break; + case _SG_VK_MEMTYPE_UNIFORMS: + vk_usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + vk_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + want_device_address = true; + break; + case _SG_VK_MEMTYPE_DESCRIPTORS: + vk_usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT; + vk_usage |= VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT; + vk_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + want_device_address = true; + break; + default: + SOKOL_UNREACHABLE; + break; + } + + shbuf->size = _sg_roundup_u32(size, align); + shbuf->align = align; + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + SOKOL_ASSERT(0 == shbuf->slots[i].buf); + SOKOL_ASSERT(0 == shbuf->slots[i].mem); + SOKOL_ASSERT(0 == shbuf->slots[i].mem_ptr); + _SG_STRUCT(VkBufferCreateInfo, buf_create_info); + buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_create_info.size = shbuf->size; + buf_create_info.usage = vk_usage; + buf_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + res = vkCreateBuffer(_sg.vk.dev, &buf_create_info, 0, &shbuf->slots[i].buf); + if (res != VK_SUCCESS) { + _SG_PANIC(VULKAN_CREATE_SHARED_BUFFER_FAILED); + } + SOKOL_ASSERT(shbuf->slots[i].buf); + _sg_vk_set_object_label(VK_OBJECT_TYPE_BUFFER, (uint64_t)shbuf->slots[i].buf, label); + + _SG_STRUCT(VkMemoryRequirements, mem_reqs); + vkGetBufferMemoryRequirements(_sg.vk.dev, shbuf->slots[i].buf, &mem_reqs); + shbuf->slots[i].mem = _sg_vk_mem_alloc_device_memory(mem_type, &mem_reqs); + if (0 == shbuf->slots[i].mem) { + _SG_PANIC(VULKAN_ALLOCATE_SHARED_BUFFER_MEMORY_FAILED); + } + res = vkBindBufferMemory(_sg.vk.dev, shbuf->slots[i].buf, shbuf->slots[i].mem, 0); + if (res != VK_SUCCESS) { + _SG_PANIC(VULKAN_BIND_SHARED_BUFFER_MEMORY_FAILED); + } + if (want_device_address) { + _SG_STRUCT(VkBufferDeviceAddressInfo, addr_info); + addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + addr_info.buffer = shbuf->slots[i].buf; + shbuf->slots[i].dev_addr = vkGetBufferDeviceAddress(_sg.vk.dev, &addr_info); + SOKOL_ASSERT(shbuf->slots[i].dev_addr); + } + res = vkMapMemory(_sg.vk.dev, shbuf->slots[i].mem, 0, VK_WHOLE_SIZE, 0, &shbuf->slots[i].mem_ptr); + if (res != VK_SUCCESS) { + _SG_PANIC(VULKAN_MAP_SHARED_BUFFER_MEMORY_FAILED); + } + SOKOL_ASSERT(shbuf->slots[i].mem_ptr); + } +} + +_SOKOL_PRIVATE void _sg_vk_shared_buffer_discard(_sg_vk_shared_buffer_t* shbuf) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(shbuf); + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + SOKOL_ASSERT(shbuf->slots[i].buf); + SOKOL_ASSERT(shbuf->slots[i].mem); + SOKOL_ASSERT(shbuf->slots[i].mem_ptr); + vkUnmapMemory(_sg.vk.dev, shbuf->slots[i].mem); + shbuf->slots[i].mem_ptr = 0; + _sg_vk_mem_free_device_memory(shbuf->slots[i].mem); + shbuf->slots[i].mem = 0; + vkDestroyBuffer(_sg.vk.dev, shbuf->slots[i].buf, 0); + shbuf->slots[i].buf = 0; + shbuf->slots[i].dev_addr = 0; + } + shbuf->size = 0; + shbuf->offset = 0; + shbuf->cur_buf = 0; + shbuf->cur_dev_addr = 0; + shbuf->overflown = false; +} + +_SOKOL_PRIVATE void _sg_vk_shared_buffer_after_acquire(_sg_vk_shared_buffer_t* shbuf) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(0 == shbuf->cur_buf); + SOKOL_ASSERT(0 == shbuf->cur_mem_ptr); + SOKOL_ASSERT(0 == shbuf->cur_dev_addr); + const uint32_t frame_slot = _sg.vk.frame_slot; + shbuf->offset = 0; + shbuf->cur_buf = shbuf->slots[frame_slot].buf; + shbuf->cur_mem_ptr = shbuf->slots[frame_slot].mem_ptr; + shbuf->cur_dev_addr = shbuf->slots[frame_slot].dev_addr; // NOTE: may be 0 + shbuf->overflown = false; + SOKOL_ASSERT(shbuf->cur_buf); + SOKOL_ASSERT(shbuf->cur_mem_ptr); +} + +_SOKOL_PRIVATE void _sg_vk_shared_buffer_before_submit(_sg_vk_shared_buffer_t* shbuf) { + SOKOL_ASSERT(shbuf->cur_buf); + SOKOL_ASSERT(shbuf->cur_mem_ptr); + // NOTE: if the buffer wouldn't be cache-coherent, this would be the place to do a flush + shbuf->cur_buf = 0; + shbuf->cur_mem_ptr = 0; + shbuf->cur_dev_addr = 0; +} + +_SOKOL_PRIVATE VkDeviceSize _sg_vk_shared_buffer_alloc(_sg_vk_shared_buffer_t* shbuf, uint32_t num_bytes) { + SOKOL_ASSERT(shbuf && (num_bytes > 0)); + if (shbuf->overflown) { + return _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT; + } + if ((shbuf->offset + num_bytes) > shbuf->size) { + shbuf->overflown = true; + return _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT; + } + SOKOL_ASSERT((shbuf->offset & (shbuf->align - 1)) == 0); + VkDeviceSize offset = shbuf->offset; + shbuf->offset = _sg_roundup_u32(shbuf->offset + num_bytes, shbuf->align); + return offset; +} + +_SOKOL_PRIVATE uint8_t* _sg_vk_shared_buffer_ptr(_sg_vk_shared_buffer_t* shbuf, VkDeviceSize offset) { + SOKOL_ASSERT(shbuf && shbuf->cur_mem_ptr); + SOKOL_ASSERT(!shbuf->overflown); + SOKOL_ASSERT(offset < shbuf->size); + return ((uint8_t*)shbuf->cur_mem_ptr) + offset; +} + +_SOKOL_PRIVATE VkDeviceSize _sg_vk_shared_buffer_memcpy(_sg_vk_shared_buffer_t* shbuf, const void* src_ptr, uint32_t num_bytes) { + SOKOL_ASSERT(shbuf && src_ptr && (num_bytes > 0)); + const VkDeviceSize offset = _sg_vk_shared_buffer_alloc(shbuf, num_bytes); + if (offset != _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT) { + memcpy(_sg_vk_shared_buffer_ptr(shbuf, offset), src_ptr, num_bytes); + } + return offset; +} + +// staging system for blocking immutable and dynamic updates, can deal arbitrarily sized data +_SOKOL_PRIVATE void _sg_vk_staging_copy_init(void) { + SOKOL_ASSERT(_sg.vk.dev); + VkResult res; + + SOKOL_ASSERT(0 == _sg.vk.stage.copy.cmd_pool); + SOKOL_ASSERT(0 == _sg.vk.stage.copy.cmd_buf); + SOKOL_ASSERT(0 == _sg.vk.stage.copy.size); + SOKOL_ASSERT(0 == _sg.vk.stage.copy.buf); + SOKOL_ASSERT(0 == _sg.vk.stage.copy.mem); + SOKOL_ASSERT(_sg.desc.vulkan.copy_staging_buffer_size > 0); + + _SG_STRUCT(VkCommandPoolCreateInfo, pool_create_info); + pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + pool_create_info.queueFamilyIndex = _sg.vk.queue_family_index; + res = vkCreateCommandPool(_sg.vk.dev, &pool_create_info, 0, &_sg.vk.stage.copy.cmd_pool); + SOKOL_ASSERT((res == VK_SUCCESS && _sg.vk.stage.copy.cmd_pool)); + _sg_vk_set_object_label(VK_OBJECT_TYPE_COMMAND_POOL, (uint64_t)_sg.vk.stage.copy.cmd_pool, "copy-staging cmd pool"); + + _SG_STRUCT(VkCommandBufferAllocateInfo, cmdbuf_alloc_info); + cmdbuf_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf_alloc_info.commandPool = _sg.vk.stage.copy.cmd_pool; + cmdbuf_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdbuf_alloc_info.commandBufferCount = 1; + res = vkAllocateCommandBuffers(_sg.vk.dev, &cmdbuf_alloc_info, &_sg.vk.stage.copy.cmd_buf); + SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.stage.copy.cmd_buf); + _sg_vk_set_object_label(VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)_sg.vk.stage.copy.cmd_buf, "copy-staging cmd buffer"); + + _sg.vk.stage.copy.size = (uint32_t) _sg.desc.vulkan.copy_staging_buffer_size; + _SG_STRUCT(VkBufferCreateInfo, buf_create_info); + buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_create_info.size = _sg.vk.stage.copy.size; + buf_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buf_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + res = vkCreateBuffer(_sg.vk.dev, &buf_create_info, 0, &_sg.vk.stage.copy.buf); + if (res != VK_SUCCESS) { + _SG_PANIC(VULKAN_STAGING_CREATE_BUFFER_FAILED); + } + SOKOL_ASSERT(_sg.vk.stage.copy.buf); + _sg_vk_set_object_label(VK_OBJECT_TYPE_BUFFER, (uint64_t)_sg.vk.stage.copy.buf, "copy-staging staging buffer"); + + _SG_STRUCT(VkMemoryRequirements, mem_reqs); + vkGetBufferMemoryRequirements(_sg.vk.dev, _sg.vk.stage.copy.buf, &mem_reqs); + _sg.vk.stage.copy.mem = _sg_vk_mem_alloc_device_memory(_SG_VK_MEMTYPE_STAGING_COPY, &mem_reqs); + if (0 == _sg.vk.stage.copy.mem) { + _SG_PANIC(VULKAN_STAGING_ALLOCATE_MEMORY_FAILED); + } + res = vkBindBufferMemory(_sg.vk.dev, _sg.vk.stage.copy.buf, _sg.vk.stage.copy.mem, 0); + if (res != VK_SUCCESS) { + _SG_PANIC(VULKAN_STAGING_BIND_BUFFER_MEMORY_FAILED); + } +} + +_SOKOL_PRIVATE void _sg_vk_staging_copy_discard(void) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.stage.copy.cmd_pool); + SOKOL_ASSERT(_sg.vk.stage.copy.cmd_buf); + SOKOL_ASSERT(_sg.vk.stage.copy.size); + SOKOL_ASSERT(_sg.vk.stage.copy.buf); + SOKOL_ASSERT(_sg.vk.stage.copy.mem); + + _sg_vk_mem_free_device_memory(_sg.vk.stage.copy.mem); + _sg.vk.stage.copy.mem = 0; + vkDestroyBuffer(_sg.vk.dev, _sg.vk.stage.copy.buf, 0); + _sg.vk.stage.copy.buf = 0; + vkDestroyCommandPool(_sg.vk.dev, _sg.vk.stage.copy.cmd_pool, 0); + _sg.vk.stage.copy.cmd_pool = 0; + _sg.vk.stage.copy.cmd_buf = 0; + _sg.vk.stage.copy.size = 0; +} + +_SOKOL_PRIVATE VkCommandBuffer _sg_vk_staging_copy_begin(void) { + VkCommandBuffer cmd_buf = _sg.vk.stage.copy.cmd_buf; + _SG_STRUCT(VkCommandBufferBeginInfo, cmdbuf_begin_info); + cmdbuf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkResult res = vkBeginCommandBuffer(cmd_buf, &cmdbuf_begin_info); + SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res); + return cmd_buf; +} + +_SOKOL_PRIVATE void _sg_vk_staging_copy_end(VkCommandBuffer cmd_buf, VkQueue queue) { + SOKOL_ASSERT(cmd_buf && queue); + VkResult res; + _SOKOL_UNUSED(res); + vkEndCommandBuffer(cmd_buf); + _SG_STRUCT(VkSubmitInfo, submit_info); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd_buf; + res = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); + SOKOL_ASSERT(res == VK_SUCCESS); + res = vkQueueWaitIdle(queue); + SOKOL_ASSERT(res == VK_SUCCESS); + res = vkResetCommandBuffer(cmd_buf, 0); + SOKOL_ASSERT(res == VK_SUCCESS); +} + +_SOKOL_PRIVATE void _sg_vk_staging_map_memcpy_unmap(VkDeviceMemory mem, const void* ptr, uint32_t num_bytes) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(mem); + SOKOL_ASSERT(ptr); + SOKOL_ASSERT(num_bytes > 0); + void* dst_ptr = 0; + VkResult res = vkMapMemory(_sg.vk.dev, mem, 0, VK_WHOLE_SIZE, 0, &dst_ptr); + SOKOL_ASSERT((res == VK_SUCCESS) && dst_ptr); _SOKOL_UNUSED(res); + memcpy(dst_ptr, ptr, num_bytes); + vkUnmapMemory(_sg.vk.dev, mem); +} + +_SOKOL_PRIVATE void _sg_vk_staging_copy_buffer_data(_sg_buffer_t* buf, const sg_range* src_data, size_t dst_offset, bool initial_wait) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.queue); + SOKOL_ASSERT(_sg.vk.stage.copy.mem); + SOKOL_ASSERT(_sg.vk.stage.copy.buf); + SOKOL_ASSERT(buf && buf->vk.buf); + SOKOL_ASSERT(src_data && src_data->ptr && (src_data->size > 0)); + SOKOL_ASSERT((dst_offset + src_data->size) <= (size_t)buf->cmn.size); + + // an inital wait is only needed for updating existing resources but not when populating a new resource + if (initial_wait) { + VkResult res = vkQueueWaitIdle(_sg.vk.queue); + SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res); + } + + VkDeviceMemory dst_mem = _sg.vk.stage.copy.mem; + VkBuffer src_buf = _sg.vk.stage.copy.buf; + VkBuffer dst_buf = buf->vk.buf; + const uint8_t* src_ptr = (const uint8_t*)src_data->ptr; + uint32_t dst_size = _sg.vk.stage.copy.size; + uint32_t bytes_remaining = (uint32_t)src_data->size; + _SG_STRUCT(VkBufferCopy, region); + region.dstOffset = dst_offset; + while (bytes_remaining > 0) { + uint64_t bytes_to_copy = bytes_remaining; + if (bytes_remaining > dst_size) { + bytes_to_copy = dst_size; + bytes_remaining -= dst_size; + } else { + bytes_to_copy = bytes_remaining; + bytes_remaining = 0; + } + region.size = bytes_to_copy; + _sg_vk_staging_map_memcpy_unmap(dst_mem, src_ptr, (uint32_t)bytes_to_copy); + VkCommandBuffer cmd_buf = _sg_vk_staging_copy_begin(); + vkCmdCopyBuffer(cmd_buf, src_buf, dst_buf, 1, ®ion); + _sg_stats_inc(vk.num_cmd_copy_buffer); + _sg_vk_staging_copy_end(cmd_buf, _sg.vk.queue); + src_ptr += bytes_to_copy; + region.dstOffset += bytes_to_copy; + } + buf->vk.cur_access = _SG_VK_ACCESS_VERTEXBUFFER | _SG_VK_ACCESS_INDEXBUFFER | _SG_VK_ACCESS_STORAGEBUFFER_RO; +} + +_SOKOL_PRIVATE void _sg_vk_init_vk_image_staging_structs(const _sg_image_t* img, VkBuffer vk_buf, VkBufferImageCopy2* region, VkCopyBufferToImageInfo2* copy_info) { + SOKOL_ASSERT(img && region && copy_info); + + region->sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + region->imageSubresource.layerCount = 1; + region->imageExtent.depth = 1; + + copy_info->sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2; + copy_info->srcBuffer = vk_buf; + copy_info->dstImage = img->vk.img; + copy_info->dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_info->regionCount = 1; + copy_info->pRegions = region; +} + +_SOKOL_PRIVATE void _sg_vk_staging_copy_image_data(_sg_image_t* img, const sg_image_data* src_data, bool initial_wait) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.queue); + SOKOL_ASSERT(_sg.vk.stage.copy.mem); + SOKOL_ASSERT(_sg.vk.stage.copy.buf); + SOKOL_ASSERT(img && img->vk.img); + const uint32_t block_dim = (uint32_t)_sg_block_dim(img->cmn.pixel_format); + + // an inital wait is only needed for updating existing resources but not when populating a new resource + if (initial_wait) { + VkResult res = vkQueueWaitIdle(_sg.vk.queue); + SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res); + } + + VkDeviceMemory mem = _sg.vk.stage.copy.mem; + _SG_STRUCT(VkBufferImageCopy2, region); + _SG_STRUCT(VkCopyBufferToImageInfo2, copy_info); + _sg_vk_init_vk_image_staging_structs(img, _sg.vk.stage.copy.buf, ®ion, ©_info); + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const uint8_t* src_ptr = (uint8_t*)src_data->mip_levels[mip_index].ptr; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + int mip_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + const uint32_t row_pitch = (uint32_t) _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const uint32_t num_rows = (uint32_t) _sg_num_rows(img->cmn.pixel_format, mip_height); + region.imageSubresource.mipLevel = (uint32_t)mip_index; + region.imageExtent.width = (uint32_t)mip_width; + + const uint32_t max_rows = _sg.vk.stage.copy.size / row_pitch; + for (int slice_index = 0; slice_index < mip_slices; slice_index++) { + if (img->cmn.type == SG_IMAGETYPE_3D) { + region.imageOffset.z = slice_index; + } else { + region.imageSubresource.baseArrayLayer = (uint32_t)slice_index; + } + uint32_t rows_remaining = num_rows; + uint32_t cur_row = 0; + while (rows_remaining > 0) { + uint32_t rows_to_copy = rows_remaining; + if (rows_remaining > max_rows) { + rows_to_copy = max_rows; + rows_remaining -= max_rows; + } else { + rows_to_copy = rows_remaining; + rows_remaining = 0; + } + const uint32_t bytes_to_copy = rows_to_copy * row_pitch; + SOKOL_ASSERT(bytes_to_copy <= _sg.vk.stage.copy.size); + _sg_vk_staging_map_memcpy_unmap(mem, src_ptr, bytes_to_copy); + src_ptr += bytes_to_copy; + VkCommandBuffer cmd_buf = _sg_vk_staging_copy_begin(); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_STAGING); + region.imageOffset.y = (int32_t)(cur_row * block_dim); + region.imageExtent.height = _sg_min((uint32_t)mip_height, rows_to_copy * block_dim); + vkCmdCopyBufferToImage2(cmd_buf, ©_info); + _sg_stats_inc(vk.num_cmd_copy_buffer_to_image); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); + + _sg_vk_staging_copy_end(cmd_buf, _sg.vk.queue); + cur_row += rows_to_copy; + } + } + } +} + +// staging system for non-blocking streaming updates with a max per-frame data limit +_SOKOL_PRIVATE void _sg_vk_staging_stream_init(void) { + SOKOL_ASSERT(_sg.desc.vulkan.stream_staging_buffer_size > 0); + _sg_vk_shared_buffer_init(&_sg.vk.stage.stream, + (uint32_t)_sg.desc.vulkan.stream_staging_buffer_size, + 16, // NOTE: arbitrary alignment (FIXME?) + _SG_VK_MEMTYPE_STAGING_STREAM, + "shared-stream-buffer"); +} + +_SOKOL_PRIVATE void _sg_vk_staging_stream_discard(void) { + _sg_vk_shared_buffer_discard(&_sg.vk.stage.stream); +} + +_SOKOL_PRIVATE void _sg_vk_staging_stream_after_acquire(void) { + _sg_vk_shared_buffer_after_acquire(&_sg.vk.stage.stream); +} + +_SOKOL_PRIVATE void _sg_vk_staging_stream_before_submit(void) { + _sg_vk_shared_buffer_before_submit(&_sg.vk.stage.stream); +} + +_SOKOL_PRIVATE void _sg_vk_staging_stream_buffer_data(_sg_buffer_t* buf, const sg_range* src_data, size_t dst_offset) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.frame.stream_cmd_buf); + SOKOL_ASSERT(_sg.vk.stage.stream.cur_buf); + SOKOL_ASSERT(buf && buf->vk.buf); + SOKOL_ASSERT(src_data && src_data->ptr && (src_data->size > 0)); + SOKOL_ASSERT((src_data->size + dst_offset) <= (size_t)buf->cmn.size); + + const uint32_t src_offset = (uint32_t)_sg_vk_shared_buffer_memcpy(&_sg.vk.stage.stream, src_data->ptr, (uint32_t)src_data->size); + if (src_offset == _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT) { + _SG_ERROR(VULKAN_STAGING_STREAM_BUFFER_OVERFLOW); + return; + } + VkCommandBuffer cmd_buf = _sg.vk.frame.stream_cmd_buf; + VkBuffer vk_src_buf = _sg.vk.stage.stream.cur_buf; + VkBuffer vk_dst_buf = buf->vk.buf; + _SG_STRUCT(VkBufferCopy, region); + region.srcOffset = src_offset; + region.dstOffset = dst_offset; + region.size = src_data->size; + _sg_vk_buffer_barrier(cmd_buf, buf, _SG_VK_ACCESS_STAGING); + vkCmdCopyBuffer(cmd_buf, vk_src_buf, vk_dst_buf, 1, ®ion); + _sg_stats_inc(vk.num_cmd_copy_buffer); + // FIXME: not great to issue a barrier right here, + // rethink buffer barrier strategy? => a single memory barrier + // at the end of the stream command buffer should be sufficient? + _sg_vk_buffer_barrier(cmd_buf, buf, _SG_VK_ACCESS_VERTEXBUFFER|_SG_VK_ACCESS_INDEXBUFFER|_SG_VK_ACCESS_STORAGEBUFFER_RO); +} + +_SOKOL_PRIVATE void _sg_vk_staging_stream_image_data(_sg_image_t* img, const sg_image_data* src_data) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.frame.stream_cmd_buf); + SOKOL_ASSERT(img && img->vk.img); + SOKOL_ASSERT(src_data); + VkCommandBuffer cmd_buf = _sg.vk.frame.stream_cmd_buf; + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_STAGING); + _SG_STRUCT(VkBufferImageCopy2, region); + _SG_STRUCT(VkCopyBufferToImageInfo2, copy_info); + _sg_vk_init_vk_image_staging_structs(img, _sg.vk.stage.stream.cur_buf, ®ion, ©_info); + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const sg_range* src_mip = &src_data->mip_levels[mip_index]; + SOKOL_ASSERT(src_mip->ptr); + SOKOL_ASSERT(src_mip->size > 0); + const uint32_t src_offset = (uint32_t)_sg_vk_shared_buffer_memcpy(&_sg.vk.stage.stream, src_mip->ptr, (uint32_t)src_mip->size); + if (src_offset == _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT) { + _SG_ERROR(VULKAN_STAGING_STREAM_BUFFER_OVERFLOW); + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); + return; + } + region.bufferOffset = src_offset; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + int mip_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + region.imageExtent.width = (uint32_t)mip_width; + region.imageExtent.height = (uint32_t)mip_height; + region.imageSubresource.mipLevel = (uint32_t)mip_index; + if (img->cmn.type == SG_IMAGETYPE_3D) { + region.imageExtent.depth = (uint32_t)mip_slices; + region.imageSubresource.layerCount = 1; + } else { + region.imageExtent.depth = 1; + region.imageSubresource.layerCount = (uint32_t)mip_slices; + } + vkCmdCopyBufferToImage2(cmd_buf, ©_info); + _sg_stats_inc(vk.num_cmd_copy_buffer_to_image); + } + _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE); +} + +// uniform data system +_SOKOL_PRIVATE void _sg_vk_uniform_init(void) { + SOKOL_ASSERT(_sg.desc.uniform_buffer_size > 0); + SOKOL_ASSERT(0 == _sg.vk.uniforms.dset_cache); + + _sg_vk_shared_buffer_init(&_sg.vk.uniforms.dbuf, + (uint32_t)_sg.desc.uniform_buffer_size, + (uint32_t)_sg.vk.dev_props.properties.limits.minUniformBufferOffsetAlignment, + _SG_VK_MEMTYPE_UNIFORMS, + "shared-uniform-buffer"); + + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + _sg.vk.uniforms.addr_info[i].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT; + _sg.vk.uniforms.get_info[i].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT; + _sg.vk.uniforms.get_info[i].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + _sg.vk.uniforms.get_info[i].data.pUniformBuffer = &_sg.vk.uniforms.addr_info[i]; + } + + // NOTE: we assume here that the max alignment for uniform buffer + // descriptors in the descriptor buffer is the same as the assumed max + // descriptor size (e.g. 256 bytes) + _sg.vk.uniforms.dset_cache_size = SG_MAX_UNIFORMBLOCK_BINDSLOTS * _SG_VK_MAX_DESCRIPTOR_DATA_SIZE; + _sg.vk.uniforms.dset_cache = (uint8_t*)_sg_malloc_clear(_sg.vk.uniforms.dset_cache_size); +} + +_SOKOL_PRIVATE void _sg_vk_uniform_discard(void) { + SOKOL_ASSERT(_sg.vk.uniforms.dset_cache); + _sg_free(_sg.vk.uniforms.dset_cache); _sg.vk.uniforms.dset_cache = 0; + _sg_vk_shared_buffer_discard(&_sg.vk.uniforms.dbuf); +} + +// called from _sg_vk_acquire_frame_command_buffer() +_SOKOL_PRIVATE void _sg_vk_uniform_after_acquire(void) { + _sg_vk_shared_buffer_after_acquire(&_sg.vk.uniforms.dbuf); + // reset uniform tracking data + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + _sg.vk.uniforms.addr_info[i].address = 0; + _sg.vk.uniforms.addr_info[i].range = 0; + } +} + +// called from _sg_vk_submit_frame_command_buffer() +_SOKOL_PRIVATE void _sg_vk_uniform_before_submit(void) { + _sg_vk_shared_buffer_before_submit(&_sg.vk.uniforms.dbuf); +} + +// called form _sg_vk_apply_uniforms, returns offset of data snippet into uniform buffer +_SOKOL_PRIVATE uint32_t _sg_vk_uniform_copy(const sg_range* data) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + return (uint32_t)_sg_vk_shared_buffer_memcpy(&_sg.vk.uniforms.dbuf, data->ptr, (uint32_t)data->size); +} + +// resource binding system +_SOKOL_PRIVATE void _sg_vk_bind_init(void) { + SOKOL_ASSERT(_sg.desc.vulkan.descriptor_buffer_size > 0); + _sg_vk_shared_buffer_init(&_sg.vk.bind, + (uint32_t)_sg.desc.vulkan.descriptor_buffer_size, + (uint32_t)_sg.vk.descriptor_buffer_props.descriptorBufferOffsetAlignment, + _SG_VK_MEMTYPE_DESCRIPTORS, + "shared-descriptor-buffer"); +} + +_SOKOL_PRIVATE void _sg_vk_bind_discard(void) { + _sg_vk_shared_buffer_discard(&_sg.vk.bind); +} + +// called from _sg_vk_acquire_frame_command_buffer() +_SOKOL_PRIVATE void _sg_vk_bind_after_acquire(void) { + _sg_vk_shared_buffer_after_acquire(&_sg.vk.bind); + + // bind the current frame's descriptor buffer + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + SOKOL_ASSERT(_sg.vk.bind.cur_buf); + SOKOL_ASSERT(_sg.vk.bind.cur_dev_addr); + _SG_STRUCT(VkDescriptorBufferBindingInfoEXT, bind_info); + bind_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT; + bind_info.address = _sg.vk.bind.cur_dev_addr; + bind_info.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | + VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT; + _sg.vk.ext.cmd_bind_descriptor_buffers(_sg.vk.frame.cmd_buf, 1, &bind_info); +} + +// called from _sg_vk_submit_frame_command_buffer() +_SOKOL_PRIVATE void _sg_vk_bind_before_submit(void) { + _sg_vk_shared_buffer_before_submit(&_sg.vk.bind); +} + +_SOKOL_PRIVATE bool _sg_vk_bind_view_smp_descriptor_set(VkCommandBuffer cmd_buf, const _sg_bindings_ptrs_t* bnd, VkPipelineBindPoint vk_bind_point) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(cmd_buf); + SOKOL_ASSERT(bnd && bnd->pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader); + + // get next pointer in descriptor buffer + const VkDeviceSize dset_size = shd->vk.view_smp_dset_size; + if (dset_size == 0) { + // nothing to bind + return true; + } + const VkDeviceSize dbuf_offset = _sg_vk_shared_buffer_alloc(&_sg.vk.bind, (uint32_t)dset_size); + if (_sg.vk.bind.overflown) { + _SG_ERROR(VULKAN_DESCRIPTOR_BUFFER_OVERFLOW); + return false; + } + _sg_stats_add(vk.size_descriptor_buffer_writes, (uint32_t)dset_size); + uint8_t* dbuf_ptr = _sg_vk_shared_buffer_ptr(&_sg.vk.bind, dbuf_offset); + + // copy pre-recorded descriptor data into descriptor buffer + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const _sg_view_t* view = bnd->views[i]; + SOKOL_ASSERT(view && (view->vk.descriptor_size > 0)); + const void* src_ptr = view->vk.descriptor_data; + size_t size = view->vk.descriptor_size; + void* dst_ptr = dbuf_ptr + shd->vk.view_dset_offsets[i]; + memcpy(dst_ptr, src_ptr, size); + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const _sg_sampler_t* smp = bnd->smps[i]; + SOKOL_ASSERT(smp && (smp->vk.descriptor_size > 0)); + const void* src_ptr = smp->vk.descriptor_data; + size_t size = smp->vk.descriptor_size; + void* dst_ptr = dbuf_ptr + shd->vk.smp_dset_offsets[i]; + memcpy(dst_ptr, src_ptr, size); + } + + // record the new descriptor buffer offset + const uint32_t dbuf_index = 0; + SOKOL_ASSERT(shd->vk.pip_layout); + _sg.vk.ext.cmd_set_descriptor_buffer_offsets( + cmd_buf, + vk_bind_point, + shd->vk.pip_layout, + _SG_VK_VIEW_SMP_DESCRIPTORSET_INDEX, // firstSet + 1, // setCount + &dbuf_index, + &dbuf_offset); + _sg_stats_inc(vk.num_cmd_set_descriptor_buffer_offsets); + return true; +} + +_SOKOL_PRIVATE bool _sg_vk_bind_uniform_descriptor_set(VkCommandBuffer cmd_buf) { + SOKOL_ASSERT(cmd_buf); + SOKOL_ASSERT(_sg.vk.uniforms.dirty); + _sg.vk.uniforms.dirty = false; + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + + // get next pointer in descriptor buffer + const VkDeviceSize dbuf_offset = _sg_vk_shared_buffer_alloc(&_sg.vk.bind, (uint32_t)shd->vk.ub_dset_size); + if (_sg.vk.bind.overflown) { + _SG_ERROR(VULKAN_DESCRIPTOR_BUFFER_OVERFLOW); + return false; + } + _sg_stats_add(vk.size_descriptor_buffer_writes, (uint32_t)shd->vk.ub_dset_size); + uint8_t* dbuf_ptr = _sg_vk_shared_buffer_ptr(&_sg.vk.bind, dbuf_offset); + + // update descriptor buffer + SOKOL_ASSERT(shd->vk.ub_dset_size <= _sg.vk.uniforms.dset_cache_size); + memcpy(dbuf_ptr, _sg.vk.uniforms.dset_cache, shd->vk.ub_dset_size); + + // record the descriptor buffer offset + const VkPipelineBindPoint vk_bind_point = _sg.cur_pass.is_compute + ? VK_PIPELINE_BIND_POINT_COMPUTE + : VK_PIPELINE_BIND_POINT_GRAPHICS; + const uint32_t dbuf_index = 0; + SOKOL_ASSERT(shd->vk.pip_layout); + _sg.vk.ext.cmd_set_descriptor_buffer_offsets( + cmd_buf, + vk_bind_point, + shd->vk.pip_layout, + _SG_VK_UB_DESCRIPTORSET_INDEX, // firstIndex + 1, // setCount + &dbuf_index, + &dbuf_offset); + _sg_stats_inc(vk.num_cmd_set_descriptor_buffer_offsets); + return true; +} + +_SOKOL_PRIVATE void _sg_vk_memory_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + _sg_vk_mem_free_device_memory((VkDeviceMemory)obj); +} + +_SOKOL_PRIVATE void _sg_vk_buffer_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyBuffer(_sg.vk.dev, (VkBuffer)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_image_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyImage(_sg.vk.dev, (VkImage)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_image_view_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyImageView(_sg.vk.dev, (VkImageView)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_sampler_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroySampler(_sg.vk.dev, (VkSampler)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_shader_module_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyShaderModule(_sg.vk.dev, (VkShaderModule)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_pipelinelayout_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyPipelineLayout(_sg.vk.dev, (VkPipelineLayout)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_descriptorsetlayout_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyDescriptorSetLayout(_sg.vk.dev, (VkDescriptorSetLayout)obj, 0); +} + +_SOKOL_PRIVATE void _sg_vk_pipeline_destructor(void* obj) { + SOKOL_ASSERT(_sg.vk.dev && obj); + vkDestroyPipeline(_sg.vk.dev, (VkPipeline)obj, 0); +} + +_SOKOL_PRIVATE VkBufferUsageFlags _sg_vk_buffer_usage(const sg_buffer_usage* usg) { + VkBufferUsageFlags res = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + if (usg->vertex_buffer) { + res |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + } + if (usg->index_buffer) { + res |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + } + if (usg->storage_buffer) { + res |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + } + return res; +} + +_SOKOL_PRIVATE VkVertexInputRate _sg_vk_vertex_input_rate(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE; +} + +_SOKOL_PRIVATE VkFormat _sg_vk_vertex_format(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return VK_FORMAT_R32_SFLOAT; + case SG_VERTEXFORMAT_FLOAT2: return VK_FORMAT_R32G32_SFLOAT; + case SG_VERTEXFORMAT_FLOAT3: return VK_FORMAT_R32G32B32_SFLOAT; + case SG_VERTEXFORMAT_FLOAT4: return VK_FORMAT_R32G32B32A32_SFLOAT; + case SG_VERTEXFORMAT_INT: return VK_FORMAT_R32_SINT; + case SG_VERTEXFORMAT_INT2: return VK_FORMAT_R32G32_SINT; + case SG_VERTEXFORMAT_INT3: return VK_FORMAT_R32G32B32_SINT; + case SG_VERTEXFORMAT_INT4: return VK_FORMAT_R32G32B32A32_SINT; + case SG_VERTEXFORMAT_UINT: return VK_FORMAT_R32_UINT; + case SG_VERTEXFORMAT_UINT2: return VK_FORMAT_R32G32_UINT; + case SG_VERTEXFORMAT_UINT3: return VK_FORMAT_R32G32B32_UINT; + case SG_VERTEXFORMAT_UINT4: return VK_FORMAT_R32G32B32A32_UINT; + case SG_VERTEXFORMAT_BYTE4: return VK_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return VK_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return VK_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return VK_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return VK_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return VK_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_USHORT2: return VK_FORMAT_R16G16_UINT; + case SG_VERTEXFORMAT_USHORT2N: return VK_FORMAT_R16G16_UNORM; + case SG_VERTEXFORMAT_SHORT4: return VK_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return VK_FORMAT_R16G16B16A16_SNORM; + case SG_VERTEXFORMAT_USHORT4: return VK_FORMAT_R16G16B16A16_UINT; + case SG_VERTEXFORMAT_USHORT4N: return VK_FORMAT_R16G16B16A16_UNORM; + case SG_VERTEXFORMAT_UINT10_N2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; + case SG_VERTEXFORMAT_HALF2: return VK_FORMAT_R16G16_SFLOAT; + case SG_VERTEXFORMAT_HALF4: return VK_FORMAT_R16G16B16A16_SFLOAT; + default: + SOKOL_UNREACHABLE; + return VK_FORMAT_UNDEFINED; + } +} + +_SOKOL_PRIVATE VkImageCreateFlags _sg_vk_image_create_flags(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return 0; + case SG_IMAGETYPE_CUBE: return VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + // FIXME: VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT needed for render to slice? + case SG_IMAGETYPE_3D: return 0; + case SG_IMAGETYPE_ARRAY: return 0; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE VkImageType _sg_vk_image_type(sg_image_type t) { + return (SG_IMAGETYPE_3D == t) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; +} + +_SOKOL_PRIVATE VkImageUsageFlags _sg_vk_image_usage(const sg_image_usage* usg) { + VkImageUsageFlags res = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + res |= VK_IMAGE_USAGE_SAMPLED_BIT; + if (usg->storage_image) { + res |= VK_IMAGE_USAGE_STORAGE_BIT; + } + if (usg->color_attachment || usg->resolve_attachment) { + res |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (usg->depth_stencil_attachment) { + res |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + return res; +} + +_SOKOL_PRIVATE VkFormat _sg_vk_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_NONE: return VK_FORMAT_UNDEFINED; + case SG_PIXELFORMAT_R8: return VK_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: return VK_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return VK_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: return VK_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: return VK_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: return VK_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: return VK_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: return VK_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: return VK_FORMAT_R16_SFLOAT; + case SG_PIXELFORMAT_RG8: return VK_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: return VK_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: return VK_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: return VK_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: return VK_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: return VK_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: return VK_FORMAT_R32_SFLOAT; + case SG_PIXELFORMAT_RG16: return VK_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: return VK_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: return VK_FORMAT_R16G16_UINT; + case SG_PIXELFORMAT_RG16SI: return VK_FORMAT_R16G16_SINT; + case SG_PIXELFORMAT_RG16F: return VK_FORMAT_R16G16_SFLOAT; + case SG_PIXELFORMAT_RGBA8: return VK_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_SRGB8A8: return VK_FORMAT_R8G8B8A8_SRGB; + case SG_PIXELFORMAT_RGBA8SN: return VK_FORMAT_R8G8B8A8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT; + case SG_PIXELFORMAT_RGBA8SI: return VK_FORMAT_R8G8B8A8_SINT; + case SG_PIXELFORMAT_BGRA8: return VK_FORMAT_B8G8R8A8_UNORM; + case SG_PIXELFORMAT_RGB10A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; + case SG_PIXELFORMAT_RG11B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + case SG_PIXELFORMAT_RGB9E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case SG_PIXELFORMAT_RG32UI: return VK_FORMAT_R32G32_UINT; + case SG_PIXELFORMAT_RG32SI: return VK_FORMAT_R32G32_SINT; + case SG_PIXELFORMAT_RG32F: return VK_FORMAT_R32G32_SFLOAT; + case SG_PIXELFORMAT_RGBA16: return VK_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: return VK_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: return VK_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: return VK_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; + case SG_PIXELFORMAT_RGBA32UI: return VK_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: return VK_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; + case SG_PIXELFORMAT_DEPTH: return VK_FORMAT_D32_SFLOAT; + case SG_PIXELFORMAT_DEPTH_STENCIL: return VK_FORMAT_D32_SFLOAT_S8_UINT; + case SG_PIXELFORMAT_BC1_RGBA: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; + case SG_PIXELFORMAT_BC2_RGBA: return VK_FORMAT_BC2_UNORM_BLOCK; + case SG_PIXELFORMAT_BC3_RGBA: return VK_FORMAT_BC3_UNORM_BLOCK; + case SG_PIXELFORMAT_BC3_SRGBA: return VK_FORMAT_BC3_SRGB_BLOCK; + case SG_PIXELFORMAT_BC4_R: return VK_FORMAT_BC4_UNORM_BLOCK; + case SG_PIXELFORMAT_BC4_RSN: return VK_FORMAT_BC4_SNORM_BLOCK; + case SG_PIXELFORMAT_BC5_RG: return VK_FORMAT_BC5_UNORM_BLOCK; + case SG_PIXELFORMAT_BC5_RGSN: return VK_FORMAT_BC5_SNORM_BLOCK; + case SG_PIXELFORMAT_BC6H_RGBF: return VK_FORMAT_BC6H_SFLOAT_BLOCK; + case SG_PIXELFORMAT_BC6H_RGBUF: return VK_FORMAT_BC6H_UFLOAT_BLOCK; + case SG_PIXELFORMAT_BC7_RGBA: return VK_FORMAT_BC7_UNORM_BLOCK; + case SG_PIXELFORMAT_BC7_SRGBA: return VK_FORMAT_BC7_SRGB_BLOCK; + case SG_PIXELFORMAT_ETC2_RGB8: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + case SG_PIXELFORMAT_ETC2_RGB8A1: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; + case SG_PIXELFORMAT_ETC2_RGBA8: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + case SG_PIXELFORMAT_ETC2_SRGB8: return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; + case SG_PIXELFORMAT_EAC_R11: return VK_FORMAT_EAC_R11_UNORM_BLOCK; + case SG_PIXELFORMAT_EAC_R11SN: return VK_FORMAT_EAC_R11_SNORM_BLOCK; + case SG_PIXELFORMAT_EAC_RG11: return VK_FORMAT_EAC_R11G11_UNORM_BLOCK; + case SG_PIXELFORMAT_EAC_RG11SN: return VK_FORMAT_EAC_R11G11_SNORM_BLOCK; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return VK_FORMAT_ASTC_4x4_SRGB_BLOCK; + default: return VK_FORMAT_UNDEFINED; + }; +} + +_SOKOL_PRIVATE VkPrimitiveTopology _sg_vk_primitive_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case SG_PRIMITIVETYPE_LINES: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + default: + SOKOL_UNREACHABLE; + return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; + } +} + +_SOKOL_PRIVATE VkCullModeFlags _sg_vk_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return VK_CULL_MODE_NONE; + case SG_CULLMODE_FRONT: return VK_CULL_MODE_FRONT_BIT; + case SG_CULLMODE_BACK: return VK_CULL_MODE_BACK_BIT; + default: + SOKOL_UNREACHABLE; + return VK_CULL_MODE_NONE; + } +} + +_SOKOL_PRIVATE VkFrontFace _sg_vk_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE; +} + +_SOKOL_PRIVATE VkCompareOp _sg_vk_compare_op(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return VK_COMPARE_OP_NEVER; + case SG_COMPAREFUNC_LESS: return VK_COMPARE_OP_LESS; + case SG_COMPAREFUNC_EQUAL: return VK_COMPARE_OP_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return VK_COMPARE_OP_LESS_OR_EQUAL; + case SG_COMPAREFUNC_GREATER: return VK_COMPARE_OP_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return VK_COMPARE_OP_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return VK_COMPARE_OP_ALWAYS; + default: + SOKOL_UNREACHABLE; + return VK_COMPARE_OP_ALWAYS; + } +} + +_SOKOL_PRIVATE VkStencilOp _sg_vk_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return VK_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return VK_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return VK_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return VK_STENCIL_OP_INCREMENT_AND_CLAMP; + case SG_STENCILOP_DECR_CLAMP: return VK_STENCIL_OP_DECREMENT_AND_CLAMP; + case SG_STENCILOP_INVERT: return VK_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return VK_STENCIL_OP_INCREMENT_AND_WRAP; + case SG_STENCILOP_DECR_WRAP: return VK_STENCIL_OP_DECREMENT_AND_WRAP; + default: + SOKOL_UNREACHABLE; + return VK_STENCIL_OP_KEEP; + } +} + +_SOKOL_PRIVATE VkBlendOp _sg_vk_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return VK_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return VK_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT; + case SG_BLENDOP_MIN: return VK_BLEND_OP_MIN; + case SG_BLENDOP_MAX: return VK_BLEND_OP_MAX; + default: + SOKOL_UNREACHABLE; + return VK_BLEND_OP_ADD; + } +} + +_SOKOL_PRIVATE VkBlendFactor _sg_vk_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return VK_BLEND_FACTOR_ZERO; + case SG_BLENDFACTOR_ONE: return VK_BLEND_FACTOR_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return VK_BLEND_FACTOR_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return VK_BLEND_FACTOR_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return VK_BLEND_FACTOR_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; + case SG_BLENDFACTOR_SRC1_COLOR: return VK_BLEND_FACTOR_SRC1_COLOR ; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; + case SG_BLENDFACTOR_SRC1_ALPHA: return VK_BLEND_FACTOR_SRC1_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; + default: + SOKOL_UNREACHABLE; + return VK_BLEND_FACTOR_ONE; + } +} + +_SOKOL_PRIVATE VkColorComponentFlags _sg_vk_color_write_mask(sg_color_mask m) { + int res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= (int)VK_COLOR_COMPONENT_R_BIT; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= (int)VK_COLOR_COMPONENT_G_BIT; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= (int)VK_COLOR_COMPONENT_B_BIT; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= (int)VK_COLOR_COMPONENT_A_BIT; + } + return (VkColorComponentFlags)res; +} + +_SOKOL_PRIVATE VkShaderStageFlags _sg_vk_shader_stage(sg_shader_stage s) { + switch (s) { + case SG_SHADERSTAGE_VERTEX: return VK_SHADER_STAGE_VERTEX_BIT; + case SG_SHADERSTAGE_FRAGMENT: return VK_SHADER_STAGE_FRAGMENT_BIT; + case SG_SHADERSTAGE_COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE VkAttachmentLoadOp _sg_vk_load_op(sg_load_action a) { + switch (a) { + case SG_LOADACTION_CLEAR: + return VK_ATTACHMENT_LOAD_OP_CLEAR; + case SG_LOADACTION_DONTCARE: + return VK_ATTACHMENT_LOAD_OP_DONT_CARE; + default: + return VK_ATTACHMENT_LOAD_OP_LOAD; + } +} + +_SOKOL_PRIVATE VkAttachmentStoreOp _sg_vk_store_op(sg_store_action a) { + switch (a) { + case SG_STOREACTION_STORE: + return VK_ATTACHMENT_STORE_OP_STORE; + default: + return VK_ATTACHMENT_STORE_OP_DONT_CARE; + } +} + +_SOKOL_PRIVATE VkIndexType _sg_vk_index_type(sg_index_type t) { + return (t == SG_INDEXTYPE_UINT16) ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; +} + +_SOKOL_PRIVATE VkImageViewType _sg_vk_texture_image_view_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return VK_IMAGE_VIEW_TYPE_2D; + case SG_IMAGETYPE_CUBE: return VK_IMAGE_VIEW_TYPE_CUBE; + case SG_IMAGETYPE_3D: return VK_IMAGE_VIEW_TYPE_3D; + case SG_IMAGETYPE_ARRAY: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return VK_IMAGE_VIEW_TYPE_2D; + } +} + +_SOKOL_PRIVATE VkImageViewType _sg_vk_attachment_image_view_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return VK_IMAGE_VIEW_TYPE_2D; + case SG_IMAGETYPE_CUBE: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; // not a bug + case SG_IMAGETYPE_3D: return VK_IMAGE_VIEW_TYPE_2D; // not a bug + case SG_IMAGETYPE_ARRAY: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return VK_IMAGE_VIEW_TYPE_2D; + } +} + +_SOKOL_PRIVATE VkFilter _sg_vk_sampler_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: return VK_FILTER_NEAREST; + case SG_FILTER_LINEAR: return VK_FILTER_LINEAR; + default: SOKOL_UNREACHABLE; return VK_FILTER_NEAREST; + } +} + +_SOKOL_PRIVATE VkSamplerMipmapMode _sg_vk_sampler_mipmap_mode(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case SG_FILTER_LINEAR: return VK_SAMPLER_MIPMAP_MODE_LINEAR; + default: SOKOL_UNREACHABLE; return VK_SAMPLER_MIPMAP_MODE_NEAREST; + } +} + +_SOKOL_PRIVATE VkSamplerAddressMode _sg_vk_sampler_address_mode(sg_wrap w) { + switch (w) { + case SG_WRAP_REPEAT: return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case SG_WRAP_CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case SG_WRAP_CLAMP_TO_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + case SG_WRAP_MIRRORED_REPEAT: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return VK_SAMPLER_ADDRESS_MODE_REPEAT; + } +} + +_SOKOL_PRIVATE VkBorderColor _sg_vk_sampler_border_color(sg_border_color c) { + switch (c) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + case SG_BORDERCOLOR_OPAQUE_BLACK: return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + case SG_BORDERCOLOR_OPAQUE_WHITE: return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + default: SOKOL_UNREACHABLE; return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + } +} + +_SOKOL_PRIVATE void _sg_vk_load_ext_funcs(void) { + SOKOL_ASSERT(_sg.vk.dev); + #if defined(SOKOL_DEBUG) + _sg.vk.ext.set_debug_utils_object_name_ext = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(_sg.vk.instance, "vkSetDebugUtilsObjectNameEXT"); + if (0 == _sg.vk.ext.set_debug_utils_object_name_ext) { + _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING); + } + #endif + _sg.vk.ext.get_descriptor_set_layout_size = (PFN_vkGetDescriptorSetLayoutSizeEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkGetDescriptorSetLayoutSizeEXT"); + if (0 == _sg.vk.ext.get_descriptor_set_layout_size) { + _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING); + } + _sg.vk.ext.get_descriptor_set_layout_binding_offset = (PFN_vkGetDescriptorSetLayoutBindingOffsetEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkGetDescriptorSetLayoutBindingOffsetEXT"); + if (0 == _sg.vk.ext.get_descriptor_set_layout_binding_offset) { + _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING); + } + _sg.vk.ext.get_descriptor = (PFN_vkGetDescriptorEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkGetDescriptorEXT"); + if (0 == _sg.vk.ext.get_descriptor) { + _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING); + } + _sg.vk.ext.cmd_bind_descriptor_buffers = (PFN_vkCmdBindDescriptorBuffersEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkCmdBindDescriptorBuffersEXT"); + if (0 == _sg.vk.ext.cmd_bind_descriptor_buffers) { + _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING); + } + _sg.vk.ext.cmd_set_descriptor_buffer_offsets = (PFN_vkCmdSetDescriptorBufferOffsetsEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkCmdSetDescriptorBufferOffsetsEXT"); + if (0 == _sg.vk.ext.cmd_set_descriptor_buffer_offsets) { + _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING); + } +} + +_SOKOL_PRIVATE void _sg_vk_init_caps(void) { + _sg.backend = SG_BACKEND_VULKAN; + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = false; // FIXME? + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.compute = true; + _sg.features.msaa_texture_bindings = true; + _sg.features.draw_base_vertex = true; + _sg.features.draw_base_instance = true; + _sg.features.dual_source_blending = true; + + SOKOL_ASSERT(_sg.vk.phys_dev); + _sg.vk.descriptor_buffer_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; + _sg.vk.dev_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + _sg.vk.dev_props.pNext = &_sg.vk.descriptor_buffer_props; + vkGetPhysicalDeviceProperties2(_sg.vk.phys_dev, &_sg.vk.dev_props); + _sg.vk.dev_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + vkGetPhysicalDeviceFeatures2(_sg.vk.phys_dev, &_sg.vk.dev_features); + + const VkPhysicalDeviceLimits* l = &_sg.vk.dev_props.properties.limits; + _sg.limits.max_image_size_2d = (int)l->maxImageDimension2D; + _sg.limits.max_image_size_cube = (int)l->maxImageDimensionCube; + _sg.limits.max_image_size_3d = (int)l->maxImageDimension3D; + _sg.limits.max_image_size_array = _sg.limits.max_image_size_2d; + _sg.limits.max_image_array_layers = (int)l->maxImageArrayLayers; + _sg.limits.max_vertex_attrs = _sg_min((int)l->maxVertexInputAttributes, SG_MAX_VERTEX_ATTRIBUTES); + _sg.limits.max_color_attachments = _sg_min((int)l->maxFragmentOutputAttachments, SG_MAX_COLOR_ATTACHMENTS); + _sg.limits.max_texture_bindings_per_stage = _sg_min((int)l->maxPerStageDescriptorSampledImages, SG_MAX_VIEW_BINDSLOTS); + _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min((int)l->maxPerStageDescriptorStorageBuffers, SG_MAX_VIEW_BINDSLOTS); + _sg.limits.max_storage_image_bindings_per_stage = _sg_min((int)l->maxPerStageDescriptorStorageImages, SG_MAX_VIEW_BINDSLOTS); + _sg.limits.vk_min_uniform_buffer_offset_alignment = (int)l->minUniformBufferOffsetAlignment; + + // FIXME: currently these are the same as in the WebGPU backend + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + + if (_sg.vk.dev_features.features.textureCompressionBC) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + } + + if (_sg.vk.dev_features.features.textureCompressionETC2) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + } + + if (_sg.vk.dev_features.features.textureCompressionASTC_LDR) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } + + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); +} + +_SOKOL_PRIVATE void _sg_vk_create_fences(void) { + SOKOL_ASSERT(_sg.vk.dev); + _SG_STRUCT(VkFenceCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + SOKOL_ASSERT(0 == _sg.vk.frame.slot[i].fence); + VkResult res = vkCreateFence(_sg.vk.dev, &create_info, 0, &_sg.vk.frame.slot[i].fence); + SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.slot[i].fence); _SOKOL_UNUSED(res); + } +} + +_SOKOL_PRIVATE void _sg_vk_destroy_fences(void) { + SOKOL_ASSERT(_sg.vk.dev); + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + SOKOL_ASSERT(_sg.vk.frame.slot[i].fence); + vkDestroyFence(_sg.vk.dev, _sg.vk.frame.slot[i].fence, 0); + _sg.vk.frame.slot[i].fence = 0; + } +} + +_SOKOL_PRIVATE void _sg_vk_create_frame_command_pool_and_buffers(void) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(0 == _sg.vk.frame.cmd_pool); + _SG_STRUCT(VkCommandPoolCreateInfo, pool_create_info); + pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + // FIXME: transient bit when the cmd buffers are reset each frame? + pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_create_info.queueFamilyIndex = _sg.vk.queue_family_index; + VkResult res = vkCreateCommandPool(_sg.vk.dev, &pool_create_info, 0, &_sg.vk.frame.cmd_pool); + SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.cmd_pool); _SOKOL_UNUSED(res); + + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _SG_STRUCT(VkCommandBufferAllocateInfo, cmdbuf_alloc_info); + cmdbuf_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf_alloc_info.commandPool = _sg.vk.frame.cmd_pool; + cmdbuf_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdbuf_alloc_info.commandBufferCount = 1; + res = vkAllocateCommandBuffers(_sg.vk.dev, &cmdbuf_alloc_info, &_sg.vk.frame.slot[i].command_buffer); + SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.slot[i].command_buffer); + res = vkAllocateCommandBuffers(_sg.vk.dev, &cmdbuf_alloc_info, &_sg.vk.frame.slot[i].stream_command_buffer); + SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.slot[i].stream_command_buffer); + } +} + +_SOKOL_PRIVATE void _sg_vk_destroy_frame_command_pool(void) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.frame.cmd_pool); + SOKOL_ASSERT(0 == _sg.vk.frame.cmd_buf); + SOKOL_ASSERT(0 == _sg.vk.frame.stream_cmd_buf); + // NOTE: command buffers owned by the pool will be automatically destroyed + vkDestroyCommandPool(_sg.vk.dev, _sg.vk.frame.cmd_pool, 0); + _sg.vk.frame.cmd_pool = 0; + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + SOKOL_ASSERT(_sg.vk.frame.slot[i].command_buffer); + _sg.vk.frame.slot[i].command_buffer = 0; + _sg.vk.frame.slot[i].stream_command_buffer = 0; + } +} + +_SOKOL_PRIVATE void _sg_vk_acquire_frame_command_buffers(void) { + SOKOL_ASSERT(_sg.vk.dev); + VkResult res; + if (0 == _sg.vk.frame.cmd_buf) { + SOKOL_ASSERT(0 == _sg.vk.frame.stream_cmd_buf); + _sg.vk.frame_slot = (_sg.vk.frame_slot + 1) % SG_NUM_INFLIGHT_FRAMES; + // block until oldest inflight-frame has finished + do { + res = vkWaitForFences(_sg.vk.dev, + 1, + &_sg.vk.frame.slot[_sg.vk.frame_slot].fence, + VK_TRUE, + UINT64_MAX); + } while (res == VK_TIMEOUT); + if (res != VK_SUCCESS) { + _SG_WARN(VULKAN_WAIT_FOR_FENCE_FAILED); + _sg.cur_pass.valid = false; + return; + } + res = vkResetFences(_sg.vk.dev, 1, &_sg.vk.frame.slot[_sg.vk.frame_slot].fence); + SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res); + + _sg_vk_delete_queue_collect(); + + _sg.vk.frame.cmd_buf = _sg.vk.frame.slot[_sg.vk.frame_slot].command_buffer; + res = vkResetCommandBuffer(_sg.vk.frame.cmd_buf, 0); + SOKOL_ASSERT(res == VK_SUCCESS); + _sg.vk.frame.stream_cmd_buf = _sg.vk.frame.slot[_sg.vk.frame_slot].stream_command_buffer; + res = vkResetCommandBuffer(_sg.vk.frame.stream_cmd_buf, 0); + SOKOL_ASSERT(res == VK_SUCCESS); + + _SG_STRUCT(VkCommandBufferBeginInfo, cmdbuf_begin_info); + cmdbuf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + res = vkBeginCommandBuffer(_sg.vk.frame.cmd_buf, &cmdbuf_begin_info); + SOKOL_ASSERT(res == VK_SUCCESS); + res = vkBeginCommandBuffer(_sg.vk.frame.stream_cmd_buf, &cmdbuf_begin_info); + SOKOL_ASSERT(res == VK_SUCCESS); + + _sg_vk_uniform_after_acquire(); + _sg_vk_bind_after_acquire(); + _sg_vk_staging_stream_after_acquire(); + } + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); +} + +_SOKOL_PRIVATE void _sg_vk_submit_frame_command_buffers(void) { + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + SOKOL_ASSERT(_sg.vk.frame.stream_cmd_buf); + VkResult res; + _SOKOL_UNUSED(res); + + _sg_vk_staging_stream_before_submit(); + _sg_vk_bind_before_submit(); + _sg_vk_uniform_before_submit(); + + res = vkEndCommandBuffer(_sg.vk.frame.stream_cmd_buf); + SOKOL_ASSERT(res == VK_SUCCESS); + res = vkEndCommandBuffer(_sg.vk.frame.cmd_buf); + SOKOL_ASSERT(res == VK_SUCCESS); + + _SG_STRUCT(VkSubmitInfo, submit_infos[2]); + // streaming-update command buffer + submit_infos[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_infos[0].commandBufferCount = 1; + submit_infos[0].pCommandBuffers = &_sg.vk.frame.stream_cmd_buf; + // render command buffer + const VkPipelineStageFlags present_wait_dst_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + submit_infos[1].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_infos[1].waitSemaphoreCount = 1; + submit_infos[1].pWaitSemaphores = &_sg.vk.present_complete_sem; + submit_infos[1].pWaitDstStageMask = &present_wait_dst_stage_mask; + submit_infos[1].commandBufferCount = 1; + submit_infos[1].pCommandBuffers = &_sg.vk.frame.cmd_buf; + submit_infos[1].signalSemaphoreCount = 1; + submit_infos[1].pSignalSemaphores = &_sg.vk.render_finished_sem; + res = vkQueueSubmit(_sg.vk.queue, 2, submit_infos, _sg.vk.frame.slot[_sg.vk.frame_slot].fence); + SOKOL_ASSERT(res == VK_SUCCESS); + + _sg.vk.frame.cmd_buf = 0; + _sg.vk.frame.stream_cmd_buf = 0; + + // NOTE: it's valid to register resource objects for destruction in the + // delete queue past this point (between _sg_vk_submit_frame_command_buffer() + // and the next _sg_vk_acquire_frame_command_buffer()) since resources which are + // destroyed in this 'gap' can at most have been used by the command + // buffer that was just submitted +} + +_SOKOL_PRIVATE void _sg_vk_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.vulkan.instance); + SOKOL_ASSERT(desc->environment.vulkan.physical_device); + SOKOL_ASSERT(desc->environment.vulkan.device); + SOKOL_ASSERT(desc->environment.vulkan.queue); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg.vk.valid = true; + _sg.vk.instance = (VkInstance) desc->environment.vulkan.instance; + _sg.vk.phys_dev = (VkPhysicalDevice) desc->environment.vulkan.physical_device; + _sg.vk.dev = (VkDevice) desc->environment.vulkan.device; + _sg.vk.queue = (VkQueue) desc->environment.vulkan.queue; + _sg.vk.queue_family_index = desc->environment.vulkan.queue_family_index; + + _sg_track_init(&_sg.vk.track.buffers, _sg.pools.buffer_pool.size); + _sg_track_init(&_sg.vk.track.images, _sg.pools.image_pool.size); + _sg_vk_load_ext_funcs(); + _sg_vk_init_caps(); + _sg_vk_create_fences(); + _sg_vk_create_frame_command_pool_and_buffers(); + _sg_vk_staging_copy_init(); + _sg_vk_staging_stream_init(); + _sg_vk_uniform_init(); + _sg_vk_bind_init(); + _sg_vk_create_delete_queues(); +} + +_SOKOL_PRIVATE void _sg_vk_discard_backend(void) { + SOKOL_ASSERT(_sg.vk.valid); + SOKOL_ASSERT(_sg.vk.dev); + vkDeviceWaitIdle(_sg.vk.dev); + _sg_vk_destroy_delete_queues(); + _sg_vk_bind_discard(); + _sg_vk_uniform_discard(); + _sg_vk_staging_stream_discard(); + _sg_vk_staging_copy_discard(); + _sg_vk_destroy_frame_command_pool(); + _sg_vk_destroy_fences(); + _sg_track_discard(&_sg.vk.track.images); + _sg_track_discard(&_sg.vk.track.buffers); + _sg.vk.valid = false; +} + +_SOKOL_PRIVATE void _sg_vk_reset_state_cache(void) { + // nothing to do here +} + +_SOKOL_PRIVATE sg_resource_state _sg_vk_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(buf->cmn.size > 0); + SOKOL_ASSERT(0 == buf->vk.buf); + SOKOL_ASSERT(0 == buf->vk.mem); + SOKOL_ASSERT(0 == buf->vk.dev_addr); + VkResult res; + // FIXME: inject external buffer + + buf->vk.cur_access = _SG_VK_ACCESS_NONE; + + _SG_STRUCT(VkBufferCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + create_info.size = (VkDeviceSize)buf->cmn.size; + create_info.usage = _sg_vk_buffer_usage(&buf->cmn.usage); + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + res = vkCreateBuffer(_sg.vk.dev, &create_info, 0, &buf->vk.buf); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(buf->vk.buf); + _sg_vk_set_object_label(VK_OBJECT_TYPE_BUFFER, (uint64_t)buf->vk.buf, desc->label); + + if (!_sg_vk_mem_alloc_buffer_device_memory(buf)) { + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(buf->vk.mem); + res = vkBindBufferMemory(_sg.vk.dev, buf->vk.buf, buf->vk.mem, 0); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_BIND_BUFFER_MEMORY_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (buf->cmn.usage.storage_buffer) { + _SG_STRUCT(VkBufferDeviceAddressInfo, addr_info); + addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + addr_info.buffer = buf->vk.buf; + buf->vk.dev_addr = vkGetBufferDeviceAddress(_sg.vk.dev, &addr_info); + SOKOL_ASSERT(buf->vk.dev_addr); + } + if (buf->cmn.usage.immutable && desc->data.ptr) { + _sg_vk_staging_copy_buffer_data(buf, &desc->data, 0, false); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_vk_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _sg_track_remove(&_sg.vk.track.buffers, buf->slot.id); + if (buf->vk.buf) { + _sg_vk_delete_queue_add(_sg_vk_buffer_destructor, (void*)buf->vk.buf); + buf->vk.buf = 0; + } + if (buf->vk.mem) { + _sg_vk_delete_queue_add(_sg_vk_memory_destructor, (void*)buf->vk.mem); + buf->vk.mem = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_vk_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + VkResult res; + // FIXME: injected images + + img->vk.cur_access = _SG_VK_ACCESS_NONE; + + _SG_STRUCT(VkImageCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + create_info.flags = _sg_vk_image_create_flags(img->cmn.type); + create_info.imageType = _sg_vk_image_type(img->cmn.type); + create_info.format = _sg_vk_format(desc->pixel_format); + create_info.extent.width = (uint32_t)img->cmn.width; + create_info.extent.height = (uint32_t)img->cmn.height; + if (desc->type == SG_IMAGETYPE_3D) { + create_info.extent.depth = (uint32_t)img->cmn.num_slices; + create_info.arrayLayers = 1; + } else { + create_info.extent.depth = 1; + create_info.arrayLayers = (uint32_t)img->cmn.num_slices; + } + create_info.mipLevels = (uint32_t)img->cmn.num_mipmaps; + create_info.samples = (VkSampleCountFlagBits)desc->sample_count; + create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + create_info.usage = _sg_vk_image_usage(&img->cmn.usage); + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + res = vkCreateImage(_sg.vk.dev, &create_info, 0, &img->vk.img); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_IMAGE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(img->vk.img); + _sg_vk_set_object_label(VK_OBJECT_TYPE_IMAGE, (uint64_t)img->vk.img, desc->label); + + if (!_sg_vk_mem_alloc_image_device_memory(img)) { + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(img->vk.mem); + res = vkBindImageMemory(_sg.vk.dev, img->vk.img, img->vk.mem, 0); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_BIND_IMAGE_MEMORY_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (img->cmn.usage.immutable && desc->data.mip_levels[0].ptr) { + _sg_vk_staging_copy_image_data(img, &desc->data, false); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_vk_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _sg_track_remove(&_sg.vk.track.images, img->slot.id); + if (img->vk.img) { + _sg_vk_delete_queue_add(_sg_vk_image_destructor, (void*)img->vk.img); + img->vk.img = 0; + } + if (img->vk.mem) { + _sg_vk_delete_queue_add(_sg_vk_memory_destructor, (void*)img->vk.mem); + img->vk.mem = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_vk_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(0 == smp->vk.smp); + // FIXME: injection + + // create sampler object + _SG_STRUCT(VkSamplerCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + create_info.magFilter = _sg_vk_sampler_minmag_filter(desc->mag_filter); + create_info.minFilter = _sg_vk_sampler_minmag_filter(desc->min_filter); + create_info.mipmapMode = _sg_vk_sampler_mipmap_mode(desc->mipmap_filter); + create_info.addressModeU = _sg_vk_sampler_address_mode(desc->wrap_u); + create_info.addressModeV = _sg_vk_sampler_address_mode(desc->wrap_v); + create_info.addressModeW = _sg_vk_sampler_address_mode(desc->wrap_w); + create_info.mipLodBias = 0.0f; + if (desc->max_anisotropy > 1) { + create_info.anisotropyEnable = VK_TRUE; + create_info.maxAnisotropy = (float)desc->max_anisotropy; + } + if (desc->compare != SG_COMPAREFUNC_NEVER) { + create_info.compareEnable = VK_TRUE; + create_info.compareOp = _sg_vk_compare_op(desc->compare); + } + create_info.minLod = desc->min_lod; + create_info.maxLod = desc->max_lod; + create_info.borderColor = _sg_vk_sampler_border_color(desc->border_color); + VkResult res = vkCreateSampler(_sg.vk.dev, &create_info, 0, &smp->vk.smp); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(smp->vk.smp); + _sg_vk_set_object_label(VK_OBJECT_TYPE_SAMPLER, (uint64_t)smp->vk.smp, desc->label); + + // record sampler descriptor data + smp->vk.descriptor_size = _sg.vk.descriptor_buffer_props.samplerDescriptorSize; + if (_SG_VK_MAX_DESCRIPTOR_DATA_SIZE < smp->vk.descriptor_size) { + _SG_ERROR(VULKAN_SAMPLER_MAX_DESCRIPTOR_SIZE); + return SG_RESOURCESTATE_FAILED; + } + _SG_STRUCT(VkDescriptorGetInfoEXT, get_info); + get_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT; + get_info.type = VK_DESCRIPTOR_TYPE_SAMPLER; + get_info.data.pSampler = &smp->vk.smp; + _sg.vk.ext.get_descriptor(_sg.vk.dev, &get_info, smp->vk.descriptor_size, &smp->vk.descriptor_data); + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_vk_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->vk.smp) { + _sg_vk_delete_queue_add(_sg_vk_sampler_destructor, (void*)smp->vk.smp); + smp->vk.smp = 0; + } +} + +_SOKOL_PRIVATE _sg_vk_shader_func_t _sg_vk_create_shader_func(const sg_shader_function* func, const char* label) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(func); + SOKOL_ASSERT(func->bytecode.ptr && (func->bytecode.size > 0)); + SOKOL_ASSERT(func->entry); + + _SG_STRUCT(_sg_vk_shader_func_t, vk_func); + _sg_strcpy(&vk_func.entry, func->entry); + + _SG_STRUCT(VkShaderModuleCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = func->bytecode.size; + create_info.pCode = (uint32_t*)func->bytecode.ptr; + VkResult res = vkCreateShaderModule(_sg.vk.dev, &create_info, 0, &vk_func.module); + if (VK_SUCCESS != res) { + _SG_ERROR(VULKAN_CREATE_SHADER_MODULE_FAILED); + } else { + SOKOL_ASSERT(vk_func.module); + _sg_vk_set_object_label(VK_OBJECT_TYPE_SHADER_MODULE, (uint64_t)vk_func.module, label); + } + return vk_func; +} + +_SOKOL_PRIVATE void _sg_vk_discard_shader_func(_sg_vk_shader_func_t* func) { + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(func); + if (func->module) { + _sg_vk_delete_queue_add(_sg_vk_shader_module_destructor, (void*)func->module); + func->module = 0; + } +} + +_SOKOL_PRIVATE bool _sg_vk_ensure_spirv_bindslot_ranges(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* ub = &desc->uniform_blocks[i]; + if (ub->stage != SG_SHADERSTAGE_NONE) { + if (ub->spirv_set0_binding_n >= _SG_VK_MAX_UB_DESCRIPTORSET_SLOTS) { + _SG_ERROR(VULKAN_UNIFORMBLOCK_SPIRV_SET0_BINDING_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &desc->views[i]; + if (view->texture.stage != SG_SHADERSTAGE_NONE) { + if (view->texture.spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) { + _SG_ERROR(VULKAN_TEXTURE_SPIRV_SET1_BINDING_OUT_OF_RANGE); + return false; + } + } + if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_buffer.spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) { + _SG_ERROR(VULKAN_STORAGEBUFFER_SPIRV_SET1_BINDING_OUT_OF_RANGE); + return false; + } + } + if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + if (view->storage_image.spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) { + _SG_ERROR(VULKAN_STORAGEIMAGE_SPIRV_SET1_BINDING_OUT_OF_RANGE); + return false; + } + } + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const sg_shader_sampler* smp = &desc->samplers[i]; + if (smp->stage != SG_SHADERSTAGE_NONE) { + if (smp->spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) { + _SG_ERROR(VULKAN_SAMPLER_SPIRV_SET1_BINDING_OUT_OF_RANGE); + return false; + } + } + } + return true; +} + +_SOKOL_PRIVATE sg_resource_state _sg_vk_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(shd->vk.vertex_func.module == 0); + SOKOL_ASSERT(shd->vk.fragment_func.module == 0); + SOKOL_ASSERT(shd->vk.compute_func.module == 0); + SOKOL_ASSERT(shd->vk.ub_dsl == 0); + SOKOL_ASSERT(shd->vk.view_smp_dsl == 0); + + if (!_sg_vk_ensure_spirv_bindslot_ranges(desc)) { + return SG_RESOURCESTATE_FAILED; + } + + // build shader modules + bool shd_valid = true; + if (desc->vertex_func.bytecode.ptr) { + shd->vk.vertex_func = _sg_vk_create_shader_func(&desc->vertex_func, desc->label); + shd_valid &= shd->vk.vertex_func.module != 0; + } + if (desc->fragment_func.bytecode.ptr) { + shd->vk.fragment_func = _sg_vk_create_shader_func(&desc->fragment_func, desc->label); + shd_valid &= shd->vk.fragment_func.module != 0; + } + if (desc->compute_func.bytecode.ptr) { + shd->vk.compute_func = _sg_vk_create_shader_func(&desc->compute_func, desc->label); + shd_valid &= shd->vk.compute_func.module != 0; + } + if (!shd_valid) { + _sg_vk_discard_shader_func(&shd->vk.vertex_func); + _sg_vk_discard_shader_func(&shd->vk.fragment_func); + _sg_vk_discard_shader_func(&shd->vk.compute_func); + return SG_RESOURCESTATE_FAILED; + } + + // descriptor set layouts and pipeline layout + VkResult res; + _SG_STRUCT(VkDescriptorSetLayoutBinding, dsl_entries[_SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES]); + _SG_STRUCT(VkDescriptorSetLayoutCreateInfo, dsl_create_info); + uint32_t dsl_index = 0; + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + shd->vk.ub_set0_bnd_n[i] = desc->uniform_blocks[i].spirv_set0_binding_n; + VkDescriptorSetLayoutBinding* dsl_entry = &dsl_entries[dsl_index]; + dsl_entry->binding = shd->vk.ub_set0_bnd_n[i]; + dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + dsl_entry->descriptorCount = 1; + dsl_entry->stageFlags = _sg_vk_shader_stage(shd->cmn.uniform_blocks[i].stage); + dsl_index += 1; + } + dsl_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + dsl_create_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + dsl_create_info.bindingCount = dsl_index; + dsl_create_info.pBindings = dsl_entries; + res = vkCreateDescriptorSetLayout(_sg.vk.dev, &dsl_create_info, 0, &shd->vk.ub_dsl); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_DESCRIPTOR_SET_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + // store uniform descriptor set size and descriptor offsets + _sg.vk.ext.get_descriptor_set_layout_size(_sg.vk.dev, shd->vk.ub_dsl, &shd->vk.ub_dset_size); + if (shd->vk.ub_dset_size > _sg.vk.uniforms.dset_cache_size) { + _SG_ERROR(VULKAN_SHADER_UNIFORM_DESCRIPTOR_SET_SIZE_VS_CACHE_SIZE); + return SG_RESOURCESTATE_FAILED; + } + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const uint8_t vk_bnd = shd->vk.ub_set0_bnd_n[i]; + VkDeviceSize dset_offset = 0; + _sg.vk.ext.get_descriptor_set_layout_binding_offset(_sg.vk.dev, shd->vk.ub_dsl, vk_bnd, &dset_offset); + shd->vk.ub_dset_offsets[i] = (uint16_t)dset_offset; + } + + _sg_clear(dsl_entries, sizeof(dsl_entries)); + _sg_clear(&dsl_create_info, sizeof(dsl_create_info)); + dsl_index = 0; + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + SOKOL_ASSERT(dsl_index < _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES); + VkDescriptorSetLayoutBinding* dsl_entry = &dsl_entries[dsl_index]; + dsl_entry->stageFlags = _sg_vk_shader_stage(shd->cmn.views[i].stage); + if (shd->cmn.views[i].view_type == SG_VIEWTYPE_TEXTURE) { + shd->vk.view_set1_bnd_n[i] = desc->views[i].texture.spirv_set1_binding_n; + dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) { + shd->vk.view_set1_bnd_n[i] = desc->views[i].storage_buffer.spirv_set1_binding_n; + dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEIMAGE) { + shd->vk.view_set1_bnd_n[i] = desc->views[i].storage_image.spirv_set1_binding_n; + dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + } else { + SOKOL_UNREACHABLE; + } + dsl_entry->binding = shd->vk.view_set1_bnd_n[i]; + dsl_entry->descriptorCount = 1; + dsl_index += 1; + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + shd->vk.smp_set1_bnd_n[i] = desc->samplers[i].spirv_set1_binding_n; + SOKOL_ASSERT(dsl_index < _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES); + VkDescriptorSetLayoutBinding* dsl_entry = &dsl_entries[dsl_index]; + dsl_entry->binding = shd->vk.smp_set1_bnd_n[i]; + dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + dsl_entry->descriptorCount = 1; + dsl_entry->stageFlags = _sg_vk_shader_stage(shd->cmn.samplers[i].stage); + dsl_index += 1; + } + dsl_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + dsl_create_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + dsl_create_info.bindingCount = dsl_index; + dsl_create_info.pBindings = dsl_entries; + res = vkCreateDescriptorSetLayout(_sg.vk.dev, &dsl_create_info, 0, &shd->vk.view_smp_dsl); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_DESCRIPTOR_SET_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + // store view/smp descriptor set size and descriptor offsets + _sg.vk.ext.get_descriptor_set_layout_size(_sg.vk.dev, shd->vk.view_smp_dsl, &shd->vk.view_smp_dset_size); + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const uint8_t vk_bnd = shd->vk.view_set1_bnd_n[i]; + VkDeviceSize dset_offset = 0; + _sg.vk.ext.get_descriptor_set_layout_binding_offset(_sg.vk.dev, shd->vk.view_smp_dsl, vk_bnd, &dset_offset); + shd->vk.view_dset_offsets[i] = (uint16_t)dset_offset; + } + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) { + continue; + } + const uint8_t vk_bnd = shd->vk.smp_set1_bnd_n[i]; + VkDeviceSize dset_offset = 0; + _sg.vk.ext.get_descriptor_set_layout_binding_offset(_sg.vk.dev, shd->vk.view_smp_dsl, vk_bnd, &dset_offset); + shd->vk.smp_dset_offsets[i] = (uint16_t)dset_offset; + } + + VkDescriptorSetLayout set_layouts[_SG_VK_NUM_DESCRIPTORSETS] = { + shd->vk.ub_dsl, + shd->vk.view_smp_dsl, + }; + _SG_STRUCT(VkPipelineLayoutCreateInfo, pl_create_info); + pl_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pl_create_info.setLayoutCount = _SG_VK_NUM_DESCRIPTORSETS; + pl_create_info.pSetLayouts = set_layouts; + res = vkCreatePipelineLayout(_sg.vk.dev, &pl_create_info, 0, &shd->vk.pip_layout); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_PIPELINE_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_vk_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + SOKOL_ASSERT(_sg.vk.dev); + _sg_vk_discard_shader_func(&shd->vk.vertex_func); + _sg_vk_discard_shader_func(&shd->vk.fragment_func); + _sg_vk_discard_shader_func(&shd->vk.compute_func); + if (shd->vk.pip_layout) { + _sg_vk_delete_queue_add(_sg_vk_pipelinelayout_destructor, (void*)shd->vk.pip_layout); + shd->vk.pip_layout = 0; + } + if (shd->vk.ub_dsl) { + _sg_vk_delete_queue_add(_sg_vk_descriptorsetlayout_destructor, (void*)shd->vk.ub_dsl); + shd->vk.ub_dsl = 0; + } + if (shd->vk.view_smp_dsl) { + _sg_vk_delete_queue_add(_sg_vk_descriptorsetlayout_destructor, (void*)shd->vk.view_smp_dsl); + shd->vk.view_smp_dsl = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_vk_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + SOKOL_ASSERT(_sg.vk.dev); + VkResult res; + + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(shd->vk.pip_layout); + if (pip->cmn.is_compute) { + SOKOL_ASSERT(shd->vk.compute_func.module); + _SG_STRUCT(VkComputePipelineCreateInfo, pip_create_info); + pip_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pip_create_info.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + pip_create_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + pip_create_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + pip_create_info.stage.module = shd->vk.compute_func.module; + pip_create_info.stage.pName = shd->vk.compute_func.entry.buf; + pip_create_info.layout = shd->vk.pip_layout; + res = vkCreateComputePipelines(_sg.vk.dev, VK_NULL_HANDLE, 1, &pip_create_info, 0, &pip->vk.pip); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_COMPUTE_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } else { + uint32_t num_stages = 0; + _SG_STRUCT(VkPipelineShaderStageCreateInfo, stages[2]); + if (shd->vk.vertex_func.module) { + stages[num_stages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[num_stages].stage = VK_SHADER_STAGE_VERTEX_BIT; + stages[num_stages].module = shd->vk.vertex_func.module; + stages[num_stages].pName = shd->vk.vertex_func.entry.buf; + num_stages += 1; + } + if (shd->vk.fragment_func.module) { + stages[num_stages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[num_stages].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stages[num_stages].module = shd->vk.fragment_func.module; + stages[num_stages].pName = shd->vk.fragment_func.entry.buf; + num_stages += 1; + } + + uint32_t num_vtx_bnds = 0; + _SG_STRUCT(VkVertexInputBindingDescription, vtx_bnds[SG_MAX_VERTEXBUFFER_BINDSLOTS]); + for (uint32_t vbl_idx = 0; vbl_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vbl_idx++, num_vtx_bnds++) { + const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vbl_idx]; + if (0 == vbl_state->stride) { + break; + } + vtx_bnds[vbl_idx].binding = vbl_idx; + vtx_bnds[vbl_idx].stride = (uint32_t)vbl_state->stride; + vtx_bnds[vbl_idx].inputRate = _sg_vk_vertex_input_rate(vbl_state->step_func); + } + + uint32_t num_vtx_attrs = 0; + _SG_STRUCT(VkVertexInputAttributeDescription, vtx_attrs[SG_MAX_VERTEX_ATTRIBUTES]); + for (uint32_t va_idx = 0; va_idx < SG_MAX_VERTEX_ATTRIBUTES; va_idx++, num_vtx_attrs++) { + const sg_vertex_attr_state* va_state = &desc->layout.attrs[va_idx]; + if (SG_VERTEXFORMAT_INVALID == va_state->format) { + break; + } + const uint32_t vbl_idx = (uint32_t)va_state->buffer_index; + SOKOL_ASSERT(vbl_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS); + SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[vbl_idx]); + vtx_attrs[va_idx].location = va_idx; + vtx_attrs[va_idx].binding = vbl_idx; + vtx_attrs[va_idx].format = _sg_vk_vertex_format(va_state->format); + vtx_attrs[va_idx].offset = (uint32_t)va_state->offset; + } + + _SG_STRUCT(VkPipelineVertexInputStateCreateInfo, vi_state); + vi_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vi_state.vertexBindingDescriptionCount = num_vtx_bnds; + vi_state.pVertexBindingDescriptions = vtx_bnds; + vi_state.vertexAttributeDescriptionCount = num_vtx_attrs; + vi_state.pVertexAttributeDescriptions = vtx_attrs; + + _SG_STRUCT(VkPipelineInputAssemblyStateCreateInfo, ia_state); + ia_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia_state.topology = _sg_vk_primitive_topology(desc->primitive_type); + ia_state.primitiveRestartEnable = VK_FALSE; // FIXME: needs 'primitiveTopologyRestart feature enabled' + + _SG_STRUCT(VkPipelineViewportStateCreateInfo, vp_state); + vp_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vp_state.viewportCount = 1; + vp_state.scissorCount = 1; + + _SG_STRUCT(VkPipelineRasterizationStateCreateInfo, rs_state); + rs_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rs_state.depthClampEnable = false; + rs_state.rasterizerDiscardEnable = false; + rs_state.polygonMode = VK_POLYGON_MODE_FILL; + rs_state.cullMode = _sg_vk_cullmode(desc->cull_mode); + rs_state.frontFace = _sg_vk_frontface(desc->face_winding); + rs_state.depthBiasEnable = ((int32_t)desc->depth.bias) != 0; + rs_state.depthBiasConstantFactor = desc->depth.bias; + rs_state.depthBiasClamp = desc->depth.bias_clamp; + rs_state.depthBiasSlopeFactor = desc->depth.bias_slope_scale; + rs_state.lineWidth = 1.0f; + + _SG_STRUCT(VkPipelineMultisampleStateCreateInfo, ms_state); + ms_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms_state.rasterizationSamples = (VkSampleCountFlagBits)desc->sample_count; + ms_state.alphaToCoverageEnable = desc->alpha_to_coverage_enabled; + + _SG_STRUCT(VkPipelineDepthStencilStateCreateInfo, ds_state); + ds_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds_state.depthTestEnable = desc->depth.compare != SG_COMPAREFUNC_ALWAYS; + ds_state.depthWriteEnable = desc->depth.write_enabled; + ds_state.depthCompareOp = _sg_vk_compare_op(desc->depth.compare); + ds_state.depthBoundsTestEnable = false; + ds_state.stencilTestEnable = desc->stencil.enabled; + ds_state.front.failOp = _sg_vk_stencil_op(desc->stencil.front.fail_op); + ds_state.front.passOp = _sg_vk_stencil_op(desc->stencil.front.pass_op); + ds_state.front.depthFailOp = _sg_vk_stencil_op(desc->stencil.front.depth_fail_op); + ds_state.front.compareOp = _sg_vk_compare_op(desc->stencil.front.compare); + ds_state.front.compareMask = desc->stencil.read_mask; + ds_state.front.writeMask = desc->stencil.write_mask; + ds_state.front.reference = desc->stencil.ref; + ds_state.back.failOp = _sg_vk_stencil_op(desc->stencil.back.fail_op); + ds_state.back.passOp = _sg_vk_stencil_op(desc->stencil.back.pass_op); + ds_state.back.depthFailOp = _sg_vk_stencil_op(desc->stencil.back.depth_fail_op); + ds_state.back.compareOp = _sg_vk_compare_op(desc->stencil.back.compare); + ds_state.back.compareMask = desc->stencil.read_mask; + ds_state.back.writeMask = desc->stencil.write_mask; + ds_state.back.reference = desc->stencil.ref; + + _SG_STRUCT(VkPipelineColorBlendAttachmentState, att_states[SG_MAX_COLOR_ATTACHMENTS]); + SOKOL_ASSERT(desc->color_count < SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < desc->color_count; i++) { + att_states[i].blendEnable = desc->colors[i].blend.enabled; + att_states[i].srcColorBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.src_factor_rgb); + att_states[i].dstColorBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.dst_factor_rgb); + att_states[i].colorBlendOp = _sg_vk_blend_op(desc->colors[i].blend.op_rgb); + att_states[i].srcAlphaBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.src_factor_alpha); + att_states[i].dstAlphaBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.dst_factor_alpha); + att_states[i].alphaBlendOp = _sg_vk_blend_op(desc->colors[i].blend.op_alpha); + att_states[i].colorWriteMask = _sg_vk_color_write_mask(desc->colors[i].write_mask); + } + + _SG_STRUCT(VkPipelineColorBlendStateCreateInfo, cb_state); + cb_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + cb_state.logicOpEnable = false; + cb_state.attachmentCount = (uint32_t)desc->color_count; + cb_state.pAttachments = att_states; + cb_state.blendConstants[0] = desc->blend_color.r; + cb_state.blendConstants[1] = desc->blend_color.g; + cb_state.blendConstants[2] = desc->blend_color.b; + cb_state.blendConstants[3] = desc->blend_color.a; + + _SG_STRUCT(VkFormat, color_formats[SG_MAX_COLOR_ATTACHMENTS]); + SOKOL_ASSERT(desc->color_count <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < desc->color_count; i++) { + color_formats[i] = _sg_vk_format(desc->colors[i].pixel_format); + } + _SG_STRUCT(VkPipelineRenderingCreateInfo, rnd_state); + rnd_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + rnd_state.colorAttachmentCount = (uint32_t)desc->color_count; + rnd_state.pColorAttachmentFormats = color_formats; + rnd_state.depthAttachmentFormat = _sg_vk_format(desc->depth.pixel_format); + if (_sg_is_depth_stencil_format(desc->depth.pixel_format)) { + rnd_state.stencilAttachmentFormat = _sg_vk_format(desc->depth.pixel_format); + } else { + rnd_state.stencilAttachmentFormat = VK_FORMAT_UNDEFINED; + } + VkDynamicState dyn_states[2] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + _SG_STRUCT(VkPipelineDynamicStateCreateInfo, dyn_state); + dyn_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dyn_state.dynamicStateCount = 2; + dyn_state.pDynamicStates = dyn_states; + + _SG_STRUCT(VkGraphicsPipelineCreateInfo, pip_create_info); + pip_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pip_create_info.pNext = &rnd_state; + pip_create_info.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + pip_create_info.stageCount = num_stages; + pip_create_info.pStages = stages; + pip_create_info.pVertexInputState = &vi_state; + pip_create_info.pInputAssemblyState = &ia_state; + pip_create_info.pViewportState = &vp_state; + pip_create_info.pRasterizationState = &rs_state; + pip_create_info.pMultisampleState = &ms_state; + pip_create_info.pDepthStencilState = &ds_state; + pip_create_info.pColorBlendState = &cb_state; + pip_create_info.pDynamicState = &dyn_state; + pip_create_info.layout = shd->vk.pip_layout; + + res = vkCreateGraphicsPipelines(_sg.vk.dev, VK_NULL_HANDLE, 1, &pip_create_info, 0, &pip->vk.pip); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_GRAPHICS_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + SOKOL_ASSERT(pip->vk.pip); + _sg_vk_set_object_label(VK_OBJECT_TYPE_PIPELINE, (uint64_t)pip->vk.pip, desc->label); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_vk_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip->vk.pip) { + _sg_vk_delete_queue_add(_sg_vk_pipeline_destructor, (void*)pip->vk.pip); + pip->vk.pip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_vk_create_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && desc); + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(0 == view->vk.img_view); + VkResult res; + _SG_STRUCT(VkDescriptorGetInfoEXT, get_info); + get_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT; + if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + // record descriptor data for storage buffer + view->vk.descriptor_size = _sg.vk.descriptor_buffer_props.storageBufferDescriptorSize; + if (_SG_VK_MAX_DESCRIPTOR_DATA_SIZE < view->vk.descriptor_size) { + _SG_ERROR(VULKAN_VIEW_MAX_DESCRIPTOR_SIZE); + return SG_RESOURCESTATE_FAILED; + } + const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + SOKOL_ASSERT(buf->vk.dev_addr); + _SG_STRUCT(VkDescriptorAddressInfoEXT, addr_info); + addr_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT; + addr_info.address = buf->vk.dev_addr + (VkDeviceSize)view->cmn.buf.offset; + addr_info.range = (VkDeviceSize)(buf->cmn.size - view->cmn.buf.offset); + get_info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + get_info.data.pStorageBuffer = &addr_info; + _sg.vk.ext.get_descriptor(_sg.vk.dev, &get_info, view->vk.descriptor_size, &view->vk.descriptor_data); + } else { + // create image view object + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + SOKOL_ASSERT(img->vk.img); + SOKOL_ASSERT(view->cmn.img.mip_level_count >= 1); + SOKOL_ASSERT(view->cmn.img.slice_count >= 1); + _SG_STRUCT(VkImageViewCreateInfo, create_info); + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.image = img->vk.img; + if (view->cmn.type == SG_VIEWTYPE_TEXTURE) { + create_info.viewType = _sg_vk_texture_image_view_type(img->cmn.type); + } else { + create_info.viewType = _sg_vk_attachment_image_view_type(img->cmn.type); + } + create_info.format = _sg_vk_format(img->cmn.pixel_format); + if (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT) { + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + create_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + create_info.subresourceRange.baseMipLevel = (uint32_t)view->cmn.img.mip_level; + create_info.subresourceRange.levelCount = (uint32_t)view->cmn.img.mip_level_count; + create_info.subresourceRange.baseArrayLayer = (uint32_t)view->cmn.img.slice; + create_info.subresourceRange.layerCount = (uint32_t)view->cmn.img.slice_count; + res = vkCreateImageView(_sg.vk.dev, &create_info, 0, &view->vk.img_view); + if (res != VK_SUCCESS) { + _SG_ERROR(VULKAN_CREATE_IMAGE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(view->vk.img_view); + _sg_vk_set_object_label(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)view->vk.img_view, desc->label); + + // record descriptor data for storage images and textures + if ((view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) || (view->cmn.type == SG_VIEWTYPE_TEXTURE)) { + _SG_STRUCT(VkDescriptorImageInfo, img_info); + img_info.imageView = view->vk.img_view; + if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) { + view->vk.descriptor_size = _sg.vk.descriptor_buffer_props.storageImageDescriptorSize; + img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + get_info.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + get_info.data.pStorageImage = &img_info; + } else { + view->vk.descriptor_size = _sg.vk.descriptor_buffer_props.sampledImageDescriptorSize; + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + get_info.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + get_info.data.pSampledImage = &img_info; + } + if (_SG_VK_MAX_DESCRIPTOR_DATA_SIZE < view->vk.descriptor_size) { + _SG_ERROR(VULKAN_VIEW_MAX_DESCRIPTOR_SIZE); + return SG_RESOURCESTATE_FAILED; + } + _sg.vk.ext.get_descriptor(_sg.vk.dev, &get_info, view->vk.descriptor_size, &view->vk.descriptor_data); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_vk_discard_view(_sg_view_t* view) { + SOKOL_ASSERT(view); + if (view->vk.img_view) { + _sg_vk_delete_queue_add(_sg_vk_image_view_destructor, (void*)view->vk.img_view); + view->vk.img_view = 0; + } +} + +_SOKOL_PRIVATE void _sg_vk_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + _SG_STRUCT(VkViewport, vp); + vp.x = (float) x; + vp.width = (float) w; + vp.height = (float) -h; + vp.maxDepth = 1.0f; + if (origin_top_left) { + vp.y = (float)(y + h); + } else { + vp.y = (float)(_sg.cur_pass.dim.height - y); + } + vkCmdSetViewport(_sg.vk.frame.cmd_buf, 0, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_vk_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height); + _SG_STRUCT(VkRect2D, rect); + rect.offset.x = clip.x; + rect.offset.y = (origin_top_left ? clip.y : (_sg.cur_pass.dim.height - (clip.y + clip.h))); + rect.extent.width = (uint32_t) clip.w; + rect.extent.height = (uint32_t) clip.h; + vkCmdSetScissor(_sg.vk.frame.cmd_buf, 0, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_vk_init_color_attachment_info(VkRenderingAttachmentInfo* info, const sg_color_attachment_action* action, VkImageView color_view, VkImageView resolve_view) { + info->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + info->imageView = color_view; + info->imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + if (resolve_view) { + info->resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT; + info->resolveImageView = resolve_view; + info->resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else { + info->resolveMode = VK_RESOLVE_MODE_NONE; + info->resolveImageView = 0; + info->resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + info->loadOp = _sg_vk_load_op(action->load_action); + info->storeOp = _sg_vk_store_op(action->store_action); + info->clearValue.color.float32[0] = action->clear_value.r; + info->clearValue.color.float32[1] = action->clear_value.g; + info->clearValue.color.float32[2] = action->clear_value.b; + info->clearValue.color.float32[3] = action->clear_value.a; +} + +_SOKOL_PRIVATE void _sg_vk_init_depth_attachment_info(VkRenderingAttachmentInfo* info, const sg_depth_attachment_action* action, VkImageView ds_view) { + info->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + info->imageView = ds_view; + info->imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + info->resolveMode = VK_RESOLVE_MODE_NONE; + info->loadOp = _sg_vk_load_op(action->load_action); + info->storeOp = _sg_vk_store_op(action->store_action); + info->clearValue.depthStencil.depth = action->clear_value; +} + +_SOKOL_PRIVATE void _sg_vk_init_stencil_attachment_info(VkRenderingAttachmentInfo* info, const sg_stencil_attachment_action* action, VkImageView ds_view) { + info->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + info->imageView = ds_view; + info->imageLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL; + info->resolveMode = VK_RESOLVE_MODE_NONE; + info->loadOp = _sg_vk_load_op(action->load_action); + info->storeOp = _sg_vk_store_op(action->store_action); + info->clearValue.depthStencil.stencil = action->clear_value; +} + +_SOKOL_PRIVATE void _sg_vk_begin_compute_pass(VkCommandBuffer cmd_buf, const sg_pass* pass) { + // FIXME: nothing to do here? + _SOKOL_UNUSED(cmd_buf && pass); +} + +_SOKOL_PRIVATE void _sg_vk_begin_render_pass(VkCommandBuffer cmd_buf, const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + const sg_pass_action* action = &pass->action; + const bool is_swapchain_pass = atts->empty; + + _SG_STRUCT(VkRenderingAttachmentInfo, color_att_infos[SG_MAX_COLOR_ATTACHMENTS]); + _SG_STRUCT(VkRenderingAttachmentInfo, depth_att_info); + _SG_STRUCT(VkRenderingAttachmentInfo, stencil_att_info); + _SG_STRUCT(VkRenderingInfo, render_info); + render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + render_info.renderArea.extent.width = (uint32_t)_sg.cur_pass.dim.width; + render_info.renderArea.extent.height = (uint32_t)_sg.cur_pass.dim.height; + render_info.layerCount = 1; + + if (is_swapchain_pass) { + _sg.vk.swapchain = pass->swapchain.vulkan; + SOKOL_ASSERT(_sg.vk.swapchain.render_view); + if (pass->swapchain.sample_count > 1) { + SOKOL_ASSERT(_sg.vk.swapchain.resolve_view); + } + SOKOL_ASSERT(_sg.vk.swapchain.present_complete_semaphore); + SOKOL_ASSERT(_sg.vk.swapchain.render_finished_semaphore); + // FIXME: need to support multiple present_complete_semaphores + SOKOL_ASSERT(0 == _sg.vk.present_complete_sem); + _sg.vk.present_complete_sem = (VkSemaphore)_sg.vk.swapchain.present_complete_semaphore; + if (0 == _sg.vk.render_finished_sem) { + _sg.vk.render_finished_sem = (VkSemaphore)_sg.vk.swapchain.render_finished_semaphore; + } else { + SOKOL_ASSERT(_sg.vk.render_finished_sem == _sg.vk.swapchain.render_finished_semaphore); + } + VkImageView vk_color_view = (VkImageView)_sg.vk.swapchain.render_view; + VkImageView vk_resolve_view = (VkImageView)_sg.vk.swapchain.resolve_view; + _sg_vk_init_color_attachment_info(&color_att_infos[0], &action->colors[0], vk_color_view, vk_resolve_view); + render_info.colorAttachmentCount = 1; + render_info.pColorAttachments = color_att_infos; + if (_sg.vk.swapchain.depth_stencil_view) { + VkImageView vk_ds_view = (VkImageView)_sg.vk.swapchain.depth_stencil_view; + const bool has_stencil = _sg_is_depth_stencil_format(pass->swapchain.depth_format); + _sg_vk_init_depth_attachment_info(&depth_att_info, &action->depth, vk_ds_view); + render_info.pDepthAttachment = &depth_att_info; + if (has_stencil) { + _sg_vk_init_stencil_attachment_info(&stencil_att_info, &action->stencil, vk_ds_view); + render_info.pStencilAttachment = &stencil_att_info; + } + } + } else { + SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < atts->num_color_views; i++) { + SOKOL_ASSERT(atts->color_views[i]); + const _sg_view_t* color_view = atts->color_views[i]; + VkImageView vk_color_view = color_view->vk.img_view; + const _sg_view_t* resolve_view = atts->resolve_views[i]; + VkImageView vk_resolve_view = 0; + if (resolve_view) { + vk_resolve_view = resolve_view->vk.img_view; + } + _sg_vk_init_color_attachment_info(&color_att_infos[i], &action->colors[i], vk_color_view, vk_resolve_view); + } + if (atts->num_color_views > 0) { + render_info.colorAttachmentCount = (uint32_t)atts->num_color_views; + render_info.pColorAttachments = color_att_infos; + } + if (atts->ds_view) { + const _sg_view_t* ds_view = atts->ds_view; + const _sg_image_t* ds_image = _sg_image_ref_ptr(&ds_view->cmn.img.ref); + const bool has_stencil = _sg_is_depth_stencil_format(ds_image->cmn.pixel_format); + VkImageView vk_ds_view = ds_view->vk.img_view; + _sg_vk_init_depth_attachment_info(&depth_att_info, &action->depth, vk_ds_view); + render_info.pDepthAttachment = &depth_att_info; + if (has_stencil) { + _sg_vk_init_stencil_attachment_info(&stencil_att_info, &action->stencil, vk_ds_view); + render_info.pStencilAttachment = &stencil_att_info; + } + } + } + vkCmdBeginRendering(cmd_buf, &render_info); + + _SG_STRUCT(VkViewport, vp); + vp.y = (float)_sg.cur_pass.dim.height; + vp.width = (float)_sg.cur_pass.dim.width; + vp.height = (float)-_sg.cur_pass.dim.height; + vp.maxDepth = 1.0f; + vkCmdSetViewport(_sg.vk.frame.cmd_buf, 0, 1, &vp); + + _SG_STRUCT(VkRect2D, rect); + rect.extent.width = (uint32_t)_sg.cur_pass.dim.width; + rect.extent.height = (uint32_t)_sg.cur_pass.dim.height; + vkCmdSetScissor(_sg.vk.frame.cmd_buf, 0, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_vk_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(pass && atts); + _sg_vk_acquire_frame_command_buffers(); + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + _sg_vk_barrier_on_begin_pass(_sg.vk.frame.cmd_buf, pass, atts, _sg.cur_pass.is_compute); + if (_sg.cur_pass.is_compute) { + _sg_vk_begin_compute_pass(_sg.vk.frame.cmd_buf, pass); + } else { + _sg_vk_begin_render_pass(_sg.vk.frame.cmd_buf, pass, atts); + } +} + +_SOKOL_PRIVATE void _sg_vk_end_pass(const _sg_attachments_ptrs_t* atts) { + SOKOL_ASSERT(atts); + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + if (!_sg.cur_pass.is_compute) { + vkCmdEndRendering(_sg.vk.frame.cmd_buf); + } + _sg_vk_barrier_on_end_pass(_sg.vk.frame.cmd_buf, atts, _sg.cur_pass.is_compute); + _sg_clear(&_sg.vk.swapchain, sizeof(_sg.vk.swapchain)); +} + +_SOKOL_PRIVATE void _sg_vk_commit(void) { + SOKOL_ASSERT(_sg.vk.queue); + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + _sg_vk_submit_frame_command_buffers(); + _sg.vk.present_complete_sem = 0; + _sg.vk.render_finished_sem = 0; +} + +_SOKOL_PRIVATE void _sg_vk_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->vk.pip); + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + _sg.vk.uniforms.dirty = false; + VkPipelineBindPoint bindpoint = pip->cmn.is_compute + ? VK_PIPELINE_BIND_POINT_COMPUTE + : VK_PIPELINE_BIND_POINT_GRAPHICS; + vkCmdBindPipeline(_sg.vk.frame.cmd_buf, bindpoint, pip->vk.pip); +} + +_SOKOL_PRIVATE bool _sg_vk_apply_bindings(_sg_bindings_ptrs_t* bnd) { + SOKOL_ASSERT(bnd && bnd->pip); + SOKOL_ASSERT(_sg.vk.dev); + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf; + + // track or insert pipeline barriers + _sg_vk_barrier_on_apply_bindings(cmd_buf, bnd, _sg.cur_pass.is_compute); + + if (!_sg.cur_pass.is_compute) { + // bind vertex buffers + // FIXME: could do this in a single call if buffer bindings are guaranteed + // to be continuous (currently that's not checked anywhere), or alternative + // via nullDescriptor robustness feature (which apparently may have performance downsides) + for (uint32_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + if (bnd->vbs[i]) { + VkBuffer vk_buf = bnd->vbs[i]->vk.buf; + VkDeviceSize vk_offset = (VkDeviceSize)bnd->vb_offsets[i]; + vkCmdBindVertexBuffers(cmd_buf, i, 1, &vk_buf, &vk_offset); + } + } + if (bnd->ib) { + VkBuffer vk_buf = bnd->ib->vk.buf; + VkDeviceSize vk_offset = (VkDeviceSize)bnd->ib_offset; + VkIndexType vk_index_type = _sg_vk_index_type(bnd->pip->cmn.index_type); + vkCmdBindIndexBuffer(cmd_buf, vk_buf, vk_offset, vk_index_type); + } + } + + // bind views and samplers + const VkPipelineBindPoint pip_bind_point = _sg.cur_pass.is_compute + ? VK_PIPELINE_BIND_POINT_COMPUTE + : VK_PIPELINE_BIND_POINT_GRAPHICS; + return _sg_vk_bind_view_smp_descriptor_set(cmd_buf, bnd, pip_bind_point); +} + +_SOKOL_PRIVATE void _sg_vk_apply_uniforms(int ub_slot, const sg_range* data) { + SOKOL_ASSERT(_sg.vk.uniforms.dbuf.cur_dev_addr); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size); + + // copy data into uniform buffer and keep track of uniform bind infos + const VkDeviceSize ubuf_offset = _sg_vk_uniform_copy(data); + if (_sg.vk.uniforms.dbuf.overflown) { + _SG_ERROR(VULKAN_UNIFORM_BUFFER_OVERFLOW); + _sg.next_draw_valid = false; + return; + } + _sg.vk.uniforms.addr_info[ub_slot].range = data->size; + _sg.vk.uniforms.addr_info[ub_slot].address = _sg.vk.uniforms.dbuf.cur_dev_addr + ubuf_offset; + + // copy uniform buffer descriptor data into intermediate sysmem buffer + // NOTE: letting vkGetDescriptorEXT write directly into the descriptor + // buffer has catastrophic performance on some Vulkan drivers, notably + // Intel's Windows driver + const size_t dsize = _sg.vk.descriptor_buffer_props.uniformBufferDescriptorSize; + SOKOL_ASSERT((shd->vk.ub_dset_offsets[ub_slot] + dsize) <= _sg.vk.uniforms.dset_cache_size); + uint8_t* dst_ptr = _sg.vk.uniforms.dset_cache + shd->vk.ub_dset_offsets[ub_slot]; + _sg.vk.ext.get_descriptor(_sg.vk.dev, &_sg.vk.uniforms.get_info[ub_slot], dsize, dst_ptr); + + // set uniforms dirty, applying the descriptor buffer offset is happens in draw/dispatch + _sg.vk.uniforms.dirty = true; +} + +_SOKOL_PRIVATE void _sg_vk_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf; + if (_sg.vk.uniforms.dirty) { + if (!_sg_vk_bind_uniform_descriptor_set(cmd_buf)) { + return; + } + } + if (_sg.use_indexed_draw) { + vkCmdDrawIndexed(cmd_buf, + (uint32_t)num_elements, + (uint32_t)num_instances, + (uint32_t)base_element, + base_vertex, + (uint32_t)base_instance); + } else { + vkCmdDraw(cmd_buf, + (uint32_t)num_elements, + (uint32_t)num_instances, + (uint32_t)base_element, + (uint32_t)base_instance); + } +} + +_SOKOL_PRIVATE void _sg_vk_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + SOKOL_ASSERT(_sg.vk.frame.cmd_buf); + VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf; + if (_sg.vk.uniforms.dirty) { + if (!_sg_vk_bind_uniform_descriptor_set(cmd_buf)) { + return; + } + } + vkCmdDispatch(cmd_buf, (uint32_t)num_groups_x, (uint32_t)num_groups_y, (uint32_t)num_groups_z); +} + +_SOKOL_PRIVATE void _sg_vk_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (buf->cmn.usage.stream_update) { + _sg_vk_acquire_frame_command_buffers(); + _sg_vk_staging_stream_buffer_data(buf, data, 0); + } else { + _sg_vk_staging_copy_buffer_data(buf, data, 0, true); + } +} + +_SOKOL_PRIVATE void _sg_vk_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(new_frame); + if (buf->cmn.usage.stream_update) { + _sg_vk_acquire_frame_command_buffers(); + _sg_vk_staging_stream_buffer_data(buf, data, (size_t)buf->cmn.append_pos); + } else { + _sg_vk_staging_copy_buffer_data(buf, data, (size_t)buf->cmn.append_pos, true); + } +} + +_SOKOL_PRIVATE void _sg_vk_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + if (img->cmn.usage.stream_update) { + _sg_vk_acquire_frame_command_buffers(); + _sg_vk_staging_stream_image_data(img, data); + } else { + _sg_vk_staging_copy_image_data(img, data, true); + } +} + +#endif + +// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>generic backend +static inline void _sg_setup_backend(const sg_desc* desc) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_setup_backend(desc); + #elif defined(SOKOL_METAL) + _sg_mtl_setup_backend(desc); + #elif defined(SOKOL_D3D11) + _sg_d3d11_setup_backend(desc); + #elif defined(SOKOL_WGPU) + _sg_wgpu_setup_backend(desc); + #elif defined(SOKOL_VULKAN) + _sg_vk_setup_backend(desc); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_setup_backend(desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_backend(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_backend(); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_backend(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_backend(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_backend(); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_backend(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_backend(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_reset_state_cache(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_reset_state_cache(); + #elif defined(SOKOL_METAL) + _sg_mtl_reset_state_cache(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_reset_state_cache(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_reset_state_cache(); + #elif defined(SOKOL_VULKAN) + _sg_vk_reset_state_cache(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_reset_state_cache(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_buffer(buf, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_buffer(buf, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_buffer(buf, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_buffer(buf, desc); + #elif defined(SOKOL_VULKAN) + return _sg_vk_create_buffer(buf, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_buffer(buf, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_buffer(_sg_buffer_t* buf) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_buffer(buf); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_buffer(buf); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_buffer(buf); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_buffer(buf); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_buffer(buf); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_buffer(buf); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_image(img, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_image(img, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_image(img, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_image(img, desc); + #elif defined(SOKOL_VULKAN) + return _sg_vk_create_image(img, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_image(img, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_image(_sg_image_t* img) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_image(img); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_image(img); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_image(img); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_image(img); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_image(img); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_image(img); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_sampler(smp, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_sampler(smp, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_sampler(smp, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_sampler(smp, desc); + #elif defined(SOKOL_VULKAN) + return _sg_vk_create_sampler(smp, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_sampler(smp, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_sampler(_sg_sampler_t* smp) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_sampler(smp); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_sampler(smp); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_sampler(smp); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_sampler(smp); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_sampler(smp); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_sampler(smp); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_shader(shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_shader(shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_shader(shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_shader(shd, desc); + #elif defined(SOKOL_VULKAN) + return _sg_vk_create_shader(shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_shader(shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_shader(_sg_shader_t* shd) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_shader(shd); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_shader(shd); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_shader(shd); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_shader(shd); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_shader(shd); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_shader(shd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pipeline(pip, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pipeline(pip, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pipeline(pip, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pipeline(pip, desc); + #elif defined(SOKOL_VULKAN) + return _sg_vk_create_pipeline(pip, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pipeline(pip, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_pipeline(pip); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_view(_sg_view_t* view, const sg_view_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_view(view, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_view(view, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_view(view, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_view(view, desc); + #elif defined(SOKOL_VULKAN) + return _sg_vk_create_view(view, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_view(view, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_view(_sg_view_t* view) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_view(view); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_view(view); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_view(view); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_view(view); + #elif defined(SOKOL_VULKAN) + _sg_vk_discard_view(view); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_view(view); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_begin_pass(pass, atts); + #elif defined(SOKOL_METAL) + _sg_mtl_begin_pass(pass, atts); + #elif defined(SOKOL_D3D11) + _sg_d3d11_begin_pass(pass, atts); + #elif defined(SOKOL_WGPU) + _sg_wgpu_begin_pass(pass, atts); + #elif defined(SOKOL_VULKAN) + _sg_vk_begin_pass(pass, atts); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_begin_pass(pass, atts); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_end_pass(const _sg_attachments_ptrs_t* atts) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_end_pass(atts); + #elif defined(SOKOL_METAL) + _sg_mtl_end_pass(atts); + #elif defined(SOKOL_D3D11) + _sg_d3d11_end_pass(atts); + #elif defined(SOKOL_WGPU) + _sg_wgpu_end_pass(atts); + #elif defined(SOKOL_VULKAN) + _sg_vk_end_pass(atts); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_end_pass(atts); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_VULKAN) + _sg_vk_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_VULKAN) + _sg_vk_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_pipeline(pip); + #elif defined(SOKOL_VULKAN) + _sg_vk_apply_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline bool _sg_apply_bindings(_sg_bindings_ptrs_t* bnd) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_apply_bindings(bnd); + #elif defined(SOKOL_METAL) + return _sg_mtl_apply_bindings(bnd); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_apply_bindings(bnd); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_apply_bindings(bnd); + #elif defined(SOKOL_VULKAN) + return _sg_vk_apply_bindings(bnd); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_apply_bindings(bnd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_uniforms(int ub_slot, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_uniforms(ub_slot, data); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_uniforms(ub_slot, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_uniforms(ub_slot, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_uniforms(ub_slot, data); + #elif defined(SOKOL_VULKAN) + _sg_vk_apply_uniforms(ub_slot, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_uniforms(ub_slot, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_index) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_draw(base_element, num_elements, num_instances, base_vertex, base_index); + #elif defined(SOKOL_METAL) + _sg_mtl_draw(base_element, num_elements, num_instances, base_vertex, base_index); + #elif defined(SOKOL_D3D11) + _sg_d3d11_draw(base_element, num_elements, num_instances, base_vertex, base_index); + #elif defined(SOKOL_WGPU) + _sg_wgpu_draw(base_element, num_elements, num_instances, base_vertex, base_index); + #elif defined(SOKOL_VULKAN) + _sg_vk_draw(base_element, num_elements, num_instances, base_vertex, base_index); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_draw(base_element, num_elements, num_instances, base_vertex, base_index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_dispatch(num_groups_x, num_groups_y, num_groups_z); + #elif defined(SOKOL_METAL) + _sg_mtl_dispatch(num_groups_x, num_groups_y, num_groups_z); + #elif defined(SOKOL_D3D11) + _sg_d3d11_dispatch(num_groups_x, num_groups_y, num_groups_z); + #elif defined(SOKOL_WGPU) + _sg_wgpu_dispatch(num_groups_x, num_groups_y, num_groups_z); + #elif defined(SOKOL_VULKAN) + _sg_vk_dispatch(num_groups_x, num_groups_y, num_groups_z); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_dispatch(num_groups_x, num_groups_y, num_groups_z); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_commit(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_commit(); + #elif defined(SOKOL_METAL) + _sg_mtl_commit(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_commit(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_commit(); + #elif defined(SOKOL_VULKAN) + _sg_vk_commit(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_commit(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_buffer(buf, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_buffer(buf, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_buffer(buf, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_buffer(buf, data); + #elif defined(SOKOL_VULKAN) + _sg_vk_update_buffer(buf, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_buffer(buf, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_METAL) + _sg_mtl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_D3D11) + _sg_d3d11_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_WGPU) + _sg_wgpu_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_VULKAN) + _sg_vk_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_append_buffer(buf, data, new_frame); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_image(img, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_image(img, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_image(img, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_image(img, data); + #elif defined(SOKOL_VULKAN) + _sg_vk_update_image(img, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_image(img, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_push_debug_group(const char* name) { + #if defined(SOKOL_METAL) + _sg_mtl_push_debug_group(name); + #else + _SOKOL_UNUSED(name); + #endif +} + +static inline void _sg_pop_debug_group(void) { + #if defined(SOKOL_METAL) + _sg_mtl_pop_debug_group(); + #endif +} + +// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ +// +// >>validation +#if defined(SOKOL_DEBUG) +_SOKOL_PRIVATE void _sg_validate_begin(void) { + _sg.validate_error = SG_LOGITEM_OK; +} + +_SOKOL_PRIVATE bool _sg_validate_end(void) { + if (_sg.validate_error != SG_LOGITEM_OK) { + #if !defined(SOKOL_VALIDATE_NON_FATAL) + _SG_PANIC(VALIDATION_FAILED); + return false; + #else + return false; + #endif + } else { + return true; + } +} +#endif + +_SOKOL_PRIVATE bool _sg_one(bool b0, bool b1, bool b2) { + return (b0 && !b1 && !b2) || (!b0 && b1 && !b2) || (!b0 && !b1 && b2); +} + +_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _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_EXPECT_NONZERO_SIZE); + _SG_VALIDATE(_sg_one(desc->usage.immutable, desc->usage.dynamic_update, desc->usage.stream_update), VALIDATE_BUFFERDESC_IMMUTABLE_DYNAMIC_STREAM); + if (_sg.features.separate_buffer_types) { + _SG_VALIDATE(_sg_one(desc->usage.vertex_buffer, desc->usage.index_buffer, desc->usage.storage_buffer), VALIDATE_BUFFERDESC_SEPARATE_BUFFER_TYPES); + } + bool injected = (0 != desc->gl_buffers[0]) || + (0 != desc->mtl_buffers[0]) || + (0 != desc->d3d11_buffer) || + (0 != desc->wgpu_buffer); + if (!injected && desc->usage.immutable) { + if (desc->data.ptr) { + _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_EXPECT_MATCHING_DATA_SIZE); + } else { + _SG_VALIDATE(desc->usage.storage_buffer, VALIDATE_BUFFERDESC_EXPECT_DATA); + _SG_VALIDATE(desc->data.size == 0, VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE); + } + } else { + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_EXPECT_NO_DATA); + _SG_VALIDATE(desc->data.size == 0, VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE); + } + if (desc->usage.storage_buffer) { + _SG_VALIDATE(_sg.features.compute, VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED); + _SG_VALIDATE(_sg_multiple_u64(desc->size, 4), VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_mips, int num_slices) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(fmt); + _SOKOL_UNUSED(width); + _SOKOL_UNUSED(height); + _SOKOL_UNUSED(num_mips); + _SOKOL_UNUSED(num_slices); + #else + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const bool has_data = data->mip_levels[mip_index].ptr != 0; + const bool has_size = data->mip_levels[mip_index].size > 0; + _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); + const int mip_width = _sg_miplevel_dim(width, mip_index); + const int mip_height = _sg_miplevel_dim(height, mip_index); + const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const int expected_size = bytes_per_slice * num_slices; + _SG_VALIDATE(expected_size == (int)data->mip_levels[mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); + } + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + const sg_image_usage* usg = &desc->usage; + const bool any_attachment = usg->color_attachment || usg->resolve_attachment || usg->depth_stencil_attachment; + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(_sg_one(usg->immutable, usg->dynamic_update, usg->stream_update), VALIDATE_IMAGEDESC_IMMUTABLE_DYNAMIC_STREAM); + switch (desc->type) { + case SG_IMAGETYPE_2D: + _SG_VALIDATE(desc->num_slices == 1, VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES); + break; + case SG_IMAGETYPE_CUBE: + _SG_VALIDATE(desc->num_slices == 6, VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES); + break; + case SG_IMAGETYPE_ARRAY: + _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_array_layers), VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES); + break; + case SG_IMAGETYPE_3D: + _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_size_3d), VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES); + break; + default: + SOKOL_UNREACHABLE; + break; + } + _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 bool injected = (0 != desc->gl_textures[0]) || + (0 != desc->mtl_textures[0]) || + (0 != desc->d3d11_texture) || + (0 != desc->wgpu_texture); + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); + } + if (any_attachment || usg->storage_image) { + SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_IMMUTABLE); + _SG_VALIDATE(desc->data.mip_levels[0].ptr==0, VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_NO_DATA); + if (any_attachment) { + _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_ATTACHMENT_PIXELFORMAT); + if (usg->resolve_attachment) { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_ATTACHMENT_RESOLVE_EXPECT_NO_MSAA); + } + if (desc->sample_count > 1) { + _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_ATTACHMENT_NO_MSAA_SUPPORT); + _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_NUM_MIPMAPS); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_ARRAY, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_ARRAY_IMAGE); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_3D_IMAGE); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_CUBE, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_CUBE_IMAGE); + } + } else if (usg->storage_image) { + _SG_VALIDATE(_sg_is_valid_storage_image_format(fmt), VALIDATE_IMAGEDESC_STORAGEIMAGE_PIXELFORMAT); + // D3D11 doesn't allow multisampled UAVs (see: https://github.com/gpuweb/gpuweb/issues/513) + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_MSAA); + } + } else { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT); + const bool valid_nonrt_fmt = !_sg_is_valid_attachment_depth_format(fmt); + _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); + if (is_compressed) { + _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + } + if (!injected && usg->immutable) { + // image desc must have valid data + _sg_validate_image_data(&desc->data, + desc->pixel_format, + desc->width, + desc->height, + desc->num_mipmaps, + desc->num_slices); + } else { + // image desc must not have data + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + const bool no_data = 0 == desc->data.mip_levels[mip_index].ptr; + const bool no_size = 0 == desc->data.mip_levels[mip_index].size; + if (injected) { + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + } + if (!usg->immutable) { + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + } + } + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + // restriction from WebGPU: when anisotropy > 1, all filters must be linear + if (desc->max_anisotropy > 1) { + _SG_VALIDATE((desc->min_filter == SG_FILTER_LINEAR) + && (desc->mag_filter == SG_FILTER_LINEAR) + && (desc->mipmap_filter == SG_FILTER_LINEAR), + VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + } + return _sg_validate_end(); + #endif +} + +typedef struct { + uint64_t lo, hi; +} _sg_u128_t; + +_SOKOL_PRIVATE _sg_u128_t _sg_u128(void) { + _SG_STRUCT(_sg_u128_t, res); + return res; +} + +_SOKOL_PRIVATE _sg_u128_t _sg_validate_set_slot_bit(_sg_u128_t bits, sg_shader_stage stage, uint8_t slot) { + switch (stage) { + case SG_SHADERSTAGE_NONE: + SOKOL_ASSERT(slot < 128); + if (slot < 64) { + bits.lo |= 1ULL << slot; + } else { + bits.hi |= 1ULL << (slot - 64); + } + break; + case SG_SHADERSTAGE_VERTEX: + SOKOL_ASSERT(slot < 64); + bits.lo |= 1ULL << slot; + break; + case SG_SHADERSTAGE_FRAGMENT: + SOKOL_ASSERT(slot < 64); + bits.hi |= 1ULL << slot; + break; + case SG_SHADERSTAGE_COMPUTE: + SOKOL_ASSERT(slot < 64); + bits.lo |= 1ULL << slot; + break; + default: + SOKOL_UNREACHABLE; + break; + } + return bits; +} + +_SOKOL_PRIVATE bool _sg_validate_slot_bits(_sg_u128_t bits, sg_shader_stage stage, uint8_t slot) { + _sg_u128_t mask = _sg_u128(); + switch (stage) { + case SG_SHADERSTAGE_NONE: + SOKOL_ASSERT(slot < 128); + if (slot < 64) { + mask.lo = 1ULL << slot; + } else { + mask.hi = 1ULL << (slot - 64); + } + break; + case SG_SHADERSTAGE_VERTEX: + SOKOL_ASSERT(slot < 64); + mask.lo = 1ULL << slot; + break; + case SG_SHADERSTAGE_FRAGMENT: + SOKOL_ASSERT(slot < 64); + mask.hi = 1ULL << slot; + break; + case SG_SHADERSTAGE_COMPUTE: + SOKOL_ASSERT(slot < 64); + mask.lo = 1ULL << slot; + break; + default: + SOKOL_UNREACHABLE; + break; + } + return ((bits.lo & mask.lo) == 0) && ((bits.hi & mask.hi) == 0); +} + +_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + bool is_compute_shader = (desc->compute_func.source != 0) || (desc->compute_func.bytecode.ptr != 0); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); + #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) + // on GL or WebGPU, must provide shader source code + if (is_compute_shader) { + _SG_VALIDATE(0 != desc->compute_func.source, VALIDATE_SHADERDESC_COMPUTE_SOURCE); + } else { + _SG_VALIDATE(0 != desc->vertex_func.source, VALIDATE_SHADERDESC_VERTEX_SOURCE); + _SG_VALIDATE(0 != desc->fragment_func.source, VALIDATE_SHADERDESC_FRAGMENT_SOURCE); + } + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + // on Metal or D3D11, must provide shader source code or byte code + if (is_compute_shader) { + _SG_VALIDATE((0 != desc->compute_func.source) || (0 != desc->compute_func.bytecode.ptr), VALIDATE_SHADERDESC_COMPUTE_SOURCE_OR_BYTECODE); + } else { + _SG_VALIDATE((0 != desc->vertex_func.source)|| (0 != desc->vertex_func.bytecode.ptr), VALIDATE_SHADERDESC_VERTEX_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->fragment_func.source) || (0 != desc->fragment_func.bytecode.ptr), VALIDATE_SHADERDESC_FRAGMENT_SOURCE_OR_BYTECODE); + } + #else + // Dummy Backend, don't require source or bytecode + #endif + if (is_compute_shader) { + _SG_VALIDATE((0 == desc->vertex_func.source) && (0 == desc->vertex_func.bytecode.ptr), VALIDATE_SHADERDESC_INVALID_SHADER_COMBO); + _SG_VALIDATE((0 == desc->fragment_func.source) && (0 == desc->fragment_func.bytecode.ptr), VALIDATE_SHADERDESC_INVALID_SHADER_COMBO); + } else { + _SG_VALIDATE((0 == desc->compute_func.source) && (0 == desc->compute_func.bytecode.ptr), VALIDATE_SHADERDESC_INVALID_SHADER_COMBO); + } + #if defined(SOKOL_METAL) + if (is_compute_shader) { + int x = desc->mtl_threads_per_threadgroup.x; + int y = desc->mtl_threads_per_threadgroup.y; + int z = desc->mtl_threads_per_threadgroup.z; + _SG_VALIDATE((x > 0) && (y > 0) && (z > 0), VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_INITIALIZED); + _SG_VALIDATE(((x * y * z) & 31) == 0, VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_MULTIPLE_32); + } + #endif + for (size_t i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (desc->attrs[i].glsl_name) { + _SG_VALIDATE(strlen(desc->attrs[i].glsl_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + if (desc->attrs[i].hlsl_sem_name) { + _SG_VALIDATE(strlen(desc->attrs[i].hlsl_sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + } + // if shader byte code, the size must also be provided + if (0 != desc->vertex_func.bytecode.ptr) { + _SG_VALIDATE(desc->vertex_func.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->fragment_func.bytecode.ptr) { + _SG_VALIDATE(desc->fragment_func.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->compute_func.bytecode.ptr) { + _SG_VALIDATE(desc->compute_func.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + + #if defined(SOKOL_METAL) + _sg_u128_t msl_buf_bits = _sg_u128(); + _sg_u128_t msl_tex_bits = _sg_u128(); + _sg_u128_t msl_smp_bits = _sg_u128(); + #elif defined(SOKOL_D3D11) + _sg_u128_t hlsl_buf_bits = _sg_u128(); + _sg_u128_t hlsl_srv_bits = _sg_u128(); + _sg_u128_t hlsl_uav_bits = _sg_u128(); + _sg_u128_t hlsl_smp_bits = _sg_u128(); + #elif defined(_SOKOL_ANY_GL) + _sg_u128_t glsl_sbuf_bnd_bits = _sg_u128(); + _sg_u128_t glsl_simg_bnd_bits = _sg_u128(); + #elif defined(SOKOL_WGPU) + _sg_u128_t wgsl_group0_bits = _sg_u128(); + _sg_u128_t wgsl_group1_bits = _sg_u128(); + #elif defined(SOKOL_VULKAN) + _sg_u128_t spirv_set0_bits = _sg_u128(); + _sg_u128_t spirv_set1_bits = _sg_u128(); + #endif + for (size_t ub_idx = 0; ub_idx < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_idx++) { + const sg_shader_uniform_block* ub_desc = &desc->uniform_blocks[ub_idx]; + if (ub_desc->stage == SG_SHADERSTAGE_NONE) { + continue; + } + _SG_VALIDATE(ub_desc->size > 0, VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_IS_ZERO); + #if defined(SOKOL_METAL) + _SG_VALIDATE(_sg_validate_slot_bits(msl_buf_bits, ub_desc->stage, ub_desc->msl_buffer_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_METAL_BUFFER_SLOT_COLLISION); + msl_buf_bits = _sg_validate_set_slot_bit(msl_buf_bits, ub_desc->stage, ub_desc->msl_buffer_n); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(_sg_validate_slot_bits(hlsl_buf_bits, ub_desc->stage, ub_desc->hlsl_register_b_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_HLSL_REGISTER_B_COLLISION); + hlsl_buf_bits = _sg_validate_set_slot_bit(hlsl_buf_bits, ub_desc->stage, ub_desc->hlsl_register_b_n); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group0_bits, SG_SHADERSTAGE_NONE, ub_desc->wgsl_group0_binding_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_WGSL_GROUP0_BINDING_COLLISION); + wgsl_group0_bits = _sg_validate_set_slot_bit(wgsl_group0_bits, SG_SHADERSTAGE_NONE, ub_desc->wgsl_group0_binding_n); + #elif defined(SOKOL_VULKAN) + _SG_VALIDATE(_sg_validate_slot_bits(spirv_set0_bits, SG_SHADERSTAGE_NONE, ub_desc->spirv_set0_binding_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_SPIRV_SET0_BINDING_COLLISION); + spirv_set0_bits = _sg_validate_set_slot_bit(spirv_set0_bits, SG_SHADERSTAGE_NONE, ub_desc->spirv_set0_binding_n); + #endif + #if defined(_SOKOL_ANY_GL) + bool uniforms_continuous = true; + uint32_t uniform_offset = 0; + int num_uniforms = 0; + for (size_t u_index = 0; u_index < SG_MAX_UNIFORMBLOCK_MEMBERS; u_index++) { + const sg_glsl_shader_uniform* u_desc = &ub_desc->glsl_uniforms[u_index]; + if (u_desc->type != SG_UNIFORMTYPE_INVALID) { + _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_CONT_MEMBERS); + _SG_VALIDATE(u_desc->glsl_name, VALIDATE_SHADERDESC_UNIFORMBLOCK_UNIFORM_GLSL_NAME); + const int array_count = u_desc->array_count; + _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UNIFORMBLOCK_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_UNIFORMBLOCK_STD140_ARRAY_TYPE); + } + } + } else { + uniforms_continuous = false; + } + } + 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_UNIFORMBLOCK_SIZE_MISMATCH); + _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_MEMBERS); + #endif + } + + uint32_t texview_slot_mask = 0; + for (size_t view_idx = 0; view_idx < SG_MAX_VIEW_BINDSLOTS; view_idx++) { + const sg_shader_view* view_desc = &desc->views[view_idx]; + if (view_desc->texture.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_texture_view* tex_desc = &view_desc->texture; + texview_slot_mask |= (1 << view_idx); + #if defined(SOKOL_METAL) + _SG_VALIDATE(_sg_validate_slot_bits(msl_tex_bits, tex_desc->stage, tex_desc->msl_texture_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_METAL_TEXTURE_SLOT_COLLISION); + msl_tex_bits = _sg_validate_set_slot_bit(msl_tex_bits, tex_desc->stage, tex_desc->msl_texture_n); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(_sg_validate_slot_bits(hlsl_srv_bits, tex_desc->stage, tex_desc->hlsl_register_t_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_HLSL_REGISTER_T_COLLISION); + hlsl_srv_bits = _sg_validate_set_slot_bit(hlsl_srv_bits, tex_desc->stage, tex_desc->hlsl_register_t_n); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, tex_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_WGSL_GROUP1_BINDING_COLLISION); + wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, tex_desc->wgsl_group1_binding_n); + #elif defined(SOKOL_VULKAN) + _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, tex_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_SPIRV_SET1_BINDING_COLLISION); + spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, tex_desc->spirv_set1_binding_n); + #elif defined(SOKOL_DUMMY_BACKEND) || defined(_SOKOL_ANY_GL) + _SOKOL_UNUSED(tex_desc); + #endif + } else if (view_desc->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_storage_buffer_view* sbuf_desc = &view_desc->storage_buffer; + #if defined(SOKOL_METAL) + _SG_VALIDATE(_sg_validate_slot_bits(msl_buf_bits, sbuf_desc->stage, sbuf_desc->msl_buffer_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_METAL_BUFFER_SLOT_COLLISION); + msl_buf_bits = _sg_validate_set_slot_bit(msl_buf_bits, sbuf_desc->stage, sbuf_desc->msl_buffer_n); + #elif defined(SOKOL_D3D11) + if (sbuf_desc->readonly) { + _SG_VALIDATE(_sg_validate_slot_bits(hlsl_srv_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_t_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_T_COLLISION); + hlsl_srv_bits = _sg_validate_set_slot_bit(hlsl_srv_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_t_n); + } else { + _SG_VALIDATE(_sg_validate_slot_bits(hlsl_uav_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_u_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_U_COLLISION); + hlsl_uav_bits = _sg_validate_set_slot_bit(hlsl_uav_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_u_n); + } + #elif defined(_SOKOL_ANY_GL) + _SG_VALIDATE(_sg_validate_slot_bits(glsl_sbuf_bnd_bits, SG_SHADERSTAGE_NONE, sbuf_desc->glsl_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_GLSL_BINDING_COLLISION); + glsl_sbuf_bnd_bits = _sg_validate_set_slot_bit(glsl_sbuf_bnd_bits, SG_SHADERSTAGE_NONE, sbuf_desc->glsl_binding_n); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_WGSL_GROUP1_BINDING_COLLISION); + wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->wgsl_group1_binding_n); + #elif defined(SOKOL_VULKAN) + _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_SPIRV_SET1_BINDING_COLLISION); + spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->spirv_set1_binding_n); + #elif defined(SOKOL_DUMMY_BACKEND) + _SOKOL_UNUSED(sbuf_desc); + #endif + } else if (view_desc->storage_image.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_storage_image_view* simg_desc = &view_desc->storage_image; + _SG_VALIDATE(simg_desc->stage == SG_SHADERSTAGE_COMPUTE, VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_EXPECT_COMPUTE_STAGE); + #if defined(SOKOL_METAL) + _SG_VALIDATE(_sg_validate_slot_bits(msl_tex_bits, simg_desc->stage, simg_desc->msl_texture_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_METAL_TEXTURE_SLOT_COLLISION); + msl_tex_bits = _sg_validate_set_slot_bit(msl_tex_bits, simg_desc->stage, simg_desc->msl_texture_n); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(_sg_validate_slot_bits(hlsl_uav_bits, simg_desc->stage, simg_desc->hlsl_register_u_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_HLSL_REGISTER_U_COLLISION); + hlsl_uav_bits = _sg_validate_set_slot_bit(hlsl_uav_bits, simg_desc->stage, simg_desc->hlsl_register_u_n); + #elif defined(_SOKOL_ANY_GL) + _SG_VALIDATE(_sg_validate_slot_bits(glsl_simg_bnd_bits, SG_SHADERSTAGE_NONE, simg_desc->glsl_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_GLSL_BINDING_COLLISION); + glsl_simg_bnd_bits = _sg_validate_set_slot_bit(glsl_simg_bnd_bits, SG_SHADERSTAGE_NONE, simg_desc->glsl_binding_n); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, simg_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_WGSL_GROUP1_BINDING_COLLISION); + wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, simg_desc->wgsl_group1_binding_n); + #elif defined(SOKOL_VULKAN) + _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, simg_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_SPIRV_SET1_BINDING_COLLISION); + spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, simg_desc->spirv_set1_binding_n); + #endif + } + } + + uint32_t smp_slot_mask = 0; + for (size_t smp_idx = 0; smp_idx < SG_MAX_SAMPLER_BINDSLOTS; smp_idx++) { + const sg_shader_sampler* smp_desc = &desc->samplers[smp_idx]; + if (smp_desc->stage == SG_SHADERSTAGE_NONE) { + continue; + } + smp_slot_mask |= (1 << smp_idx); + #if defined(SOKOL_METAL) + _SG_VALIDATE(_sg_validate_slot_bits(msl_smp_bits, smp_desc->stage, smp_desc->msl_sampler_n), VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_COLLISION); + msl_smp_bits = _sg_validate_set_slot_bit(msl_smp_bits, smp_desc->stage, smp_desc->msl_sampler_n); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(_sg_validate_slot_bits(hlsl_smp_bits, smp_desc->stage, smp_desc->hlsl_register_s_n), VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_COLLISION); + hlsl_smp_bits = _sg_validate_set_slot_bit(hlsl_smp_bits, smp_desc->stage, smp_desc->hlsl_register_s_n); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, smp_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_COLLISION); + wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, smp_desc->wgsl_group1_binding_n); + #elif defined(SOKOL_VULKAN) + _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, smp_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_SAMPLER_SPIRV_SET1_BINDING_COLLISION); + spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, smp_desc->spirv_set1_binding_n); + #endif + } + + uint32_t ref_texview_slot_mask = 0; + uint32_t ref_smp_slot_mask = 0; + for (size_t tex_smp_idx = 0; tex_smp_idx < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_idx++) { + const sg_shader_texture_sampler_pair* tex_smp_desc = &desc->texture_sampler_pairs[tex_smp_idx]; + if (tex_smp_desc->stage == SG_SHADERSTAGE_NONE) { + continue; + } + #if defined(_SOKOL_ANY_GL) + _SG_VALIDATE(tex_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_GLSL_NAME); + #endif + const bool view_slot_in_range = tex_smp_desc->view_slot < SG_MAX_VIEW_BINDSLOTS; + const bool smp_slot_in_range = tex_smp_desc->sampler_slot < SG_MAX_SAMPLER_BINDSLOTS; + _SG_VALIDATE(view_slot_in_range, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_VIEW_SLOT_OUT_OF_RANGE); + _SG_VALIDATE(smp_slot_in_range, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE); + if (view_slot_in_range && smp_slot_in_range) { + ref_texview_slot_mask |= 1 << tex_smp_desc->view_slot; + ref_smp_slot_mask |= 1 << tex_smp_desc->sampler_slot; + const sg_shader_view* view_desc = &desc->views[tex_smp_desc->view_slot]; + const sg_shader_sampler* smp_desc = &desc->samplers[tex_smp_desc->sampler_slot]; + _SG_VALIDATE(view_desc->texture.stage != SG_SHADERSTAGE_NONE, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_EXPECT_TEXTURE_VIEW); + _SG_VALIDATE(view_desc->texture.stage == tex_smp_desc->stage, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_TEXTURE_STAGE_MISMATCH); + _SG_VALIDATE(smp_desc->stage == tex_smp_desc->stage, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_STAGE_MISMATCH); + const bool needs_nonfiltering = (view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_UINT) + || (view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_SINT) + || (view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT); + const bool needs_comparison = view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_DEPTH; + if (needs_nonfiltering) { + _SG_VALIDATE(needs_nonfiltering && (smp_desc->sampler_type == SG_SAMPLERTYPE_NONFILTERING), VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED); + } + if (needs_comparison) { + _SG_VALIDATE(needs_comparison && (smp_desc->sampler_type == SG_SAMPLERTYPE_COMPARISON), VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED); + } + } + } + // each image and sampler must be referenced by an image sampler + _SG_VALIDATE(texview_slot_mask == ref_texview_slot_mask, VALIDATE_SHADERDESC_TEXVIEW_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS); + _SG_VALIDATE(smp_slot_mask == ref_smp_slot_mask, VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS); + + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _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); + const _sg_shader_t* shd = _sg_lookup_shader(desc->shader.id); + _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); + if (shd) { + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PIPELINEDESC_SHADER); + if (desc->compute) { + _SG_VALIDATE(shd->cmn.is_compute, VALIDATE_PIPELINEDESC_COMPUTE_SHADER_EXPECTED); + } else { + _SG_VALIDATE(!shd->cmn.is_compute, VALIDATE_PIPELINEDESC_NO_COMPUTE_SHADER_EXPECTED); + bool attrs_cont = true; + for (size_t attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_CONT_ATTRS); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + // vertex format must match expected shader attribute base type (if provided) + if (shd->cmn.attrs[attr_index].base_type != SG_SHADERATTRBASETYPE_UNDEFINED) { + if (_sg_vertexformat_basetype(a_state->format) != shd->cmn.attrs[attr_index].base_type) { + _SG_VALIDATE(false, VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH); + _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, "attr format:"); + _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, _sg_vertexformat_to_string(a_state->format)); + _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, "shader attr base type:"); + _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, _sg_shaderattrbasetype_to_string(shd->cmn.attrs[attr_index].base_type)); + } + } + #if defined(SOKOL_D3D11) + // on D3D11, semantic names (and semantic indices) must be provided + _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + #endif + } + // must only use readonly storage buffer bindings in render pipelines + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) { + _SG_VALIDATE(shd->cmn.views[i].sbuf_readonly, VALIDATE_PIPELINEDESC_SHADER_READONLY_STORAGEBUFFERS); + } + } + for (int buf_index = 0; buf_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; buf_index++) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[buf_index]; + if (l_state->stride == 0) { + continue; + } + _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + } + } + } + for (size_t color_index = 0; color_index < (size_t)desc->color_count; color_index++) { + SOKOL_ASSERT(color_index < SG_MAX_COLOR_ATTACHMENTS); + const sg_blend_state* bs = &desc->colors[color_index].blend; + if ((bs->op_rgb == SG_BLENDOP_MIN) || (bs->op_rgb == SG_BLENDOP_MAX)) { + _SG_VALIDATE((bs->src_factor_rgb == SG_BLENDFACTOR_ONE) && (bs->dst_factor_rgb == SG_BLENDFACTOR_ONE), VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE); + } + if ((bs->op_alpha == SG_BLENDOP_MIN) || (bs->op_alpha == SG_BLENDOP_MAX)) { + _SG_VALIDATE((bs->src_factor_alpha == SG_BLENDFACTOR_ONE) && (bs->dst_factor_alpha == SG_BLENDFACTOR_ONE), VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE); + } + const bool needs_dualsource_blending = + _sg_is_dualsource_blendfactor(bs->src_factor_rgb) || + _sg_is_dualsource_blendfactor(bs->dst_factor_rgb) || + _sg_is_dualsource_blendfactor(bs->src_factor_alpha) || + _sg_is_dualsource_blendfactor(bs->dst_factor_alpha); + if (needs_dualsource_blending) { + _SG_VALIDATE(_sg.features.dual_source_blending, VALIDATE_PIPELINEDESC_DUAL_SOURCE_BLENDING_NOT_SUPPORTED); + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_view_desc(const sg_view_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_VIEWDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_VIEWDESC_CANARY); + + // only one view type can be define + sg_view_type view_type = SG_VIEWTYPE_INVALID; + const sg_image_view_desc* img_desc = 0; + const sg_texture_view_desc* tex_desc = 0; + const sg_buffer_view_desc* buf_desc = 0; + if (desc->texture.image.id != SG_INVALID_ID) { + view_type = SG_VIEWTYPE_TEXTURE; + tex_desc = &desc->texture; + } + if (desc->storage_buffer.buffer.id != SG_INVALID_ID) { + _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE); + view_type = SG_VIEWTYPE_STORAGEBUFFER; + buf_desc = &desc->storage_buffer; + } + if (desc->storage_image.image.id != SG_INVALID_ID) { + _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE); + view_type = SG_VIEWTYPE_STORAGEIMAGE; + img_desc = &desc->storage_image; + } + if (desc->color_attachment.image.id != SG_INVALID_ID) { + _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE); + view_type = SG_VIEWTYPE_COLORATTACHMENT; + img_desc = &desc->color_attachment; + } + if (desc->resolve_attachment.image.id != SG_INVALID_ID) { + _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE); + view_type = SG_VIEWTYPE_RESOLVEATTACHMENT; + img_desc = &desc->resolve_attachment; + } + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE); + view_type = SG_VIEWTYPE_DEPTHSTENCILATTACHMENT; + img_desc = &desc->depth_stencil_attachment; + } + _SG_VALIDATE(SG_VIEWTYPE_INVALID != view_type, VALIDATE_VIEWDESC_ANY_VIEWTYPE); + + const _sg_buffer_t* buf = 0; + const _sg_image_t* img = 0; + bool res_valid = false; + if (buf_desc) { + SOKOL_ASSERT((img_desc == 0) && (tex_desc == 0)); + buf = _sg_lookup_buffer(buf_desc->buffer.id); + _SG_VALIDATE(buf, VALIDATE_VIEWDESC_RESOURCE_ALIVE); + if (buf) { + _SG_VALIDATE(buf->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_VIEWDESC_RESOURCE_FAILED); + res_valid = buf->slot.state == SG_RESOURCESTATE_VALID; + } + } else if (img_desc) { + SOKOL_ASSERT((tex_desc == 0) && (buf_desc == 0)); + img = _sg_lookup_image(img_desc->image.id); + _SG_VALIDATE(img, VALIDATE_VIEWDESC_RESOURCE_ALIVE); + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_VIEWDESC_RESOURCE_FAILED); + res_valid = img->slot.state == SG_RESOURCESTATE_VALID; + } + } else { + SOKOL_ASSERT(tex_desc && (img_desc == 0) && (buf_desc == 0)); + img = _sg_lookup_image(tex_desc->image.id); + _SG_VALIDATE(img, VALIDATE_VIEWDESC_RESOURCE_ALIVE); + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_VIEWDESC_RESOURCE_FAILED); + res_valid = img->slot.state == SG_RESOURCESTATE_VALID; + } + } + if (res_valid) { + // check usage flags + switch (view_type) { + case SG_VIEWTYPE_STORAGEBUFFER: + SOKOL_ASSERT(buf); + _SG_VALIDATE(buf->cmn.usage.storage_buffer, VALIDATE_VIEWDESC_STORAGEBUFFER_USAGE); + break; + case SG_VIEWTYPE_STORAGEIMAGE: + SOKOL_ASSERT(img); + _SG_VALIDATE(img->cmn.usage.storage_image, VALIDATE_VIEWDESC_STORAGEIMAGE_USAGE); + _SG_VALIDATE(_sg_is_valid_storage_image_format(img->cmn.pixel_format), VALIDATE_VIEWDESC_STORAGEIMAGE_PIXELFORMAT); + break; + case SG_VIEWTYPE_TEXTURE: + if (!_sg.features.msaa_texture_bindings) { + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_VIEWDESC_TEXTURE_EXPECT_NO_MSAA); + } + break; + case SG_VIEWTYPE_COLORATTACHMENT: + SOKOL_ASSERT(img); + _SG_VALIDATE(img->cmn.usage.color_attachment, VALIDATE_VIEWDESC_COLORATTACHMENT_USAGE); + _SG_VALIDATE(_sg_is_valid_attachment_color_format(img->cmn.pixel_format), VALIDATE_VIEWDESC_COLORATTACHMENT_PIXELFORMAT); + break; + case SG_VIEWTYPE_RESOLVEATTACHMENT: + SOKOL_ASSERT(img); + _SG_VALIDATE(img->cmn.usage.resolve_attachment, VALIDATE_VIEWDESC_RESOLVEATTACHMENT_USAGE); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_VIEWDESC_RESOLVEATTACHMENT_SAMPLECOUNT); + break; + case SG_VIEWTYPE_DEPTHSTENCILATTACHMENT: + SOKOL_ASSERT(img); + _SG_VALIDATE(img->cmn.usage.depth_stencil_attachment, VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_USAGE); + _SG_VALIDATE(_sg_is_valid_attachment_depth_format(img->cmn.pixel_format), VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_PIXELFORMAT); + break; + default: + SOKOL_UNREACHABLE; + break; + } + if (buf_desc) { + SOKOL_ASSERT(buf); + _SG_VALIDATE(buf_desc->offset < buf->cmn.size, VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_VS_BUFFER_SIZE); + _SG_VALIDATE(_sg_multiple_u64((uint64_t)buf_desc->offset, 256), VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_MULTIPLE_256); + } else if (img_desc) { + SOKOL_ASSERT(img); + _SG_VALIDATE((img_desc->mip_level >= 0) && (img_desc->mip_level < img->cmn.num_mipmaps), VALIDATE_VIEWDESC_IMAGE_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_2D) { + _SG_VALIDATE(img_desc->slice == 0, VALIDATE_VIEWDESC_IMAGE_2D_SLICE); + } else if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE((img_desc->slice >= 0) && (img_desc->slice < 6), VALIDATE_VIEWDESC_IMAGE_CUBEMAP_SLICE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE((img_desc->slice >= 0) && (img_desc->slice < img->cmn.num_slices), VALIDATE_VIEWDESC_IMAGE_ARRAY_SLICE); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(img_desc->slice == 0, VALIDATE_VIEWDESC_IMAGE_3D_SLICE); + } + } else if (tex_desc) { + SOKOL_ASSERT(img); + // NOTE: it doesn't matter here if the mip/slice count is default-zero! + int max_mip_level = tex_desc->mip_levels.base + tex_desc->mip_levels.count; + int max_slice = tex_desc->slices.base + tex_desc->slices.count; + _SG_VALIDATE((tex_desc->mip_levels.base >= 0) && (max_mip_level <= img->cmn.num_mipmaps), VALIDATE_VIEWDESC_TEXTURE_MIPLEVELS); + if (img->cmn.type == SG_IMAGETYPE_2D) { + _SG_VALIDATE((tex_desc->slices.base == 0) && (max_slice <= 1), VALIDATE_VIEWDESC_TEXTURE_2D_SLICES); + } else if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE((tex_desc->slices.base == 0) && (max_slice <= 1), VALIDATE_VIEWDESC_TEXTURE_CUBEMAP_SLICES); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE((tex_desc->slices.base >= 0) && (max_slice <= img->cmn.num_slices), VALIDATE_VIEWDESC_TEXTURE_ARRAY_SLICES); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE((tex_desc->slices.base == 0) && (max_slice <= 1), VALIDATE_VIEWDESC_TEXTURE_3D_SLICES); + } + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_begin_pass(const sg_pass* pass) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pass); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + const bool is_compute_pass = pass->compute; + const bool is_swapchain_pass = !is_compute_pass && _sg_attachments_empty(&pass->attachments); + const bool is_offscreen_pass = !(is_compute_pass || is_swapchain_pass); + _sg_validate_begin(); + _SG_VALIDATE(pass->_start_canary == 0, VALIDATE_BEGINPASS_CANARY); + _SG_VALIDATE(pass->_end_canary == 0, VALIDATE_BEGINPASS_CANARY); + if (is_compute_pass) { + _SG_VALIDATE(_sg_attachments_empty(&pass->attachments), VALIDATE_BEGINPASS_COMPUTEPASS_EXPECT_NO_ATTACHMENTS); + } else if (is_swapchain_pass) { + // this is a swapchain pass + _SG_VALIDATE(pass->swapchain.width > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH); + _SG_VALIDATE(pass->swapchain.height > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT); + _SG_VALIDATE(pass->swapchain.sample_count > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT); + _SG_VALIDATE(pass->swapchain.color_format > SG_PIXELFORMAT_NONE, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT); + // NOTE: depth buffer is optional, so depth_format is allowed to be invalid + // NOTE: the GL framebuffer handle may actually be 0 + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE); + } else { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + } + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + } + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + } + #endif + } else { + // this is an 'offscreen pass' + bool has_color_atts = false; + bool has_depth_stencil_atts = false; + bool atts_cont = true; + int color_width = -1, color_height = -1, color_sample_count = -1; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + if (pass->attachments.colors[att_index].id == SG_INVALID_ID) { + atts_cont = false; + continue; + } + has_color_atts = true; + _SG_VALIDATE(atts_cont, VALIDATE_BEGINPASS_COLORATTACHMENTVIEWS_CONTINUOUS); + const _sg_view_t* view = _sg_lookup_view(pass->attachments.colors[att_index].id); + // the view object must be alive + _SG_VALIDATE(view != 0, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_ALIVE); + if (view) { + // the view object must be in valid state + _SG_VALIDATE(view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_VALID); + if (view->slot.state == SG_RESOURCESTATE_VALID) { + // the view object must be a color attachment view + _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_COLORATTACHMENT, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_TYPE); + // the view's image object must be alive and valid + const _sg_image_t* img = _sg_image_ref_ptr_or_null(&view->cmn.img.ref); + _SG_VALIDATE(img, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_ALIVE); + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_VALID); + if (img->slot.state == SG_RESOURCESTATE_VALID) { + if (color_width == -1) { + color_width = _sg_image_view_dim(view).width; + color_height = _sg_image_view_dim(view).height; + color_sample_count = img->cmn.sample_count; + } else { + _SG_VALIDATE(color_width == _sg_image_view_dim(view).width, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SIZES); + _SG_VALIDATE(color_height == _sg_image_view_dim(view).height, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SIZES); + _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNTS_EQUAL); + } + } + } + } + } + } + // check resolve views + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + if (pass->attachments.resolves[att_index].id == SG_INVALID_ID) { + continue; + } + _SG_VALIDATE(pass->attachments.colors[att_index].id != SG_INVALID_ID, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_NO_COLORATTACHMENTVIEW); + const _sg_view_t* view = _sg_lookup_view(pass->attachments.resolves[att_index].id); + // the view object must be alive + _SG_VALIDATE(view != 0, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_ALIVE); + if (view) { + // the view object must be in valid state + _SG_VALIDATE(view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_VALID); + if (view->slot.state == SG_RESOURCESTATE_VALID) { + // the view object must be a resolve attachment view + _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_RESOLVEATTACHMENT, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_TYPE); + // the view's image object must be alive and valid + const _sg_image_t* img = _sg_image_ref_ptr_or_null(&view->cmn.img.ref); + _SG_VALIDATE(img, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_ALIVE); + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_VALID); + if (img->slot.state == SG_RESOURCESTATE_VALID) { + if (color_width != -1) { + _SG_VALIDATE(color_sample_count > 1, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNT); + _SG_VALIDATE(color_width == _sg_image_view_dim(view).width, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES); + _SG_VALIDATE(color_height == _sg_image_view_dim(view).height, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES); + } + } + } + } + } + } + // check depth-stencil view + if (pass->attachments.depth_stencil.id != SG_INVALID_ID) { + has_depth_stencil_atts = true; + const _sg_view_t* view = _sg_lookup_view(pass->attachments.depth_stencil.id); + // the view object must be valid + _SG_VALIDATE(view != 0, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_ALIVE); + if (view) { + // the view object must be in valid state + _SG_VALIDATE(view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_VALID); + if (view->slot.state == SG_RESOURCESTATE_VALID) { + // the view object must be a depth stencil attachment view + _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_TYPE); + // the view's image object must be alive and valid + const _sg_image_t* img = _sg_image_ref_ptr_or_null(&view->cmn.img.ref); + _SG_VALIDATE(img, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_ALIVE); + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_VALID); + if (img->slot.state == SG_RESOURCESTATE_VALID) { + if (color_width != -1) { + _SG_VALIDATE(color_width == _sg_image_view_dim(view).width, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SIZES); + _SG_VALIDATE(color_height == _sg_image_view_dim(view).height, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SIZES); + _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SAMPLECOUNT); + } + } + } + } + } + } + // must have at least color- or depth-stencil-attachments + _SG_VALIDATE(has_color_atts || has_depth_stencil_atts, VALIDATE_BEGINPASS_ATTACHMENTS_EXPECTED); + } + if (is_compute_pass || is_offscreen_pass) { + _SG_VALIDATE(pass->swapchain.width == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET); + _SG_VALIDATE(pass->swapchain.height == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET); + _SG_VALIDATE(pass->swapchain.sample_count == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET); + _SG_VALIDATE(pass->swapchain.color_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET); + _SG_VALIDATE(pass->swapchain.depth_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET); + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(_SOKOL_ANY_GL) + _SG_VALIDATE(pass->swapchain.gl.framebuffer == 0, VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET); + #endif + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(width); + _SOKOL_UNUSED(height); + _SOKOL_UNUSED(origin_top_left); + #if !defined(SOKOL_DEBUG) + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_AVP_RENDERPASS_EXPECTED); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(width); + _SOKOL_UNUSED(height); + _SOKOL_UNUSED(origin_top_left); + #if !defined(SOKOL_DEBUG) + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_ASR_RENDERPASS_EXPECTED); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pip_id); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _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(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 + _SG_VALIDATE(_sg.cur_pass.in_pass, VALIDATE_APIP_PASS_EXPECTED); + const bool shd_alive = _sg_shader_ref_alive(&pip->cmn.shader); + const _sg_shader_t* shd = shd_alive ? _sg_shader_ref_ptr(&pip->cmn.shader) : 0; + _SG_VALIDATE(shd_alive, VALIDATE_APIP_PIPELINE_SHADER_ALIVE); + if (shd_alive) { + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_SHADER_VALID); + } else { + return _sg_validate_end(); + } + + if (pip->cmn.is_compute) { + _SG_VALIDATE(_sg.cur_pass.is_compute, VALIDATE_APIP_COMPUTEPASS_EXPECTED); + } else { + _SG_VALIDATE(!_sg.cur_pass.is_compute, VALIDATE_APIP_RENDERPASS_EXPECTED); + if (_sg_attachments_empty(&_sg.cur_pass.atts)) { + // a swapchain pass + _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_SWAPCHAIN_COLOR_COUNT); + _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.cur_pass.swapchain.color_fmt, VALIDATE_APIP_SWAPCHAIN_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.cur_pass.swapchain.depth_fmt, VALIDATE_APIP_SWAPCHAIN_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == _sg.cur_pass.swapchain.sample_count, VALIDATE_APIP_SWAPCHAIN_SAMPLE_COUNT); + } else { + // an offscreen render pass check that pipeline attributes match current pass attachment attributes + const _sg_attachments_ptrs_t atts_ptrs = _sg_attachments_ptrs(&_sg.cur_pass.atts); + const bool alive = _sg_attachments_alive(&atts_ptrs); + _SG_VALIDATE(alive, VALIDATE_APIP_ATTACHMENTS_ALIVE); + if (alive) { + _SG_VALIDATE(pip->cmn.color_count == atts_ptrs.num_color_views, VALIDATE_APIP_COLORATTACHMENTS_COUNT); + for (int i = 0; i < pip->cmn.color_count; i++) { + const _sg_view_t* clr_view = atts_ptrs.color_views[i]; + SOKOL_ASSERT(clr_view); + _SG_VALIDATE(clr_view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_COLORATTACHMENTS_VIEW_VALID); + const _sg_image_t* clr_img = _sg_image_ref_ptr(&clr_view->cmn.img.ref); + SOKOL_ASSERT(clr_img); + _SG_VALIDATE(clr_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_COLORATTACHMENTS_IMAGE_VALID); + _SG_VALIDATE(pip->cmn.colors[i].pixel_format == clr_img->cmn.pixel_format, VALIDATE_APIP_COLORATTACHMENTS_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == clr_img->cmn.sample_count, VALIDATE_APIP_ATTACHMENT_SAMPLE_COUNT); + } + const _sg_view_t* ds_view = atts_ptrs.ds_view; + if (ds_view) { + _SG_VALIDATE(ds_view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_VIEW_VALID); + const _sg_image_t* ds_img = _sg_image_ref_ptr(&ds_view->cmn.img.ref); + SOKOL_ASSERT(ds_img); + _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_IMAGE_VALID); + _SG_VALIDATE(pip->cmn.depth.pixel_format == ds_img->cmn.pixel_format, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == ds_img->cmn.sample_count, VALIDATE_APIP_ATTACHMENT_SAMPLE_COUNT); + } else { + _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_FORMAT); + } + } + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(bindings); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + + // must be called in a pass + _SG_VALIDATE(_sg.cur_pass.in_pass, VALIDATE_ABND_PASS_EXPECTED); + + // bindings must not be empty + bool has_any_bindings = bindings->index_buffer.id != SG_INVALID_ID; + if (!has_any_bindings) for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + has_any_bindings |= bindings->vertex_buffers[i].id != SG_INVALID_ID; + } + if (!has_any_bindings) for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + has_any_bindings |= bindings->views[i].id != SG_INVALID_ID; + } + if (!has_any_bindings) for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + has_any_bindings |= bindings->samplers[i].id != SG_INVALID_ID; + } + _SG_VALIDATE(has_any_bindings, VALIDATE_ABND_EMPTY_BINDINGS); + + // a pipeline object must have been applied + const bool pip_null = _sg_pipeline_ref_null(&_sg.cur_pip); + const bool pip_alive = _sg_pipeline_ref_alive(&_sg.cur_pip); + _SG_VALIDATE(!pip_null, VALIDATE_ABND_NO_PIPELINE); + _SG_VALIDATE(pip_alive, VALIDATE_ABND_PIPELINE_ALIVE); + if (!pip_alive) { + return _sg_validate_end(); + } + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID); + + const bool shd_alive = _sg_shader_ref_alive(&pip->cmn.shader); + _SG_VALIDATE(shd_alive, VALIDATE_ABND_PIPELINE_SHADER_ALIVE); + if (!shd_alive) { + return _sg_validate_end(); + } + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_SHADER_VALID); + + if (_sg.cur_pass.is_compute) { + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + _SG_VALIDATE(bindings->vertex_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_COMPUTE_EXPECTED_NO_VBUFS); + } + } else { + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + if (pip->cmn.vertex_buffer_layout_active[i]) { + _SG_VALIDATE(bindings->vertex_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_VBUF); + if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* buf = _sg_lookup_buffer(bindings->vertex_buffers[i].id); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_VBUF_ALIVE); + // NOTE: state != VALID is legal and skips rendering! + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(buf->cmn.usage.vertex_buffer, VALIDATE_ABND_VBUF_USAGE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VBUF_OVERFLOW); + } + } + } + } + } + + if (_sg.cur_pass.is_compute) { + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_COMPUTE_EXPECTED_NO_IBUF); + } else { + // 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 + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_EXPECTED_NO_IBUF); + } else { + // pipeline defines indexed rendering, but no index buffer provided + _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_IBUF); + } + if (bindings->index_buffer.id != SG_INVALID_ID) { + // buffer in index-buffer-slot must have index buffer usage + const _sg_buffer_t* buf = _sg_lookup_buffer(bindings->index_buffer.id); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_IBUF_ALIVE); + // NOTE: state != VALID is legal and skips rendering! + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(buf->cmn.usage.index_buffer, VALIDATE_ABND_IBUF_USAGE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IBUF_OVERFLOW); + } + } + } + + // has expected view bindings + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].view_type != SG_VIEWTYPE_INVALID) { + _SG_VALIDATE(bindings->views[i].id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_VIEW_BINDING); + if (bindings->views[i].id != SG_INVALID_ID) { + const _sg_view_t* view = _sg_lookup_view(bindings->views[i].id); + _SG_VALIDATE(view != 0, VALIDATE_ABND_VIEW_ALIVE); + // the view object must be alive + if (view) { + // NOTE: an invalid view state is allowed and skips rendering + if (view->slot.state == SG_RESOURCESTATE_VALID) { + if (shd->cmn.views[i].view_type == SG_VIEWTYPE_TEXTURE) { + // the view object must be a texture view + _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_TEXTURE, VALIDATE_ABND_EXPECT_TEXVIEW); + // NOTE: an invalid image ref is allowed and skips rendering + if (_sg_image_ref_valid(&view->cmn.img.ref)) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + _SG_VALIDATE(img->cmn.type == shd->cmn.views[i].image_type, VALIDATE_ABND_TEXVIEW_IMAGETYPE_MISMATCH); + if (shd->cmn.views[i].multisampled) { + _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_ABND_TEXVIEW_EXPECTED_MULTISAMPLED_IMAGE); + } else { + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_TEXVIEW_EXPECTED_NON_MULTISAMPLED_IMAGE); + } + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (shd->cmn.views[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_TEXVIEW_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_TEXVIEW_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } + } + } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) { + // the view object must be a storage buffer view + _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER, VALIDATE_ABND_EXPECT_SBVIEW); + // NOTE: an invalid buffer ref is allowed and skips rendering + if (_sg_buffer_ref_valid(&view->cmn.buf.ref)) { + const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref); + if (!shd->cmn.views[i].sbuf_readonly) { + _SG_VALIDATE(buf->cmn.usage.immutable, VALIDATE_ABND_SBVIEW_READWRITE_IMMUTABLE); + } + } + } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEIMAGE) { + // the view object must be a storage-image-view + _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE, VALIDATE_ABND_EXPECT_SIMGVIEW); + // storage images only allowed in compute passes + _SG_VALIDATE(_sg.cur_pass.is_compute, VALIDATE_ABND_SIMGVIEW_COMPUTE_PASS_EXPECTED); + // NOTE: an invalid image ref is allowed and skips rendering + if (_sg_image_ref_valid(&view->cmn.img.ref)) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + _SG_VALIDATE(img->cmn.type == shd->cmn.views[i].image_type, VALIDATE_ABND_SIMGVIEW_IMAGETYPE_MISMATCH); + _SG_VALIDATE(img->cmn.pixel_format == shd->cmn.views[i].access_format, VALIDATE_ABND_SIMGVIEW_ACCESSFORMAT); + } + } + } + } + } + } + } + + // has expected samplers + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage != SG_SHADERSTAGE_NONE) { + _SG_VALIDATE(bindings->samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_SAMPLER_BINDING); + if (bindings->samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(bindings->samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_SAMPLER_ALIVE); + if (smp) { + // NOTE: for invalid samplers don't skip rendering, but are actually an error + _SG_VALIDATE(smp->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_SAMPLER_VALID); + if (shd->cmn.samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_EXPECTED_SAMPLER_COMPARE_NEVER); + } + if (shd->cmn.samplers[i].sampler_type == SG_SAMPLERTYPE_NONFILTERING) { + const bool nonfiltering = (smp->cmn.min_filter != SG_FILTER_LINEAR) + && (smp->cmn.mag_filter != SG_FILTER_LINEAR) + && (smp->cmn.mipmap_filter != SG_FILTER_LINEAR); + _SG_VALIDATE(nonfiltering, VALIDATE_ABND_EXPECTED_NONFILTERING_SAMPLER); + } + } + } + } + } + + // the same image cannot be used as texture binding and pass attachment or storage image binding + for (size_t tex_view_idx = 0; tex_view_idx < SG_MAX_VIEW_BINDSLOTS; tex_view_idx++) { + if (shd->cmn.views[tex_view_idx].view_type == SG_VIEWTYPE_TEXTURE) { + if (bindings->views[tex_view_idx].id == SG_INVALID_ID) { + continue; + } + const _sg_view_t* tex_view = _sg_lookup_view(bindings->views[tex_view_idx].id); + if (tex_view) { + const uint32_t img_id = tex_view->cmn.img.ref.sref.id; + if (!_sg_attachments_empty(&_sg.cur_pass.atts)) { + const _sg_view_t* ds_view = _sg_lookup_view(_sg.cur_pass.atts.depth_stencil.id); + if (ds_view) { + _SG_VALIDATE(img_id != ds_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_BINDING_VS_DEPTHSTENCIL_ATTACHMENT); + } + for (size_t att_idx = 0; att_idx < SG_MAX_COLOR_ATTACHMENTS; att_idx++) { + const _sg_view_t* color_view = _sg_lookup_view(_sg.cur_pass.atts.colors[att_idx].id); + if (color_view) { + _SG_VALIDATE(img_id != color_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_BINDING_VS_COLOR_ATTACHMENT); + } + const _sg_view_t* resolve_view = _sg_lookup_view(_sg.cur_pass.atts.resolves[att_idx].id); + if (resolve_view) { + _SG_VALIDATE(img_id != resolve_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_BINDING_VS_RESOLVE_ATTACHMENT); + } + } + } + for (size_t simg_view_idx = 0; simg_view_idx < SG_MAX_VIEW_BINDSLOTS; simg_view_idx++) { + if (shd->cmn.views[simg_view_idx].view_type == SG_VIEWTYPE_STORAGEIMAGE) { + if (bindings->views[simg_view_idx].id == SG_INVALID_ID) { + continue; + } + const _sg_view_t* simg_view = _sg_lookup_view(bindings->views[simg_view_idx].id); + if (simg_view) { + _SG_VALIDATE(img_id != simg_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_VS_STORAGEIMAGE_BINDING); + } + } + } + } + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(int ub_slot, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(ub_slot); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass, VALIDATE_AU_PASS_EXPECTED); + const _sg_pipeline_ref_t* pip_ref = &_sg.cur_pip; + const bool pip_null = _sg_pipeline_ref_null(pip_ref); + const bool pip_alive = _sg_pipeline_ref_alive(pip_ref); + _SG_VALIDATE(!pip_null, VALIDATE_AU_NO_PIPELINE); + _SG_VALIDATE(pip_alive, VALIDATE_AU_PIPELINE_ALIVE); + if (pip_alive) { + const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(pip_ref); + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_AU_PIPELINE_VALID); + const _sg_shader_ref_t* shd_ref = &pip->cmn.shader; + const bool shd_alive = _sg_shader_ref_alive(shd_ref); + _SG_VALIDATE(shd_alive, VALIDATE_AU_PIPELINE_SHADER_ALIVE); + if (shd_alive) { + const _sg_shader_t* shd = _sg_shader_ref_ptr(shd_ref); + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_AU_PIPELINE_SHADER_VALID); + _SG_VALIDATE(shd->cmn.uniform_blocks[ub_slot].stage != SG_SHADERSTAGE_NONE, VALIDATE_AU_NO_UNIFORMBLOCK_AT_SLOT); + _SG_VALIDATE(data->size == shd->cmn.uniform_blocks[ub_slot].size, VALIDATE_AU_SIZE); + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_draw(int base_element, int num_elements, int num_instances) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_DRAW_RENDERPASS_EXPECTED); + _SG_VALIDATE(base_element >= 0, VALIDATE_DRAW_BASEELEMENT_GE_ZERO); + _SG_VALIDATE(num_elements >= 0, VALIDATE_DRAW_NUMELEMENTS_GE_ZERO); + _SG_VALIDATE(num_instances >= 0, VALIDATE_DRAW_NUMINSTANCES_GE_ZERO); + _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); + _SOKOL_UNUSED(base_vertex); + _SOKOL_UNUSED(base_instance); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_DRAW_EX_RENDERPASS_EXPECTED); + // NOTE: base_vertex is allowed to be < 0 + _SG_VALIDATE(base_element >= 0, VALIDATE_DRAW_EX_BASEELEMENT_GE_ZERO); + _SG_VALIDATE(num_elements >= 0, VALIDATE_DRAW_EX_NUMELEMENTS_GE_ZERO); + _SG_VALIDATE(num_instances >= 0, VALIDATE_DRAW_EX_NUMINSTANCES_GE_ZERO); + _SG_VALIDATE(base_instance >= 0, VALIDATE_DRAW_EX_BASEINSTANCE_GE_ZERO); + if (base_vertex != 0) { + _SG_VALIDATE(_sg.features.draw_base_vertex, VALIDATE_DRAW_EX_BASEVERTEX_NOT_SUPPORTED); + } + if (base_instance > 0) { + _SG_VALIDATE(_sg.features.draw_base_instance, VALIDATE_DRAW_EX_BASEINSTANCE_NOT_SUPPORTED); + } + if (!_sg.use_indexed_draw) { + _SG_VALIDATE(base_vertex == 0, VALIDATE_DRAW_EX_BASEVERTEX_VS_INDEXED); + } + const bool use_instanced_draw = (num_instances > 1) || _sg.use_instanced_draw; + if (!use_instanced_draw) { + _SG_VALIDATE(base_instance == 0, VALIDATE_DRAW_EX_BASEINSTANCE_VS_INSTANCED); + } + _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(num_groups_x); + _SOKOL_UNUSED(num_groups_y); + _SOKOL_UNUSED(num_groups_z); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass && _sg.cur_pass.is_compute, VALIDATE_DISPATCH_COMPUTEPASS_EXPECTED); + _SG_VALIDATE((num_groups_x >= 0) && (num_groups_x < (1<<16)), VALIDATE_DISPATCH_NUMGROUPSX); + _SG_VALIDATE((num_groups_y >= 0) && (num_groups_y < (1<<16)), VALIDATE_DISPATCH_NUMGROUPSY); + _SG_VALIDATE((num_groups_z >= 0) && (num_groups_z < (1<<16)), VALIDATE_DISPATCH_NUMGROUPSZ); + _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(buf && data && data->ptr); + _sg_validate_begin(); + _SG_VALIDATE(!buf->cmn.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 +} + +_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(buf && data && data->ptr); + _sg_validate_begin(); + _SG_VALIDATE(!buf->cmn.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 +} + +_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(img && data); + _sg_validate_begin(); + _SG_VALIDATE(!img->cmn.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, + img->cmn.height, + img->cmn.num_mipmaps, + img->cmn.num_slices); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_shader_binding_limits(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); + + // NOTE: this validation check is also active in release mode, if a shader uses + // more bindings than allowed, shader creation will fail + int vs_num_tex = 0; + int fs_num_tex = 0; + int cs_num_tex = 0; + int vs_num_sbuf = 0; + int fs_num_sbuf = 0; + int cs_num_sbuf = 0; + int vs_num_simg = 0; + int fs_num_simg = 0; + int cs_num_simg = 0; + int vs_num_texsmp = 0; + int fs_num_texsmp = 0; + int cs_num_texsmp = 0; + for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + switch (desc->views[i].texture.stage) { + case SG_SHADERSTAGE_VERTEX: vs_num_tex++; break; + case SG_SHADERSTAGE_FRAGMENT: fs_num_tex++; break; + case SG_SHADERSTAGE_COMPUTE: cs_num_tex++; break; + default: break; + } + switch (desc->views[i].storage_buffer.stage) { + case SG_SHADERSTAGE_VERTEX: vs_num_sbuf++; break; + case SG_SHADERSTAGE_FRAGMENT: fs_num_sbuf++; break; + case SG_SHADERSTAGE_COMPUTE: cs_num_sbuf++; break; + default: break; + } + switch (desc->views[i].storage_image.stage) { + case SG_SHADERSTAGE_VERTEX: vs_num_simg++; break; + case SG_SHADERSTAGE_FRAGMENT: fs_num_simg++; break; + case SG_SHADERSTAGE_COMPUTE: cs_num_simg++; break; + default: break; + } + } + for (size_t i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) { + switch (desc->texture_sampler_pairs[i].stage) { + case SG_SHADERSTAGE_VERTEX: vs_num_texsmp++; break; + case SG_SHADERSTAGE_FRAGMENT: fs_num_texsmp++; break; + case SG_SHADERSTAGE_COMPUTE: cs_num_texsmp++; break; + default: break; + } + } + const int max_tex = _sg.limits.max_texture_bindings_per_stage; + const int max_sbuf = _sg.limits.max_storage_buffer_bindings_per_stage; + const int max_simg = _sg.limits.max_storage_image_bindings_per_stage; + bool retval = true; + if (vs_num_tex > max_tex) { + _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURES); + retval = false; + } + if (fs_num_tex > max_tex) { + _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURES); + retval = false; + } + if (cs_num_tex > max_tex) { + _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURES); + retval = false; + } + if (vs_num_sbuf > max_sbuf) { + _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEBUFFERS); + retval = false; + } + if (fs_num_sbuf > max_sbuf) { + _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEBUFFERS); + retval = false; + } + if (cs_num_sbuf > max_sbuf) { + _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEBUFFERS); + retval = false; + } + if (vs_num_simg > max_simg) { + _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEIMAGES); + retval = false; + } + if (fs_num_simg > max_simg) { + _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEIMAGES); + retval = false; + } + if (cs_num_simg > max_simg) { + _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEIMAGES); + retval = false; + } + if (vs_num_texsmp > max_tex) { + _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURESAMPLERPAIRS); + retval = false; + } + if (fs_num_texsmp > max_tex) { + _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURESAMPLERPAIRS); + retval = false; + } + if (cs_num_texsmp > max_tex) { + _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURESAMPLERPAIRS); + retval = false; + } + return retval; +} + +_SOKOL_PRIVATE bool _sg_validate_pass_attachment_limits(const sg_pass* pass) { + SOKOL_ASSERT(pass); + int num_color_atts = 0; + int num_resolve_atts = 0; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + if (pass->attachments.colors[att_index].id != SG_INVALID_ID) { + num_color_atts += 1; + } + if (pass->attachments.resolves[att_index].id != SG_INVALID_ID) { + num_resolve_atts += 1; + } + } + bool retval = true; + int max_color_atts = _sg.limits.max_color_attachments; + if (num_color_atts > max_color_atts) { + _SG_ERROR(BEGINPASS_TOO_MANY_COLOR_ATTACHMENTS); + retval = false; + } + // max_color_attachments not a bug + if (num_resolve_atts > max_color_atts) { + _SG_ERROR(BEGINPASS_TOO_MANY_RESOLVE_ATTACHMENTS); + retval = false; + } + return retval; +} + +// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████ +// +// >>resources +_SOKOL_PRIVATE sg_buffer_usage _sg_buffer_usage_defaults(const sg_buffer_usage* usg) { + sg_buffer_usage def = *usg; + if (!(def.vertex_buffer || def.index_buffer || def.storage_buffer)) { + def.vertex_buffer = true; + } + if (!(def.immutable || def.stream_update || def.dynamic_update)) { + def.immutable = true; + } + return def; +} + + +_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { + sg_buffer_desc def = *desc; + def.usage = _sg_buffer_usage_defaults(&def.usage); + if (def.size == 0) { + def.size = def.data.size; + } + return def; +} + +_SOKOL_PRIVATE sg_image_usage _sg_image_usage_defaults(const sg_image_usage *usg) { + sg_image_usage def = *usg; + if (!(def.immutable || def.stream_update || def.dynamic_update)) { + def.immutable = true; + } + return def; +} + +_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { + sg_image_desc def = *desc; + def.type = _sg_def(def.type, SG_IMAGETYPE_2D); + def.usage = _sg_image_usage_defaults(&def.usage); + def.num_slices = _sg_def(def.num_slices, def.type == SG_IMAGETYPE_CUBE ? 6 : 1); + def.num_mipmaps = _sg_def(def.num_mipmaps, 1); + if (def.usage.color_attachment || def.usage.resolve_attachment) { + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + } else if (def.usage.depth_stencil_attachment) { + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.depth_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + } else { + def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); + def.sample_count = _sg_def(def.sample_count, 1); + } + return def; +} + +_SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* desc) { + sg_sampler_desc def = *desc; + def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); + def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); + def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NEAREST); + def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); + def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); + def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); + def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.compare = _sg_def(def.compare, SG_COMPAREFUNC_NEVER); + def.max_anisotropy = _sg_def(def.max_anisotropy, 1); + return def; +} + +_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { + sg_shader_desc def = *desc; + #if defined(SOKOL_METAL) + def.vertex_func.entry = _sg_def(def.vertex_func.entry, "_main"); + def.fragment_func.entry = _sg_def(def.fragment_func.entry, "_main"); + def.compute_func.entry = _sg_def(def.compute_func.entry, "_main"); + #else + def.vertex_func.entry = _sg_def(def.vertex_func.entry, "main"); + def.fragment_func.entry = _sg_def(def.fragment_func.entry, "main"); + def.compute_func.entry = _sg_def(def.compute_func.entry, "main"); + #endif + #if defined(SOKOL_D3D11) + if (def.vertex_func.source) { + def.vertex_func.d3d11_target = _sg_def(def.vertex_func.d3d11_target, "vs_4_0"); + } + if (def.fragment_func.source) { + def.fragment_func.d3d11_target = _sg_def(def.fragment_func.d3d11_target, "ps_4_0"); + } + if (def.compute_func.source) { + def.compute_func.d3d11_target = _sg_def(def.fragment_func.d3d11_target,"cs_5_0"); + } + #endif + def.mtl_threads_per_threadgroup.y = _sg_def(desc->mtl_threads_per_threadgroup.y, 1); + def.mtl_threads_per_threadgroup.z = _sg_def(desc->mtl_threads_per_threadgroup.z, 1); + for (size_t ub_index = 0; ub_index < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_index++) { + sg_shader_uniform_block* ub_desc = &def.uniform_blocks[ub_index]; + if (ub_desc->stage != SG_SHADERSTAGE_NONE) { + ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE); + for (size_t u_index = 0; u_index < SG_MAX_UNIFORMBLOCK_MEMBERS; u_index++) { + sg_glsl_shader_uniform* u_desc = &ub_desc->glsl_uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + u_desc->array_count = _sg_def(u_desc->array_count, 1); + } + } + } + for (size_t view_index = 0; view_index < SG_MAX_VIEW_BINDSLOTS; view_index++) { + sg_shader_view* view_desc = &def.views[view_index]; + if (view_desc->texture.stage != SG_SHADERSTAGE_NONE) { + view_desc->texture.image_type = _sg_def(view_desc->texture.image_type, SG_IMAGETYPE_2D); + view_desc->texture.sample_type = _sg_def(view_desc->texture.sample_type, SG_IMAGESAMPLETYPE_FLOAT); + } else if (view_desc->storage_image.stage != SG_SHADERSTAGE_NONE) { + view_desc->storage_image.image_type = _sg_def(view_desc->storage_image.image_type, SG_IMAGETYPE_2D); + } + } + for (size_t smp_index = 0; smp_index < SG_MAX_SAMPLER_BINDSLOTS; smp_index++) { + sg_shader_sampler* smp_desc = &def.samplers[smp_index]; + if (smp_desc->stage != SG_SHADERSTAGE_NONE) { + smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_FILTERING); + } + } + return def; +} + +_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { + sg_pipeline_desc def = *desc; + + // FIXME: should we actually do all this stuff for a compute pipeline? + + def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); + def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); + def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + + def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); + def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP); + def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP); + def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); + + def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); + def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.environment.defaults.depth_format); + if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) { + // special case depth-only rendering, enforce a color count of 0 + def.color_count = 0; + } else { + def.color_count = _sg_def(def.color_count, 1); + } + if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { + def.color_count = SG_MAX_COLOR_ATTACHMENTS; + } + for (int i = 0; i < def.color_count; i++) { + sg_color_target_state* cs = &def.colors[i]; + cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.environment.defaults.color_format); + cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); + sg_blend_state* bs = &def.colors[i].blend; + bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD); + bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); + if ((bs->op_rgb == SG_BLENDOP_MIN) || (bs->op_rgb == SG_BLENDOP_MAX)) { + bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ONE); + } else { + bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO); + } + bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD); + bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE); + if ((bs->op_alpha == SG_BLENDOP_MIN) || (bs->op_alpha == SG_BLENDOP_MAX)) { + bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ONE); + } else { + bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO); + } + } + + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT((a_state->buffer_index >= 0) && (a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS)); + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[a_state->buffer_index]; + l_state->step_func = _sg_def(l_state->step_func, SG_VERTEXSTEP_PER_VERTEX); + l_state->step_rate = _sg_def(l_state->step_rate, 1); + } + + // resolve vertex layout strides and offsets + _SG_STRUCT(int, auto_offset[SG_MAX_VERTEXBUFFER_BINDSLOTS]); + 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 + if (def.layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT((a_state->buffer_index >= 0) && (a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS)); + if (use_auto_offset) { + a_state->offset = auto_offset[a_state->buffer_index]; + } + auto_offset[a_state->buffer_index] += _sg_vertexformat_bytesize(a_state->format); + } + // compute vertex strides if needed + for (int buf_index = 0; buf_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; buf_index++) { + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[buf_index]; + if (l_state->stride == 0) { + l_state->stride = auto_offset[buf_index]; + } + } + + return def; +} + +_SOKOL_PRIVATE sg_view_desc _sg_view_desc_defaults(const sg_view_desc* desc) { + sg_view_desc def = *desc; + return def; +} + +_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { + sg_buffer res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); + _sg_resource_stats_inc(buffers.allocated); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(BUFFER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { + sg_image res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + _sg_resource_stats_inc(images.allocated); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(IMAGE_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_sampler _sg_alloc_sampler(void) { + sg_sampler res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.sampler_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.sampler_pool, &_sg.pools.samplers[slot_index].slot, slot_index); + _sg_resource_stats_inc(samplers.allocated); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(SAMPLER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { + sg_shader res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); + _sg_resource_stats_inc(shaders.allocated); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(SHADER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { + sg_pipeline res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); + _sg_resource_stats_inc(pipelines.allocated); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(PIPELINE_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_view _sg_alloc_view(void) { + sg_view res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.view_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.view_pool, &_sg.pools.views[slot_index].slot, slot_index); + _sg_resource_stats_inc(views.allocated); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(VIEW_POOL_EXHAUSTED); + } + return res; +} + +_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_slot_reset(&buf->slot); + _sg_resource_stats_inc(buffers.deallocated); +} + +_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_slot_reset(&img->slot); + _sg_resource_stats_inc(images.deallocated); +} + +_SOKOL_PRIVATE void _sg_dealloc_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC) && (smp->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.sampler_pool, _sg_slot_index(smp->slot.id)); + _sg_slot_reset(&smp->slot); + _sg_resource_stats_inc(samplers.deallocated); +} + +_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_slot_reset(&shd->slot); + _sg_resource_stats_inc(shaders.deallocated); +} + +_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_slot_reset(&pip->slot); + _sg_resource_stats_inc(pipelines.deallocated); +} + +_SOKOL_PRIVATE void _sg_dealloc_view(_sg_view_t* view) { + SOKOL_ASSERT(view && (view->slot.state == SG_RESOURCESTATE_ALLOC) && (view->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.view_pool, _sg_slot_index(view->slot.id)); + _sg_slot_reset(&view->slot); + _sg_resource_stats_inc(views.deallocated); +} + +_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); + if (_sg_validate_buffer_desc(desc)) { + _sg_buffer_common_init(&buf->cmn, desc); + buf->slot.state = _sg_create_buffer(buf, desc); + } else { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); + _sg_resource_stats_inc(buffers.inited); +} + +_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); + if (_sg_validate_image_desc(desc)) { + _sg_image_common_init(&img->cmn, desc); + img->slot.state = _sg_create_image(img, desc); + } else { + img->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); + _sg_resource_stats_inc(images.inited); +} + +_SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_sampler_desc(desc)) { + _sg_sampler_common_init(&smp->cmn, desc); + smp->slot.state = _sg_create_sampler(smp, desc); + } else { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID)||(smp->slot.state == SG_RESOURCESTATE_FAILED)); + _sg_resource_stats_inc(samplers.inited); +} + +_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); + if (!_sg_validate_shader_desc(desc)) { + shd->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + if (!_sg_validate_shader_binding_limits(desc)) { + shd->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + _sg_shader_common_init(&shd->cmn, desc); + shd->slot.state = _sg_create_shader(shd, desc); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); + _sg_resource_stats_inc(shaders.inited); +} + +_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); + if (_sg_validate_pipeline_desc(desc)) { + _sg_shader_t* shd = _sg_lookup_shader(desc->shader.id); + if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + _sg_pipeline_common_init(&pip->cmn, desc, shd); + pip->slot.state = _sg_create_pipeline(pip, desc); + } else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); + _sg_resource_stats_inc(pipelines.inited); +} + +_SOKOL_PRIVATE void _sg_init_view(_sg_view_t* view, const sg_view_desc* desc) { + SOKOL_ASSERT(view && view->slot.state == SG_RESOURCESTATE_ALLOC); + SOKOL_ASSERT(desc); + if (_sg_validate_view_desc(desc)) { + uint32_t buf_id = desc->storage_buffer.buffer.id; + uint32_t img_id = desc->texture.image.id; + img_id = img_id ? img_id : desc->storage_image.image.id; + img_id = img_id ? img_id : desc->color_attachment.image.id; + img_id = img_id ? img_id : desc->resolve_attachment.image.id; + img_id = img_id ? img_id : desc->depth_stencil_attachment.image.id; + _sg_buffer_t* buf = buf_id ? _sg_lookup_buffer(buf_id) : 0; + _sg_image_t* img = img_id ? _sg_lookup_image(img_id) : 0; + sg_resource_state res_state = SG_RESOURCESTATE_INVALID; + if (buf) { + SOKOL_ASSERT(!img); + res_state = buf->slot.state; + } else if (img) { + SOKOL_ASSERT(!buf); + res_state = img->slot.state; + } + if (res_state == SG_RESOURCESTATE_VALID) { + _sg_view_common_init(&view->cmn, desc, buf, img); + view->slot.state = _sg_create_view(view, desc); + } else { + view->slot.state = SG_RESOURCESTATE_FAILED; + } + } else { + view->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)); + _sg_resource_stats_inc(views.inited); +} + +_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))); + _sg_discard_buffer(buf); + _sg_reset_buffer_to_alloc_state(buf); + _sg_resource_stats_inc(buffers.uninited); +} + +_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))); + _sg_discard_image(img); + _sg_reset_image_to_alloc_state(img); + _sg_resource_stats_inc(images.uninited); +} + +_SOKOL_PRIVATE void _sg_uninit_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_sampler(smp); + _sg_reset_sampler_to_alloc_state(smp); + _sg_resource_stats_inc(samplers.uninited); +} + +_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))); + _sg_discard_shader(shd); + _sg_reset_shader_to_alloc_state(shd); + _sg_resource_stats_inc(shaders.uninited); +} + +_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))); + _sg_discard_pipeline(pip); + _sg_reset_pipeline_to_alloc_state(pip); + _sg_resource_stats_inc(pipelines.uninited); +} + +_SOKOL_PRIVATE void _sg_uninit_view(_sg_view_t* view) { + SOKOL_ASSERT(view && ((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_view(view); + _sg_reset_view_to_alloc_state(view); + _sg_resource_stats_inc(views.uninited); +} + +_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); + } + } +} + +_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 sg_desc _sg_desc_defaults(const sg_desc* desc) { + /* + NOTE: on WebGPU, the default color pixel format MUST be provided, + it cannot be a default compile-time constant. + */ + sg_desc res = *desc; + #if defined(SOKOL_WGPU) + SOKOL_ASSERT(SG_PIXELFORMAT_NONE < res.environment.defaults.color_format); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_BGRA8); + #else + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_RGBA8); + #endif + res.environment.defaults.depth_format = _sg_def(res.environment.defaults.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + res.environment.defaults.sample_count = _sg_def(res.environment.defaults.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.sampler_pool_size = _sg_def(res.sampler_pool_size, _SG_DEFAULT_SAMPLER_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.view_pool_size = _sg_def(res.view_pool_size, _SG_DEFAULT_VIEW_POOL_SIZE); + res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); + res.wgpu.bindgroups_cache_size = _sg_def(res.wgpu.bindgroups_cache_size, _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE); + res.vulkan.copy_staging_buffer_size = _sg_def(res.vulkan.copy_staging_buffer_size, _SG_DEFAULT_VK_COPY_STAGING_SIZE); + res.vulkan.stream_staging_buffer_size = _sg_def(res.vulkan.stream_staging_buffer_size, _SG_DEFAULT_VK_STREAM_STAGING_SIZE); + res.vulkan.descriptor_buffer_size = _sg_def(res.vulkan.descriptor_buffer_size, _SG_DEFAULT_VK_DESCRIPTOR_BUFFER_SIZE); + return res; +} + +_SOKOL_PRIVATE sg_pass _sg_pass_defaults(const sg_pass* pass) { + sg_pass res = *pass; + if (!res.compute) { + if (_sg_attachments_empty(&pass->attachments)) { + // this is a swapchain-pass + res.swapchain.sample_count = _sg_def(res.swapchain.sample_count, _sg.desc.environment.defaults.sample_count); + res.swapchain.color_format = _sg_def(res.swapchain.color_format, _sg.desc.environment.defaults.color_format); + res.swapchain.depth_format = _sg_def(res.swapchain.depth_format, _sg.desc.environment.defaults.depth_format); + } + res.action = _sg_pass_action_defaults(&res.action); + } + return res; +} + +_SOKOL_PRIVATE void _sg_discard_all_resources(void) { + /* 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 + NOTE: ONLY EXECUTE THIS AT SHUTDOWN + ...because the free queues will not be reset + and the resource slots not be cleared! + */ + for (int i = 1; i < _sg.pools.buffer_pool.size; i++) { + sg_resource_state state = _sg.pools.buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_buffer(&_sg.pools.buffers[i]); + } + } + for (int i = 1; i < _sg.pools.image_pool.size; i++) { + sg_resource_state state = _sg.pools.images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_image(&_sg.pools.images[i]); + } + } + for (int i = 1; i < _sg.pools.sampler_pool.size; i++) { + sg_resource_state state = _sg.pools.samplers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_sampler(&_sg.pools.samplers[i]); + } + } + for (int i = 1; i < _sg.pools.shader_pool.size; i++) { + sg_resource_state state = _sg.pools.shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_shader(&_sg.pools.shaders[i]); + } + } + for (int i = 1; i < _sg.pools.pipeline_pool.size; i++) { + sg_resource_state state = _sg.pools.pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_pipeline(&_sg.pools.pipelines[i]); + } + } + for (int i = 1; i < _sg.pools.view_pool.size; i++) { + sg_resource_state state = _sg.pools.views[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_view(&_sg.pools.views[i]); + } + } +} + +_SOKOL_PRIVATE void _sg_override_portable_limits(void) { + if (_sg.desc.enforce_portable_limits) { + _sg.limits.max_color_attachments = SG_MAX_PORTABLE_COLOR_ATTACHMENTS; + _sg.limits.max_texture_bindings_per_stage = SG_MAX_PORTABLE_TEXTURE_BINDINGS_PER_STAGE; + if (_sg.features.compute) { + _sg.limits.max_storage_buffer_bindings_per_stage = SG_MAX_PORTABLE_STORAGEBUFFER_BINDINGS_PER_STAGE; + _sg.limits.max_storage_image_bindings_per_stage = SG_MAX_PORTABLE_STORAGEIMAGE_BINDINGS_PER_STAGE; + } + } +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(!_sg.valid); + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); + _sg.desc = _sg_desc_defaults(desc); + _sg_setup_pools(&_sg.pools, &_sg.desc); + _sg_setup_commit_listeners(&_sg.desc); + _sg.frame_index = 1; + _sg.stats_enabled = true; + _sg_setup_backend(&_sg.desc); + _sg_override_portable_limits(); + _sg.valid = true; +} + +SOKOL_API_IMPL void sg_shutdown(void) { + SOKOL_ASSERT(_sg.valid); + _sg_discard_all_resources(); + _sg_discard_backend(); + _sg_discard_commit_listeners(); + _sg_discard_pools(&_sg.pools); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); +} + +SOKOL_API_IMPL bool sg_isvalid(void) { + return _sg.valid; +} + +SOKOL_API_IMPL sg_desc sg_query_desc(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.desc; +} + +SOKOL_API_IMPL sg_backend sg_query_backend(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.backend; +} + +SOKOL_API_IMPL sg_features sg_query_features(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.features; +} + +SOKOL_API_IMPL sg_limits sg_query_limits(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.limits; +} + +SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { + SOKOL_ASSERT(_sg.valid); + int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index]; + _SG_STRUCT(sg_pixelformat_info, res); + res.sample = src->sample; + res.filter = src->filter; + res.render = src->render; + res.blend = src->blend; + res.msaa = src->msaa; + res.depth = src->depth; + res.compressed = _sg_is_compressed_pixel_format(fmt); + res.read = src->read; + res.write = src->write; + if (!res.compressed) { + res.bytes_per_pixel = _sg_pixelformat_bytesize(fmt); + } + return res; +} + +SOKOL_API_IMPL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(width > 0); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_row_pitch(fmt, width, row_align_bytes); +} + +SOKOL_API_IMPL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT((width > 0) && (height > 0)); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_surface_pitch(fmt, width, height, row_align_bytes); +} + +SOKOL_API_IMPL sg_stats sg_query_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg_update_alive_free_resource_stats(&_sg.stats.total.buffers, &_sg.pools.buffer_pool); + _sg_update_alive_free_resource_stats(&_sg.stats.total.images, &_sg.pools.image_pool); + _sg_update_alive_free_resource_stats(&_sg.stats.total.views, &_sg.pools.view_pool); + _sg_update_alive_free_resource_stats(&_sg.stats.total.samplers, &_sg.pools.sampler_pool); + _sg_update_alive_free_resource_stats(&_sg.stats.total.shaders, &_sg.pools.shader_pool); + _sg_update_alive_free_resource_stats(&_sg.stats.total.pipelines, &_sg.pools.pipeline_pool); + return _sg.stats; +} + +SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(trace_hooks); + _SOKOL_UNUSED(trace_hooks); + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks old_hooks = _sg.hooks; + _sg.hooks = *trace_hooks; + #else + static sg_trace_hooks old_hooks; + _SG_WARN(TRACE_HOOKS_NOT_ENABLED); + #endif + return old_hooks; +} + +SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { + SOKOL_ASSERT(_sg.valid); + sg_buffer res = _sg_alloc_buffer(); + _SG_TRACE_ARGS(alloc_buffer, res); + return res; +} + +SOKOL_API_IMPL sg_image sg_alloc_image(void) { + SOKOL_ASSERT(_sg.valid); + sg_image res = _sg_alloc_image(); + _SG_TRACE_ARGS(alloc_image, res); + return res; +} + +SOKOL_API_IMPL sg_sampler sg_alloc_sampler(void) { + SOKOL_ASSERT(_sg.valid); + sg_sampler res = _sg_alloc_sampler(); + _SG_TRACE_ARGS(alloc_sampler, res); + return res; +} + +SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { + SOKOL_ASSERT(_sg.valid); + sg_shader res = _sg_alloc_shader(); + _SG_TRACE_ARGS(alloc_shader, res); + return res; +} + +SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline res = _sg_alloc_pipeline(); + _SG_TRACE_ARGS(alloc_pipeline, res); + return res; +} + +SOKOL_API_IMPL sg_view sg_alloc_view(void) { + SOKOL_ASSERT(_sg.valid); + sg_view res = _sg_alloc_view(); + _SG_TRACE_ARGS(alloc_view, res); + return res; +} + +SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(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_image_t* img = _sg_lookup_image(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_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + } else { + _SG_ERROR(DEALLOC_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(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_pipeline_t* pip = _sg_lookup_pipeline(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_view(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + if (view->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_view(view); + } else { + _SG_ERROR(DEALLOC_VIEW_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_view, view_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_buffer_t* buf = _sg_lookup_buffer(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_image_t* img = _sg_lookup_image(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_sampler(sg_sampler smp_id, const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_sampler, smp_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_shader_t* shd = _sg_lookup_shader(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_pipeline_t* pip = _sg_lookup_pipeline(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_view(sg_view view_id, const sg_view_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_view_desc desc_def = _sg_view_desc_defaults(desc); + _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + if (view->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_view(view, &desc_def); + SOKOL_ASSERT((view->slot.state == SG_RESOURCESTATE_VALID) + || (view->slot.state == SG_RESOURCESTATE_FAILED) + || (view->slot.state == SG_RESOURCESTATE_ALLOC)); + } else { + _SG_ERROR(INIT_VIEW_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_view, view_id, &desc_def); +} + +SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(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 if (buf->slot.state != SG_RESOURCESTATE_ALLOC) { + _SG_ERROR(UNINIT_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(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 if (img->slot.state != SG_RESOURCESTATE_ALLOC) { + _SG_ERROR(UNINIT_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_image, img_id); +} + +SOKOL_API_IMPL void sg_uninit_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } else if (smp->slot.state != SG_RESOURCESTATE_ALLOC) { + _SG_ERROR(UNINIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(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 if (shd->slot.state != SG_RESOURCESTATE_ALLOC) { + _SG_ERROR(UNINIT_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_shader, shd_id); +} + +SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(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 if (pip->slot.state != SG_RESOURCESTATE_ALLOC) { + _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_uninit_view(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + if ((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_view(view); + SOKOL_ASSERT(view->slot.state == SG_RESOURCESTATE_ALLOC); + } else if (view->slot.state != SG_RESOURCESTATE_ALLOC) { + _SG_ERROR(UNINIT_VIEW_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_view, view_id); +} + +SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + 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); + _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + 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_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + 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); + _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + 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_view(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + if (view->slot.state == SG_RESOURCESTATE_ALLOC) { + view->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_VIEW_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_view, view_id); +} + +SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(img_id.id); + sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_sampler_state(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + sg_resource_state res = smp ? smp->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_view_state(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _sg_view_t* view = _sg_lookup_view(view_id.id); + sg_resource_state res = view ? view->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(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_buffer_t* buf = _sg_buffer_at(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; +} + +SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(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_image_t* img = _sg_image_at(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; +} + +SOKOL_API_IMPL sg_sampler sg_make_sampler(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + sg_sampler smp_id = _sg_alloc_sampler(); + if (smp_id.id != SG_INVALID_ID) { + _sg_sampler_t* smp = _sg_sampler_at(smp_id.id); + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_sampler, &desc_def, smp_id); + return smp_id; +} + +SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(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_shader_t* shd = _sg_shader_at(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; +} + +SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(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_pipeline_t* pip = _sg_pipeline_at(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; +} + +SOKOL_API_IMPL sg_view sg_make_view(const sg_view_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_view_desc desc_def = _sg_view_desc_defaults(desc); + sg_view view_id = _sg_alloc_view(); + if (view_id.id != SG_INVALID_ID) { + _sg_view_t* view = _sg_view_at(view_id.id); + SOKOL_ASSERT(view && (view->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_view(view, &desc_def); + SOKOL_ASSERT((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_view, &desc_def, view_id); + return view_id; +} + +SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_buffer, buf_id); + _sg_buffer_t* buf = _sg_lookup_buffer(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); + _sg_image_t* img = _sg_lookup_image(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_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_sampler, smp_id); + _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + SOKOL_ASSERT(smp->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); + _sg_shader_t* shd = _sg_lookup_shader(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); + _sg_pipeline_t* pip = _sg_lookup_pipeline(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_view(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_view, view_id); + _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + if ((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_view(view); + SOKOL_ASSERT(view->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (view->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_view(view); + SOKOL_ASSERT(view->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + SOKOL_ASSERT(_sg_attachments_empty(&_sg.cur_pass.atts)); + SOKOL_ASSERT(pass); + SOKOL_ASSERT((pass->_start_canary == 0) && (pass->_end_canary == 0)); + _sg.cur_pass.in_pass = true; + const sg_pass pass_def = _sg_pass_defaults(pass); + if (!_sg_validate_pass_attachment_limits(&pass_def)) { + return; + } + if (!_sg_validate_begin_pass(&pass_def)) { + return; + } + const _sg_attachments_ptrs_t atts_ptrs = _sg_attachments_ptrs(&pass_def.attachments); + if (!atts_ptrs.empty) { + if (!_sg_attachments_alive(&atts_ptrs)) { + _SG_ERROR(BEGINPASS_ATTACHMENTS_ALIVE); + return; + } + _sg.cur_pass.atts = pass->attachments; + _sg.cur_pass.dim = _sg_attachments_dim(&atts_ptrs); + } else if (!pass_def.compute) { + // a swapchain pass + SOKOL_ASSERT(pass_def.swapchain.width > 0); + SOKOL_ASSERT(pass_def.swapchain.height > 0); + SOKOL_ASSERT(pass_def.swapchain.color_format > SG_PIXELFORMAT_NONE); + SOKOL_ASSERT(pass_def.swapchain.sample_count > 0); + _sg.cur_pass.dim.width = pass_def.swapchain.width; + _sg.cur_pass.dim.height = pass_def.swapchain.height; + _sg.cur_pass.swapchain.color_fmt = pass_def.swapchain.color_format; + _sg.cur_pass.swapchain.depth_fmt = pass_def.swapchain.depth_format; + _sg.cur_pass.swapchain.sample_count = pass_def.swapchain.sample_count; + } + _sg.cur_pass.action = pass_def.action; + _sg.cur_pass.valid = true; // may be overruled by backend begin-pass functions + _sg.cur_pass.is_compute = pass_def.compute; + _sg_begin_pass(&pass_def, &atts_ptrs); + _SG_TRACE_ARGS(begin_pass, &pass_def); +} + +SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + #if defined(SOKOL_DEBUG) + if (!_sg_validate_apply_viewport(x, y, width, height, origin_top_left)) { + return; + } + #endif + _sg_stats_inc(num_apply_viewport); + if (!_sg.cur_pass.valid) { + return; + } + _sg_apply_viewport(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + #if defined(SOKOL_DEBUG) + if (!_sg_validate_apply_scissor_rect(x, y, width, height, origin_top_left)) { + return; + } + #endif + _sg_stats_inc(num_apply_scissor_rect); + if (!_sg.cur_pass.valid) { + return; + } + _sg_apply_scissor_rect(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_stats_inc(num_apply_pipeline); + if (!_sg_validate_apply_pipeline(pip_id)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + SOKOL_ASSERT(pip); + _sg.cur_pip = _sg_pipeline_ref(pip); + + _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); + if (!_sg.next_draw_valid) { + return; + } + _sg.use_indexed_draw = pip->cmn.index_type != SG_INDEXTYPE_NONE; + _sg.use_instanced_draw = pip->cmn.use_instanced_draw; + + _sg_apply_pipeline(pip); + + // set the expected bindings and uniform block flags + const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader); + _sg.required_bindings_and_uniforms = pip->cmn.required_bindings_and_uniforms | shd->cmn.required_bindings_and_uniforms; + _sg.applied_bindings_and_uniforms = 0; + + _SG_TRACE_ARGS(apply_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(bindings); + _sg_stats_inc(num_apply_bindings); + _sg.applied_bindings_and_uniforms |= (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS); + if (!_sg_validate_apply_bindings(bindings)) { + _sg.next_draw_valid = false; + } + SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + if (!_sg_pipeline_ref_alive(&_sg.cur_pip)) { + _sg.next_draw_valid = false; + } + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + + _SG_STRUCT(_sg_bindings_ptrs_t, bnd); + bnd.pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); + const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd.pip->cmn.shader); + if (!_sg.cur_pass.is_compute) { + for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + if (bnd.pip->cmn.vertex_buffer_layout_active[i]) { + SOKOL_ASSERT(bindings->vertex_buffers[i].id != SG_INVALID_ID); + bnd.vbs[i] = _sg_lookup_buffer(bindings->vertex_buffers[i].id); + bnd.vb_offsets[i] = bindings->vertex_buffer_offsets[i]; + _sg.next_draw_valid &= bnd.vbs[i] && (SG_RESOURCESTATE_VALID == bnd.vbs[i]->slot.state); + } + } + if (bindings->index_buffer.id) { + bnd.ib = _sg_lookup_buffer(bindings->index_buffer.id); + bnd.ib_offset = bindings->index_buffer_offset; + _sg.next_draw_valid &= bnd.ib && (SG_RESOURCESTATE_VALID == bnd.ib->slot.state); + } + } + + for (int i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + if (shd->cmn.views[i].view_type != SG_VIEWTYPE_INVALID) { + SOKOL_ASSERT(bindings->views[i].id != SG_INVALID_ID); + bnd.views[i] = _sg_lookup_view(bindings->views[i].id); + if (bnd.views[i]) { + if (bnd.views[i]->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) { + _sg.next_draw_valid &= _sg_buffer_ref_valid(&bnd.views[i]->cmn.buf.ref); + } else { + _sg.next_draw_valid &= _sg_image_ref_valid(&bnd.views[i]->cmn.img.ref); + } + } else { + _sg.next_draw_valid = false; + } + } + } + + for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd->cmn.samplers[i].stage != SG_SHADERSTAGE_NONE) { + SOKOL_ASSERT(bindings->samplers[i].id != SG_INVALID_ID); + bnd.smps[i] = _sg_lookup_sampler(bindings->samplers[i].id); + SOKOL_ASSERT(bnd.smps[i]); + } + } + + if (_sg.next_draw_valid) { + _sg.next_draw_valid &= _sg_apply_bindings(&bnd); + _SG_TRACE_ARGS(apply_bindings, bindings); + } +} + +SOKOL_API_IMPL void sg_apply_uniforms(int ub_slot, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS)); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_inc(num_apply_uniforms); + _sg_stats_add(size_apply_uniforms, (uint32_t)data->size); + _sg.applied_bindings_and_uniforms |= 1 << ub_slot; + if (!_sg_validate_apply_uniforms(ub_slot, data)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + _sg_apply_uniforms(ub_slot, data); + _SG_TRACE_ARGS(apply_uniforms, ub_slot, data); +} + +_SOKOL_PRIVATE bool _sg_check_skip_draw(int num_elements, int num_instances) { + if (!_sg.cur_pass.valid) { + return true; + } + if (!_sg.next_draw_valid) { + return true; + } + // skip no-op draws + if ((0 == num_elements) || (0 == num_instances)) { + return true; + } + return false; +} + +SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.valid); + #if defined(SOKOL_DEBUG) + if (!_sg_validate_draw(base_element, num_elements, num_instances)) { + return; + } + #endif + _sg_stats_inc(num_draw); + if (_sg_check_skip_draw(num_elements, num_instances)) { + return; + } + _sg_draw(base_element, num_elements, num_instances, 0, 0); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + SOKOL_ASSERT(_sg.valid); + #if defined(SOKOL_DEBUG) + if (!_sg_validate_draw_ex(base_element, num_elements, num_instances, base_vertex, base_instance)) { + return; + } + #endif + _sg_stats_inc(num_draw_ex); + if (_sg_check_skip_draw(num_elements, num_instances)) { + return; + } + _sg_draw(base_element, num_elements, num_instances, base_vertex, base_instance); + _SG_TRACE_ARGS(draw_ex, base_element, num_elements, num_instances, base_vertex, base_instance); +} + +SOKOL_API_IMPL void sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { + SOKOL_ASSERT(_sg.valid); + #if defined(SOKOL_DEBUG) + if (!_sg_validate_dispatch(num_groups_x, num_groups_y, num_groups_z)) { + return; + } + #endif + _sg_stats_inc(num_dispatch); + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + // skip no-op dispatches + if ((0 == num_groups_x) || (0 == num_groups_y) || (0 == num_groups_z)) { + return; + } + _sg_dispatch(num_groups_x, num_groups_y, num_groups_z); + _SG_TRACE_ARGS(dispatch, num_groups_x, num_groups_y, num_groups_z); +} + +SOKOL_API_IMPL void sg_end_pass(void) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_inc(num_passes); + // NOTE: don't exit early if !_sg.cur_pass.valid + const _sg_attachments_ptrs_t atts_ptrs = _sg_attachments_ptrs(&_sg.cur_pass.atts); + _sg_end_pass(&atts_ptrs); + _sg.cur_pip = _sg_pipeline_ref(0); + _sg_clear(&_sg.cur_pass, sizeof(_sg.cur_pass)); + _SG_TRACE_NOARGS(end_pass); +} + +SOKOL_API_IMPL void sg_commit(void) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + _sg_commit(); + _sg_update_stats(); + _sg_notify_commit_listeners(); + _SG_TRACE_NOARGS(commit); + _sg.frame_index++; +} + +SOKOL_API_IMPL void sg_reset_state_cache(void) { + SOKOL_ASSERT(_sg.valid); + _sg_reset_state_cache(); + _SG_TRACE_NOARGS(reset_state_cache); +} + +SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_inc(num_update_buffer); + _sg_stats_add(size_update_buffer, (uint32_t)data->size); + _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { + if (_sg_validate_update_buffer(buf, data)) { + SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); + // only one update allowed per buffer and frame + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + // update and append on same buffer in same frame not allowed + SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data); + buf->cmn.update_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_buffer, buf_id, data); +} + +SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr); + _sg_stats_inc(num_append_buffer); + _sg_stats_add(size_append_buffer, (uint32_t)data->size); + _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + int result; + if (buf) { + // rewind append cursor in a new frame + if (buf->cmn.append_frame_index != _sg.frame_index) { + buf->cmn.append_pos = 0; + buf->cmn.append_overflow = false; + } + if (((size_t)buf->cmn.append_pos + data->size) > (size_t)buf->cmn.size) { + buf->cmn.append_overflow = true; + } + const int start_pos = buf->cmn.append_pos; + // NOTE: the multiple-of-4 requirement for the buffer offset is coming + // from WebGPU, but we want identical behaviour between backends + SOKOL_ASSERT(_sg_multiple_u64((uint64_t)start_pos, 4)); + if (buf->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_append_buffer(buf, data)) { + if (!buf->cmn.append_overflow && (data->size > 0)) { + // update and append on same buffer in same frame not allowed + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); + buf->cmn.append_pos += (int) _sg_roundup_u64(data->size, 4); + buf->cmn.append_frame_index = _sg.frame_index; + } + } + } + result = start_pos; + } else { + // FIXME: should we return -1 here? + result = 0; + } + _SG_TRACE_ARGS(append_buffer, buf_id, data, result); + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + bool result = buf ? buf->cmn.append_overflow : false; + 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(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_stats_inc(num_update_image); + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + if (data->mip_levels[mip_index].size == 0) { + break; + } + _sg_stats_add(size_update_image, (uint32_t)data->mip_levels[mip_index].size); + } + _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_update_image(img, data)) { + SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); + _sg_update_image(img, data); + img->cmn.upd_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_image, img_id, data); +} + +SOKOL_API_IMPL void sg_push_debug_group(const char* name) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(name); + _sg_push_debug_group(name); + _SG_TRACE_ARGS(push_debug_group, name); +} + +SOKOL_API_IMPL void sg_pop_debug_group(void) { + SOKOL_ASSERT(_sg.valid); + _sg_pop_debug_group(); + _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 void sg_enable_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = true; +} + +SOKOL_API_IMPL void sg_disable_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = false; +} + +SOKOL_API_IMPL bool sg_stats_enabled(void) { + return _sg.stats_enabled; +} + +SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_buffer_info, info); + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + info.slot.state = buf->slot.state; + info.slot.res_id = buf->slot.id; + info.slot.uninit_count = buf->slot.uninit_count; + info.update_frame_index = buf->cmn.update_frame_index; + info.append_frame_index = buf->cmn.append_frame_index; + info.append_pos = buf->cmn.append_pos; + info.append_overflow = buf->cmn.append_overflow; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = buf->cmn.num_slots; + info.active_slot = buf->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_image_info, info); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + info.slot.state = img->slot.state; + info.slot.res_id = img->slot.id; + info.slot.uninit_count = img->slot.uninit_count; + info.upd_frame_index = img->cmn.upd_frame_index; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = img->cmn.num_slots; + info.active_slot = img->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_sampler_info, info); + const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + info.slot.state = smp->slot.state; + info.slot.res_id = smp->slot.id; + info.slot.uninit_count = smp->slot.uninit_count; + } + return info; +} + +SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_shader_info, info); + const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + info.slot.state = shd->slot.state; + info.slot.res_id = shd->slot.id; + info.slot.uninit_count = shd->slot.uninit_count; + } + return info; +} + +SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_pipeline_info, info); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + if (pip) { + info.slot.state = pip->slot.state; + info.slot.res_id = pip->slot.id; + info.slot.uninit_count = pip->slot.uninit_count; + } + return info; +} + +SOKOL_API_IMPL sg_view_info sg_query_view_info(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_view_info, info); + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + info.slot.state = view->slot.state; + info.slot.res_id = view->slot.id; + info.slot.uninit_count = view->slot.uninit_count; + } + return info; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_buffer_desc, desc); + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + desc.size = (size_t)buf->cmn.size; + desc.usage = buf->cmn.usage; + } + return desc; +} + +SOKOL_API_IMPL size_t sg_query_buffer_size(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + return (size_t)buf->cmn.size; + } + return 0; +} + +SOKOL_API_IMPL sg_buffer_usage sg_query_buffer_usage(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_buffer_usage, usg); + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + usg = buf->cmn.usage; + } + return usg; +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_image_desc, desc); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + desc.type = img->cmn.type; + 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; + } + return desc; +} + +SOKOL_API_IMPL sg_image_type sg_query_image_type(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.type; + } + return _SG_IMAGETYPE_DEFAULT; +} + +SOKOL_API_IMPL int sg_query_image_width(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.width; + } + return 0; +} + +SOKOL_API_IMPL int sg_query_image_height(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.height; + } + return 0; +} + +SOKOL_API_IMPL int sg_query_image_num_slices(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.num_slices; + } + return 0; +} + +SOKOL_API_IMPL int sg_query_image_num_mipmaps(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.num_mipmaps; + } + return 0; +} + +SOKOL_API_IMPL sg_pixel_format sg_query_image_pixelformat(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.pixel_format; + } + return _SG_PIXELFORMAT_DEFAULT; +} + +SOKOL_API_IMPL sg_image_usage sg_query_image_usage(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_image_usage, usg); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + usg = img->cmn.usage; + } + return usg; +} + +SOKOL_API_IMPL int sg_query_image_sample_count(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + return img->cmn.sample_count; + } + return 0; +} + +SOKOL_API_IMPL sg_view_type sg_query_view_type(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + return view->cmn.type; + } else { + return SG_VIEWTYPE_INVALID; + } +} + +// NOTE: may return SG_INVALID_ID if view invalid or view not an image view +SOKOL_API_IMPL sg_image sg_query_view_image(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_image, img); + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + img.id = view->cmn.img.ref.sref.id; + } + return img; +} + +// NOTE: may return SG_INVALID_ID if view invalid or view not a buffer view +SOKOL_API_IMPL sg_buffer sg_query_view_buffer(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_buffer, buf); + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + buf.id = view->cmn.buf.ref.sref.id; + } + return buf; +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_sampler_desc, desc); + const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + desc.min_filter = smp->cmn.min_filter; + desc.mag_filter = smp->cmn.mag_filter; + desc.mipmap_filter = smp->cmn.mipmap_filter; + desc.wrap_u = smp->cmn.wrap_u; + desc.wrap_v = smp->cmn.wrap_v; + desc.wrap_w = smp->cmn.wrap_w; + desc.min_lod = smp->cmn.min_lod; + desc.max_lod = smp->cmn.max_lod; + desc.border_color = smp->cmn.border_color; + desc.compare = smp->cmn.compare; + desc.max_anisotropy = smp->cmn.max_anisotropy; + } + return desc; +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_shader_desc, desc); + const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + for (size_t ub_idx = 0; ub_idx < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_idx++) { + sg_shader_uniform_block* ub_desc = &desc.uniform_blocks[ub_idx]; + const _sg_shader_uniform_block_t* ub = &shd->cmn.uniform_blocks[ub_idx]; + ub_desc->stage = ub->stage; + ub_desc->size = ub->size; + } + for (size_t view_idx = 0; view_idx < SG_MAX_VIEW_BINDSLOTS; view_idx++) { + const _sg_shader_view_t* view = &shd->cmn.views[view_idx]; + if (view->view_type == SG_VIEWTYPE_TEXTURE) { + sg_shader_texture_view* tex_desc = &desc.views[view_idx].texture; + tex_desc->stage = view->stage; + tex_desc->image_type = view->image_type; + tex_desc->sample_type = view->sample_type; + tex_desc->multisampled = view->multisampled; + } else if (shd->cmn.views[view_idx].view_type == SG_VIEWTYPE_STORAGEBUFFER) { + sg_shader_storage_buffer_view* sbuf_desc = &desc.views[view_idx].storage_buffer; + sbuf_desc->stage = view->stage; + sbuf_desc->readonly = view->sbuf_readonly; + } else if (shd->cmn.views[view_idx].view_type == SG_VIEWTYPE_STORAGEIMAGE) { + sg_shader_storage_image_view* simg_desc = &desc.views[view_idx].storage_image; + simg_desc->stage = view->stage; + simg_desc->access_format = view->access_format; + simg_desc->image_type = view->image_type; + simg_desc->writeonly = view->simg_writeonly; + } + } + for (size_t smp_idx = 0; smp_idx < SG_MAX_SAMPLER_BINDSLOTS; smp_idx++) { + sg_shader_sampler* smp_desc = &desc.samplers[smp_idx]; + const _sg_shader_sampler_t* smp = &shd->cmn.samplers[smp_idx]; + smp_desc->stage = smp->stage; + smp_desc->sampler_type = smp->sampler_type; + } + for (size_t tex_smp_idx = 0; tex_smp_idx < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_idx++) { + sg_shader_texture_sampler_pair* tex_smp_desc = &desc.texture_sampler_pairs[tex_smp_idx]; + const _sg_shader_texture_sampler_t* tex_smp = &shd->cmn.texture_samplers[tex_smp_idx]; + tex_smp_desc->stage = tex_smp->stage; + tex_smp_desc->view_slot = tex_smp->view_slot; + tex_smp_desc->sampler_slot = tex_smp->sampler_slot; + } + } + return desc; +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_pipeline_desc, desc); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + if (pip) { + desc.compute = pip->cmn.is_compute; + desc.shader.id = pip->cmn.shader.sref.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_view_desc sg_query_view_desc(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_view_desc, desc); + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + switch (view->cmn.type) { + case SG_VIEWTYPE_STORAGEBUFFER: + desc.storage_buffer.buffer.id = view->cmn.buf.ref.sref.id; + desc.storage_buffer.offset = view->cmn.buf.offset; + break; + case SG_VIEWTYPE_STORAGEIMAGE: + desc.storage_image.image.id = view->cmn.img.ref.sref.id; + desc.storage_image.mip_level = view->cmn.img.mip_level; + desc.storage_image.slice = view->cmn.img.slice; + break; + case SG_VIEWTYPE_TEXTURE: + desc.texture.image.id = view->cmn.img.ref.sref.id; + desc.texture.mip_levels.base = view->cmn.img.mip_level; + desc.texture.mip_levels.count = view->cmn.img.mip_level_count; + desc.texture.slices.base = view->cmn.img.slice; + desc.texture.slices.count = view->cmn.img.slice_count; + break; + case SG_VIEWTYPE_COLORATTACHMENT: + desc.color_attachment.image.id = view->cmn.img.ref.sref.id; + desc.color_attachment.mip_level = view->cmn.img.mip_level; + desc.color_attachment.slice = view->cmn.img.slice; + break; + case SG_VIEWTYPE_RESOLVEATTACHMENT: + desc.resolve_attachment.image.id = view->cmn.img.ref.sref.id; + desc.resolve_attachment.mip_level = view->cmn.img.mip_level; + desc.resolve_attachment.slice = view->cmn.img.slice; + break; + case SG_VIEWTYPE_DEPTHSTENCILATTACHMENT: + desc.depth_stencil_attachment.image.id = view->cmn.img.ref.sref.id; + desc.depth_stencil_attachment.mip_level = view->cmn.img.mip_level; + desc.depth_stencil_attachment.slice = view->cmn.img.slice; + break; + default: + SOKOL_UNREACHABLE; + } + } + 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); +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_image_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_sampler_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_shader_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pipeline_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_view_desc sg_query_view_defaults(const sg_view_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_view_desc_defaults(desc); +} + +SOKOL_API_IMPL const void* sg_d3d11_device(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_d3d11_device_context(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.ctx; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_d3d11_buffer_info, res); + #if defined(SOKOL_D3D11) + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + res.buf = (const void*) buf->d3d11.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_d3d11_image_info, res); + #if defined(SOKOL_D3D11) + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + res.tex2d = (const void*) img->d3d11.tex2d; + res.tex3d = (const void*) img->d3d11.tex3d; + res.res = (const void*) img->d3d11.res; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_d3d11_sampler_info, res); + #if defined(SOKOL_D3D11) + const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + res.smp = (const void*) smp->d3d11.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_d3d11_shader_info, res); + #if defined(SOKOL_D3D11) + const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + res.cbufs[i] = (const void*) shd->d3d11.all_cbufs[i]; + } + res.vs = (const void*) shd->d3d11.vs; + res.fs = (const void*) shd->d3d11.fs; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_d3d11_pipeline_info, res); + #if defined(SOKOL_D3D11) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + if (pip) { + res.il = (const void*) pip->d3d11.il; + res.rs = (const void*) pip->d3d11.rs; + res.dss = (const void*) pip->d3d11.dss; + res.bs = (const void*) pip->d3d11.bs; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_view_info sg_d3d11_query_view_info(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_d3d11_view_info, res); + #if defined(SOKOL_D3D11) + const _sg_view_t* view = _sg_lookup_view(view_id.id); + res.srv = (const void*) view->d3d11.srv; + res.uav = (const void*) view->d3d11.uav; + res.rtv = (const void*) view->d3d11.rtv; + res.dsv = (const void*) view->d3d11.dsv; + #else + _SOKOL_UNUSED(view_id); + #endif + return res; +} + +SOKOL_API_IMPL const void* sg_mtl_device(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.render_cmd_encoder) { + return (__bridge const void*) _sg.mtl.render_cmd_encoder; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_mtl_compute_command_encoder(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.compute_cmd_encoder) { + return (__bridge const void*) _sg.mtl.compute_cmd_encoder; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_mtl_command_queue(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.cmd_queue) { + return (__bridge const void*) _sg.mtl.cmd_queue; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_mtl_buffer_info, res); + #if defined(SOKOL_METAL) + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (buf->mtl.buf[i] != 0) { + res.buf[i] = (__bridge void*) _sg_mtl_id(buf->mtl.buf[i]); + } + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_image_info sg_mtl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_mtl_image_info, res); + #if defined(SOKOL_METAL) + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (img->mtl.tex[i] != 0) { + res.tex[i] = (__bridge void*) _sg_mtl_id(img->mtl.tex[i]); + } + } + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_mtl_sampler_info, res); + #if defined(SOKOL_METAL) + const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + if (smp->mtl.sampler_state != 0) { + res.smp = (__bridge void*) _sg_mtl_id(smp->mtl.sampler_state); + } + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_mtl_shader_info, res); + #if defined(SOKOL_METAL) + const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + const int vertex_lib = shd->mtl.vertex_func.mtl_lib; + const int vertex_func = shd->mtl.vertex_func.mtl_func; + const int fragment_lib = shd->mtl.fragment_func.mtl_lib; + const int fragment_func = shd->mtl.fragment_func.mtl_func; + if (vertex_lib != 0) { + res.vertex_lib = (__bridge void*) _sg_mtl_id(vertex_lib); + } + if (fragment_lib != 0) { + res.fragment_lib = (__bridge void*) _sg_mtl_id(fragment_lib); + } + if (vertex_func != 0) { + res.vertex_func = (__bridge void*) _sg_mtl_id(vertex_func); + } + if (fragment_func != 0) { + res.fragment_func = (__bridge void*) _sg_mtl_id(fragment_func); + } + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_mtl_pipeline_info, res); + #if defined(SOKOL_METAL) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + if (pip) { + if (pip->mtl.rps != 0) { + res.rps = (__bridge void*) _sg_mtl_id(pip->mtl.rps); + } + if (pip->mtl.dss != 0) { + res.dss = (__bridge void*) _sg_mtl_id(pip->mtl.dss); + } + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL const void* sg_wgpu_device(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_queue(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.queue; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_command_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.cmd_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_render_pass_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.rpass_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_compute_pass_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.cpass_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_wgpu_buffer_info, res); + #if defined(SOKOL_WGPU) + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + res.buf = (const void*) buf->wgpu.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_wgpu_image_info, res); + #if defined(SOKOL_WGPU) + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + res.tex = (const void*) img->wgpu.tex; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_wgpu_sampler_info, res); + #if defined(SOKOL_WGPU) + const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + res.smp = (const void*) smp->wgpu.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_wgpu_shader_info, res); + #if defined(SOKOL_WGPU) + const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + res.vs_mod = (const void*) shd->wgpu.vertex_func.module; + res.fs_mod = (const void*) shd->wgpu.fragment_func.module; + res.bgl = (const void*) shd->wgpu.bgl_view_smp; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_wgpu_pipeline_info, res); + #if defined(SOKOL_WGPU) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id); + if (pip) { + res.render_pipeline = (const void*) pip->wgpu.rpip; + res.compute_pipeline = (const void*) pip->wgpu.cpip; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_view_info sg_wgpu_query_view_info(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_wgpu_view_info, res); + #if defined(SOKOL_WGPU) + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + res.view = (const void*) view->wgpu.view; + } + #else + _SOKOL_UNUSED(view_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_gl_buffer_info, res); + #if defined(_SOKOL_ANY_GL) + const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.buf[i] = buf->gl.buf[i]; + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_image_info sg_gl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_gl_image_info, res); + #if defined(_SOKOL_ANY_GL) + const _sg_image_t* img = _sg_lookup_image(img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.tex[i] = img->gl.tex[i]; + } + res.tex_target = img->gl.target; + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_gl_sampler_info, res); + #if defined(_SOKOL_ANY_GL) + const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id); + if (smp) { + res.smp = smp->gl.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_gl_shader_info, res); + #if defined(_SOKOL_ANY_GL) + const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id); + if (shd) { + res.prog = shd->gl.prog; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_view_info sg_gl_query_view_info(sg_view view_id) { + SOKOL_ASSERT(_sg.valid); + _SG_STRUCT(sg_gl_view_info, res); + #if defined(_SOKOL_ANY_GL) + const _sg_view_t* view = _sg_lookup_view(view_id.id); + if (view) { + for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.tex_view[i] = view->gl.tex_view[i]; + } + res.msaa_render_buffer = view->gl.msaa_render_buffer; + res.msaa_resolve_frame_buffer = view->gl.msaa_resolve_frame_buffer; + } + #else + _SOKOL_UNUSED(view_id); + #endif + return res; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // SOKOL_GFX_IMPL diff --git a/thirdparty/sokol/sokol_glue.h b/thirdparty/sokol/sokol_glue.h new file mode 100644 index 0000000..d9a2ad4 --- /dev/null +++ b/thirdparty/sokol/sokol_glue.h @@ -0,0 +1,207 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) +#define SOKOL_GLUE_IMPL +#endif +#ifndef SOKOL_GLUE_INCLUDED +/* + sokol_glue.h -- glue helper functions for sokol headers + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GLUE_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_glue.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + OVERVIEW + ======== + sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, + so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be + used with different window system glue libraries. + + PROVIDED FUNCTIONS + ================== + + sg_environment sglue_environment(void) + + Returns an sg_environment struct initialized by calling sokol_app.h + functions. Use this in the sg_setup() call like this: + + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + ... + }); + + sg_swapchain sglue_swapchain(void) + + Returns an sg_swapchain struct initialized by calling sokol_app.h + functions. Use this in sg_begin_pass() for a 'swapchain pass' like + this: + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 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_GLUE_INCLUDED + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) +#define SOKOL_GLUE_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GLUE_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) +#define SOKOL_GLUE_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GLUE_API_DECL __declspec(dllimport) +#else +#define SOKOL_GLUE_API_DECL extern +#endif +#endif + +#ifndef SOKOL_GFX_INCLUDED +#error "Please include sokol_gfx.h before sokol_glue.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +SOKOL_GLUE_API_DECL sg_environment sglue_environment(void); +SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* SOKOL_GLUE_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_GLUE_IMPL +#define SOKOL_GLUE_IMPL_INCLUDED (1) +#include /* memset */ + +#ifndef SOKOL_APP_INCLUDED +#error "Please include sokol_app.h before the sokol_glue.h implementation" +#endif + +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif + +_SOKOL_PRIVATE sg_pixel_format _sglue_to_sgpixelformat(sapp_pixel_format fmt) { + switch (fmt) { + case SAPP_PIXELFORMAT_NONE: return SG_PIXELFORMAT_NONE; + case SAPP_PIXELFORMAT_RGBA8: return SG_PIXELFORMAT_RGBA8; + case SAPP_PIXELFORMAT_SRGB8A8: return SG_PIXELFORMAT_SRGB8A8; + case SAPP_PIXELFORMAT_BGRA8: return SG_PIXELFORMAT_BGRA8; + case SAPP_PIXELFORMAT_DEPTH_STENCIL: return SG_PIXELFORMAT_DEPTH_STENCIL; + case SAPP_PIXELFORMAT_DEPTH: return SG_PIXELFORMAT_DEPTH; + case SAPP_PIXELFORMAT_SBGRA8: // FIXME! + default: + SOKOL_UNREACHABLE; + return SG_PIXELFORMAT_NONE; + } +} + +SOKOL_API_IMPL sg_environment sglue_environment(void) { + sg_environment res; + memset(&res, 0, sizeof(res)); + const sapp_environment env = sapp_get_environment(); + res.defaults.color_format = _sglue_to_sgpixelformat(env.defaults.color_format); + res.defaults.depth_format = _sglue_to_sgpixelformat(env.defaults.depth_format); + res.defaults.sample_count = env.defaults.sample_count; + res.metal.device = env.metal.device; + res.d3d11.device = env.d3d11.device; + res.d3d11.device_context = env.d3d11.device_context; + res.wgpu.device = env.wgpu.device; + res.vulkan.instance = env.vulkan.instance; + res.vulkan.physical_device = env.vulkan.physical_device; + res.vulkan.device = env.vulkan.device; + res.vulkan.queue = env.vulkan.queue; + res.vulkan.queue_family_index = env.vulkan.queue_family_index; + return res; +} + +SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { + sg_swapchain res; + memset(&res, 0, sizeof(res)); + const sapp_swapchain sc = sapp_get_swapchain(); + res.width = sc.width; + res.height = sc.height; + res.sample_count = sc.sample_count; + res.color_format = _sglue_to_sgpixelformat(sc.color_format); + res.depth_format = _sglue_to_sgpixelformat(sc.depth_format); + res.metal.current_drawable = sc.metal.current_drawable; + res.metal.depth_stencil_texture = sc.metal.depth_stencil_texture; + res.metal.msaa_color_texture = sc.metal.msaa_color_texture; + res.d3d11.render_view = sc.d3d11.render_view; + res.d3d11.resolve_view = sc.d3d11.resolve_view; + res.d3d11.depth_stencil_view = sc.d3d11.depth_stencil_view; + res.wgpu.render_view = sc.wgpu.render_view; + res.wgpu.resolve_view = sc.wgpu.resolve_view; + res.wgpu.depth_stencil_view = sc.wgpu.depth_stencil_view; + res.vulkan.render_image = sc.vulkan.render_image; + res.vulkan.render_view = sc.vulkan.render_view; + res.vulkan.resolve_image = sc.vulkan.resolve_image; + res.vulkan.resolve_view = sc.vulkan.resolve_view; + res.vulkan.depth_stencil_image = sc.vulkan.depth_stencil_image; + res.vulkan.depth_stencil_view = sc.vulkan.depth_stencil_view; + res.vulkan.render_finished_semaphore = sc.vulkan.render_finished_semaphore; + res.vulkan.present_complete_semaphore = sc.vulkan.present_complete_semaphore; + res.gl.framebuffer = sc.gl.framebuffer; + return res; +} + +#endif /* SOKOL_GLUE_IMPL */ diff --git a/thirdparty/sokol/sokol_log.h b/thirdparty/sokol/sokol_log.h new file mode 100644 index 0000000..7a271a6 --- /dev/null +++ b/thirdparty/sokol/sokol_log.h @@ -0,0 +1,334 @@ +#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 NDEBUG is not 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 initializing any of the sokol + headers. For instance for sokol_audio.h it would look 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); + #endif + if (0 == log_level) { + abort(); + } +} +#endif // SOKOL_LOG_IMPL diff --git a/thirdparty/sokol/sokol_time.h b/thirdparty/sokol/sokol_time.h new file mode 100644 index 0000000..fd766d8 --- /dev/null +++ b/thirdparty/sokol/sokol_time.h @@ -0,0 +1,319 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) +#define SOKOL_TIME_IMPL +#endif +#ifndef SOKOL_TIME_INCLUDED +/* + sokol_time.h -- simple cross-platform time measurement + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_TIME_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_TIME_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_time.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + void stm_setup(); + Call once before any other functions to initialize sokol_time + (this calls for instance QueryPerformanceFrequency on Windows) + + uint64_t stm_now(); + Get current point in time in unspecified 'ticks'. The value that + is returned has no relation to the 'wall-clock' time and is + not in a specific time unit, it is only useful to compute + time differences. + + uint64_t stm_diff(uint64_t new, uint64_t old); + Computes the time difference between new and old. This will always + return a positive, non-zero value. + + uint64_t stm_since(uint64_t start); + Takes the current time, and returns the elapsed time since start + (this is a shortcut for "stm_diff(stm_now(), start)") + + uint64_t stm_laptime(uint64_t* last_time); + This is useful for measuring frame time and other recurring + events. It takes the current time, returns the time difference + to the value in last_time, and stores the current time in + last_time for the next call. If the value in last_time is 0, + the return value will be zero (this usually happens on the + very first call). + + uint64_t stm_round_to_common_refresh_rate(uint64_t duration) + This oddly named function takes a measured frame time and + returns the closest "nearby" common display refresh rate frame duration + in ticks. If the input duration isn't close to any common display + refresh rate, the input duration will be returned unchanged as a fallback. + 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: + + double stm_sec(uint64_t ticks); + double stm_ms(uint64_t ticks); + double stm_us(uint64_t ticks); + double stm_ns(uint64_t ticks); + Converts a tick value into seconds, milliseconds, microseconds + or nanoseconds. Note that not all platforms will have nanosecond + or even microsecond precision. + + Uses the following time measurement functions under the hood: + + Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() + MacOS/iOS: mach_absolute_time() + emscripten: emscripten_get_now() + Linux+others: clock_gettime(CLOCK_MONOTONIC) + + zlib/libpng license + + Copyright (c) 2018 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_TIME_INCLUDED (1) +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) +#define SOKOL_TIME_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_TIME_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) +#define SOKOL_TIME_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_TIME_API_DECL __declspec(dllimport) +#else +#define SOKOL_TIME_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +SOKOL_TIME_API_DECL void stm_setup(void); +SOKOL_TIME_API_DECL uint64_t stm_now(void); +SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); +SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); +SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); +SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); +SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); +SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); +SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); +SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif // SOKOL_TIME_INCLUDED + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_TIME_IMPL +#define SOKOL_TIME_IMPL_INCLUDED (1) +#include /* memset */ + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#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 + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +typedef struct { + uint32_t initialized; + LARGE_INTEGER freq; + LARGE_INTEGER start; +} _stm_state_t; +#elif defined(__APPLE__) && defined(__MACH__) +#include +typedef struct { + uint32_t initialized; + mach_timebase_info_data_t timebase; + uint64_t start; +} _stm_state_t; +#elif defined(__EMSCRIPTEN__) +#include +typedef struct { + uint32_t initialized; + double start; +} _stm_state_t; +#else /* anything else, this will need more care for non-Linux platforms */ +#ifdef ESP8266 +// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined +#define CLOCK_MONOTONIC 0 +#endif +#include +typedef struct { + uint32_t initialized; + uint64_t start; +} _stm_state_t; +#endif +static _stm_state_t _stm; + +/* prevent 64-bit overflow when computing relative timestamp + see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 +*/ +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) +_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 + +SOKOL_API_IMPL void stm_setup(void) { + memset(&_stm, 0, sizeof(_stm)); + _stm.initialized = 0xABCDABCD; + #if defined(_WIN32) + QueryPerformanceFrequency(&_stm.freq); + QueryPerformanceCounter(&_stm.start); + #elif defined(__APPLE__) && defined(__MACH__) + mach_timebase_info(&_stm.timebase); + _stm.start = mach_absolute_time(); + #elif defined(__EMSCRIPTEN__) + _stm.start = emscripten_get_now(); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; + #endif +} + +SOKOL_API_IMPL uint64_t stm_now(void) { + SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); + uint64_t now; + #if defined(_WIN32) + LARGE_INTEGER qpc_t; + QueryPerformanceCounter(&qpc_t); + 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) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); + #elif defined(__EMSCRIPTEN__) + double js_now = emscripten_get_now() - _stm.start; + now = (uint64_t) (js_now * 1000000.0); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; + #endif + return now; +} + +SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { + if (new_ticks > old_ticks) { + return new_ticks - old_ticks; + } + else { + return 1; + } +} + +SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { + return stm_diff(stm_now(), start_ticks); +} + +SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { + SOKOL_ASSERT(last_time); + uint64_t dt = 0; + uint64_t now = stm_now(); + if (0 != *last_time) { + dt = stm_diff(now, *last_time); + } + *last_time = now; + return dt; +} + +// first number is frame duration in ns, second number is tolerance in ns, +// the resulting min/max values must not overlap! +static const uint64_t _stm_refresh_rates[][2] = { + { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms + { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms + { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms + { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 + { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms + { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms + { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms + { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms + { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms + { 0, 0 }, // keep the last element always at zero +}; + +SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { + uint64_t ns; + int i = 0; + while (0 != (ns = _stm_refresh_rates[i][0])) { + uint64_t tol = _stm_refresh_rates[i][1]; + if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { + return ns; + } + i++; + } + // fallthrough: didn't fit into any buckets + return ticks; +} + +SOKOL_API_IMPL double stm_sec(uint64_t ticks) { + return (double)ticks / 1000000000.0; +} + +SOKOL_API_IMPL double stm_ms(uint64_t ticks) { + return (double)ticks / 1000000.0; +} + +SOKOL_API_IMPL double stm_us(uint64_t ticks) { + return (double)ticks / 1000.0; +} + +SOKOL_API_IMPL double stm_ns(uint64_t ticks) { + return (double)ticks; +} +#endif /* SOKOL_TIME_IMPL */ + diff --git a/thirdparty/sokol/util/gen_sokol_color.py b/thirdparty/sokol/util/gen_sokol_color.py new file mode 100644 index 0000000..f19a2ba --- /dev/null +++ b/thirdparty/sokol/util/gen_sokol_color.py @@ -0,0 +1,500 @@ +#------------------------------------------------------------------------------- +# Generate the sokol_color.h header from a predefined palette +#------------------------------------------------------------------------------- +# LICENSE +# ======= +# +# zlib/libpng license +# +# Copyright (c) 2020 Stuart Adams +# +# 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. + +colors = [ +("Alice Blue", 0xF0F8FFFF), +("Antique White", 0xFAEBD7FF), +("Aqua", 0x00FFFFFF), +("Aquamarine", 0x7FFFD4FF), +("Azure", 0xF0FFFFFF), +("Beige", 0xF5F5DCFF), +("Bisque", 0xFFE4C4FF), +("Black", 0x000000FF), +("Blanched Almond", 0xFFEBCDFF), +("Blue", 0x0000FFFF), +("Blue Violet", 0x8A2BE2FF), +("Brown", 0xA52A2AFF), +("Burlywood", 0xDEB887FF), +("Cadet Blue", 0x5F9EA0FF), +("Chartreuse", 0x7FFF00FF), +("Chocolate", 0xD2691EFF), +("Coral", 0xFF7F50FF), +("Cornflower Blue", 0x6495EDFF), +("Cornsilk", 0xFFF8DCFF), +("Crimson", 0xDC143CFF), +("Cyan", 0x00FFFFFF), +("Dark Blue", 0x00008BFF), +("Dark Cyan", 0x008B8BFF), +("Dark Goldenrod", 0xB8860BFF), +("Dark Gray", 0xA9A9A9FF), +("Dark Green", 0x006400FF), +("Dark Khaki", 0xBDB76BFF), +("Dark Magenta", 0x8B008BFF), +("Dark Olive Green", 0x556B2FFF), +("Dark Orange", 0xFF8C00FF), +("Dark Orchid", 0x9932CCFF), +("Dark Red", 0x8B0000FF), +("Dark Salmon", 0xE9967AFF), +("Dark Sea Green", 0x8FBC8FFF), +("Dark Slate Blue", 0x483D8BFF), +("Dark Slate Gray", 0x2F4F4FFF), +("Dark Turquoise", 0x00CED1FF), +("Dark Violet", 0x9400D3FF), +("Deep Pink", 0xFF1493FF), +("Deep Sky Blue", 0x00BFFFFF), +("Dim Gray", 0x696969FF), +("Dodger Blue", 0x1E90FFFF), +("Firebrick", 0xB22222FF), +("Floral White", 0xFFFAF0FF), +("Forest Green", 0x228B22FF), +("Fuchsia", 0xFF00FFFF), +("Gainsboro", 0xDCDCDCFF), +("Ghost White", 0xF8F8FFFF), +("Gold", 0xFFD700FF), +("Goldenrod", 0xDAA520FF), +("Gray", 0xBEBEBEFF), +("Web Gray", 0x808080FF), +("Green", 0x00FF00FF), +("Web Green", 0x008000FF), +("Green Yellow", 0xADFF2FFF), +("Honeydew", 0xF0FFF0FF), +("Hot Pink", 0xFF69B4FF), +("Indian Red", 0xCD5C5CFF), +("Indigo", 0x4B0082FF), +("Ivory", 0xFFFFF0FF), +("Khaki", 0xF0E68CFF), +("Lavender", 0xE6E6FAFF), +("Lavender Blush", 0xFFF0F5FF), +("Lawn Green", 0x7CFC00FF), +("Lemon Chiffon", 0xFFFACDFF), +("Light Blue", 0xADD8E6FF), +("Light Coral", 0xF08080FF), +("Light Cyan", 0xE0FFFFFF), +("Light Goldenrod", 0xFAFAD2FF), +("Light Gray", 0xD3D3D3FF), +("Light Green", 0x90EE90FF), +("Light Pink", 0xFFB6C1FF), +("Light Salmon", 0xFFA07AFF), +("Light Sea Green", 0x20B2AAFF), +("Light Sky Blue", 0x87CEFAFF), +("Light Slate Gray", 0x778899FF), +("Light Steel Blue", 0xB0C4DEFF), +("Light Yellow", 0xFFFFE0FF), +("Lime", 0x00FF00FF), +("Lime Green", 0x32CD32FF), +("Linen", 0xFAF0E6FF), +("Magenta", 0xFF00FFFF), +("Maroon", 0xB03060FF), +("Web Maroon", 0x800000FF), +("Medium Aquamarine", 0x66CDAAFF), +("Medium Blue", 0x0000CDFF), +("Medium Orchid", 0xBA55D3FF), +("Medium Purple", 0x9370DBFF), +("Medium Sea Green", 0x3CB371FF), +("Medium Slate Blue", 0x7B68EEFF), +("Medium Spring Green", 0x00FA9AFF), +("Medium Turquoise", 0x48D1CCFF), +("Medium Violet Red", 0xC71585FF), +("Midnight Blue", 0x191970FF), +("Mint Cream", 0xF5FFFAFF), +("Misty Rose", 0xFFE4E1FF), +("Moccasin", 0xFFE4B5FF), +("Navajo White", 0xFFDEADFF), +("Navy Blue", 0x000080FF), +("Old Lace", 0xFDF5E6FF), +("Olive", 0x808000FF), +("Olive Drab", 0x6B8E23FF), +("Orange", 0xFFA500FF), +("Orange Red", 0xFF4500FF), +("Orchid", 0xDA70D6FF), +("Pale Goldenrod", 0xEEE8AAFF), +("Pale Green", 0x98FB98FF), +("Pale Turquoise", 0xAFEEEEFF), +("Pale Violet Red", 0xDB7093FF), +("Papaya Whip", 0xFFEFD5FF), +("Peach Puff", 0xFFDAB9FF), +("Peru", 0xCD853FFF), +("Pink", 0xFFC0CBFF), +("Plum", 0xDDA0DDFF), +("Powder Blue", 0xB0E0E6FF), +("Purple", 0xA020F0FF), +("Web Purple", 0x800080FF), +("Rebecca Purple", 0x663399FF), +("Red", 0xFF0000FF), +("Rosy Brown", 0xBC8F8FFF), +("Royal Blue", 0x4169E1FF), +("Saddle Brown", 0x8B4513FF), +("Salmon", 0xFA8072FF), +("Sandy Brown", 0xF4A460FF), +("Sea Green", 0x2E8B57FF), +("Seashell", 0xFFF5EEFF), +("Sienna", 0xA0522DFF), +("Silver", 0xC0C0C0FF), +("Sky Blue", 0x87CEEBFF), +("Slate Blue", 0x6A5ACDFF), +("Slate Gray", 0x708090FF), +("Snow", 0xFFFAFAFF), +("Spring Green", 0x00FF7FFF), +("Steel Blue", 0x4682B4FF), +("Tan", 0xD2B48CFF), +("Teal", 0x008080FF), +("Thistle", 0xD8BFD8FF), +("Tomato", 0xFF6347FF), +("Transparent", 0x00000000), +("Turquoise", 0x40E0D0FF), +("Violet", 0xEE82EEFF), +("Wheat", 0xF5DEB3FF), +("White", 0xFFFFFFFF), +("White Smoke", 0xF5F5F5FF), +("Yellow", 0xFFFF00FF), +("Yellow Green", 0x9ACD32FF) +] + +header = open("sokol_color.h", "w") + +header.write("""#if defined(SOKOL_IMPL) && !defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_IMPL +#endif +#ifndef SOKOL_COLOR_INCLUDED +/* + sokol_color.h -- sg_color utilities + + This header was generated by gen_sokol_color.py. Do not modify it. + + Project URL: https://github.com/floooh/sokol + + Include the following headers before including sokol_color.h: + + sokol_gfx.h + + FEATURE OVERVIEW + ================ + sokol_color.h defines preset colors based on the X11 color names, + alongside utility functions to create and modify sg_color objects. + + The predefined colors are based on the X11 color names: + + https://en.wikipedia.org/wiki/X11_color_names + + This palette is useful for prototyping - lots of programmers are familiar with + these colours due to their use in X11, web development and XNA / MonoGame. They + are also handy when you want to reference a familiar color, but don't want to + write it out by hand. + + COLORS + ====== + The palette is defined using static const (or constexpr if you are using a + C++ compiler) objects. These objects use lowercase names: + + static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED; + static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN; + static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE; + + An sg_color preset object like sg_red can be used to initialize + an sg_pass_action: + + sg_pass_action pass_action = { + .colors[0] = { .action=SG_ACTION_CLEAR, .value = sg_red } + }; + + Initializing an object with static storage duration is more complicated + because of C language rules. Technically, a static const is not a + compile-time constant in C. To work around this, the palette is also + defined as a series of brace-enclosed list macro definitions. These + definitions use uppercase names: + + #define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f } + #define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f } + #define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f } + + A preset macro like SG_RED can be used to initialize objects with static + storage duration: + + static struct { + sg_pass_action pass_action; + } state = { + .pass_action = { + .colors[0] = { .action = SG_ACTION_CLEAR, .value = SG_RED } + } + }; + + A second set of macro definitions exists for colors packed as 32 bit integer + values. These definitions are also uppercase, but use the _RGBA32 suffix: + + #define SG_RED_RGBA32 0xFF0000FF + #define SG_GREEN_RGBA32 0x00FF00FF + #define SG_BLUE_RGBA32 0x0000FFFF + + This is useful if your code makes use of packed colors, as sokol_gl.h does for its + internal vertex format: + + sgl_begin_triangles(); + sgl_v2f_c1i( 0.0f, 0.5f, SG_RED_RGBA32); + sgl_v2f_c1i( 0.5f, -0.5f, SG_GREEN_RGBA32); + sgl_v2f_c1i(-0.5f, -0.5f, SG_BLUE_RGBA32); + sgl_end(); + + UTILITY FUNCTIONS + ================= + + Utility functions for creating colours are provided: + + - sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + Create a sg_color object from separate R, G, B, A bytes. + + - sg_make_color_1i(uint32_t rgba) + Create a sg_color object from RGBA bytes packed into a 32-bit unsigned integer. + + - sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. + + - sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. Less efficient but more precise than sg_color_lerp. + + - sg_color_multiply(const sg_color* color, float scale) + Multiply each color component by the scale factor. + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2020 Stuart Adams + + 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_COLOR_INCLUDED (1) + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_color.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_COLOR_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_COLOR_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_COLOR_API_DECL __declspec(dllimport) +#else +#define SOKOL_COLOR_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +#define SOKOL_COLOR_CONSTEXPR constexpr +extern "C" { +#else +#define SOKOL_COLOR_CONSTEXPR const +#endif + +SOKOL_COLOR_API_DECL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_COLOR_API_DECL sg_color sg_make_color_1i(uint32_t rgba); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_multiply(const sg_color* color, float scale); + +""") + +def unpack_rgba(color): + red = (color & 0xFF000000) >> 24 + green = (color & 0xFF0000) >> 16 + blue = (color & 0xFF00) >> 8 + alpha = (color & 0xFF) + return (red, green, blue, alpha) + +def add_documentation(color): + documentation = "/* {name} color {{ R:{r}, G:{g}, B:{b}, A:{a} }} */\n" + rgba = unpack_rgba(color[1]) + header.write(documentation.format( + name = color[0], r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3])) + +for color in colors: + add_documentation(color) + init_color = "SG_" + color[0].upper().replace(" ", "_") + init_color_definition = "#define {name} {{ {r}f, {g}f, {b}f, {a}f }}\n" + rgba = unpack_rgba(color[1]) + r = rgba[0] / 255 + g = rgba[1] / 255 + b = rgba[2] / 255 + a = rgba[3] / 255 + r_text = "{:.1f}".format(r) if r.is_integer() else "{:.9g}".format(r) + g_text = "{:.1f}".format(g) if g.is_integer() else "{:.9g}".format(g) + b_text = "{:.1f}".format(b) if b.is_integer() else "{:.9g}".format(b) + a_text = "{:.1f}".format(a) if a.is_integer() else "{:.9g}".format(a) + header.write(init_color_definition.format( + name = init_color, r = r_text, g = g_text, b = b_text, a = a_text)) + +header.write("\n") + +for color in colors: + add_documentation(color) + init_color = "sg_" + color[0].lower().replace(" ", "_") + init_color_definition = "static SOKOL_COLOR_CONSTEXPR sg_color {name} = {init};\n" + init_color_name = "SG_" + color[0].upper().replace(" ", "_") + header.write(init_color_definition.format(name = init_color, init = init_color_name)) + +header.write("\n") + +for color in colors: + add_documentation(color) + hex_color = "0x{0:08X}".format(color[1]) + packed_color = "SG_" + color[0].upper().replace(" ", "_") + "_RGBA32" + packed_color_definition = "#define {name} {rgba}\n" + header.write(packed_color_definition.format(name = packed_color, rgba = hex_color)) + +header.write(""" +#ifdef __cplusplus +} /* extern "C" */ + +inline sg_color sg_make_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return sg_make_color_4b(r, g, b, a); +} + +inline sg_color sg_make_color(uint32_t rgba) { + return sg_make_color_1i(rgba); +} + +inline sg_color sg_color_lerp(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp(&color_a, &color_b, amount); +} + +inline sg_color sg_color_lerp_precise(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp_precise(&color_a, &color_b, amount); +} + +inline sg_color sg_color_multiply(const sg_color& color, float scale) { + return sg_color_multiply(&color, scale); +} + +#endif /* __cplusplus */ + +#endif /* SOKOL_COLOR_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_COLOR_IMPL +#define SOKOL_COLOR_IMPL_INCLUDED (1) + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +static inline float _sg_color_clamp(float v, float low, float high) { + if (v < low) { + return low; + } else if (v > high) { + return high; + } + return v; +} + +static inline float _sg_color_lerp(float a, float b, float amount) { + return a + (b - a) * amount; +} + +static inline float _sg_color_lerp_precise(float a, float b, float amount) { + return ((1.0f - amount) * a) + (b * amount); +} + +SOKOL_API_IMPL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + sg_color result; + result.r = r / 255.0f; + result.g = g / 255.0f; + result.b = b / 255.0f; + result.a = a / 255.0f; + return result; +} + +SOKOL_API_IMPL sg_color sg_make_color_1i(uint32_t rgba) { + return sg_make_color_4b( + (uint8_t)(rgba >> 24), + (uint8_t)(rgba >> 16), + (uint8_t)(rgba >> 8), + (uint8_t)(rgba >> 0) + ); +} + +SOKOL_API_IMPL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp_precise(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp_precise(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp_precise(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp_precise(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_multiply(const sg_color* color, float scale) { + SOKOL_ASSERT(color); + sg_color result; + result.r = color->r * scale; + result.g = color->g * scale; + result.b = color->b * scale; + result.a = color->a * scale; + return result; +} + +#endif /* SOKOL_COLOR_IMPL */ +""") diff --git a/thirdparty/sokol/util/sokol_color.h b/thirdparty/sokol/util/sokol_color.h new file mode 100644 index 0000000..cc31b44 --- /dev/null +++ b/thirdparty/sokol/util/sokol_color.h @@ -0,0 +1,1148 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_IMPL +#endif +#ifndef SOKOL_COLOR_INCLUDED +/* + sokol_color.h -- sg_color utilities + + This header was generated by gen_sokol_color.py. Do not modify it. + + Project URL: https://github.com/floooh/sokol + + Include the following headers before including sokol_color.h: + + sokol_gfx.h + + FEATURE OVERVIEW + ================ + sokol_color.h defines preset colors based on the X11 color names, + alongside utility functions to create and modify sg_color objects. + + The predefined colors are based on the X11 color names: + + https://en.wikipedia.org/wiki/X11_color_names + + This palette is useful for prototyping - lots of programmers are familiar with + these colours due to their use in X11, web development and XNA / MonoGame. They + are also handy when you want to reference a familiar color, but don't want to + write it out by hand. + + COLORS + ====== + The palette is defined using static const (or constexpr if you are using a + C++ compiler) objects. These objects use lowercase names: + + static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED; + static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN; + static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE; + + An sg_color preset object like sg_red can be used to initialize + an sg_pass_action: + + sg_pass_action pass_action = { + .colors[0] = { .action=SG_ACTION_CLEAR, .value = sg_red } + }; + + Initializing an object with static storage duration is more complicated + because of C language rules. Technically, a static const is not a + compile-time constant in C. To work around this, the palette is also + defined as a series of brace-enclosed list macro definitions. These + definitions use uppercase names: + + #define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f } + #define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f } + #define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f } + + A preset macro like SG_RED can be used to initialize objects with static + storage duration: + + static struct { + sg_pass_action pass_action; + } state = { + .pass_action = { + .colors[0] = { .action = SG_ACTION_CLEAR, .value = SG_RED } + } + }; + + A second set of macro definitions exists for colors packed as 32 bit integer + values. These definitions are also uppercase, but use the _RGBA32 suffix: + + #define SG_RED_RGBA32 0xFF0000FF + #define SG_GREEN_RGBA32 0x00FF00FF + #define SG_BLUE_RGBA32 0x0000FFFF + + This is useful if your code makes use of packed colors, as sokol_gl.h does for its + internal vertex format: + + sgl_begin_triangles(); + sgl_v2f_c1i( 0.0f, 0.5f, SG_RED_RGBA32); + sgl_v2f_c1i( 0.5f, -0.5f, SG_GREEN_RGBA32); + sgl_v2f_c1i(-0.5f, -0.5f, SG_BLUE_RGBA32); + sgl_end(); + + UTILITY FUNCTIONS + ================= + + Utility functions for creating colours are provided: + + - sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + Create a sg_color object from separate R, G, B, A bytes. + + - sg_make_color_1i(uint32_t rgba) + Create a sg_color object from RGBA bytes packed into a 32-bit unsigned integer. + + - sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. + + - sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. Less efficient but more precise than sg_color_lerp. + + - sg_color_multiply(const sg_color* color, float scale) + Multiply each color component by the scale factor. + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2020 Stuart Adams + + 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_COLOR_INCLUDED (1) + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_color.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_COLOR_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_COLOR_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_COLOR_API_DECL __declspec(dllimport) +#else +#define SOKOL_COLOR_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +#define SOKOL_COLOR_CONSTEXPR constexpr +extern "C" { +#else +#define SOKOL_COLOR_CONSTEXPR const +#endif + +SOKOL_COLOR_API_DECL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_COLOR_API_DECL sg_color sg_make_color_1i(uint32_t rgba); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_multiply(const sg_color* color, float scale); + +/* Alice Blue color { R:240, G:248, B:255, A:255 } */ +#define SG_ALICE_BLUE { 0.941176471f, 0.97254902f, 1.0f, 1.0f } +/* Antique White color { R:250, G:235, B:215, A:255 } */ +#define SG_ANTIQUE_WHITE { 0.980392157f, 0.921568627f, 0.843137255f, 1.0f } +/* Aqua color { R:0, G:255, B:255, A:255 } */ +#define SG_AQUA { 0.0f, 1.0f, 1.0f, 1.0f } +/* Aquamarine color { R:127, G:255, B:212, A:255 } */ +#define SG_AQUAMARINE { 0.498039216f, 1.0f, 0.831372549f, 1.0f } +/* Azure color { R:240, G:255, B:255, A:255 } */ +#define SG_AZURE { 0.941176471f, 1.0f, 1.0f, 1.0f } +/* Beige color { R:245, G:245, B:220, A:255 } */ +#define SG_BEIGE { 0.960784314f, 0.960784314f, 0.862745098f, 1.0f } +/* Bisque color { R:255, G:228, B:196, A:255 } */ +#define SG_BISQUE { 1.0f, 0.894117647f, 0.768627451f, 1.0f } +/* Black color { R:0, G:0, B:0, A:255 } */ +#define SG_BLACK { 0.0f, 0.0f, 0.0f, 1.0f } +/* Blanched Almond color { R:255, G:235, B:205, A:255 } */ +#define SG_BLANCHED_ALMOND { 1.0f, 0.921568627f, 0.803921569f, 1.0f } +/* Blue color { R:0, G:0, B:255, A:255 } */ +#define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f } +/* Blue Violet color { R:138, G:43, B:226, A:255 } */ +#define SG_BLUE_VIOLET { 0.541176471f, 0.168627451f, 0.88627451f, 1.0f } +/* Brown color { R:165, G:42, B:42, A:255 } */ +#define SG_BROWN { 0.647058824f, 0.164705882f, 0.164705882f, 1.0f } +/* Burlywood color { R:222, G:184, B:135, A:255 } */ +#define SG_BURLYWOOD { 0.870588235f, 0.721568627f, 0.529411765f, 1.0f } +/* Cadet Blue color { R:95, G:158, B:160, A:255 } */ +#define SG_CADET_BLUE { 0.37254902f, 0.619607843f, 0.62745098f, 1.0f } +/* Chartreuse color { R:127, G:255, B:0, A:255 } */ +#define SG_CHARTREUSE { 0.498039216f, 1.0f, 0.0f, 1.0f } +/* Chocolate color { R:210, G:105, B:30, A:255 } */ +#define SG_CHOCOLATE { 0.823529412f, 0.411764706f, 0.117647059f, 1.0f } +/* Coral color { R:255, G:127, B:80, A:255 } */ +#define SG_CORAL { 1.0f, 0.498039216f, 0.31372549f, 1.0f } +/* Cornflower Blue color { R:100, G:149, B:237, A:255 } */ +#define SG_CORNFLOWER_BLUE { 0.392156863f, 0.584313725f, 0.929411765f, 1.0f } +/* Cornsilk color { R:255, G:248, B:220, A:255 } */ +#define SG_CORNSILK { 1.0f, 0.97254902f, 0.862745098f, 1.0f } +/* Crimson color { R:220, G:20, B:60, A:255 } */ +#define SG_CRIMSON { 0.862745098f, 0.0784313725f, 0.235294118f, 1.0f } +/* Cyan color { R:0, G:255, B:255, A:255 } */ +#define SG_CYAN { 0.0f, 1.0f, 1.0f, 1.0f } +/* Dark Blue color { R:0, G:0, B:139, A:255 } */ +#define SG_DARK_BLUE { 0.0f, 0.0f, 0.545098039f, 1.0f } +/* Dark Cyan color { R:0, G:139, B:139, A:255 } */ +#define SG_DARK_CYAN { 0.0f, 0.545098039f, 0.545098039f, 1.0f } +/* Dark Goldenrod color { R:184, G:134, B:11, A:255 } */ +#define SG_DARK_GOLDENROD { 0.721568627f, 0.525490196f, 0.0431372549f, 1.0f } +/* Dark Gray color { R:169, G:169, B:169, A:255 } */ +#define SG_DARK_GRAY { 0.662745098f, 0.662745098f, 0.662745098f, 1.0f } +/* Dark Green color { R:0, G:100, B:0, A:255 } */ +#define SG_DARK_GREEN { 0.0f, 0.392156863f, 0.0f, 1.0f } +/* Dark Khaki color { R:189, G:183, B:107, A:255 } */ +#define SG_DARK_KHAKI { 0.741176471f, 0.717647059f, 0.419607843f, 1.0f } +/* Dark Magenta color { R:139, G:0, B:139, A:255 } */ +#define SG_DARK_MAGENTA { 0.545098039f, 0.0f, 0.545098039f, 1.0f } +/* Dark Olive Green color { R:85, G:107, B:47, A:255 } */ +#define SG_DARK_OLIVE_GREEN { 0.333333333f, 0.419607843f, 0.184313725f, 1.0f } +/* Dark Orange color { R:255, G:140, B:0, A:255 } */ +#define SG_DARK_ORANGE { 1.0f, 0.549019608f, 0.0f, 1.0f } +/* Dark Orchid color { R:153, G:50, B:204, A:255 } */ +#define SG_DARK_ORCHID { 0.6f, 0.196078431f, 0.8f, 1.0f } +/* Dark Red color { R:139, G:0, B:0, A:255 } */ +#define SG_DARK_RED { 0.545098039f, 0.0f, 0.0f, 1.0f } +/* Dark Salmon color { R:233, G:150, B:122, A:255 } */ +#define SG_DARK_SALMON { 0.91372549f, 0.588235294f, 0.478431373f, 1.0f } +/* Dark Sea Green color { R:143, G:188, B:143, A:255 } */ +#define SG_DARK_SEA_GREEN { 0.560784314f, 0.737254902f, 0.560784314f, 1.0f } +/* Dark Slate Blue color { R:72, G:61, B:139, A:255 } */ +#define SG_DARK_SLATE_BLUE { 0.282352941f, 0.239215686f, 0.545098039f, 1.0f } +/* Dark Slate Gray color { R:47, G:79, B:79, A:255 } */ +#define SG_DARK_SLATE_GRAY { 0.184313725f, 0.309803922f, 0.309803922f, 1.0f } +/* Dark Turquoise color { R:0, G:206, B:209, A:255 } */ +#define SG_DARK_TURQUOISE { 0.0f, 0.807843137f, 0.819607843f, 1.0f } +/* Dark Violet color { R:148, G:0, B:211, A:255 } */ +#define SG_DARK_VIOLET { 0.580392157f, 0.0f, 0.82745098f, 1.0f } +/* Deep Pink color { R:255, G:20, B:147, A:255 } */ +#define SG_DEEP_PINK { 1.0f, 0.0784313725f, 0.576470588f, 1.0f } +/* Deep Sky Blue color { R:0, G:191, B:255, A:255 } */ +#define SG_DEEP_SKY_BLUE { 0.0f, 0.749019608f, 1.0f, 1.0f } +/* Dim Gray color { R:105, G:105, B:105, A:255 } */ +#define SG_DIM_GRAY { 0.411764706f, 0.411764706f, 0.411764706f, 1.0f } +/* Dodger Blue color { R:30, G:144, B:255, A:255 } */ +#define SG_DODGER_BLUE { 0.117647059f, 0.564705882f, 1.0f, 1.0f } +/* Firebrick color { R:178, G:34, B:34, A:255 } */ +#define SG_FIREBRICK { 0.698039216f, 0.133333333f, 0.133333333f, 1.0f } +/* Floral White color { R:255, G:250, B:240, A:255 } */ +#define SG_FLORAL_WHITE { 1.0f, 0.980392157f, 0.941176471f, 1.0f } +/* Forest Green color { R:34, G:139, B:34, A:255 } */ +#define SG_FOREST_GREEN { 0.133333333f, 0.545098039f, 0.133333333f, 1.0f } +/* Fuchsia color { R:255, G:0, B:255, A:255 } */ +#define SG_FUCHSIA { 1.0f, 0.0f, 1.0f, 1.0f } +/* Gainsboro color { R:220, G:220, B:220, A:255 } */ +#define SG_GAINSBORO { 0.862745098f, 0.862745098f, 0.862745098f, 1.0f } +/* Ghost White color { R:248, G:248, B:255, A:255 } */ +#define SG_GHOST_WHITE { 0.97254902f, 0.97254902f, 1.0f, 1.0f } +/* Gold color { R:255, G:215, B:0, A:255 } */ +#define SG_GOLD { 1.0f, 0.843137255f, 0.0f, 1.0f } +/* Goldenrod color { R:218, G:165, B:32, A:255 } */ +#define SG_GOLDENROD { 0.854901961f, 0.647058824f, 0.125490196f, 1.0f } +/* Gray color { R:190, G:190, B:190, A:255 } */ +#define SG_GRAY { 0.745098039f, 0.745098039f, 0.745098039f, 1.0f } +/* Web Gray color { R:128, G:128, B:128, A:255 } */ +#define SG_WEB_GRAY { 0.501960784f, 0.501960784f, 0.501960784f, 1.0f } +/* Green color { R:0, G:255, B:0, A:255 } */ +#define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f } +/* Web Green color { R:0, G:128, B:0, A:255 } */ +#define SG_WEB_GREEN { 0.0f, 0.501960784f, 0.0f, 1.0f } +/* Green Yellow color { R:173, G:255, B:47, A:255 } */ +#define SG_GREEN_YELLOW { 0.678431373f, 1.0f, 0.184313725f, 1.0f } +/* Honeydew color { R:240, G:255, B:240, A:255 } */ +#define SG_HONEYDEW { 0.941176471f, 1.0f, 0.941176471f, 1.0f } +/* Hot Pink color { R:255, G:105, B:180, A:255 } */ +#define SG_HOT_PINK { 1.0f, 0.411764706f, 0.705882353f, 1.0f } +/* Indian Red color { R:205, G:92, B:92, A:255 } */ +#define SG_INDIAN_RED { 0.803921569f, 0.360784314f, 0.360784314f, 1.0f } +/* Indigo color { R:75, G:0, B:130, A:255 } */ +#define SG_INDIGO { 0.294117647f, 0.0f, 0.509803922f, 1.0f } +/* Ivory color { R:255, G:255, B:240, A:255 } */ +#define SG_IVORY { 1.0f, 1.0f, 0.941176471f, 1.0f } +/* Khaki color { R:240, G:230, B:140, A:255 } */ +#define SG_KHAKI { 0.941176471f, 0.901960784f, 0.549019608f, 1.0f } +/* Lavender color { R:230, G:230, B:250, A:255 } */ +#define SG_LAVENDER { 0.901960784f, 0.901960784f, 0.980392157f, 1.0f } +/* Lavender Blush color { R:255, G:240, B:245, A:255 } */ +#define SG_LAVENDER_BLUSH { 1.0f, 0.941176471f, 0.960784314f, 1.0f } +/* Lawn Green color { R:124, G:252, B:0, A:255 } */ +#define SG_LAWN_GREEN { 0.48627451f, 0.988235294f, 0.0f, 1.0f } +/* Lemon Chiffon color { R:255, G:250, B:205, A:255 } */ +#define SG_LEMON_CHIFFON { 1.0f, 0.980392157f, 0.803921569f, 1.0f } +/* Light Blue color { R:173, G:216, B:230, A:255 } */ +#define SG_LIGHT_BLUE { 0.678431373f, 0.847058824f, 0.901960784f, 1.0f } +/* Light Coral color { R:240, G:128, B:128, A:255 } */ +#define SG_LIGHT_CORAL { 0.941176471f, 0.501960784f, 0.501960784f, 1.0f } +/* Light Cyan color { R:224, G:255, B:255, A:255 } */ +#define SG_LIGHT_CYAN { 0.878431373f, 1.0f, 1.0f, 1.0f } +/* Light Goldenrod color { R:250, G:250, B:210, A:255 } */ +#define SG_LIGHT_GOLDENROD { 0.980392157f, 0.980392157f, 0.823529412f, 1.0f } +/* Light Gray color { R:211, G:211, B:211, A:255 } */ +#define SG_LIGHT_GRAY { 0.82745098f, 0.82745098f, 0.82745098f, 1.0f } +/* Light Green color { R:144, G:238, B:144, A:255 } */ +#define SG_LIGHT_GREEN { 0.564705882f, 0.933333333f, 0.564705882f, 1.0f } +/* Light Pink color { R:255, G:182, B:193, A:255 } */ +#define SG_LIGHT_PINK { 1.0f, 0.71372549f, 0.756862745f, 1.0f } +/* Light Salmon color { R:255, G:160, B:122, A:255 } */ +#define SG_LIGHT_SALMON { 1.0f, 0.62745098f, 0.478431373f, 1.0f } +/* Light Sea Green color { R:32, G:178, B:170, A:255 } */ +#define SG_LIGHT_SEA_GREEN { 0.125490196f, 0.698039216f, 0.666666667f, 1.0f } +/* Light Sky Blue color { R:135, G:206, B:250, A:255 } */ +#define SG_LIGHT_SKY_BLUE { 0.529411765f, 0.807843137f, 0.980392157f, 1.0f } +/* Light Slate Gray color { R:119, G:136, B:153, A:255 } */ +#define SG_LIGHT_SLATE_GRAY { 0.466666667f, 0.533333333f, 0.6f, 1.0f } +/* Light Steel Blue color { R:176, G:196, B:222, A:255 } */ +#define SG_LIGHT_STEEL_BLUE { 0.690196078f, 0.768627451f, 0.870588235f, 1.0f } +/* Light Yellow color { R:255, G:255, B:224, A:255 } */ +#define SG_LIGHT_YELLOW { 1.0f, 1.0f, 0.878431373f, 1.0f } +/* Lime color { R:0, G:255, B:0, A:255 } */ +#define SG_LIME { 0.0f, 1.0f, 0.0f, 1.0f } +/* Lime Green color { R:50, G:205, B:50, A:255 } */ +#define SG_LIME_GREEN { 0.196078431f, 0.803921569f, 0.196078431f, 1.0f } +/* Linen color { R:250, G:240, B:230, A:255 } */ +#define SG_LINEN { 0.980392157f, 0.941176471f, 0.901960784f, 1.0f } +/* Magenta color { R:255, G:0, B:255, A:255 } */ +#define SG_MAGENTA { 1.0f, 0.0f, 1.0f, 1.0f } +/* Maroon color { R:176, G:48, B:96, A:255 } */ +#define SG_MAROON { 0.690196078f, 0.188235294f, 0.376470588f, 1.0f } +/* Web Maroon color { R:128, G:0, B:0, A:255 } */ +#define SG_WEB_MAROON { 0.501960784f, 0.0f, 0.0f, 1.0f } +/* Medium Aquamarine color { R:102, G:205, B:170, A:255 } */ +#define SG_MEDIUM_AQUAMARINE { 0.4f, 0.803921569f, 0.666666667f, 1.0f } +/* Medium Blue color { R:0, G:0, B:205, A:255 } */ +#define SG_MEDIUM_BLUE { 0.0f, 0.0f, 0.803921569f, 1.0f } +/* Medium Orchid color { R:186, G:85, B:211, A:255 } */ +#define SG_MEDIUM_ORCHID { 0.729411765f, 0.333333333f, 0.82745098f, 1.0f } +/* Medium Purple color { R:147, G:112, B:219, A:255 } */ +#define SG_MEDIUM_PURPLE { 0.576470588f, 0.439215686f, 0.858823529f, 1.0f } +/* Medium Sea Green color { R:60, G:179, B:113, A:255 } */ +#define SG_MEDIUM_SEA_GREEN { 0.235294118f, 0.701960784f, 0.443137255f, 1.0f } +/* Medium Slate Blue color { R:123, G:104, B:238, A:255 } */ +#define SG_MEDIUM_SLATE_BLUE { 0.482352941f, 0.407843137f, 0.933333333f, 1.0f } +/* Medium Spring Green color { R:0, G:250, B:154, A:255 } */ +#define SG_MEDIUM_SPRING_GREEN { 0.0f, 0.980392157f, 0.603921569f, 1.0f } +/* Medium Turquoise color { R:72, G:209, B:204, A:255 } */ +#define SG_MEDIUM_TURQUOISE { 0.282352941f, 0.819607843f, 0.8f, 1.0f } +/* Medium Violet Red color { R:199, G:21, B:133, A:255 } */ +#define SG_MEDIUM_VIOLET_RED { 0.780392157f, 0.0823529412f, 0.521568627f, 1.0f } +/* Midnight Blue color { R:25, G:25, B:112, A:255 } */ +#define SG_MIDNIGHT_BLUE { 0.0980392157f, 0.0980392157f, 0.439215686f, 1.0f } +/* Mint Cream color { R:245, G:255, B:250, A:255 } */ +#define SG_MINT_CREAM { 0.960784314f, 1.0f, 0.980392157f, 1.0f } +/* Misty Rose color { R:255, G:228, B:225, A:255 } */ +#define SG_MISTY_ROSE { 1.0f, 0.894117647f, 0.882352941f, 1.0f } +/* Moccasin color { R:255, G:228, B:181, A:255 } */ +#define SG_MOCCASIN { 1.0f, 0.894117647f, 0.709803922f, 1.0f } +/* Navajo White color { R:255, G:222, B:173, A:255 } */ +#define SG_NAVAJO_WHITE { 1.0f, 0.870588235f, 0.678431373f, 1.0f } +/* Navy Blue color { R:0, G:0, B:128, A:255 } */ +#define SG_NAVY_BLUE { 0.0f, 0.0f, 0.501960784f, 1.0f } +/* Old Lace color { R:253, G:245, B:230, A:255 } */ +#define SG_OLD_LACE { 0.992156863f, 0.960784314f, 0.901960784f, 1.0f } +/* Olive color { R:128, G:128, B:0, A:255 } */ +#define SG_OLIVE { 0.501960784f, 0.501960784f, 0.0f, 1.0f } +/* Olive Drab color { R:107, G:142, B:35, A:255 } */ +#define SG_OLIVE_DRAB { 0.419607843f, 0.556862745f, 0.137254902f, 1.0f } +/* Orange color { R:255, G:165, B:0, A:255 } */ +#define SG_ORANGE { 1.0f, 0.647058824f, 0.0f, 1.0f } +/* Orange Red color { R:255, G:69, B:0, A:255 } */ +#define SG_ORANGE_RED { 1.0f, 0.270588235f, 0.0f, 1.0f } +/* Orchid color { R:218, G:112, B:214, A:255 } */ +#define SG_ORCHID { 0.854901961f, 0.439215686f, 0.839215686f, 1.0f } +/* Pale Goldenrod color { R:238, G:232, B:170, A:255 } */ +#define SG_PALE_GOLDENROD { 0.933333333f, 0.909803922f, 0.666666667f, 1.0f } +/* Pale Green color { R:152, G:251, B:152, A:255 } */ +#define SG_PALE_GREEN { 0.596078431f, 0.984313725f, 0.596078431f, 1.0f } +/* Pale Turquoise color { R:175, G:238, B:238, A:255 } */ +#define SG_PALE_TURQUOISE { 0.68627451f, 0.933333333f, 0.933333333f, 1.0f } +/* Pale Violet Red color { R:219, G:112, B:147, A:255 } */ +#define SG_PALE_VIOLET_RED { 0.858823529f, 0.439215686f, 0.576470588f, 1.0f } +/* Papaya Whip color { R:255, G:239, B:213, A:255 } */ +#define SG_PAPAYA_WHIP { 1.0f, 0.937254902f, 0.835294118f, 1.0f } +/* Peach Puff color { R:255, G:218, B:185, A:255 } */ +#define SG_PEACH_PUFF { 1.0f, 0.854901961f, 0.725490196f, 1.0f } +/* Peru color { R:205, G:133, B:63, A:255 } */ +#define SG_PERU { 0.803921569f, 0.521568627f, 0.247058824f, 1.0f } +/* Pink color { R:255, G:192, B:203, A:255 } */ +#define SG_PINK { 1.0f, 0.752941176f, 0.796078431f, 1.0f } +/* Plum color { R:221, G:160, B:221, A:255 } */ +#define SG_PLUM { 0.866666667f, 0.62745098f, 0.866666667f, 1.0f } +/* Powder Blue color { R:176, G:224, B:230, A:255 } */ +#define SG_POWDER_BLUE { 0.690196078f, 0.878431373f, 0.901960784f, 1.0f } +/* Purple color { R:160, G:32, B:240, A:255 } */ +#define SG_PURPLE { 0.62745098f, 0.125490196f, 0.941176471f, 1.0f } +/* Web Purple color { R:128, G:0, B:128, A:255 } */ +#define SG_WEB_PURPLE { 0.501960784f, 0.0f, 0.501960784f, 1.0f } +/* Rebecca Purple color { R:102, G:51, B:153, A:255 } */ +#define SG_REBECCA_PURPLE { 0.4f, 0.2f, 0.6f, 1.0f } +/* Red color { R:255, G:0, B:0, A:255 } */ +#define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f } +/* Rosy Brown color { R:188, G:143, B:143, A:255 } */ +#define SG_ROSY_BROWN { 0.737254902f, 0.560784314f, 0.560784314f, 1.0f } +/* Royal Blue color { R:65, G:105, B:225, A:255 } */ +#define SG_ROYAL_BLUE { 0.254901961f, 0.411764706f, 0.882352941f, 1.0f } +/* Saddle Brown color { R:139, G:69, B:19, A:255 } */ +#define SG_SADDLE_BROWN { 0.545098039f, 0.270588235f, 0.0745098039f, 1.0f } +/* Salmon color { R:250, G:128, B:114, A:255 } */ +#define SG_SALMON { 0.980392157f, 0.501960784f, 0.447058824f, 1.0f } +/* Sandy Brown color { R:244, G:164, B:96, A:255 } */ +#define SG_SANDY_BROWN { 0.956862745f, 0.643137255f, 0.376470588f, 1.0f } +/* Sea Green color { R:46, G:139, B:87, A:255 } */ +#define SG_SEA_GREEN { 0.180392157f, 0.545098039f, 0.341176471f, 1.0f } +/* Seashell color { R:255, G:245, B:238, A:255 } */ +#define SG_SEASHELL { 1.0f, 0.960784314f, 0.933333333f, 1.0f } +/* Sienna color { R:160, G:82, B:45, A:255 } */ +#define SG_SIENNA { 0.62745098f, 0.321568627f, 0.176470588f, 1.0f } +/* Silver color { R:192, G:192, B:192, A:255 } */ +#define SG_SILVER { 0.752941176f, 0.752941176f, 0.752941176f, 1.0f } +/* Sky Blue color { R:135, G:206, B:235, A:255 } */ +#define SG_SKY_BLUE { 0.529411765f, 0.807843137f, 0.921568627f, 1.0f } +/* Slate Blue color { R:106, G:90, B:205, A:255 } */ +#define SG_SLATE_BLUE { 0.415686275f, 0.352941176f, 0.803921569f, 1.0f } +/* Slate Gray color { R:112, G:128, B:144, A:255 } */ +#define SG_SLATE_GRAY { 0.439215686f, 0.501960784f, 0.564705882f, 1.0f } +/* Snow color { R:255, G:250, B:250, A:255 } */ +#define SG_SNOW { 1.0f, 0.980392157f, 0.980392157f, 1.0f } +/* Spring Green color { R:0, G:255, B:127, A:255 } */ +#define SG_SPRING_GREEN { 0.0f, 1.0f, 0.498039216f, 1.0f } +/* Steel Blue color { R:70, G:130, B:180, A:255 } */ +#define SG_STEEL_BLUE { 0.274509804f, 0.509803922f, 0.705882353f, 1.0f } +/* Tan color { R:210, G:180, B:140, A:255 } */ +#define SG_TAN { 0.823529412f, 0.705882353f, 0.549019608f, 1.0f } +/* Teal color { R:0, G:128, B:128, A:255 } */ +#define SG_TEAL { 0.0f, 0.501960784f, 0.501960784f, 1.0f } +/* Thistle color { R:216, G:191, B:216, A:255 } */ +#define SG_THISTLE { 0.847058824f, 0.749019608f, 0.847058824f, 1.0f } +/* Tomato color { R:255, G:99, B:71, A:255 } */ +#define SG_TOMATO { 1.0f, 0.388235294f, 0.278431373f, 1.0f } +/* Transparent color { R:0, G:0, B:0, A:0 } */ +#define SG_TRANSPARENT { 0.0f, 0.0f, 0.0f, 0.0f } +/* Turquoise color { R:64, G:224, B:208, A:255 } */ +#define SG_TURQUOISE { 0.250980392f, 0.878431373f, 0.815686275f, 1.0f } +/* Violet color { R:238, G:130, B:238, A:255 } */ +#define SG_VIOLET { 0.933333333f, 0.509803922f, 0.933333333f, 1.0f } +/* Wheat color { R:245, G:222, B:179, A:255 } */ +#define SG_WHEAT { 0.960784314f, 0.870588235f, 0.701960784f, 1.0f } +/* White color { R:255, G:255, B:255, A:255 } */ +#define SG_WHITE { 1.0f, 1.0f, 1.0f, 1.0f } +/* White Smoke color { R:245, G:245, B:245, A:255 } */ +#define SG_WHITE_SMOKE { 0.960784314f, 0.960784314f, 0.960784314f, 1.0f } +/* Yellow color { R:255, G:255, B:0, A:255 } */ +#define SG_YELLOW { 1.0f, 1.0f, 0.0f, 1.0f } +/* Yellow Green color { R:154, G:205, B:50, A:255 } */ +#define SG_YELLOW_GREEN { 0.603921569f, 0.803921569f, 0.196078431f, 1.0f } + +/* Alice Blue color { R:240, G:248, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_alice_blue = SG_ALICE_BLUE; +/* Antique White color { R:250, G:235, B:215, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_antique_white = SG_ANTIQUE_WHITE; +/* Aqua color { R:0, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_aqua = SG_AQUA; +/* Aquamarine color { R:127, G:255, B:212, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_aquamarine = SG_AQUAMARINE; +/* Azure color { R:240, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_azure = SG_AZURE; +/* Beige color { R:245, G:245, B:220, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_beige = SG_BEIGE; +/* Bisque color { R:255, G:228, B:196, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_bisque = SG_BISQUE; +/* Black color { R:0, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_black = SG_BLACK; +/* Blanched Almond color { R:255, G:235, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_blanched_almond = SG_BLANCHED_ALMOND; +/* Blue color { R:0, G:0, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE; +/* Blue Violet color { R:138, G:43, B:226, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_blue_violet = SG_BLUE_VIOLET; +/* Brown color { R:165, G:42, B:42, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_brown = SG_BROWN; +/* Burlywood color { R:222, G:184, B:135, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_burlywood = SG_BURLYWOOD; +/* Cadet Blue color { R:95, G:158, B:160, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cadet_blue = SG_CADET_BLUE; +/* Chartreuse color { R:127, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_chartreuse = SG_CHARTREUSE; +/* Chocolate color { R:210, G:105, B:30, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_chocolate = SG_CHOCOLATE; +/* Coral color { R:255, G:127, B:80, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_coral = SG_CORAL; +/* Cornflower Blue color { R:100, G:149, B:237, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cornflower_blue = SG_CORNFLOWER_BLUE; +/* Cornsilk color { R:255, G:248, B:220, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cornsilk = SG_CORNSILK; +/* Crimson color { R:220, G:20, B:60, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_crimson = SG_CRIMSON; +/* Cyan color { R:0, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cyan = SG_CYAN; +/* Dark Blue color { R:0, G:0, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_blue = SG_DARK_BLUE; +/* Dark Cyan color { R:0, G:139, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_cyan = SG_DARK_CYAN; +/* Dark Goldenrod color { R:184, G:134, B:11, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_goldenrod = SG_DARK_GOLDENROD; +/* Dark Gray color { R:169, G:169, B:169, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_gray = SG_DARK_GRAY; +/* Dark Green color { R:0, G:100, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_green = SG_DARK_GREEN; +/* Dark Khaki color { R:189, G:183, B:107, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_khaki = SG_DARK_KHAKI; +/* Dark Magenta color { R:139, G:0, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_magenta = SG_DARK_MAGENTA; +/* Dark Olive Green color { R:85, G:107, B:47, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_olive_green = SG_DARK_OLIVE_GREEN; +/* Dark Orange color { R:255, G:140, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_orange = SG_DARK_ORANGE; +/* Dark Orchid color { R:153, G:50, B:204, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_orchid = SG_DARK_ORCHID; +/* Dark Red color { R:139, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_red = SG_DARK_RED; +/* Dark Salmon color { R:233, G:150, B:122, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_salmon = SG_DARK_SALMON; +/* Dark Sea Green color { R:143, G:188, B:143, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_sea_green = SG_DARK_SEA_GREEN; +/* Dark Slate Blue color { R:72, G:61, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_slate_blue = SG_DARK_SLATE_BLUE; +/* Dark Slate Gray color { R:47, G:79, B:79, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_slate_gray = SG_DARK_SLATE_GRAY; +/* Dark Turquoise color { R:0, G:206, B:209, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_turquoise = SG_DARK_TURQUOISE; +/* Dark Violet color { R:148, G:0, B:211, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_violet = SG_DARK_VIOLET; +/* Deep Pink color { R:255, G:20, B:147, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_deep_pink = SG_DEEP_PINK; +/* Deep Sky Blue color { R:0, G:191, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_deep_sky_blue = SG_DEEP_SKY_BLUE; +/* Dim Gray color { R:105, G:105, B:105, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dim_gray = SG_DIM_GRAY; +/* Dodger Blue color { R:30, G:144, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dodger_blue = SG_DODGER_BLUE; +/* Firebrick color { R:178, G:34, B:34, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_firebrick = SG_FIREBRICK; +/* Floral White color { R:255, G:250, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_floral_white = SG_FLORAL_WHITE; +/* Forest Green color { R:34, G:139, B:34, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_forest_green = SG_FOREST_GREEN; +/* Fuchsia color { R:255, G:0, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_fuchsia = SG_FUCHSIA; +/* Gainsboro color { R:220, G:220, B:220, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_gainsboro = SG_GAINSBORO; +/* Ghost White color { R:248, G:248, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_ghost_white = SG_GHOST_WHITE; +/* Gold color { R:255, G:215, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_gold = SG_GOLD; +/* Goldenrod color { R:218, G:165, B:32, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_goldenrod = SG_GOLDENROD; +/* Gray color { R:190, G:190, B:190, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_gray = SG_GRAY; +/* Web Gray color { R:128, G:128, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_gray = SG_WEB_GRAY; +/* Green color { R:0, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN; +/* Web Green color { R:0, G:128, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_green = SG_WEB_GREEN; +/* Green Yellow color { R:173, G:255, B:47, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_green_yellow = SG_GREEN_YELLOW; +/* Honeydew color { R:240, G:255, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_honeydew = SG_HONEYDEW; +/* Hot Pink color { R:255, G:105, B:180, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_hot_pink = SG_HOT_PINK; +/* Indian Red color { R:205, G:92, B:92, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_indian_red = SG_INDIAN_RED; +/* Indigo color { R:75, G:0, B:130, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_indigo = SG_INDIGO; +/* Ivory color { R:255, G:255, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_ivory = SG_IVORY; +/* Khaki color { R:240, G:230, B:140, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_khaki = SG_KHAKI; +/* Lavender color { R:230, G:230, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lavender = SG_LAVENDER; +/* Lavender Blush color { R:255, G:240, B:245, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lavender_blush = SG_LAVENDER_BLUSH; +/* Lawn Green color { R:124, G:252, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lawn_green = SG_LAWN_GREEN; +/* Lemon Chiffon color { R:255, G:250, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lemon_chiffon = SG_LEMON_CHIFFON; +/* Light Blue color { R:173, G:216, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_blue = SG_LIGHT_BLUE; +/* Light Coral color { R:240, G:128, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_coral = SG_LIGHT_CORAL; +/* Light Cyan color { R:224, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_cyan = SG_LIGHT_CYAN; +/* Light Goldenrod color { R:250, G:250, B:210, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_goldenrod = SG_LIGHT_GOLDENROD; +/* Light Gray color { R:211, G:211, B:211, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_gray = SG_LIGHT_GRAY; +/* Light Green color { R:144, G:238, B:144, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_green = SG_LIGHT_GREEN; +/* Light Pink color { R:255, G:182, B:193, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_pink = SG_LIGHT_PINK; +/* Light Salmon color { R:255, G:160, B:122, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_salmon = SG_LIGHT_SALMON; +/* Light Sea Green color { R:32, G:178, B:170, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_sea_green = SG_LIGHT_SEA_GREEN; +/* Light Sky Blue color { R:135, G:206, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_sky_blue = SG_LIGHT_SKY_BLUE; +/* Light Slate Gray color { R:119, G:136, B:153, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_slate_gray = SG_LIGHT_SLATE_GRAY; +/* Light Steel Blue color { R:176, G:196, B:222, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_steel_blue = SG_LIGHT_STEEL_BLUE; +/* Light Yellow color { R:255, G:255, B:224, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_yellow = SG_LIGHT_YELLOW; +/* Lime color { R:0, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lime = SG_LIME; +/* Lime Green color { R:50, G:205, B:50, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lime_green = SG_LIME_GREEN; +/* Linen color { R:250, G:240, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_linen = SG_LINEN; +/* Magenta color { R:255, G:0, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_magenta = SG_MAGENTA; +/* Maroon color { R:176, G:48, B:96, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_maroon = SG_MAROON; +/* Web Maroon color { R:128, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_maroon = SG_WEB_MAROON; +/* Medium Aquamarine color { R:102, G:205, B:170, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_aquamarine = SG_MEDIUM_AQUAMARINE; +/* Medium Blue color { R:0, G:0, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_blue = SG_MEDIUM_BLUE; +/* Medium Orchid color { R:186, G:85, B:211, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_orchid = SG_MEDIUM_ORCHID; +/* Medium Purple color { R:147, G:112, B:219, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_purple = SG_MEDIUM_PURPLE; +/* Medium Sea Green color { R:60, G:179, B:113, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_sea_green = SG_MEDIUM_SEA_GREEN; +/* Medium Slate Blue color { R:123, G:104, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_slate_blue = SG_MEDIUM_SLATE_BLUE; +/* Medium Spring Green color { R:0, G:250, B:154, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_spring_green = SG_MEDIUM_SPRING_GREEN; +/* Medium Turquoise color { R:72, G:209, B:204, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_turquoise = SG_MEDIUM_TURQUOISE; +/* Medium Violet Red color { R:199, G:21, B:133, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_violet_red = SG_MEDIUM_VIOLET_RED; +/* Midnight Blue color { R:25, G:25, B:112, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_midnight_blue = SG_MIDNIGHT_BLUE; +/* Mint Cream color { R:245, G:255, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_mint_cream = SG_MINT_CREAM; +/* Misty Rose color { R:255, G:228, B:225, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_misty_rose = SG_MISTY_ROSE; +/* Moccasin color { R:255, G:228, B:181, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_moccasin = SG_MOCCASIN; +/* Navajo White color { R:255, G:222, B:173, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_navajo_white = SG_NAVAJO_WHITE; +/* Navy Blue color { R:0, G:0, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_navy_blue = SG_NAVY_BLUE; +/* Old Lace color { R:253, G:245, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_old_lace = SG_OLD_LACE; +/* Olive color { R:128, G:128, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_olive = SG_OLIVE; +/* Olive Drab color { R:107, G:142, B:35, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_olive_drab = SG_OLIVE_DRAB; +/* Orange color { R:255, G:165, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_orange = SG_ORANGE; +/* Orange Red color { R:255, G:69, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_orange_red = SG_ORANGE_RED; +/* Orchid color { R:218, G:112, B:214, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_orchid = SG_ORCHID; +/* Pale Goldenrod color { R:238, G:232, B:170, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_goldenrod = SG_PALE_GOLDENROD; +/* Pale Green color { R:152, G:251, B:152, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_green = SG_PALE_GREEN; +/* Pale Turquoise color { R:175, G:238, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_turquoise = SG_PALE_TURQUOISE; +/* Pale Violet Red color { R:219, G:112, B:147, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_violet_red = SG_PALE_VIOLET_RED; +/* Papaya Whip color { R:255, G:239, B:213, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_papaya_whip = SG_PAPAYA_WHIP; +/* Peach Puff color { R:255, G:218, B:185, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_peach_puff = SG_PEACH_PUFF; +/* Peru color { R:205, G:133, B:63, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_peru = SG_PERU; +/* Pink color { R:255, G:192, B:203, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pink = SG_PINK; +/* Plum color { R:221, G:160, B:221, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_plum = SG_PLUM; +/* Powder Blue color { R:176, G:224, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_powder_blue = SG_POWDER_BLUE; +/* Purple color { R:160, G:32, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_purple = SG_PURPLE; +/* Web Purple color { R:128, G:0, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_purple = SG_WEB_PURPLE; +/* Rebecca Purple color { R:102, G:51, B:153, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_rebecca_purple = SG_REBECCA_PURPLE; +/* Red color { R:255, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED; +/* Rosy Brown color { R:188, G:143, B:143, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_rosy_brown = SG_ROSY_BROWN; +/* Royal Blue color { R:65, G:105, B:225, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_royal_blue = SG_ROYAL_BLUE; +/* Saddle Brown color { R:139, G:69, B:19, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_saddle_brown = SG_SADDLE_BROWN; +/* Salmon color { R:250, G:128, B:114, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_salmon = SG_SALMON; +/* Sandy Brown color { R:244, G:164, B:96, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sandy_brown = SG_SANDY_BROWN; +/* Sea Green color { R:46, G:139, B:87, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sea_green = SG_SEA_GREEN; +/* Seashell color { R:255, G:245, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_seashell = SG_SEASHELL; +/* Sienna color { R:160, G:82, B:45, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sienna = SG_SIENNA; +/* Silver color { R:192, G:192, B:192, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_silver = SG_SILVER; +/* Sky Blue color { R:135, G:206, B:235, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sky_blue = SG_SKY_BLUE; +/* Slate Blue color { R:106, G:90, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_slate_blue = SG_SLATE_BLUE; +/* Slate Gray color { R:112, G:128, B:144, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_slate_gray = SG_SLATE_GRAY; +/* Snow color { R:255, G:250, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_snow = SG_SNOW; +/* Spring Green color { R:0, G:255, B:127, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_spring_green = SG_SPRING_GREEN; +/* Steel Blue color { R:70, G:130, B:180, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_steel_blue = SG_STEEL_BLUE; +/* Tan color { R:210, G:180, B:140, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_tan = SG_TAN; +/* Teal color { R:0, G:128, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_teal = SG_TEAL; +/* Thistle color { R:216, G:191, B:216, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_thistle = SG_THISTLE; +/* Tomato color { R:255, G:99, B:71, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_tomato = SG_TOMATO; +/* Transparent color { R:0, G:0, B:0, A:0 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_transparent = SG_TRANSPARENT; +/* Turquoise color { R:64, G:224, B:208, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_turquoise = SG_TURQUOISE; +/* Violet color { R:238, G:130, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_violet = SG_VIOLET; +/* Wheat color { R:245, G:222, B:179, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_wheat = SG_WHEAT; +/* White color { R:255, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_white = SG_WHITE; +/* White Smoke color { R:245, G:245, B:245, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_white_smoke = SG_WHITE_SMOKE; +/* Yellow color { R:255, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_yellow = SG_YELLOW; +/* Yellow Green color { R:154, G:205, B:50, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_yellow_green = SG_YELLOW_GREEN; + +/* Alice Blue color { R:240, G:248, B:255, A:255 } */ +#define SG_ALICE_BLUE_RGBA32 0xF0F8FFFF +/* Antique White color { R:250, G:235, B:215, A:255 } */ +#define SG_ANTIQUE_WHITE_RGBA32 0xFAEBD7FF +/* Aqua color { R:0, G:255, B:255, A:255 } */ +#define SG_AQUA_RGBA32 0x00FFFFFF +/* Aquamarine color { R:127, G:255, B:212, A:255 } */ +#define SG_AQUAMARINE_RGBA32 0x7FFFD4FF +/* Azure color { R:240, G:255, B:255, A:255 } */ +#define SG_AZURE_RGBA32 0xF0FFFFFF +/* Beige color { R:245, G:245, B:220, A:255 } */ +#define SG_BEIGE_RGBA32 0xF5F5DCFF +/* Bisque color { R:255, G:228, B:196, A:255 } */ +#define SG_BISQUE_RGBA32 0xFFE4C4FF +/* Black color { R:0, G:0, B:0, A:255 } */ +#define SG_BLACK_RGBA32 0x000000FF +/* Blanched Almond color { R:255, G:235, B:205, A:255 } */ +#define SG_BLANCHED_ALMOND_RGBA32 0xFFEBCDFF +/* Blue color { R:0, G:0, B:255, A:255 } */ +#define SG_BLUE_RGBA32 0x0000FFFF +/* Blue Violet color { R:138, G:43, B:226, A:255 } */ +#define SG_BLUE_VIOLET_RGBA32 0x8A2BE2FF +/* Brown color { R:165, G:42, B:42, A:255 } */ +#define SG_BROWN_RGBA32 0xA52A2AFF +/* Burlywood color { R:222, G:184, B:135, A:255 } */ +#define SG_BURLYWOOD_RGBA32 0xDEB887FF +/* Cadet Blue color { R:95, G:158, B:160, A:255 } */ +#define SG_CADET_BLUE_RGBA32 0x5F9EA0FF +/* Chartreuse color { R:127, G:255, B:0, A:255 } */ +#define SG_CHARTREUSE_RGBA32 0x7FFF00FF +/* Chocolate color { R:210, G:105, B:30, A:255 } */ +#define SG_CHOCOLATE_RGBA32 0xD2691EFF +/* Coral color { R:255, G:127, B:80, A:255 } */ +#define SG_CORAL_RGBA32 0xFF7F50FF +/* Cornflower Blue color { R:100, G:149, B:237, A:255 } */ +#define SG_CORNFLOWER_BLUE_RGBA32 0x6495EDFF +/* Cornsilk color { R:255, G:248, B:220, A:255 } */ +#define SG_CORNSILK_RGBA32 0xFFF8DCFF +/* Crimson color { R:220, G:20, B:60, A:255 } */ +#define SG_CRIMSON_RGBA32 0xDC143CFF +/* Cyan color { R:0, G:255, B:255, A:255 } */ +#define SG_CYAN_RGBA32 0x00FFFFFF +/* Dark Blue color { R:0, G:0, B:139, A:255 } */ +#define SG_DARK_BLUE_RGBA32 0x00008BFF +/* Dark Cyan color { R:0, G:139, B:139, A:255 } */ +#define SG_DARK_CYAN_RGBA32 0x008B8BFF +/* Dark Goldenrod color { R:184, G:134, B:11, A:255 } */ +#define SG_DARK_GOLDENROD_RGBA32 0xB8860BFF +/* Dark Gray color { R:169, G:169, B:169, A:255 } */ +#define SG_DARK_GRAY_RGBA32 0xA9A9A9FF +/* Dark Green color { R:0, G:100, B:0, A:255 } */ +#define SG_DARK_GREEN_RGBA32 0x006400FF +/* Dark Khaki color { R:189, G:183, B:107, A:255 } */ +#define SG_DARK_KHAKI_RGBA32 0xBDB76BFF +/* Dark Magenta color { R:139, G:0, B:139, A:255 } */ +#define SG_DARK_MAGENTA_RGBA32 0x8B008BFF +/* Dark Olive Green color { R:85, G:107, B:47, A:255 } */ +#define SG_DARK_OLIVE_GREEN_RGBA32 0x556B2FFF +/* Dark Orange color { R:255, G:140, B:0, A:255 } */ +#define SG_DARK_ORANGE_RGBA32 0xFF8C00FF +/* Dark Orchid color { R:153, G:50, B:204, A:255 } */ +#define SG_DARK_ORCHID_RGBA32 0x9932CCFF +/* Dark Red color { R:139, G:0, B:0, A:255 } */ +#define SG_DARK_RED_RGBA32 0x8B0000FF +/* Dark Salmon color { R:233, G:150, B:122, A:255 } */ +#define SG_DARK_SALMON_RGBA32 0xE9967AFF +/* Dark Sea Green color { R:143, G:188, B:143, A:255 } */ +#define SG_DARK_SEA_GREEN_RGBA32 0x8FBC8FFF +/* Dark Slate Blue color { R:72, G:61, B:139, A:255 } */ +#define SG_DARK_SLATE_BLUE_RGBA32 0x483D8BFF +/* Dark Slate Gray color { R:47, G:79, B:79, A:255 } */ +#define SG_DARK_SLATE_GRAY_RGBA32 0x2F4F4FFF +/* Dark Turquoise color { R:0, G:206, B:209, A:255 } */ +#define SG_DARK_TURQUOISE_RGBA32 0x00CED1FF +/* Dark Violet color { R:148, G:0, B:211, A:255 } */ +#define SG_DARK_VIOLET_RGBA32 0x9400D3FF +/* Deep Pink color { R:255, G:20, B:147, A:255 } */ +#define SG_DEEP_PINK_RGBA32 0xFF1493FF +/* Deep Sky Blue color { R:0, G:191, B:255, A:255 } */ +#define SG_DEEP_SKY_BLUE_RGBA32 0x00BFFFFF +/* Dim Gray color { R:105, G:105, B:105, A:255 } */ +#define SG_DIM_GRAY_RGBA32 0x696969FF +/* Dodger Blue color { R:30, G:144, B:255, A:255 } */ +#define SG_DODGER_BLUE_RGBA32 0x1E90FFFF +/* Firebrick color { R:178, G:34, B:34, A:255 } */ +#define SG_FIREBRICK_RGBA32 0xB22222FF +/* Floral White color { R:255, G:250, B:240, A:255 } */ +#define SG_FLORAL_WHITE_RGBA32 0xFFFAF0FF +/* Forest Green color { R:34, G:139, B:34, A:255 } */ +#define SG_FOREST_GREEN_RGBA32 0x228B22FF +/* Fuchsia color { R:255, G:0, B:255, A:255 } */ +#define SG_FUCHSIA_RGBA32 0xFF00FFFF +/* Gainsboro color { R:220, G:220, B:220, A:255 } */ +#define SG_GAINSBORO_RGBA32 0xDCDCDCFF +/* Ghost White color { R:248, G:248, B:255, A:255 } */ +#define SG_GHOST_WHITE_RGBA32 0xF8F8FFFF +/* Gold color { R:255, G:215, B:0, A:255 } */ +#define SG_GOLD_RGBA32 0xFFD700FF +/* Goldenrod color { R:218, G:165, B:32, A:255 } */ +#define SG_GOLDENROD_RGBA32 0xDAA520FF +/* Gray color { R:190, G:190, B:190, A:255 } */ +#define SG_GRAY_RGBA32 0xBEBEBEFF +/* Web Gray color { R:128, G:128, B:128, A:255 } */ +#define SG_WEB_GRAY_RGBA32 0x808080FF +/* Green color { R:0, G:255, B:0, A:255 } */ +#define SG_GREEN_RGBA32 0x00FF00FF +/* Web Green color { R:0, G:128, B:0, A:255 } */ +#define SG_WEB_GREEN_RGBA32 0x008000FF +/* Green Yellow color { R:173, G:255, B:47, A:255 } */ +#define SG_GREEN_YELLOW_RGBA32 0xADFF2FFF +/* Honeydew color { R:240, G:255, B:240, A:255 } */ +#define SG_HONEYDEW_RGBA32 0xF0FFF0FF +/* Hot Pink color { R:255, G:105, B:180, A:255 } */ +#define SG_HOT_PINK_RGBA32 0xFF69B4FF +/* Indian Red color { R:205, G:92, B:92, A:255 } */ +#define SG_INDIAN_RED_RGBA32 0xCD5C5CFF +/* Indigo color { R:75, G:0, B:130, A:255 } */ +#define SG_INDIGO_RGBA32 0x4B0082FF +/* Ivory color { R:255, G:255, B:240, A:255 } */ +#define SG_IVORY_RGBA32 0xFFFFF0FF +/* Khaki color { R:240, G:230, B:140, A:255 } */ +#define SG_KHAKI_RGBA32 0xF0E68CFF +/* Lavender color { R:230, G:230, B:250, A:255 } */ +#define SG_LAVENDER_RGBA32 0xE6E6FAFF +/* Lavender Blush color { R:255, G:240, B:245, A:255 } */ +#define SG_LAVENDER_BLUSH_RGBA32 0xFFF0F5FF +/* Lawn Green color { R:124, G:252, B:0, A:255 } */ +#define SG_LAWN_GREEN_RGBA32 0x7CFC00FF +/* Lemon Chiffon color { R:255, G:250, B:205, A:255 } */ +#define SG_LEMON_CHIFFON_RGBA32 0xFFFACDFF +/* Light Blue color { R:173, G:216, B:230, A:255 } */ +#define SG_LIGHT_BLUE_RGBA32 0xADD8E6FF +/* Light Coral color { R:240, G:128, B:128, A:255 } */ +#define SG_LIGHT_CORAL_RGBA32 0xF08080FF +/* Light Cyan color { R:224, G:255, B:255, A:255 } */ +#define SG_LIGHT_CYAN_RGBA32 0xE0FFFFFF +/* Light Goldenrod color { R:250, G:250, B:210, A:255 } */ +#define SG_LIGHT_GOLDENROD_RGBA32 0xFAFAD2FF +/* Light Gray color { R:211, G:211, B:211, A:255 } */ +#define SG_LIGHT_GRAY_RGBA32 0xD3D3D3FF +/* Light Green color { R:144, G:238, B:144, A:255 } */ +#define SG_LIGHT_GREEN_RGBA32 0x90EE90FF +/* Light Pink color { R:255, G:182, B:193, A:255 } */ +#define SG_LIGHT_PINK_RGBA32 0xFFB6C1FF +/* Light Salmon color { R:255, G:160, B:122, A:255 } */ +#define SG_LIGHT_SALMON_RGBA32 0xFFA07AFF +/* Light Sea Green color { R:32, G:178, B:170, A:255 } */ +#define SG_LIGHT_SEA_GREEN_RGBA32 0x20B2AAFF +/* Light Sky Blue color { R:135, G:206, B:250, A:255 } */ +#define SG_LIGHT_SKY_BLUE_RGBA32 0x87CEFAFF +/* Light Slate Gray color { R:119, G:136, B:153, A:255 } */ +#define SG_LIGHT_SLATE_GRAY_RGBA32 0x778899FF +/* Light Steel Blue color { R:176, G:196, B:222, A:255 } */ +#define SG_LIGHT_STEEL_BLUE_RGBA32 0xB0C4DEFF +/* Light Yellow color { R:255, G:255, B:224, A:255 } */ +#define SG_LIGHT_YELLOW_RGBA32 0xFFFFE0FF +/* Lime color { R:0, G:255, B:0, A:255 } */ +#define SG_LIME_RGBA32 0x00FF00FF +/* Lime Green color { R:50, G:205, B:50, A:255 } */ +#define SG_LIME_GREEN_RGBA32 0x32CD32FF +/* Linen color { R:250, G:240, B:230, A:255 } */ +#define SG_LINEN_RGBA32 0xFAF0E6FF +/* Magenta color { R:255, G:0, B:255, A:255 } */ +#define SG_MAGENTA_RGBA32 0xFF00FFFF +/* Maroon color { R:176, G:48, B:96, A:255 } */ +#define SG_MAROON_RGBA32 0xB03060FF +/* Web Maroon color { R:128, G:0, B:0, A:255 } */ +#define SG_WEB_MAROON_RGBA32 0x800000FF +/* Medium Aquamarine color { R:102, G:205, B:170, A:255 } */ +#define SG_MEDIUM_AQUAMARINE_RGBA32 0x66CDAAFF +/* Medium Blue color { R:0, G:0, B:205, A:255 } */ +#define SG_MEDIUM_BLUE_RGBA32 0x0000CDFF +/* Medium Orchid color { R:186, G:85, B:211, A:255 } */ +#define SG_MEDIUM_ORCHID_RGBA32 0xBA55D3FF +/* Medium Purple color { R:147, G:112, B:219, A:255 } */ +#define SG_MEDIUM_PURPLE_RGBA32 0x9370DBFF +/* Medium Sea Green color { R:60, G:179, B:113, A:255 } */ +#define SG_MEDIUM_SEA_GREEN_RGBA32 0x3CB371FF +/* Medium Slate Blue color { R:123, G:104, B:238, A:255 } */ +#define SG_MEDIUM_SLATE_BLUE_RGBA32 0x7B68EEFF +/* Medium Spring Green color { R:0, G:250, B:154, A:255 } */ +#define SG_MEDIUM_SPRING_GREEN_RGBA32 0x00FA9AFF +/* Medium Turquoise color { R:72, G:209, B:204, A:255 } */ +#define SG_MEDIUM_TURQUOISE_RGBA32 0x48D1CCFF +/* Medium Violet Red color { R:199, G:21, B:133, A:255 } */ +#define SG_MEDIUM_VIOLET_RED_RGBA32 0xC71585FF +/* Midnight Blue color { R:25, G:25, B:112, A:255 } */ +#define SG_MIDNIGHT_BLUE_RGBA32 0x191970FF +/* Mint Cream color { R:245, G:255, B:250, A:255 } */ +#define SG_MINT_CREAM_RGBA32 0xF5FFFAFF +/* Misty Rose color { R:255, G:228, B:225, A:255 } */ +#define SG_MISTY_ROSE_RGBA32 0xFFE4E1FF +/* Moccasin color { R:255, G:228, B:181, A:255 } */ +#define SG_MOCCASIN_RGBA32 0xFFE4B5FF +/* Navajo White color { R:255, G:222, B:173, A:255 } */ +#define SG_NAVAJO_WHITE_RGBA32 0xFFDEADFF +/* Navy Blue color { R:0, G:0, B:128, A:255 } */ +#define SG_NAVY_BLUE_RGBA32 0x000080FF +/* Old Lace color { R:253, G:245, B:230, A:255 } */ +#define SG_OLD_LACE_RGBA32 0xFDF5E6FF +/* Olive color { R:128, G:128, B:0, A:255 } */ +#define SG_OLIVE_RGBA32 0x808000FF +/* Olive Drab color { R:107, G:142, B:35, A:255 } */ +#define SG_OLIVE_DRAB_RGBA32 0x6B8E23FF +/* Orange color { R:255, G:165, B:0, A:255 } */ +#define SG_ORANGE_RGBA32 0xFFA500FF +/* Orange Red color { R:255, G:69, B:0, A:255 } */ +#define SG_ORANGE_RED_RGBA32 0xFF4500FF +/* Orchid color { R:218, G:112, B:214, A:255 } */ +#define SG_ORCHID_RGBA32 0xDA70D6FF +/* Pale Goldenrod color { R:238, G:232, B:170, A:255 } */ +#define SG_PALE_GOLDENROD_RGBA32 0xEEE8AAFF +/* Pale Green color { R:152, G:251, B:152, A:255 } */ +#define SG_PALE_GREEN_RGBA32 0x98FB98FF +/* Pale Turquoise color { R:175, G:238, B:238, A:255 } */ +#define SG_PALE_TURQUOISE_RGBA32 0xAFEEEEFF +/* Pale Violet Red color { R:219, G:112, B:147, A:255 } */ +#define SG_PALE_VIOLET_RED_RGBA32 0xDB7093FF +/* Papaya Whip color { R:255, G:239, B:213, A:255 } */ +#define SG_PAPAYA_WHIP_RGBA32 0xFFEFD5FF +/* Peach Puff color { R:255, G:218, B:185, A:255 } */ +#define SG_PEACH_PUFF_RGBA32 0xFFDAB9FF +/* Peru color { R:205, G:133, B:63, A:255 } */ +#define SG_PERU_RGBA32 0xCD853FFF +/* Pink color { R:255, G:192, B:203, A:255 } */ +#define SG_PINK_RGBA32 0xFFC0CBFF +/* Plum color { R:221, G:160, B:221, A:255 } */ +#define SG_PLUM_RGBA32 0xDDA0DDFF +/* Powder Blue color { R:176, G:224, B:230, A:255 } */ +#define SG_POWDER_BLUE_RGBA32 0xB0E0E6FF +/* Purple color { R:160, G:32, B:240, A:255 } */ +#define SG_PURPLE_RGBA32 0xA020F0FF +/* Web Purple color { R:128, G:0, B:128, A:255 } */ +#define SG_WEB_PURPLE_RGBA32 0x800080FF +/* Rebecca Purple color { R:102, G:51, B:153, A:255 } */ +#define SG_REBECCA_PURPLE_RGBA32 0x663399FF +/* Red color { R:255, G:0, B:0, A:255 } */ +#define SG_RED_RGBA32 0xFF0000FF +/* Rosy Brown color { R:188, G:143, B:143, A:255 } */ +#define SG_ROSY_BROWN_RGBA32 0xBC8F8FFF +/* Royal Blue color { R:65, G:105, B:225, A:255 } */ +#define SG_ROYAL_BLUE_RGBA32 0x4169E1FF +/* Saddle Brown color { R:139, G:69, B:19, A:255 } */ +#define SG_SADDLE_BROWN_RGBA32 0x8B4513FF +/* Salmon color { R:250, G:128, B:114, A:255 } */ +#define SG_SALMON_RGBA32 0xFA8072FF +/* Sandy Brown color { R:244, G:164, B:96, A:255 } */ +#define SG_SANDY_BROWN_RGBA32 0xF4A460FF +/* Sea Green color { R:46, G:139, B:87, A:255 } */ +#define SG_SEA_GREEN_RGBA32 0x2E8B57FF +/* Seashell color { R:255, G:245, B:238, A:255 } */ +#define SG_SEASHELL_RGBA32 0xFFF5EEFF +/* Sienna color { R:160, G:82, B:45, A:255 } */ +#define SG_SIENNA_RGBA32 0xA0522DFF +/* Silver color { R:192, G:192, B:192, A:255 } */ +#define SG_SILVER_RGBA32 0xC0C0C0FF +/* Sky Blue color { R:135, G:206, B:235, A:255 } */ +#define SG_SKY_BLUE_RGBA32 0x87CEEBFF +/* Slate Blue color { R:106, G:90, B:205, A:255 } */ +#define SG_SLATE_BLUE_RGBA32 0x6A5ACDFF +/* Slate Gray color { R:112, G:128, B:144, A:255 } */ +#define SG_SLATE_GRAY_RGBA32 0x708090FF +/* Snow color { R:255, G:250, B:250, A:255 } */ +#define SG_SNOW_RGBA32 0xFFFAFAFF +/* Spring Green color { R:0, G:255, B:127, A:255 } */ +#define SG_SPRING_GREEN_RGBA32 0x00FF7FFF +/* Steel Blue color { R:70, G:130, B:180, A:255 } */ +#define SG_STEEL_BLUE_RGBA32 0x4682B4FF +/* Tan color { R:210, G:180, B:140, A:255 } */ +#define SG_TAN_RGBA32 0xD2B48CFF +/* Teal color { R:0, G:128, B:128, A:255 } */ +#define SG_TEAL_RGBA32 0x008080FF +/* Thistle color { R:216, G:191, B:216, A:255 } */ +#define SG_THISTLE_RGBA32 0xD8BFD8FF +/* Tomato color { R:255, G:99, B:71, A:255 } */ +#define SG_TOMATO_RGBA32 0xFF6347FF +/* Transparent color { R:0, G:0, B:0, A:0 } */ +#define SG_TRANSPARENT_RGBA32 0x00000000 +/* Turquoise color { R:64, G:224, B:208, A:255 } */ +#define SG_TURQUOISE_RGBA32 0x40E0D0FF +/* Violet color { R:238, G:130, B:238, A:255 } */ +#define SG_VIOLET_RGBA32 0xEE82EEFF +/* Wheat color { R:245, G:222, B:179, A:255 } */ +#define SG_WHEAT_RGBA32 0xF5DEB3FF +/* White color { R:255, G:255, B:255, A:255 } */ +#define SG_WHITE_RGBA32 0xFFFFFFFF +/* White Smoke color { R:245, G:245, B:245, A:255 } */ +#define SG_WHITE_SMOKE_RGBA32 0xF5F5F5FF +/* Yellow color { R:255, G:255, B:0, A:255 } */ +#define SG_YELLOW_RGBA32 0xFFFF00FF +/* Yellow Green color { R:154, G:205, B:50, A:255 } */ +#define SG_YELLOW_GREEN_RGBA32 0x9ACD32FF + +#ifdef __cplusplus +} /* extern "C" */ + +inline sg_color sg_make_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return sg_make_color_4b(r, g, b, a); +} + +inline sg_color sg_make_color(uint32_t rgba) { + return sg_make_color_1i(rgba); +} + +inline sg_color sg_color_lerp(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp(&color_a, &color_b, amount); +} + +inline sg_color sg_color_lerp_precise(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp_precise(&color_a, &color_b, amount); +} + +inline sg_color sg_color_multiply(const sg_color& color, float scale) { + return sg_color_multiply(&color, scale); +} + +#endif /* __cplusplus */ + +#endif /* SOKOL_COLOR_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_COLOR_IMPL +#define SOKOL_COLOR_IMPL_INCLUDED (1) + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +static inline float _sg_color_clamp(float v, float low, float high) { + if (v < low) { + return low; + } else if (v > high) { + return high; + } + return v; +} + +static inline float _sg_color_lerp(float a, float b, float amount) { + return a + (b - a) * amount; +} + +static inline float _sg_color_lerp_precise(float a, float b, float amount) { + return ((1.0f - amount) * a) + (b * amount); +} + +SOKOL_API_IMPL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + sg_color result; + result.r = r / 255.0f; + result.g = g / 255.0f; + result.b = b / 255.0f; + result.a = a / 255.0f; + return result; +} + +SOKOL_API_IMPL sg_color sg_make_color_1i(uint32_t rgba) { + return sg_make_color_4b( + (uint8_t)(rgba >> 24), + (uint8_t)(rgba >> 16), + (uint8_t)(rgba >> 8), + (uint8_t)(rgba >> 0) + ); +} + +SOKOL_API_IMPL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp_precise(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp_precise(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp_precise(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp_precise(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_multiply(const sg_color* color, float scale) { + SOKOL_ASSERT(color); + sg_color result; + result.r = color->r * scale; + result.g = color->g * scale; + result.b = color->b * scale; + result.a = color->a * scale; + return result; +} + +#endif /* SOKOL_COLOR_IMPL */ diff --git a/thirdparty/sokol/util/sokol_debugtext.h b/thirdparty/sokol/util/sokol_debugtext.h new file mode 100644 index 0000000..7088b76 --- /dev/null +++ b/thirdparty/sokol/util/sokol_debugtext.h @@ -0,0 +1,5173 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_DEBUGTEXT_IMPL) +#define SOKOL_DEBUGTEXT_IMPL +#endif +#ifndef SOKOL_DEBUGTEXT_INCLUDED +/* + sokol_debugtext.h - simple ASCII debug text rendering on top of sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_DEBUGTEXT_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + SOKOL_VULKAN + + ...optionally provide the following macros to override defaults: + + SOKOL_VSNPRINTF - the function name of an alternative vsnprintf() function (default: vsnprintf) + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_debugtext.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_DEBUGTEXT_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_debugtext.h: + + sokol_gfx.h + + FEATURES AND CONCEPTS + ===================== + - renders 8-bit ASCII text as fixed-size 8x8 pixel characters + - comes with 6 embedded 8-bit home computer fonts (each taking up 2 KBytes) + - easily plug in your own fonts + - create multiple contexts for rendering text in different layers or render passes + + STEP BY STEP + ============ + + --- to initialize sokol-debugtext, call sdtx_setup() *after* initializing + sokol-gfx: + + 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) + The max number of text contexts that can be created. + + .printf_buf_size (default: 4096) + The size of the internal text formatting buffer used by + sdtx_printf() and sdtx_vprintf(). + + .fonts (default: none) + An array of sdtx_font_desc_t structs used to configure the + fonts that can be used for rendering. To use all builtin + fonts call sdtx_setup() like this (in C99): + + sdtx_setup(&(sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = sdtx_font_kc854(), + [2] = sdtx_font_z1013(), + [3] = sdtx_font_cpc(), + [4] = sdtx_font_c64(), + [5] = sdtx_font_oric() + } + }); + + For documentation on how to use you own font data, search + below for "USING YOUR OWN FONT DATA". + + .context + The setup parameters for the default text context. This will + 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 + buffer. Any additional characters will be silently ignored. + + .canvas_width (default: 640) + .canvas_height (default: 480) + The 'virtual canvas size' in pixels. This defines how big + characters will be rendered relative to the default framebuffer + dimensions. Each character occupies a grid of 8x8 'virtual canvas + pixels' (so a virtual canvas size of 640x480 means that 80x60 characters + fit on the screen). For rendering in a resizeable window, you + should dynamically update the canvas size in each frame by + calling sdtx_canvas(w, h). + + .tab_width (default: 4) + The width of a tab character in number of character cells. + + .color_format (default: 0) + .depth_format (default: 0) + .sample_count (default: 0) + The pixel format description for the default context needed + for creating the context's sg_pipeline object. When + rendering to the default framebuffer you can leave those + zero-initialized, in this case the proper values will be + filled in by sokol-gfx. You only need to provide non-default + values here when rendering to render targets with different + pixel format attributes than the default framebuffer. + + --- Before starting to render text, optionally call sdtx_canvas() to + dynamically resize the virtual canvas. This is recommended when + rendering to a resizeable window. The virtual canvas size can + also be used to scale text in relation to the display resolution. + + Examples when using sokol-app: + + - to render characters at 8x8 'physical pixels': + + sdtx_canvas(sapp_width(), sapp_height()); + + - to render characters at 16x16 physical pixels: + + sdtx_canvas(sapp_width()/2.0f, sapp_height()/2.0f); + + Do *not* use integer math here, since this will not look nice + when the render target size isn't divisible by 2. + + --- Optionally define the origin for the character grid with: + + sdtx_origin(x, y); + + The provided coordinates are in character grid cells, not in + virtual canvas pixels. E.g. to set the origin to 2 character tiles + from the left and top border: + + sdtx_origin(2, 2); + + You can define fractions, e.g. to start rendering half + a character tile from the top-left corner: + + sdtx_origin(0.5f, 0.5f); + + --- Optionally set a different font by calling: + + sdtx_font(font_index) + + sokol-debugtext provides 8 font slots which can be populated + with the builtin fonts or with user-provided font data, so + 'font_index' must be a number from 0 to 7. + + --- Position the text cursor with one of the following calls. All arguments + are in character grid cells as floats and relative to the + origin defined with sdtx_origin(): + + sdtx_pos(x, y) - sets absolute cursor position + sdtx_pos_x(x) - only set absolute x cursor position + sdtx_pos_y(y) - only set absolute y cursor position + + sdtx_move(x, y) - move cursor relative in x and y direction + sdtx_move_x(x) - move cursor relative only in x direction + sdtx_move_y(y) - move cursor relative only in y direction + + sdtx_crlf() - set cursor to beginning of next line + (same as sdtx_pos_x(0) + sdtx_move_y(1)) + sdtx_home() - resets the cursor to the origin + (same as sdtx_pos(0, 0)) + + --- Set a new text color with any of the following functions: + + sdtx_color3b(r, g, b) - RGB 0..255, A=255 + sdtx_color3f(r, g, b) - RGB 0.0f..1.0f, A=1.0f + sdtx_color4b(r, g, b, a) - RGBA 0..255 + sdtx_color4f(r, g, b, a) - RGBA 0.0f..1.0f + sdtx_color1i(uint32_t rgba) - ABGR (0xAABBGGRR) + + --- Output 8-bit ASCII text with the following functions: + + sdtx_putc(c) - output a single character + + sdtx_puts(str) - output a null-terminated C string, note that + this will *not* append a newline (so it behaves + differently than the CRT's puts() function) + + sdtx_putr(str, len) - 'put range' output the first 'len' characters of + a C string or until the zero character is encountered + + sdtx_printf(fmt, ...) - output with printf-formatting, note that you + can inject your own printf-compatible function + by overriding the SOKOL_VSNPRINTF define before + including the implementation + + sdtx_vprintf(fmt, args) - same as sdtx_printf() but with the arguments + provided in a va_list + + - Note that the text will not yet be rendered, only recorded for rendering + at a later time, the actual rendering happens when sdtx_draw() is called + inside a sokol-gfx render pass. + - This means also you can output text anywhere in the frame, it doesn't + 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 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() + + ...for non-layered rendering, or to draw a specific layer: + + 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 + + + RENDERING WITH MULTIPLE CONTEXTS + ================================ + Use multiple text contexts if you need to render debug text in different + sokol-gfx render passes, or want to render text to different layers + in the same render pass, each with its own set of parameters. + + To create a new text context call: + + sdtx_context ctx = sdtx_make_context(&(sdtx_context_desc_t){ ... }); + + The creation parameters in the sdtx_context_desc_t struct are the same + as already described above in the sdtx_setup() function: + + .char_buf_size -- max number of characters rendered in one frame, default: 4096 + .canvas_width -- the initial virtual canvas width, default: 640 + .canvas_height -- the initial virtual canvas height, default: 400 + .tab_width -- tab width in number of characters, default: 4 + .color_format -- color pixel format of target render pass + .depth_format -- depth pixel format of target render pass + .sample_count -- MSAA sample count of target render pass + + To make a new context the active context, call: + + sdtx_set_context(ctx) + + ...and after that call the text output functions as described above, and + finally, inside a sokol-gfx render pass, call sdtx_draw() to actually + render the text for this context. + + A context keeps track of the following parameters: + + - the active font + - the virtual canvas size + - the origin position + - the current cursor position + - the current tab width + - the current color + - and the current layer-id + + You can get the currently active context with: + + sdtx_get_context() + + To make the default context current, call sdtx_set_context() with the + special SDTX_DEFAULT_CONTEXT handle: + + sdtx_set_context(SDTX_DEFAULT_CONTEXT) + + Alternatively, use the function sdtx_default_context() to get the default + context handle: + + sdtx_set_context(sdtx_default_context()); + + To destroy a context, call: + + sdtx_destroy_context(ctx) + + 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 + ======================== + + Instead of the built-in fonts you can also plug your own font data + into sokol-debugtext by providing one or several sdtx_font_desc_t + structures in the sdtx_setup call. + + For instance to use a built-in font at slot 0, and a user-font at + font slot 1, the sdtx_setup() call might look like this: + + sdtx_setup(&sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = { + .data = { + .ptr = my_font_data, + .size = sizeof(my_font_data) + }, + .first_char = ..., + .last_char = ... + } + } + }); + + Where 'my_font_data' is a byte array where every character is described + by 8 bytes arranged like this: + + bits + 7 6 5 4 3 2 1 0 + . . . X X . . . byte 0: 0x18 + . . X X X X . . byte 1: 0x3C + . X X . . X X . byte 2: 0x66 + . X X . . X X . byte 3: 0x66 + . X X X X X X . byte 4: 0x7E + . X X . . X X . byte 5: 0x66 + . X X . . X X . byte 6: 0x66 + . . . . . . . . byte 7: 0x00 + + A complete font consists of 256 characters, resulting in 2048 bytes for + the font data array (but note that the character codes 0..31 will never + be rendered). + + If you provide such a complete font data array, you can drop the .first_char + and .last_char initialization parameters since those default to 0 and 255, + note that you can also use the SDTX_RANGE() helper macro to build the + .data item: + + sdtx_setup(&sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = { + .data = SDTX_RANGE(my_font_data) + } + } + }); + + If the font doesn't define all 256 character tiles, or you don't need an + entire 256-character font and want to save a couple of bytes, use the + .first_char and .last_char initialization parameters to define a sub-range. + For instance if the font only contains the characters between the Space + (ASCII code 32) and uppercase character 'Z' (ASCII code 90): + + sdtx_setup(&sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = { + .data = SDTX_RANGE(my_font_data), + .first_char = 32, // could also write ' ' + .last_char = 90 // could also write 'Z' + } + } + }); + + 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_fn = my_alloc, + .free_fn = 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 + + Copyright (c) 2020 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_DEBUGTEXT_INCLUDED (1) +#include +#include +#include // size_t +#include // va_list + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_debugtext.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_DEBUGTEXT_API_DECL) +#define SOKOL_DEBUGTEXT_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_DEBUGTEXT_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_DEBUGTEXT_IMPL) +#define SOKOL_DEBUGTEXT_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_DEBUGTEXT_API_DECL __declspec(dllimport) +#else +#define SOKOL_DEBUGTEXT_API_DECL extern +#endif +#endif + +#if defined(__GNUC__) +#define SOKOL_DEBUGTEXT_PRINTF_ATTR __attribute__((format(printf, 1, 2))) +#else +#define SOKOL_DEBUGTEXT_PRINTF_ATTR +#endif + +#ifdef __cplusplus +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; + +/* the default context handle */ +static const sdtx_context SDTX_DEFAULT_CONTEXT = { 0x00010001 }; + +/* + sdtx_range is a pointer-size-pair struct used to pass memory + blobs into sokol-debugtext. When initialized from a value type + (array or struct), use the SDTX_RANGE() macro to build + an sdtx_range struct. +*/ +typedef struct sdtx_range { + const void* ptr; + size_t size; +} sdtx_range; + +// disabling this for every includer isn't great, but the warning is 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 SDTX_RANGE(x) sdtx_range{ &x, sizeof(x) } +#else +#define SDTX_RANGE(x) (sdtx_range){ &x, sizeof(x) } +#endif + +/* + sdtx_font_desc_t + + Describes the pixel data of a font. A font consists of up to + 256 8x8 character tiles, where each character tile is described + by 8 consecutive bytes, each byte describing 8 pixels. + + For instance the character 'A' could look like this (this is also + how most home computers used to describe their fonts in ROM): + + bits + 7 6 5 4 3 2 1 0 + . . . X X . . . byte 0: 0x18 + . . X X X X . . byte 1: 0x3C + . X X . . X X . byte 2: 0x66 + . X X . . X X . byte 3: 0x66 + . X X X X X X . byte 4: 0x7E + . X X . . X X . byte 5: 0x66 + . X X . . X X . byte 6: 0x66 + . . . . . . . . byte 7: 0x00 + */ +#define SDTX_MAX_FONTS (8) + +typedef struct sdtx_font_desc_t { + sdtx_range data; // pointer to and size of font pixel data + uint8_t first_char; // first character index in font pixel data + uint8_t last_char; // last character index in font pixel data, inclusive (default: 255) +} sdtx_font_desc_t; + +/* + sdtx_context_desc_t + + Describes the initialization parameters of a rendering context. Creating + additional rendering contexts is useful if you want to render in + different sokol-gfx rendering passes, or when rendering several layers + 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 + int tab_width; // tab width in number of characters, default: 4 + sg_pixel_format color_format; // color pixel format of target render pass + sg_pixel_format depth_format; // depth pixel format of target render pass + 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_fn and free_fn 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_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sdtx_allocator_t; + +/* + sdtx_desc_t + + Describes the sokol-debugtext API initialization parameters. Passed + to the sdtx_setup() function. + + NOTE: to populate the fonts item array with builtin fonts, use any + of the following functions: + + sdtx_font_kc853() + sdtx_font_kc854() + sdtx_font_z1013() + sdtx_font_cpc() + sdtx_font_c64() + sdtx_font_oric() +*/ +typedef struct sdtx_desc_t { + int context_pool_size; // max number of rendering contexts that can be created, default: 8 + 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 */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_setup(const sdtx_desc_t* desc); +SOKOL_DEBUGTEXT_API_DECL void sdtx_shutdown(void); + +/* builtin font data (use to populate sdtx_desc.font[]) */ +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_kc853(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_kc854(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_z1013(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_cpc(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_c64(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_oric(void); + +/* context functions */ +SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_make_context(const sdtx_context_desc_t* desc); +SOKOL_DEBUGTEXT_API_DECL void sdtx_destroy_context(sdtx_context ctx); +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); + +/* 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); + +/* set a new virtual canvas size in screen pixels */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_canvas(float w, float h); + +/* set a new origin in character grid coordinates */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_origin(float x, float y); + +/* cursor movement functions (relative to origin in character grid coordinates) */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_home(void); +SOKOL_DEBUGTEXT_API_DECL void sdtx_pos(float x, float y); +SOKOL_DEBUGTEXT_API_DECL void sdtx_pos_x(float x); +SOKOL_DEBUGTEXT_API_DECL void sdtx_pos_y(float y); +SOKOL_DEBUGTEXT_API_DECL void sdtx_move(float dx, float dy); +SOKOL_DEBUGTEXT_API_DECL void sdtx_move_x(float dx); +SOKOL_DEBUGTEXT_API_DECL void sdtx_move_y(float dy); +SOKOL_DEBUGTEXT_API_DECL void sdtx_crlf(void); + +/* set the current text color */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_color3b(uint8_t r, uint8_t g, uint8_t b); // RGB 0..255, A=255 +SOKOL_DEBUGTEXT_API_DECL void sdtx_color3f(float r, float g, float b); // RGB 0.0f..1.0f, A=1.0f +SOKOL_DEBUGTEXT_API_DECL void sdtx_color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); // RGBA 0..255 +SOKOL_DEBUGTEXT_API_DECL void sdtx_color4f(float r, float g, float b, float a); // RGBA 0.0f..1.0f +SOKOL_DEBUGTEXT_API_DECL void sdtx_color1i(uint32_t rgba); // ABGR 0xAABBGGRR + +/* text rendering */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_putc(char c); +SOKOL_DEBUGTEXT_API_DECL void sdtx_puts(const char* str); // does NOT append newline! +SOKOL_DEBUGTEXT_API_DECL void sdtx_putr(const char* str, int len); // 'put range', also stops at zero-char +SOKOL_DEBUGTEXT_API_DECL int sdtx_printf(const char* fmt, ...) SOKOL_DEBUGTEXT_PRINTF_ATTR; +SOKOL_DEBUGTEXT_API_DECL int sdtx_vprintf(const char* fmt, va_list args); + +/* language bindings helper: get the internal printf format buffer */ +SOKOL_DEBUGTEXT_API_DECL sdtx_range sdtx_get_cleared_fmt_buffer(void); + +#ifdef __cplusplus +} /* extern "C" */ +/* C++ const-ref wrappers */ +inline void sdtx_setup(const sdtx_desc_t& desc) { return sdtx_setup(&desc); } +inline sdtx_context sdtx_make_context(const sdtx_context_desc_t& desc) { return sdtx_make_context(&desc); } +#endif +#endif /* SOKOL_DEBUGTEXT_INCLUDED */ + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>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 + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#ifndef SOKOL_VSNPRINTF +#include +#define SOKOL_VSNPRINTF vsnprintf +#endif + +#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 (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) +#define _SDTX_DEFAULT_COLOR (0xFF00FFFF) +#define _SDTX_INVALID_SLOT_INDEX (0) +#define _SDTX_SLOT_SHIFT (16) +#define _SDTX_MAX_POOL_SIZE (1<<_SDTX_SLOT_SHIFT) +#define _SDTX_SLOT_MASK (_SDTX_MAX_POOL_SIZE-1) + +/* embedded font data */ +static const uint8_t _sdtx_font_kc853[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xFF, // 00 + 0x00, 0x00, 0x22, 0x72, 0x22, 0x3E, 0x00, 0x00, // 01 + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x32, 0x12, 0x00, // 02 + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xB9, 0x81, // 03 + 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 04 + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 05 + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, // 06 + 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x00, 0x00, // 07 + 0x00, 0x10, 0x30, 0x7E, 0x30, 0x10, 0x00, 0x00, // 08 + 0x00, 0x08, 0x0C, 0x7E, 0x0C, 0x08, 0x00, 0x00, // 09 + 0x00, 0x10, 0x10, 0x10, 0x7C, 0x38, 0x10, 0x00, // 0A + 0x08, 0x1C, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x00, // 0B + 0x38, 0x30, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, // 0C + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x30, 0x10, 0x00, // 0D + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // 0E + 0x3E, 0x7C, 0x7C, 0x3E, 0x3E, 0x7C, 0xF8, 0xF8, // 0F + 0x38, 0x30, 0x28, 0x04, 0x04, 0x04, 0x04, 0x00, // 10 + 0x7F, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x00, // 11 + 0x00, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x7F, // 12 + 0x7E, 0x81, 0x9D, 0xA1, 0xB9, 0x85, 0x85, 0xB9, // 13 + 0x00, 0x3C, 0x42, 0x5A, 0x5A, 0x42, 0x3C, 0x00, // 14 + 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11, // 15 + 0x00, 0x7F, 0x22, 0x72, 0x27, 0x22, 0x7F, 0x00, // 16 + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, // 17 + 0x00, 0x01, 0x09, 0x0D, 0x7F, 0x0D, 0x09, 0x01, // 18 + 0x00, 0x90, 0xB0, 0xFE, 0xB0, 0x90, 0x00, 0x00, // 19 + 0x00, 0x08, 0x7C, 0x06, 0x7C, 0x08, 0x00, 0x00, // 1A + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // 1B + 0x7E, 0x81, 0xA1, 0xA1, 0xA1, 0xA1, 0xBD, 0x81, // 1C + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xA5, 0x81, // 1D + 0x7E, 0x81, 0x99, 0xA1, 0xA1, 0xA1, 0x99, 0x81, // 1E + 0x00, 0x10, 0x3E, 0x60, 0x3E, 0x10, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // 21 + 0x00, 0x66, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, // 22 + 0x00, 0x36, 0x7F, 0x36, 0x36, 0x7F, 0x36, 0x00, // 23 + 0x18, 0x3E, 0x6C, 0x3E, 0x1B, 0x1B, 0x7E, 0x18, // 24 + 0x00, 0x63, 0x66, 0x0C, 0x18, 0x36, 0x66, 0x00, // 25 + 0x18, 0x24, 0x28, 0x11, 0x2A, 0x44, 0x4A, 0x31, // 26 + 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // 27 + 0x00, 0x18, 0x30, 0x30, 0x30, 0x30, 0x18, 0x00, // 28 + 0x00, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x00, // 29 + 0x00, 0x00, 0x24, 0x18, 0x7E, 0x18, 0x24, 0x00, // 2A + 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, // 2C + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x00, // 2F + 0x00, 0x3C, 0x6E, 0x6E, 0x76, 0x76, 0x3C, 0x00, // 30 + 0x00, 0x1C, 0x3C, 0x0C, 0x0C, 0x0C, 0x3E, 0x00, // 31 + 0x00, 0x3C, 0x66, 0x06, 0x3C, 0x60, 0x7E, 0x00, // 32 + 0x00, 0x3C, 0x66, 0x0C, 0x06, 0x66, 0x3C, 0x00, // 33 + 0x00, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x00, // 34 + 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x66, 0x3C, 0x00, // 35 + 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 + 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, // 37 + 0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 + 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x3C, 0x00, // 39 + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // 3A + 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x30, 0x00, // 3B + 0x00, 0x00, 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, // 3C + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, // 3D + 0x00, 0x00, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x00, // 3E + 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x18, 0x00, 0x18, // 3F + 0x3C, 0x42, 0x81, 0x35, 0x49, 0x49, 0x49, 0x36, // 40 + 0x00, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00, // 41 + 0x00, 0x7C, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 42 + 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x00, // 43 + 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // 44 + 0x00, 0x7E, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, // 45 + 0x00, 0x7E, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, // 46 + 0x00, 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x3C, 0x00, // 47 + 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 + 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 49 + 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, // 4A + 0x00, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x63, 0x00, // 4B + 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // 4C + 0x00, 0x63, 0x77, 0x6B, 0x63, 0x63, 0x63, 0x00, // 4D + 0x00, 0x63, 0x73, 0x6B, 0x67, 0x63, 0x63, 0x00, // 4E + 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 4F + 0x00, 0x7C, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, // 50 + 0x00, 0x3C, 0x66, 0x66, 0x6E, 0x66, 0x3A, 0x01, // 51 + 0x00, 0x7C, 0x66, 0x7C, 0x6C, 0x66, 0x63, 0x00, // 52 + 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // 53 + 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 54 + 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 55 + 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 + 0x00, 0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, // 57 + 0x00, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0x00, // 58 + 0x00, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, // 59 + 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // 5A + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 5B + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 5C + 0x00, 0x7E, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, // 5D + 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x00, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 5F + 0x7E, 0x81, 0x99, 0xA1, 0xA1, 0x99, 0x81, 0x7E, // 60 + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3B, 0x00, // 61 + 0x00, 0x60, 0x60, 0x78, 0x6C, 0x6C, 0x78, 0x00, // 62 + 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, // 63 + 0x00, 0x06, 0x06, 0x1E, 0x36, 0x36, 0x1E, 0x00, // 64 + 0x00, 0x00, 0x38, 0x6C, 0x7C, 0x60, 0x38, 0x00, // 65 + 0x00, 0x1E, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x00, // 66 + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x3F, 0x06, 0x3C, // 67 + 0x00, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x00, // 68 + 0x00, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, // 69 + 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x30, // 6A + 0x00, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00, // 6B + 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x00, // 6C + 0x00, 0x00, 0x36, 0x7F, 0x6B, 0x63, 0x63, 0x00, // 6D + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // 70 + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x06, // 71 + 0x00, 0x00, 0x36, 0x38, 0x30, 0x30, 0x30, 0x00, // 72 + 0x00, 0x00, 0x1C, 0x30, 0x1C, 0x06, 0x3C, 0x00, // 73 + 0x00, 0x18, 0x18, 0x3C, 0x18, 0x18, 0x0C, 0x00, // 74 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 75 + 0x00, 0x00, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x00, // 76 + 0x00, 0x00, 0x63, 0x63, 0x6B, 0x7F, 0x36, 0x00, // 77 + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // 78 + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x30, 0x60, 0x00, // 79 + 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 7A + 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3B, 0x00, // 7B + 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 7C + 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 7D + 0x00, 0x38, 0x6C, 0x78, 0x6C, 0x78, 0x60, 0x60, // 7E + 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, // 7F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x00, // 80 + 0xFF, 0xFF, 0xDD, 0x8D, 0xDD, 0xC1, 0xFF, 0xFF, // 81 + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCD, 0xED, 0xFF, // 82 + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x46, 0x7E, // 83 + 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, // 84 + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // 85 + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, // 86 + 0xFF, 0xFF, 0xC3, 0xBD, 0xBD, 0x81, 0xFF, 0xFF, // 87 + 0xFF, 0xEF, 0xCF, 0x81, 0xCF, 0xEF, 0xFF, 0xFF, // 88 + 0xFF, 0xF7, 0xF3, 0x81, 0xF3, 0xF7, 0xFF, 0xFF, // 89 + 0xFF, 0xEF, 0xEF, 0xEF, 0x83, 0xC7, 0xEF, 0xFF, // 8A + 0xF7, 0xE3, 0xC1, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // 8B + 0xC7, 0xCF, 0xD7, 0xF7, 0xF7, 0xF7, 0xC1, 0xFF, // 8C + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCF, 0xEF, 0xFF, // 8D + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 8E + 0xC1, 0x83, 0x83, 0xC1, 0xC1, 0x83, 0x07, 0x07, // 8F + 0xC7, 0xCF, 0xD7, 0xFB, 0xFB, 0xFB, 0xFB, 0xFF, // 90 + 0x80, 0xF7, 0xE3, 0xD5, 0xF7, 0xF7, 0xF7, 0xFF, // 91 + 0xFF, 0xF7, 0xF7, 0xF7, 0xD5, 0xE3, 0xF7, 0x80, // 92 + 0x81, 0x7E, 0x62, 0x5E, 0x46, 0x7A, 0x7A, 0x46, // 93 + 0xFF, 0xC3, 0xBD, 0xA5, 0xA5, 0xBD, 0xC3, 0xFF, // 94 + 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, // 95 + 0xFF, 0x80, 0xDD, 0x8D, 0xD8, 0xDD, 0x80, 0xFF, // 96 + 0xEE, 0xDD, 0xBB, 0x77, 0xEE, 0xDD, 0xBB, 0x77, // 97 + 0xFF, 0xFE, 0xF6, 0xF2, 0x80, 0xF2, 0xF6, 0xFE, // 98 + 0xFF, 0x6F, 0x4F, 0x01, 0x4F, 0x6F, 0xFF, 0xFF, // 99 + 0xFF, 0xF7, 0x83, 0xF9, 0x83, 0xF7, 0xFF, 0xFF, // 9A + 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // 9B + 0x81, 0x7E, 0x5E, 0x5E, 0x5E, 0x5E, 0x42, 0x7E, // 9C + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x5A, 0x7E, // 9D + 0x81, 0x7E, 0x66, 0x5E, 0x5E, 0x5E, 0x66, 0x7E, // 9E + 0xFF, 0xEF, 0xC1, 0x9F, 0xC1, 0xEF, 0xFF, 0xFF, // 9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0 + 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xE7, 0xFF, // A1 + 0xFF, 0x99, 0x99, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xFF, 0xC9, 0x80, 0xC9, 0xC9, 0x80, 0xC9, 0xFF, // A3 + 0xE7, 0xC1, 0x93, 0xC1, 0xE4, 0xE4, 0x81, 0xE7, // A4 + 0xFF, 0x9C, 0x99, 0xF3, 0xE7, 0xC9, 0x99, 0xFF, // A5 + 0xE7, 0xDB, 0xD7, 0xEE, 0xD5, 0xBB, 0xB5, 0xCE, // A6 + 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, // A7 + 0xFF, 0xE7, 0xCF, 0xCF, 0xCF, 0xCF, 0xE7, 0xFF, // A8 + 0xFF, 0xE7, 0xF3, 0xF3, 0xF3, 0xF3, 0xE7, 0xFF, // A9 + 0xFF, 0xFF, 0xDB, 0xE7, 0x81, 0xE7, 0xDB, 0xFF, // AA + 0xFF, 0xFF, 0xE7, 0xE7, 0x81, 0xE7, 0xE7, 0xFF, // AB + 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, // AC + 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, // AD + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, // AE + 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3F, 0xFF, 0xFF, // AF + 0xFF, 0xC3, 0x91, 0x91, 0x89, 0x89, 0xC3, 0xFF, // B0 + 0xFF, 0xE3, 0xC3, 0xF3, 0xF3, 0xF3, 0xC1, 0xFF, // B1 + 0xFF, 0xC3, 0x99, 0xF9, 0xC3, 0x9F, 0x81, 0xFF, // B2 + 0xFF, 0xC3, 0x99, 0xF3, 0xF9, 0x99, 0xC3, 0xFF, // B3 + 0xFF, 0xC3, 0x93, 0x33, 0x01, 0xF3, 0xF3, 0xFF, // B4 + 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0x99, 0xC3, 0xFF, // B5 + 0xFF, 0xC3, 0x9F, 0x83, 0x99, 0x99, 0xC3, 0xFF, // B6 + 0xFF, 0x81, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0xFF, // B7 + 0xFF, 0xC3, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0xFF, // B8 + 0xFF, 0xC3, 0x99, 0x99, 0xC1, 0xF9, 0xC3, 0xFF, // B9 + 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xE7, 0xE7, 0xFF, // BA + 0xFF, 0xFF, 0xE7, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, // BB + 0xFF, 0xFF, 0xE7, 0xCF, 0x9F, 0xCF, 0xE7, 0xFF, // BC + 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xFF, // BD + 0xFF, 0xFF, 0xCF, 0xE7, 0xF3, 0xE7, 0xCF, 0xFF, // BE + 0xFF, 0xC3, 0x99, 0xF9, 0xE3, 0xE7, 0xFF, 0xE7, // BF + 0xC3, 0xBD, 0x7E, 0xCA, 0xB6, 0xB6, 0xB6, 0xC9, // C0 + 0xFF, 0xC3, 0x99, 0x99, 0x81, 0x99, 0x99, 0xFF, // C1 + 0xFF, 0x83, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFF, // C2 + 0xFF, 0xC3, 0x99, 0x9F, 0x9F, 0x99, 0xC3, 0xFF, // C3 + 0xFF, 0x83, 0x99, 0x99, 0x99, 0x99, 0x83, 0xFF, // C4 + 0xFF, 0x81, 0x9F, 0x83, 0x9F, 0x9F, 0x81, 0xFF, // C5 + 0xFF, 0x81, 0x9F, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, // C6 + 0xFF, 0xC3, 0x99, 0x9F, 0x91, 0x99, 0xC3, 0xFF, // C7 + 0xFF, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF, // C8 + 0xFF, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF, // C9 + 0xFF, 0xE1, 0xF3, 0xF3, 0xF3, 0x93, 0xC7, 0xFF, // CA + 0xFF, 0x99, 0x93, 0x87, 0x93, 0x99, 0x9C, 0xFF, // CB + 0xFF, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x81, 0xFF, // CC + 0xFF, 0x9C, 0x88, 0x94, 0x9C, 0x9C, 0x9C, 0xFF, // CD + 0xFF, 0x9C, 0x8C, 0x94, 0x98, 0x9C, 0x9C, 0xFF, // CE + 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // CF + 0xFF, 0x83, 0x99, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, // D0 + 0xFF, 0xC3, 0x99, 0x99, 0x91, 0x99, 0xC5, 0xFE, // D1 + 0xFF, 0x83, 0x99, 0x83, 0x93, 0x99, 0x9C, 0xFF, // D2 + 0xFF, 0xC3, 0x9F, 0xC3, 0xF9, 0x99, 0xC3, 0xFF, // D3 + 0xFF, 0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // D4 + 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // D5 + 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF, // D6 + 0xFF, 0x9C, 0x9C, 0x94, 0x94, 0x80, 0xC9, 0xFF, // D7 + 0xFF, 0x99, 0xC3, 0xE7, 0xE7, 0xC3, 0x99, 0xFF, // D8 + 0xFF, 0x99, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // D9 + 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x9F, 0x81, 0xFF, // DA + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DB + 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // DC + 0xFF, 0x81, 0xF9, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, // DD + 0xFF, 0xFF, 0xFF, 0xF7, 0xE3, 0xC9, 0xFF, 0xFF, // DE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // DF + 0x81, 0x7E, 0x66, 0x5E, 0x5E, 0x66, 0x7E, 0x81, // E0 + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC4, 0xFF, // E1 + 0xFF, 0x9F, 0x9F, 0x87, 0x93, 0x93, 0x87, 0xFF, // E2 + 0xFF, 0xFF, 0xC3, 0x99, 0x9F, 0x99, 0xC3, 0xFF, // E3 + 0xFF, 0xF9, 0xF9, 0xE1, 0xC9, 0xC9, 0xE1, 0xFF, // E4 + 0xFF, 0xFF, 0xC7, 0x93, 0x83, 0x9F, 0xC7, 0xFF, // E5 + 0xFF, 0xE1, 0xE7, 0x81, 0xE7, 0xE7, 0xE7, 0xFF, // E6 + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0xC0, 0xF9, 0xC3, // E7 + 0xFF, 0x9F, 0x9F, 0x93, 0x89, 0x99, 0x99, 0xFF, // E8 + 0xFF, 0xE7, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // E9 + 0xFF, 0xE7, 0xFF, 0xC7, 0xE7, 0xE7, 0xE7, 0xCF, // EA + 0xFF, 0x9F, 0x99, 0x93, 0x87, 0x93, 0x99, 0xFF, // EB + 0xFF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xE7, 0xFF, // EC + 0xFF, 0xFF, 0xC9, 0x80, 0x94, 0x9C, 0x9C, 0xFF, // ED + 0xFF, 0xFF, 0x83, 0x99, 0x99, 0x99, 0x99, 0xFF, // EE + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC3, 0xFF, // EF + 0xFF, 0xFF, 0x83, 0x99, 0x99, 0x83, 0x9F, 0x9F, // F0 + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0xC1, 0xF9, 0xF9, // F1 + 0xFF, 0xFF, 0xC9, 0xC7, 0xCF, 0xCF, 0xCF, 0xFF, // F2 + 0xFF, 0xFF, 0xE3, 0xCF, 0xE3, 0xF9, 0xC3, 0xFF, // F3 + 0xFF, 0xE7, 0xE7, 0xC3, 0xE7, 0xE7, 0xF3, 0xFF, // F4 + 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // F5 + 0xFF, 0xFF, 0x99, 0x99, 0xC3, 0xC3, 0xE7, 0xFF, // F6 + 0xFF, 0xFF, 0x9C, 0x9C, 0x94, 0x80, 0xC9, 0xFF, // F7 + 0xFF, 0xFF, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0xFF, // F8 + 0xFF, 0xFF, 0x99, 0xC3, 0xE7, 0xCF, 0x9F, 0xFF, // F9 + 0xFF, 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x81, 0xFF, // FA + 0x99, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC4, 0xFF, // FB + 0x99, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC3, 0xFF, // FC + 0x99, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // FD + 0xFF, 0xC7, 0x93, 0x87, 0x93, 0x87, 0x9F, 0x9F, // FE + 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, // FF +}; +static const uint8_t _sdtx_font_kc854[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xFF, // 00 + 0x00, 0x00, 0x22, 0x72, 0x22, 0x3E, 0x00, 0x00, // 01 + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x32, 0x12, 0x00, // 02 + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xB9, 0x81, // 03 + 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 04 + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 05 + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, // 06 + 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x00, 0x00, // 07 + 0x00, 0x10, 0x30, 0x7E, 0x30, 0x10, 0x00, 0x00, // 08 + 0x00, 0x08, 0x0C, 0x7E, 0x0C, 0x08, 0x00, 0x00, // 09 + 0x00, 0x10, 0x10, 0x10, 0x7C, 0x38, 0x10, 0x00, // 0A + 0x08, 0x1C, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x00, // 0B + 0x38, 0x30, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, // 0C + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x30, 0x10, 0x00, // 0D + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // 0E + 0x3E, 0x7C, 0x7C, 0x3E, 0x3E, 0x7C, 0xF8, 0xF8, // 0F + 0x38, 0x30, 0x28, 0x04, 0x04, 0x04, 0x04, 0x00, // 10 + 0x7F, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x00, // 11 + 0x00, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x7F, // 12 + 0x7E, 0x81, 0x9D, 0xA1, 0xB9, 0x85, 0x85, 0xB9, // 13 + 0x00, 0x3C, 0x42, 0x5A, 0x5A, 0x42, 0x3C, 0x00, // 14 + 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11, // 15 + 0x00, 0x7F, 0x22, 0x72, 0x27, 0x22, 0x7F, 0x00, // 16 + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, // 17 + 0x00, 0x01, 0x09, 0x0D, 0x7F, 0x0D, 0x09, 0x01, // 18 + 0x00, 0x90, 0xB0, 0xFE, 0xB0, 0x90, 0x00, 0x00, // 19 + 0x00, 0x08, 0x7C, 0x06, 0x7C, 0x08, 0x00, 0x00, // 1A + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // 1B + 0x7E, 0x81, 0xA1, 0xA1, 0xA1, 0xA1, 0xBD, 0x81, // 1C + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xA5, 0x81, // 1D + 0x7E, 0x81, 0x99, 0xA1, 0xA1, 0xA1, 0x99, 0x81, // 1E + 0x00, 0x10, 0x3E, 0x60, 0x3E, 0x10, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00, // 21 + 0x77, 0x33, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x36, 0x36, 0xFE, 0x6C, 0xFE, 0xD8, 0xD8, 0x00, // 23 + 0x18, 0x3E, 0x6C, 0x3E, 0x1B, 0x1B, 0x7E, 0x18, // 24 + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, // 25 + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, // 26 + 0x1C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, // 28 + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, // 29 + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A + 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0C, 0x18, // 2C + 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, // 2E + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, // 2F + 0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00, // 30 + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, // 31 + 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, // 32 + 0xFC, 0x18, 0x30, 0x78, 0x0C, 0xCC, 0x78, 0x00, // 33 + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, // 34 + 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, // 35 + 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, // 36 + 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, // 37 + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, // 38 + 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, // 39 + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, // 3A + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, // 3B + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, // 3C + 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00, // 3D + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // 3E + 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00, // 3F + 0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x78, 0x00, // 40 + 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, // 41 + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, // 42 + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, // 43 + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, // 44 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, // 45 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, // 46 + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3C, 0x00, // 47 + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, // 48 + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // 49 + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, // 4A + 0xE6, 0x66, 0x6C, 0x70, 0x6C, 0x66, 0xE6, 0x00, // 4B + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, // 4C + 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00, // 4D + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, // 4E + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, // 4F + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, // 50 + 0x78, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x1C, 0x00, // 51 + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, // 52 + 0x7C, 0xC6, 0xF0, 0x3C, 0x0E, 0xC6, 0x7C, 0x00, // 53 + 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // 54 + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // 55 + 0xCC, 0xCC, 0xCC, 0x78, 0x78, 0x30, 0x30, 0x00, // 56 + 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, // 57 + 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, // 58 + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, // 59 + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, // 5A + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 5B + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 5C + 0x00, 0xFE, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, // 5D + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 5F + 0x3C, 0x42, 0x99, 0xA1, 0xA1, 0x99, 0x42, 0x3C, // 60 + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // 61 + 0xE0, 0x60, 0x7C, 0x66, 0x66, 0x66, 0xDC, 0x00, // 62 + 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, // 63 + 0x1C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 64 + 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, // 65 + 0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xF0, 0x00, // 66 + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // 67 + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, // 68 + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0xFC, 0x00, // 69 + 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, // 6A + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, // 6B + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, // 6C + 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // 6D + 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // 6E + 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // 6F + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, // 70 + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, // 71 + 0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0xF0, 0x00, // 72 + 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x00, // 73 + 0x10, 0x30, 0x7C, 0x30, 0x30, 0x34, 0x18, 0x00, // 74 + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 75 + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, // 76 + 0x00, 0x00, 0xC6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00, // 77 + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, // 78 + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // 79 + 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, // 7A + 0x6C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // 7B + 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // 7C + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 7D + 0x3C, 0x66, 0x66, 0x6C, 0x66, 0x66, 0x6C, 0xF0, // 7E + 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, // 7F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x00, // 80 + 0xFF, 0xFF, 0xDD, 0x8D, 0xDD, 0xC1, 0xFF, 0xFF, // 81 + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCD, 0xED, 0xFF, // 82 + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x46, 0x7E, // 83 + 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, // 84 + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // 85 + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, // 86 + 0xFF, 0xFF, 0xC3, 0xBD, 0xBD, 0x81, 0xFF, 0xFF, // 87 + 0xFF, 0xEF, 0xCF, 0x81, 0xCF, 0xEF, 0xFF, 0xFF, // 88 + 0xFF, 0xF7, 0xF3, 0x81, 0xF3, 0xF7, 0xFF, 0xFF, // 89 + 0xFF, 0xEF, 0xEF, 0xEF, 0x83, 0xC7, 0xEF, 0xFF, // 8A + 0xF7, 0xE3, 0xC1, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // 8B + 0xC7, 0xCF, 0xD7, 0xF7, 0xF7, 0xF7, 0xC1, 0xFF, // 8C + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCF, 0xEF, 0xFF, // 8D + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 8E + 0xC1, 0x83, 0x83, 0xC1, 0xC1, 0x83, 0x07, 0x07, // 8F + 0xC7, 0xCF, 0xD7, 0xFB, 0xFB, 0xFB, 0xFB, 0xFF, // 90 + 0x80, 0xF7, 0xE3, 0xD5, 0xF7, 0xF7, 0xF7, 0xFF, // 91 + 0xFF, 0xF7, 0xF7, 0xF7, 0xD5, 0xE3, 0xF7, 0x80, // 92 + 0x81, 0x7E, 0x62, 0x5E, 0x46, 0x7A, 0x7A, 0x46, // 93 + 0xFF, 0xC3, 0xBD, 0xA5, 0xA5, 0xBD, 0xC3, 0xFF, // 94 + 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, // 95 + 0xFF, 0x80, 0xDD, 0x8D, 0xD8, 0xDD, 0x80, 0xFF, // 96 + 0xEE, 0xDD, 0xBB, 0x77, 0xEE, 0xDD, 0xBB, 0x77, // 97 + 0xFF, 0xFE, 0xF6, 0xF2, 0x80, 0xF2, 0xF6, 0xFE, // 98 + 0xFF, 0x6F, 0x4F, 0x01, 0x4F, 0x6F, 0xFF, 0xFF, // 99 + 0xFF, 0xF7, 0x83, 0xF9, 0x83, 0xF7, 0xFF, 0xFF, // 9A + 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // 9B + 0x81, 0x7E, 0x5E, 0x5E, 0x5E, 0x5E, 0x42, 0x7E, // 9C + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x5A, 0x7E, // 9D + 0x81, 0x7E, 0x66, 0x5E, 0x5E, 0x5E, 0x66, 0x7E, // 9E + 0xFF, 0xEF, 0xC1, 0x9F, 0xC1, 0xEF, 0xFF, 0xFF, // 9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0 + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xFF, 0xCF, 0xFF, // A1 + 0x88, 0xCC, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xC9, 0xC9, 0x01, 0x93, 0x01, 0x27, 0x27, 0xFF, // A3 + 0xE7, 0xC1, 0x93, 0xC1, 0xE4, 0xE4, 0x81, 0xE7, // A4 + 0xFF, 0x39, 0x33, 0xE7, 0xCF, 0x99, 0x39, 0xFF, // A5 + 0xC7, 0x93, 0xC7, 0x89, 0x23, 0x33, 0x89, 0xFF, // A6 + 0xE3, 0xF3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A7 + 0xE7, 0xCF, 0x9F, 0x9F, 0x9F, 0xCF, 0xE7, 0xFF, // A8 + 0x9F, 0xCF, 0xE7, 0xE7, 0xE7, 0xCF, 0x9F, 0xFF, // A9 + 0xFF, 0x99, 0xC3, 0x00, 0xC3, 0x99, 0xFF, 0xFF, // AA + 0xFF, 0xCF, 0xCF, 0x03, 0xCF, 0xCF, 0xFF, 0xFF, // AB + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xF3, 0xE7, // AC + 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // AD + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xCF, 0xFF, // AE + 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3F, 0x7F, 0xFF, // AF + 0x83, 0x39, 0x31, 0x21, 0x09, 0x19, 0x83, 0xFF, // B0 + 0xCF, 0x8F, 0xCF, 0xCF, 0xCF, 0xCF, 0x03, 0xFF, // B1 + 0x87, 0x33, 0xF3, 0xC7, 0x9F, 0x33, 0x03, 0xFF, // B2 + 0x03, 0xE7, 0xCF, 0x87, 0xF3, 0x33, 0x87, 0xFF, // B3 + 0xE3, 0xC3, 0x93, 0x33, 0x01, 0xF3, 0xE1, 0xFF, // B4 + 0x03, 0x3F, 0x07, 0xF3, 0xF3, 0x33, 0x87, 0xFF, // B5 + 0xC7, 0x9F, 0x3F, 0x07, 0x33, 0x33, 0x87, 0xFF, // B6 + 0x03, 0x33, 0xF3, 0xE7, 0xCF, 0xCF, 0xCF, 0xFF, // B7 + 0x87, 0x33, 0x33, 0x87, 0x33, 0x33, 0x87, 0xFF, // B8 + 0x87, 0x33, 0x33, 0x83, 0xF3, 0xE7, 0x8F, 0xFF, // B9 + 0xFF, 0xFF, 0xCF, 0xCF, 0xFF, 0xCF, 0xCF, 0xFF, // BA + 0xFF, 0xFF, 0xCF, 0xCF, 0xFF, 0xCF, 0xCF, 0x9F, // BB + 0xE7, 0xCF, 0x9F, 0x3F, 0x9F, 0xCF, 0xE7, 0xFF, // BC + 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, // BD + 0x9F, 0xCF, 0xE7, 0xF3, 0xE7, 0xCF, 0x9F, 0xFF, // BE + 0x87, 0x33, 0xF3, 0xE7, 0xCF, 0xFF, 0xCF, 0xFF, // BF + 0x83, 0x39, 0x21, 0x21, 0x21, 0x3F, 0x87, 0xFF, // C0 + 0xCF, 0x87, 0x33, 0x33, 0x03, 0x33, 0x33, 0xFF, // C1 + 0x03, 0x99, 0x99, 0x83, 0x99, 0x99, 0x03, 0xFF, // C2 + 0xC3, 0x99, 0x3F, 0x3F, 0x3F, 0x99, 0xC3, 0xFF, // C3 + 0x07, 0x93, 0x99, 0x99, 0x99, 0x93, 0x07, 0xFF, // C4 + 0x01, 0x9D, 0x97, 0x87, 0x97, 0x9D, 0x01, 0xFF, // C5 + 0x01, 0x9D, 0x97, 0x87, 0x97, 0x9F, 0x0F, 0xFF, // C6 + 0xC3, 0x99, 0x3F, 0x3F, 0x31, 0x99, 0xC3, 0xFF, // C7 + 0x33, 0x33, 0x33, 0x03, 0x33, 0x33, 0x33, 0xFF, // C8 + 0x87, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0x87, 0xFF, // C9 + 0xE1, 0xF3, 0xF3, 0xF3, 0x33, 0x33, 0x87, 0xFF, // CA + 0x19, 0x99, 0x93, 0x8F, 0x93, 0x99, 0x19, 0xFF, // CB + 0x0F, 0x9F, 0x9F, 0x9F, 0x9D, 0x99, 0x01, 0xFF, // CC + 0x39, 0x11, 0x01, 0x29, 0x39, 0x39, 0x39, 0xFF, // CD + 0x39, 0x19, 0x09, 0x21, 0x31, 0x39, 0x39, 0xFF, // CE + 0xC7, 0x93, 0x39, 0x39, 0x39, 0x93, 0xC7, 0xFF, // CF + 0x03, 0x99, 0x99, 0x83, 0x9F, 0x9F, 0x0F, 0xFF, // D0 + 0x87, 0x33, 0x33, 0x33, 0x23, 0x87, 0xE3, 0xFF, // D1 + 0x03, 0x99, 0x99, 0x83, 0x93, 0x99, 0x19, 0xFF, // D2 + 0x83, 0x39, 0x0F, 0xC3, 0xF1, 0x39, 0x83, 0xFF, // D3 + 0x03, 0x4B, 0xCF, 0xCF, 0xCF, 0xCF, 0x87, 0xFF, // D4 + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x87, 0xFF, // D5 + 0x33, 0x33, 0x33, 0x87, 0x87, 0xCF, 0xCF, 0xFF, // D6 + 0x39, 0x39, 0x39, 0x29, 0x01, 0x11, 0x39, 0xFF, // D7 + 0x39, 0x39, 0x93, 0xC7, 0x93, 0x39, 0x39, 0xFF, // D8 + 0x33, 0x33, 0x33, 0x87, 0xCF, 0xCF, 0x87, 0xFF, // D9 + 0x01, 0x39, 0x73, 0xE7, 0xCD, 0x99, 0x01, 0xFF, // DA + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DB + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // DC + 0xFF, 0x01, 0xF9, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, // DD + 0xEF, 0xC7, 0x93, 0x39, 0xFF, 0xFF, 0xFF, 0xFF, // DE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // DF + 0xC3, 0xBD, 0x66, 0x5E, 0x5E, 0x66, 0xBD, 0xC3, // E0 + 0xFF, 0xFF, 0x87, 0xF3, 0x83, 0x33, 0x89, 0xFF, // E1 + 0x1F, 0x9F, 0x83, 0x99, 0x99, 0x99, 0x23, 0xFF, // E2 + 0xFF, 0xFF, 0x87, 0x33, 0x3F, 0x33, 0x87, 0xFF, // E3 + 0xE3, 0xF3, 0x83, 0x33, 0x33, 0x33, 0x89, 0xFF, // E4 + 0xFF, 0xFF, 0x87, 0x33, 0x03, 0x3F, 0x87, 0xFF, // E5 + 0xC7, 0x93, 0x9F, 0x0F, 0x9F, 0x9F, 0x0F, 0xFF, // E6 + 0xFF, 0xFF, 0x89, 0x33, 0x33, 0x83, 0xF3, 0x07, // E7 + 0x1F, 0x9F, 0x93, 0x89, 0x99, 0x99, 0x19, 0xFF, // E8 + 0xCF, 0xFF, 0x8F, 0xCF, 0xCF, 0xCF, 0x03, 0xFF, // E9 + 0xF3, 0xFF, 0xE3, 0xF3, 0xF3, 0x33, 0x33, 0x87, // EA + 0x1F, 0x9F, 0x99, 0x93, 0x87, 0x93, 0x19, 0xFF, // EB + 0x8F, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0x03, 0xFF, // EC + 0xFF, 0xFF, 0x33, 0x01, 0x01, 0x29, 0x39, 0xFF, // ED + 0xFF, 0xFF, 0x07, 0x33, 0x33, 0x33, 0x33, 0xFF, // EE + 0xFF, 0xFF, 0x87, 0x33, 0x33, 0x33, 0x87, 0xFF, // EF + 0xFF, 0xFF, 0x23, 0x99, 0x99, 0x83, 0x9F, 0x0F, // F0 + 0xFF, 0xFF, 0x89, 0x33, 0x33, 0x83, 0xF3, 0xE1, // F1 + 0xFF, 0xFF, 0x23, 0x89, 0x99, 0x9F, 0x0F, 0xFF, // F2 + 0xFF, 0xFF, 0x83, 0x3F, 0x87, 0xF3, 0x07, 0xFF, // F3 + 0xEF, 0xCF, 0x83, 0xCF, 0xCF, 0xCB, 0xE7, 0xFF, // F4 + 0xFF, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x89, 0xFF, // F5 + 0xFF, 0xFF, 0x33, 0x33, 0x33, 0x87, 0xCF, 0xFF, // F6 + 0xFF, 0xFF, 0x39, 0x29, 0x01, 0x01, 0x93, 0xFF, // F7 + 0xFF, 0xFF, 0x39, 0x93, 0xC7, 0x93, 0x39, 0xFF, // F8 + 0xFF, 0xFF, 0x33, 0x33, 0x33, 0x83, 0xF3, 0x07, // F9 + 0xFF, 0xFF, 0x03, 0x67, 0xCF, 0x9B, 0x03, 0xFF, // FA + 0x93, 0xFF, 0x87, 0xF3, 0x83, 0x33, 0x89, 0xFF, // FB + 0x33, 0xFF, 0x87, 0x33, 0x33, 0x33, 0x87, 0xFF, // FC + 0x33, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x89, 0xFF, // FD + 0xC3, 0x99, 0x99, 0x93, 0x99, 0x99, 0x93, 0x0F, // FE + 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, // FF +}; +static const uint8_t _sdtx_font_z1013[2048] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 00 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 01 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 02 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 03 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 04 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 05 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 06 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 07 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 08 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 09 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0A + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0B + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0C + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0D + 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x24, 0x42, // 0E + 0xDB, 0xA5, 0x81, 0xFF, 0x24, 0x24, 0x24, 0x42, // 0F + 0x08, 0x34, 0x42, 0x81, 0x91, 0x69, 0x09, 0x31, // 10 + 0x42, 0x7E, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, // 11 + 0x18, 0x24, 0x42, 0x99, 0xBD, 0x99, 0x42, 0x24, // 12 + 0x7E, 0x42, 0x99, 0xE7, 0x00, 0x00, 0x00, 0x00, // 13 + 0x18, 0xDB, 0xC3, 0x18, 0x99, 0xE7, 0x81, 0x42, // 14 + 0x18, 0x24, 0x18, 0xC3, 0xBD, 0x81, 0x81, 0x42, // 15 + 0x24, 0x7E, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, // 16 + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x3C, 0x7E, // 17 + 0xDB, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x7E, // 18 + 0x08, 0x3C, 0x7E, 0xFF, 0xFF, 0x6F, 0x0F, 0x3F, // 19 + 0x7E, 0x7E, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 1A + 0x18, 0x3C, 0x7E, 0xE7, 0xC3, 0xE7, 0x7E, 0x3C, // 1B + 0x7E, 0x7E, 0xFF, 0xE7, 0x00, 0x00, 0x00, 0x00, // 1C + 0x18, 0xDB, 0xC3, 0x18, 0x99, 0xFF, 0xFF, 0x7E, // 1D + 0x18, 0x3C, 0x18, 0xC3, 0xFF, 0xFF, 0xFF, 0x7E, // 1E + 0x3C, 0x3C, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, // 21 + 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x24, 0x7E, 0x24, 0x24, 0x24, 0x7E, 0x24, 0x00, // 23 + 0x10, 0x3C, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00, // 24 + 0x60, 0x64, 0x08, 0x10, 0x20, 0x4C, 0x0C, 0x00, // 25 + 0x10, 0x28, 0x28, 0x30, 0x54, 0x48, 0x34, 0x00, // 26 + 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // 28 + 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00, // 29 + 0x00, 0x10, 0x54, 0x38, 0x54, 0x10, 0x00, 0x00, // 2A + 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, // 2C + 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, // 2E + 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, // 2F + 0x38, 0x44, 0x44, 0x54, 0x44, 0x44, 0x38, 0x00, // 30 + 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, // 31 + 0x38, 0x44, 0x04, 0x08, 0x10, 0x20, 0x7C, 0x00, // 32 + 0x7C, 0x08, 0x10, 0x08, 0x04, 0x44, 0x38, 0x00, // 33 + 0x08, 0x18, 0x28, 0x48, 0x7C, 0x08, 0x08, 0x00, // 34 + 0x7C, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, // 35 + 0x18, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, // 36 + 0x7C, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00, // 37 + 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, // 38 + 0x38, 0x44, 0x44, 0x3C, 0x04, 0x08, 0x30, 0x00, // 39 + 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, // 3A + 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00, // 3B + 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, // 3C + 0x00, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x00, 0x00, // 3D + 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, // 3E + 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, // 3F + 0x38, 0x44, 0x5C, 0x54, 0x5C, 0x40, 0x3C, 0x00, // 40 + 0x38, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00, // 41 + 0x78, 0x24, 0x24, 0x38, 0x24, 0x24, 0x78, 0x00, // 42 + 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, // 43 + 0x78, 0x24, 0x24, 0x24, 0x24, 0x24, 0x78, 0x00, // 44 + 0x7C, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7C, 0x00, // 45 + 0x7C, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00, // 46 + 0x38, 0x44, 0x40, 0x40, 0x4C, 0x44, 0x3C, 0x00, // 47 + 0x44, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00, // 48 + 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, // 49 + 0x1C, 0x08, 0x08, 0x08, 0x08, 0x48, 0x30, 0x00, // 4A + 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00, // 4B + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x00, // 4C + 0x44, 0x6C, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00, // 4D + 0x44, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x44, 0x00, // 4E + 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, // 4F + 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00, // 50 + 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00, // 51 + 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00, // 52 + 0x3C, 0x40, 0x40, 0x38, 0x04, 0x04, 0x78, 0x00, // 53 + 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, // 54 + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, // 55 + 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, // 56 + 0x44, 0x44, 0x44, 0x54, 0x54, 0x6C, 0x44, 0x00, // 57 + 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00, // 58 + 0x44, 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, // 59 + 0x7C, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7C, 0x00, // 5A + 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, // 5B + 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, // 5C + 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, // 5D + 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, // 5F + 0x00, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, // 60 + 0x00, 0x00, 0x34, 0x4C, 0x44, 0x44, 0x3A, 0x00, // 61 + 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x78, 0x00, // 62 + 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, // 63 + 0x04, 0x04, 0x34, 0x4C, 0x44, 0x44, 0x3A, 0x00, // 64 + 0x00, 0x00, 0x38, 0x44, 0x7C, 0x40, 0x38, 0x00, // 65 + 0x08, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x00, // 66 + 0x00, 0x00, 0x34, 0x4C, 0x44, 0x3C, 0x04, 0x38, // 67 + 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, // 68 + 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, // 69 + 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, // 6A + 0x40, 0x40, 0x48, 0x50, 0x70, 0x48, 0x44, 0x00, // 6B + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, // 6C + 0x00, 0x00, 0x68, 0x54, 0x54, 0x54, 0x54, 0x00, // 6D + 0x00, 0x00, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, // 6E + 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, // 6F + 0x00, 0x00, 0x58, 0x64, 0x44, 0x78, 0x40, 0x40, // 70 + 0x00, 0x00, 0x34, 0x4C, 0x44, 0x3C, 0x04, 0x04, // 71 + 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, // 72 + 0x00, 0x00, 0x38, 0x40, 0x38, 0x04, 0x78, 0x00, // 73 + 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x08, 0x00, // 74 + 0x00, 0x00, 0x44, 0x44, 0x44, 0x4C, 0x34, 0x00, // 75 + 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, // 76 + 0x00, 0x00, 0x54, 0x54, 0x54, 0x54, 0x28, 0x00, // 77 + 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, // 78 + 0x00, 0x00, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x38, // 79 + 0x00, 0x00, 0x7C, 0x08, 0x10, 0x20, 0x7C, 0x00, // 7A + 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, // 7B + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, // 7C + 0x20, 0x10, 0x10, 0x08, 0x10, 0x10, 0x20, 0x00, // 7D + 0x00, 0x00, 0x00, 0x32, 0x4C, 0x00, 0x00, 0x00, // 7E + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 7F + 0xC0, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0xC0, // 80 + 0x03, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, // 81 + 0x81, 0x81, 0x42, 0x3C, 0x00, 0x00, 0x00, 0x00, // 82 + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x81, 0x81, // 83 + 0x10, 0x10, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, // 84 + 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, // 85 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x08, // 86 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x10, 0x10, // 87 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, // 88 + 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 89 + 0x00, 0x10, 0x28, 0x44, 0x82, 0x44, 0x28, 0x10, // 8A + 0xFF, 0xEF, 0xC7, 0x83, 0x01, 0x83, 0xC7, 0xEF, // 8B + 0x3C, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3C, // 8C + 0xC3, 0x81, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC3, // 8D + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, // 8E + 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, // 8F + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, // 90 + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, // 91 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x30, 0xC0, // 92 + 0x03, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, // 93 + 0x03, 0x0C, 0x30, 0xC0, 0xC0, 0x30, 0x0C, 0x03, // 94 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x30, 0x0C, 0x03, // 95 + 0xC0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, // 96 + 0xC0, 0x30, 0x0C, 0x03, 0x03, 0x0C, 0x30, 0xC0, // 97 + 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, // 98 + 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, // 99 + 0x81, 0x81, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18, // 9A + 0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, // 9B + 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01, // 9C + 0x18, 0x18, 0x24, 0x24, 0x42, 0x42, 0x81, 0x81, // 9D + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9E + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 9F + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // A0 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // A1 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // A2 + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // A3 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // A4 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // A5 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // A6 + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // A7 + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // A8 + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // A9 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // AA + 0x80, 0x80, 0x80, 0x40, 0x40, 0x20, 0x18, 0x07, // AB + 0x01, 0x01, 0x01, 0x02, 0x02, 0x04, 0x18, 0xE0, // AC + 0xE0, 0x18, 0x04, 0x02, 0x02, 0x01, 0x01, 0x01, // AD + 0x07, 0x18, 0x20, 0x40, 0x40, 0x80, 0x80, 0x80, // AE + 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, // AF + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // B0 + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // B1 + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // B2 + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // B3 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // B4 + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // B5 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // B6 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // B7 + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // B8 + 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // B9 + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, // BA + 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, // BB + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, // BC + 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, // BD + 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, // BE + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // BF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // C0 + 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // C1 + 0xFF, 0x80, 0x80, 0x9C, 0x9C, 0x9C, 0x80, 0x80, // C2 + 0xFF, 0xFF, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0xFF, // C3 + 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x3C, 0x7E, 0xFF, // C4 + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, // C5 + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // C6 + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // C7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, // C8 + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, // C9 + 0x38, 0x10, 0x92, 0xFE, 0x92, 0x10, 0x38, 0x7C, // CA + 0x00, 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, // CB + 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x7C, 0x10, 0x7C, // CC + 0xE7, 0xE7, 0x42, 0xFF, 0xFF, 0x42, 0xE7, 0xE7, // CD + 0xDB, 0xFF, 0xDB, 0x18, 0x18, 0xDB, 0xFF, 0xDB, // CE + 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, // CF + 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D0 + 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D1 + 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D2 + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D3 + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, // D4 + 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, // D5 + 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, // D6 + 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, // D7 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, // D8 + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, // D9 + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, // DA + 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, // DB + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, // DC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, // DD + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, // DE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, // DF + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, // E0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3F, // E1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // E2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, // E3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, // E4 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, // E5 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, // E6 + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // E7 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // E8 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, // E9 + 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, // EA + 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EB + 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EC + 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ED + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EE + 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EF + 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // F0 + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // F1 + 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, // F2 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, // F3 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // F4 + 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // F5 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, // F6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, // F7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // F8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // F9 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // FA + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // FB + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FC + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FD + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FF +}; +static const uint8_t _sdtx_font_cpc[2048] = { + 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, // 00 + 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 01 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, // 02 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, // 03 + 0x0C, 0x18, 0x30, 0x7E, 0x0C, 0x18, 0x30, 0x00, // 04 + 0xFF, 0xC3, 0xE7, 0xDB, 0xDB, 0xE7, 0xC3, 0xFF, // 05 + 0x00, 0x01, 0x03, 0x06, 0xCC, 0x78, 0x30, 0x00, // 06 + 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0x24, 0xE7, 0x00, // 07 + 0x00, 0x00, 0x30, 0x60, 0xFF, 0x60, 0x30, 0x00, // 08 + 0x00, 0x00, 0x0C, 0x06, 0xFF, 0x06, 0x0C, 0x00, // 09 + 0x18, 0x18, 0x18, 0x18, 0xDB, 0x7E, 0x3C, 0x18, // 0A + 0x18, 0x3C, 0x7E, 0xDB, 0x18, 0x18, 0x18, 0x18, // 0B + 0x18, 0x5A, 0x3C, 0x99, 0xDB, 0x7E, 0x3C, 0x18, // 0C + 0x00, 0x03, 0x33, 0x63, 0xFE, 0x60, 0x30, 0x00, // 0D + 0x3C, 0x66, 0xFF, 0xDB, 0xDB, 0xFF, 0x66, 0x3C, // 0E + 0x3C, 0x66, 0xC3, 0xDB, 0xDB, 0xC3, 0x66, 0x3C, // 0F + 0xFF, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, // 10 + 0x3C, 0x7E, 0xDB, 0xDB, 0xDF, 0xC3, 0x66, 0x3C, // 11 + 0x3C, 0x66, 0xC3, 0xDF, 0xDB, 0xDB, 0x7E, 0x3C, // 12 + 0x3C, 0x66, 0xC3, 0xFB, 0xDB, 0xDB, 0x7E, 0x3C, // 13 + 0x3C, 0x7E, 0xDB, 0xDB, 0xFB, 0xC3, 0x66, 0x3C, // 14 + 0x00, 0x01, 0x33, 0x1E, 0xCE, 0x7B, 0x31, 0x00, // 15 + 0x7E, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xE7, // 16 + 0x03, 0x03, 0x03, 0xFF, 0x03, 0x03, 0x03, 0x00, // 17 + 0xFF, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0xFF, // 18 + 0x18, 0x18, 0x3C, 0x3C, 0x3C, 0x3C, 0x18, 0x18, // 19 + 0x3C, 0x66, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // 1A + 0x3C, 0x66, 0xC3, 0xFF, 0xC3, 0xC3, 0x66, 0x3C, // 1B + 0xFF, 0xDB, 0xDB, 0xDB, 0xFB, 0xC3, 0xC3, 0xFF, // 1C + 0xFF, 0xC3, 0xC3, 0xFB, 0xDB, 0xDB, 0xDB, 0xFF, // 1D + 0xFF, 0xC3, 0xC3, 0xDF, 0xDB, 0xDB, 0xDB, 0xFF, // 1E + 0xFF, 0xDB, 0xDB, 0xDB, 0xDF, 0xC3, 0xC3, 0xFF, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // 21 + 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, // 23 + 0x18, 0x3E, 0x58, 0x3C, 0x1A, 0x7C, 0x18, 0x00, // 24 + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, // 25 + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, // 26 + 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, // 28 + 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, // 29 + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // 2C + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, // 2F + 0x7C, 0xC6, 0xCE, 0xD6, 0xE6, 0xC6, 0x7C, 0x00, // 30 + 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 31 + 0x3C, 0x66, 0x06, 0x3C, 0x60, 0x66, 0x7E, 0x00, // 32 + 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, // 33 + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, // 34 + 0x7E, 0x62, 0x60, 0x7C, 0x06, 0x66, 0x3C, 0x00, // 35 + 0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 + 0x7E, 0x66, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x00, // 37 + 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 + 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, // 39 + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // 3A + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, // 3B + 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, // 3C + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, // 3D + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // 3E + 0x3C, 0x66, 0x66, 0x0C, 0x18, 0x00, 0x18, 0x00, // 3F + 0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x7C, 0x00, // 40 + 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00, // 41 + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, // 42 + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, // 43 + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, // 44 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, // 45 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, // 46 + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3E, 0x00, // 47 + 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 + 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 49 + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, // 4A + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, // 4B + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, // 4C + 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, // 4D + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, // 4E + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, // 4F + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, // 50 + 0x38, 0x6C, 0xC6, 0xC6, 0xDA, 0xCC, 0x76, 0x00, // 51 + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, // 52 + 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // 53 + 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 54 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 55 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 + 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, // 57 + 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0xC6, 0x00, // 58 + 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x00, // 59 + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, // 5A + 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, // 5B + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, // 5C + 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, // 5D + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 5F + 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, // 60 + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // 61 + 0xE0, 0x60, 0x7C, 0x66, 0x66, 0x66, 0xDC, 0x00, // 62 + 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, // 63 + 0x1C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 64 + 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // 65 + 0x1C, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00, // 66 + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 67 + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, // 68 + 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // 69 + 0x06, 0x00, 0x0E, 0x06, 0x06, 0x66, 0x66, 0x3C, // 6A + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, // 6B + 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 6C + 0x00, 0x00, 0x6C, 0xFE, 0xD6, 0xD6, 0xC6, 0x00, // 6D + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, // 70 + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, // 71 + 0x00, 0x00, 0xDC, 0x76, 0x60, 0x60, 0xF0, 0x00, // 72 + 0x00, 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x7C, 0x00, // 73 + 0x30, 0x30, 0x7C, 0x30, 0x30, 0x36, 0x1C, 0x00, // 74 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // 75 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 76 + 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00, // 77 + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, // 78 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 79 + 0x00, 0x00, 0x7E, 0x4C, 0x18, 0x32, 0x7E, 0x00, // 7A + 0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00, // 7B + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 7C + 0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00, // 7D + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7E + 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, // 7F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // 81 + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // 82 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 83 + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // 84 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // 85 + 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // 86 + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, // 87 + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // 88 + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // 89 + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // 8A + 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, // 8B + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 8C + 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, // 8D + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, // 8E + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 8F + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, // 90 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, // 91 + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, // 92 + 0x18, 0x18, 0x18, 0x1F, 0x0F, 0x00, 0x00, 0x00, // 93 + 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, // 94 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 95 + 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x18, 0x18, 0x18, // 96 + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 97 + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, // 98 + 0x18, 0x18, 0x18, 0xF8, 0xF0, 0x00, 0x00, 0x00, // 99 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 9A + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 9B + 0x00, 0x00, 0x00, 0xF0, 0xF8, 0x18, 0x18, 0x18, // 9C + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 9D + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 9E + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 9F + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, // A0 + 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // A1 + 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A2 + 0x3C, 0x66, 0x60, 0xF8, 0x60, 0x66, 0xFE, 0x00, // A3 + 0x38, 0x44, 0xBA, 0xA2, 0xBA, 0x44, 0x38, 0x00, // A4 + 0x7E, 0xF4, 0xF4, 0x74, 0x34, 0x34, 0x34, 0x00, // A5 + 0x1E, 0x30, 0x38, 0x6C, 0x38, 0x18, 0xF0, 0x00, // A6 + 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, // A7 + 0x40, 0xC0, 0x44, 0x4C, 0x54, 0x1E, 0x04, 0x00, // A8 + 0x40, 0xC0, 0x4C, 0x52, 0x44, 0x08, 0x1E, 0x00, // A9 + 0xE0, 0x10, 0x62, 0x16, 0xEA, 0x0F, 0x02, 0x00, // AA + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x7E, 0x00, // AB + 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, // AC + 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x00, 0x00, // AD + 0x18, 0x00, 0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, // AE + 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // AF + 0x00, 0x00, 0x73, 0xDE, 0xCC, 0xDE, 0x73, 0x00, // B0 + 0x7C, 0xC6, 0xC6, 0xFC, 0xC6, 0xC6, 0xF8, 0xC0, // B1 + 0x00, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // B2 + 0x3C, 0x60, 0x60, 0x3C, 0x66, 0x66, 0x3C, 0x00, // B3 + 0x00, 0x00, 0x1E, 0x30, 0x7C, 0x30, 0x1E, 0x00, // B4 + 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, // B5 + 0x00, 0xC0, 0x60, 0x30, 0x38, 0x6C, 0xC6, 0x00, // B6 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, // B7 + 0x00, 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x00, // B8 + 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0x70, 0x00, // B9 + 0x03, 0x06, 0x0C, 0x3C, 0x66, 0x3C, 0x60, 0xC0, // BA + 0x03, 0x06, 0x0C, 0x66, 0x66, 0x3C, 0x60, 0xC0, // BB + 0x00, 0xE6, 0x3C, 0x18, 0x38, 0x6C, 0xC7, 0x00, // BC + 0x00, 0x00, 0x66, 0xC3, 0xDB, 0xDB, 0x7E, 0x00, // BD + 0xFE, 0xC6, 0x60, 0x30, 0x60, 0xC6, 0xFE, 0x00, // BE + 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x6C, 0xEE, 0x00, // BF + 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, // C0 + 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, // C1 + 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0C, 0x18, // C2 + 0x00, 0x00, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x18, // C3 + 0x18, 0x3C, 0x66, 0xC3, 0x81, 0x00, 0x00, 0x00, // C4 + 0x18, 0x0C, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x18, // C5 + 0x00, 0x00, 0x00, 0x81, 0xC3, 0x66, 0x3C, 0x18, // C6 + 0x18, 0x30, 0x60, 0xC0, 0xC0, 0x60, 0x30, 0x18, // C7 + 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, // C8 + 0x18, 0x0C, 0x06, 0x83, 0xC1, 0x60, 0x30, 0x18, // C9 + 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x66, 0x3C, 0x18, // CA + 0xC3, 0xE7, 0x7E, 0x3C, 0x3C, 0x7E, 0xE7, 0xC3, // CB + 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xC0, // CC + 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, // CD + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // CE + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // CF + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D0 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // D1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // D2 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // D3 + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, // D4 + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // D5 + 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, // D6 + 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, // D7 + 0xAA, 0x55, 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, // D8 + 0x0A, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0A, 0x05, // D9 + 0x00, 0x00, 0x00, 0x00, 0xAA, 0x55, 0xAA, 0x55, // DA + 0xA0, 0x50, 0xA0, 0x50, 0xA0, 0x50, 0xA0, 0x50, // DB + 0xAA, 0x54, 0xA8, 0x50, 0xA0, 0x40, 0x80, 0x00, // DC + 0xAA, 0x55, 0x2A, 0x15, 0x0A, 0x05, 0x02, 0x01, // DD + 0x01, 0x02, 0x05, 0x0A, 0x15, 0x2A, 0x55, 0xAA, // DE + 0x00, 0x80, 0x40, 0xA0, 0x50, 0xA8, 0x54, 0xAA, // DF + 0x7E, 0xFF, 0x99, 0xFF, 0xBD, 0xC3, 0xFF, 0x7E, // E0 + 0x7E, 0xFF, 0x99, 0xFF, 0xC3, 0xBD, 0xFF, 0x7E, // E1 + 0x38, 0x38, 0xFE, 0xFE, 0xFE, 0x10, 0x38, 0x00, // E2 + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, // E3 + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, // E4 + 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x10, 0x38, 0x00, // E5 + 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0x66, 0x3C, 0x00, // E6 + 0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x00, // E7 + 0x00, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x00, // E8 + 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, // E9 + 0x0F, 0x07, 0x0D, 0x78, 0xCC, 0xCC, 0xCC, 0x78, // EA + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, // EB + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x7C, 0x38, // EC + 0x18, 0x1C, 0x1E, 0x1B, 0x18, 0x78, 0xF8, 0x70, // ED + 0x99, 0x5A, 0x24, 0xC3, 0xC3, 0x24, 0x5A, 0x99, // EE + 0x10, 0x38, 0x38, 0x38, 0x38, 0x38, 0x7C, 0xD6, // EF + 0x18, 0x3C, 0x7E, 0xFF, 0x18, 0x18, 0x18, 0x18, // F0 + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x7E, 0x3C, 0x18, // F1 + 0x10, 0x30, 0x70, 0xFF, 0xFF, 0x70, 0x30, 0x10, // F2 + 0x08, 0x0C, 0x0E, 0xFF, 0xFF, 0x0E, 0x0C, 0x08, // F3 + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, // F4 + 0x00, 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, // F5 + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, // F6 + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, // F7 + 0x38, 0x38, 0x92, 0x7C, 0x10, 0x28, 0x28, 0x28, // F8 + 0x38, 0x38, 0x10, 0xFE, 0x10, 0x28, 0x44, 0x82, // F9 + 0x38, 0x38, 0x12, 0x7C, 0x90, 0x28, 0x24, 0x22, // FA + 0x38, 0x38, 0x90, 0x7C, 0x12, 0x28, 0x48, 0x88, // FB + 0x00, 0x3C, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x00, // FC + 0x3C, 0xFF, 0xFF, 0x18, 0x0C, 0x18, 0x30, 0x18, // FD + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, // FE + 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, // FF +}; +static const uint8_t _sdtx_font_c64[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 00 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // 01 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 02 + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 03 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 04 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 05 + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // 06 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 07 + 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x33, 0x33, // 08 + 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, // 09 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 0A + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 0B + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // 0C + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // 0D + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 0E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // 0F + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 10 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 11 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 12 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 13 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 14 + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, // 15 + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, // 16 + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 17 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // 19 + 0x01, 0x03, 0x06, 0x6C, 0x78, 0x70, 0x60, 0x00, // 1A + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // 1B + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // 1C + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // 1D + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // 1E + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00, // 21 + 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 0x00, // 23 + 0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00, // 24 + 0x62, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x46, 0x00, // 25 + 0x3C, 0x66, 0x3C, 0x38, 0x67, 0x66, 0x3F, 0x00, // 26 + 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, // 28 + 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, // 29 + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // 2C + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E + 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, // 2F + 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00, // 30 + 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00, // 31 + 0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00, // 32 + 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, // 33 + 0x06, 0x0E, 0x1E, 0x66, 0x7F, 0x06, 0x06, 0x00, // 34 + 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, // 35 + 0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 + 0x7E, 0x66, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, // 37 + 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 + 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, // 39 + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, // 3A + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, // 3B + 0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00, // 3C + 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, // 3D + 0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00, // 3E + 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00, // 3F + 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, // 40 + 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 41 + 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 42 + 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, // 43 + 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, // 44 + 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00, // 45 + 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, // 46 + 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, // 47 + 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 + 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 49 + 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, // 4A + 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00, // 4B + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // 4C + 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00, // 4D + 0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00, // 4E + 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 4F + 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, // 50 + 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x0E, 0x00, // 51 + 0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00, // 52 + 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // 53 + 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 54 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 55 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 + 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // 57 + 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // 58 + 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // 59 + 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // 5A + 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, // 5B + 0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00, // 5C + 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, // 5D + 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, // 5E + 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00, // 5F + 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, // 60 + 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, // 61 + 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 62 + 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00, // 63 + 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // 64 + 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // 65 + 0x00, 0x0E, 0x18, 0x3E, 0x18, 0x18, 0x18, 0x00, // 66 + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 67 + 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x00, // 68 + 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3C, 0x00, // 69 + 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3C, // 6A + 0x00, 0x60, 0x60, 0x6C, 0x78, 0x6C, 0x66, 0x00, // 6B + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 6C + 0x00, 0x00, 0x66, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // 6D + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // 70 + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06, // 71 + 0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00, // 72 + 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, // 73 + 0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x0E, 0x00, // 74 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // 75 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 76 + 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00, // 77 + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // 78 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x0C, 0x78, // 79 + 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 7A + 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, // 7B + 0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00, // 7C + 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, // 7D + 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, // 7E + 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00, // 7F + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 80 + 0x08, 0x1C, 0x3E, 0x7F, 0x7F, 0x1C, 0x3E, 0x00, // 81 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 82 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 83 + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 84 + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // 85 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, // 86 + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 87 + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, // 88 + 0x00, 0x00, 0x00, 0xE0, 0xF0, 0x38, 0x18, 0x18, // 89 + 0x18, 0x18, 0x1C, 0x0F, 0x07, 0x00, 0x00, 0x00, // 8A + 0x18, 0x18, 0x38, 0xF0, 0xE0, 0x00, 0x00, 0x00, // 8B + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, // 8C + 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, // 8D + 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xC0, // 8E + 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 8F + 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 90 + 0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x00, // 91 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, // 92 + 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 93 + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, // 94 + 0x00, 0x00, 0x00, 0x07, 0x0F, 0x1C, 0x18, 0x18, // 95 + 0xC3, 0xE7, 0x7E, 0x3C, 0x3C, 0x7E, 0xE7, 0xC3, // 96 + 0x00, 0x3C, 0x7E, 0x66, 0x66, 0x7E, 0x3C, 0x00, // 97 + 0x18, 0x18, 0x66, 0x66, 0x18, 0x18, 0x3C, 0x00, // 98 + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, // 99 + 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 9A + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 9B + 0xC0, 0xC0, 0x30, 0x30, 0xC0, 0xC0, 0x30, 0x30, // 9C + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 9D + 0x00, 0x00, 0x03, 0x3E, 0x76, 0x36, 0x36, 0x00, // 9E + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // 9F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A0 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // A1 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // A4 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // A5 + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // A6 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // A7 + 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x33, 0x33, // A8 + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, // A9 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // AA + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // AB + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // AC + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // AD + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // AE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // AF + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // B0 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // B1 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // B2 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // B3 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // B4 + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, // B5 + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, // B6 + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // B7 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // B8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // B9 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF, // BA + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // BB + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // BC + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // BD + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // BE + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // BF + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // C0 + 0xF7, 0xE3, 0xC1, 0x80, 0x80, 0xE3, 0xC1, 0xFF, // C1 + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, // C2 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // C3 + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // C4 + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C5 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, // C6 + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, // C7 + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, // C8 + 0xFF, 0xFF, 0xFF, 0x1F, 0x0F, 0xC7, 0xE7, 0xE7, // C9 + 0xE7, 0xE7, 0xE3, 0xF0, 0xF8, 0xFF, 0xFF, 0xFF, // CA + 0xE7, 0xE7, 0xC7, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF, // CB + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x00, // CC + 0x3F, 0x1F, 0x8F, 0xC7, 0xE3, 0xF1, 0xF8, 0xFC, // CD + 0xFC, 0xF8, 0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0x3F, // CE + 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // CF + 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // D0 + 0xFF, 0xC3, 0x81, 0x81, 0x81, 0x81, 0xC3, 0xFF, // D1 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, // D2 + 0xC9, 0x80, 0x80, 0x80, 0xC1, 0xE3, 0xF7, 0xFF, // D3 + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, // D4 + 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0xE3, 0xE7, 0xE7, // D5 + 0x3C, 0x18, 0x81, 0xC3, 0xC3, 0x81, 0x18, 0x3C, // D6 + 0xFF, 0xC3, 0x81, 0x99, 0x99, 0x81, 0xC3, 0xFF, // D7 + 0xE7, 0xE7, 0x99, 0x99, 0xE7, 0xE7, 0xC3, 0xFF, // D8 + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, // D9 + 0xF7, 0xE3, 0xC1, 0x80, 0xC1, 0xE3, 0xF7, 0xFF, // DA + 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xE7, 0xE7, 0xE7, // DB + 0x3F, 0x3F, 0xCF, 0xCF, 0x3F, 0x3F, 0xCF, 0xCF, // DC + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, // DD + 0xFF, 0xFF, 0xFC, 0xC1, 0x89, 0xC9, 0xC9, 0xFF, // DE + 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, // DF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E0 + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // E1 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // E2 + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E3 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // E4 + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // E5 + 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // E6 + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // E7 + 0xFF, 0xFF, 0xFF, 0xFF, 0x33, 0x33, 0xCC, 0xCC, // E8 + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, // E9 + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // EA + 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7, // EB + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, // EC + 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xFF, 0xFF, 0xFF, // ED + 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xE7, 0xE7, 0xE7, // EE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, // EF + 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7, // F0 + 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // F1 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE7, 0xE7, 0xE7, // F2 + 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xE7, 0xE7, 0xE7, // F3 + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // F4 + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, // F5 + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // F6 + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F7 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F8 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, // F9 + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, // FA + 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, // FB + 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, // FC + 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xFF, 0xFF, 0xFF, // FD + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, // FE + 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // FF +}; +static const uint8_t _sdtx_font_oric[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 01 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 02 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 03 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 04 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 05 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 06 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 07 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 08 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 09 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 11 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 13 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 14 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 15 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 17 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 19 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, // 21 + 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x14, 0x14, 0x3E, 0x14, 0x3E, 0x14, 0x14, 0x00, // 23 + 0x08, 0x1E, 0x28, 0x1C, 0x0A, 0x3C, 0x08, 0x00, // 24 + 0x30, 0x32, 0x04, 0x08, 0x10, 0x26, 0x06, 0x00, // 25 + 0x10, 0x28, 0x28, 0x10, 0x2A, 0x24, 0x1A, 0x00, // 26 + 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // 28 + 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, // 29 + 0x08, 0x2A, 0x1C, 0x08, 0x1C, 0x2A, 0x08, 0x00, // 2A + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, // 2C + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // 2E + 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 2F + 0x1C, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x1C, 0x00, // 30 + 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 31 + 0x1C, 0x22, 0x02, 0x04, 0x08, 0x10, 0x3E, 0x00, // 32 + 0x3E, 0x02, 0x04, 0x0C, 0x02, 0x22, 0x1C, 0x00, // 33 + 0x04, 0x0C, 0x14, 0x24, 0x3E, 0x04, 0x04, 0x00, // 34 + 0x3E, 0x20, 0x3C, 0x02, 0x02, 0x22, 0x1C, 0x00, // 35 + 0x0C, 0x10, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, // 36 + 0x3E, 0x02, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00, // 37 + 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, // 38 + 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x04, 0x18, 0x00, // 39 + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, // 3A + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, // 3B + 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, // 3C + 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // 3D + 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, // 3E + 0x1C, 0x22, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, // 3F + 0x1C, 0x22, 0x2A, 0x2E, 0x2C, 0x20, 0x1E, 0x00, // 40 + 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, // 41 + 0x3C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x3C, 0x00, // 42 + 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, // 43 + 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, // 44 + 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3E, 0x00, // 45 + 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, // 46 + 0x1E, 0x20, 0x20, 0x20, 0x26, 0x22, 0x1E, 0x00, // 47 + 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, // 48 + 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 49 + 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, // 4A + 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, // 4B + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, // 4C + 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, // 4D + 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x00, // 4E + 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 4F + 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, // 50 + 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, // 51 + 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, // 52 + 0x1C, 0x22, 0x20, 0x1C, 0x02, 0x22, 0x1C, 0x00, // 53 + 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // 54 + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 55 + 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 56 + 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, // 57 + 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, // 58 + 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, // 59 + 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, // 5A + 0x1E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x00, // 5B + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, // 5C + 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x3C, 0x00, // 5D + 0x08, 0x14, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, // 5E + 0x0E, 0x11, 0x3C, 0x10, 0x3C, 0x11, 0x0E, 0x00, // 5F + 0x0C, 0x12, 0x2D, 0x29, 0x29, 0x2D, 0x12, 0x0C, // 60 + 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x1E, 0x00, // 61 + 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, // 62 + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1E, 0x00, // 63 + 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, // 64 + 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x20, 0x1E, 0x00, // 65 + 0x0C, 0x12, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x00, // 66 + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x1C, // 67 + 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, // 68 + 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x1C, 0x00, // 69 + 0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x24, 0x18, // 6A + 0x20, 0x20, 0x22, 0x24, 0x38, 0x24, 0x22, 0x00, // 6B + 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 6C + 0x00, 0x00, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x00, // 6D + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, // 6E + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, // 6F + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, // 70 + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, // 71 + 0x00, 0x00, 0x2E, 0x30, 0x20, 0x20, 0x20, 0x00, // 72 + 0x00, 0x00, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x00, // 73 + 0x10, 0x10, 0x3C, 0x10, 0x10, 0x12, 0x0C, 0x00, // 74 + 0x00, 0x00, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, // 75 + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 76 + 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x00, // 77 + 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, // 78 + 0x00, 0x00, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C, // 79 + 0x00, 0x00, 0x3E, 0x04, 0x08, 0x10, 0x3E, 0x00, // 7A + 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, // 7B + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // 7C + 0x38, 0x0C, 0x0C, 0x06, 0x0C, 0x0C, 0x38, 0x00, // 7D + 0x2A, 0x15, 0x2A, 0x15, 0x2A, 0x15, 0x2A, 0x15, // 7E + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // 7F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 81 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 82 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 83 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 84 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 85 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 86 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 87 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 90 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 91 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 92 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 93 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 94 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 95 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 97 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 98 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 99 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0 + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, 0xF7, 0xFF, // A1 + 0xEB, 0xEB, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xEB, 0xEB, 0xC1, 0xEB, 0xC1, 0xEB, 0xEB, 0xFF, // A3 + 0xF7, 0xE1, 0xD7, 0xE3, 0xF5, 0xC3, 0xF7, 0xFF, // A4 + 0xCF, 0xCD, 0xFB, 0xF7, 0xEF, 0xD9, 0xF9, 0xFF, // A5 + 0xEF, 0xD7, 0xD7, 0xEF, 0xD5, 0xDB, 0xE5, 0xFF, // A6 + 0xF7, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A7 + 0xF7, 0xEF, 0xDF, 0xDF, 0xDF, 0xEF, 0xF7, 0xFF, // A8 + 0xF7, 0xFB, 0xFD, 0xFD, 0xFD, 0xFB, 0xF7, 0xFF, // A9 + 0xF7, 0xD5, 0xE3, 0xF7, 0xE3, 0xD5, 0xF7, 0xFF, // AA + 0xFF, 0xF7, 0xF7, 0xC1, 0xF7, 0xF7, 0xFF, 0xFF, // AB + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xF7, 0xEF, // AC + 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, // AD + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, // AE + 0xFF, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xFF, 0xFF, // AF + 0xE3, 0xDD, 0xD9, 0xD5, 0xCD, 0xDD, 0xE3, 0xFF, // B0 + 0xF7, 0xE7, 0xF7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // B1 + 0xE3, 0xDD, 0xFD, 0xFB, 0xF7, 0xEF, 0xC1, 0xFF, // B2 + 0xC1, 0xFD, 0xFB, 0xF3, 0xFD, 0xDD, 0xE3, 0xFF, // B3 + 0xFB, 0xF3, 0xEB, 0xDB, 0xC1, 0xFB, 0xFB, 0xFF, // B4 + 0xC1, 0xDF, 0xC3, 0xFD, 0xFD, 0xDD, 0xE3, 0xFF, // B5 + 0xF3, 0xEF, 0xDF, 0xC3, 0xDD, 0xDD, 0xE3, 0xFF, // B6 + 0xC1, 0xFD, 0xFB, 0xF7, 0xEF, 0xEF, 0xEF, 0xFF, // B7 + 0xE3, 0xDD, 0xDD, 0xE3, 0xDD, 0xDD, 0xE3, 0xFF, // B8 + 0xE3, 0xDD, 0xDD, 0xE1, 0xFD, 0xFB, 0xE7, 0xFF, // B9 + 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, // BA + 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xF7, 0xEF, // BB + 0xFB, 0xF7, 0xEF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFF, // BC + 0xFF, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, // BD + 0xEF, 0xF7, 0xFB, 0xFD, 0xFB, 0xF7, 0xEF, 0xFF, // BE + 0xE3, 0xDD, 0xFB, 0xF7, 0xF7, 0xFF, 0xF7, 0xFF, // BF + 0xE3, 0xDD, 0xD5, 0xD1, 0xD3, 0xDF, 0xE1, 0xFF, // C0 + 0xF7, 0xEB, 0xDD, 0xDD, 0xC1, 0xDD, 0xDD, 0xFF, // C1 + 0xC3, 0xDD, 0xDD, 0xC3, 0xDD, 0xDD, 0xC3, 0xFF, // C2 + 0xE3, 0xDD, 0xDF, 0xDF, 0xDF, 0xDD, 0xE3, 0xFF, // C3 + 0xC3, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xC3, 0xFF, // C4 + 0xC1, 0xDF, 0xDF, 0xC3, 0xDF, 0xDF, 0xC1, 0xFF, // C5 + 0xC1, 0xDF, 0xDF, 0xC3, 0xDF, 0xDF, 0xDF, 0xFF, // C6 + 0xE1, 0xDF, 0xDF, 0xDF, 0xD9, 0xDD, 0xE1, 0xFF, // C7 + 0xDD, 0xDD, 0xDD, 0xC1, 0xDD, 0xDD, 0xDD, 0xFF, // C8 + 0xE3, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // C9 + 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xDD, 0xE3, 0xFF, // CA + 0xDD, 0xDB, 0xD7, 0xCF, 0xD7, 0xDB, 0xDD, 0xFF, // CB + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xC1, 0xFF, // CC + 0xDD, 0xC9, 0xD5, 0xD5, 0xDD, 0xDD, 0xDD, 0xFF, // CD + 0xDD, 0xDD, 0xCD, 0xD5, 0xD9, 0xDD, 0xDD, 0xFF, // CE + 0xE3, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF, // CF + 0xC3, 0xDD, 0xDD, 0xC3, 0xDF, 0xDF, 0xDF, 0xFF, // D0 + 0xE3, 0xDD, 0xDD, 0xDD, 0xD5, 0xDB, 0xE5, 0xFF, // D1 + 0xC3, 0xDD, 0xDD, 0xC3, 0xD7, 0xDB, 0xDD, 0xFF, // D2 + 0xE3, 0xDD, 0xDF, 0xE3, 0xFD, 0xDD, 0xE3, 0xFF, // D3 + 0xC1, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // D4 + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF, // D5 + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xEB, 0xF7, 0xFF, // D6 + 0xDD, 0xDD, 0xDD, 0xD5, 0xD5, 0xC9, 0xDD, 0xFF, // D7 + 0xDD, 0xDD, 0xEB, 0xF7, 0xEB, 0xDD, 0xDD, 0xFF, // D8 + 0xDD, 0xDD, 0xEB, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // D9 + 0xC1, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xC1, 0xFF, // DA + 0xE1, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xE1, 0xFF, // DB + 0xFF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFF, 0xFF, // DC + 0xC3, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xC3, 0xFF, // DD + 0xF7, 0xEB, 0xD5, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // DE + 0xF1, 0xEE, 0xC3, 0xEF, 0xC3, 0xEE, 0xF1, 0xFF, // DF + 0xF3, 0xED, 0xD2, 0xD6, 0xD6, 0xD2, 0xED, 0xF3, // E0 + 0xFF, 0xFF, 0xE3, 0xFD, 0xE1, 0xDD, 0xE1, 0xFF, // E1 + 0xDF, 0xDF, 0xC3, 0xDD, 0xDD, 0xDD, 0xC3, 0xFF, // E2 + 0xFF, 0xFF, 0xE1, 0xDF, 0xDF, 0xDF, 0xE1, 0xFF, // E3 + 0xFD, 0xFD, 0xE1, 0xDD, 0xDD, 0xDD, 0xE1, 0xFF, // E4 + 0xFF, 0xFF, 0xE3, 0xDD, 0xC1, 0xDF, 0xE1, 0xFF, // E5 + 0xF3, 0xED, 0xEF, 0xC3, 0xEF, 0xEF, 0xEF, 0xFF, // E6 + 0xFF, 0xFF, 0xE3, 0xDD, 0xDD, 0xE1, 0xFD, 0xE3, // E7 + 0xDF, 0xDF, 0xC3, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, // E8 + 0xF7, 0xFF, 0xE7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // E9 + 0xFB, 0xFF, 0xF3, 0xFB, 0xFB, 0xFB, 0xDB, 0xE7, // EA + 0xDF, 0xDF, 0xDD, 0xDB, 0xC7, 0xDB, 0xDD, 0xFF, // EB + 0xE7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // EC + 0xFF, 0xFF, 0xC9, 0xD5, 0xD5, 0xD5, 0xDD, 0xFF, // ED + 0xFF, 0xFF, 0xC3, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, // EE + 0xFF, 0xFF, 0xE3, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF, // EF + 0xFF, 0xFF, 0xC3, 0xDD, 0xDD, 0xC3, 0xDF, 0xDF, // F0 + 0xFF, 0xFF, 0xE1, 0xDD, 0xDD, 0xE1, 0xFD, 0xFD, // F1 + 0xFF, 0xFF, 0xD1, 0xCF, 0xDF, 0xDF, 0xDF, 0xFF, // F2 + 0xFF, 0xFF, 0xE1, 0xDF, 0xE3, 0xFD, 0xC3, 0xFF, // F3 + 0xEF, 0xEF, 0xC3, 0xEF, 0xEF, 0xED, 0xF3, 0xFF, // F4 + 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xD9, 0xE5, 0xFF, // F5 + 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xEB, 0xF7, 0xFF, // F6 + 0xFF, 0xFF, 0xDD, 0xDD, 0xD5, 0xD5, 0xC9, 0xFF, // F7 + 0xFF, 0xFF, 0xDD, 0xEB, 0xF7, 0xEB, 0xDD, 0xFF, // F8 + 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xE1, 0xFD, 0xE3, // F9 + 0xFF, 0xFF, 0xC1, 0xFB, 0xF7, 0xEF, 0xC1, 0xFF, // FA + 0xF1, 0xE7, 0xE7, 0xCF, 0xE7, 0xE7, 0xF1, 0xFF, // FB + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, // FC + 0xC7, 0xF3, 0xF3, 0xF9, 0xF3, 0xF3, 0xC7, 0xFF, // FD + 0xD5, 0xEA, 0xD5, 0xEA, 0xD5, 0xEA, 0xD5, 0xEA, // FE + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // FF +}; + +/* + Embedded source code compiled with: + + sokol-shdc -i debugtext.glsl -o debugtext.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl:spirv_vk -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(position * vec2(2.0, -2.0) + vec2(-1.0, +1.0), 0.0, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + layout(binding=0) uniform texture2D tex; + layout(binding=0) uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv).xxxx * color; + } + @end + + @program debugtext vs fs +*/ +#if defined(SOKOL_GLCORE) +/* + #version 410 + + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(fma(position, vec2(2.0, -2.0), vec2(-1.0, 1.0)), 0.0, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _sdtx_vs_source_glsl410[343] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x32,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,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,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,0x76,0x65,0x63,0x34,0x28,0x66, + 0x6d,0x61,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x76,0x65,0x63, + 0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x76,0x65, + 0x63,0x32,0x28,0x2d,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x29,0x2c,0x20, + 0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75, + 0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,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, +}; +/* + #version 410 + + uniform sampler2D tex_smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv).xxxx * color; + } +*/ +static const uint8_t _sdtx_fs_source_glsl410[224] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,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,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 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, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x78, + 0x78,0x78,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + +}; +#elif defined(SOKOL_GLES3) +/* + #version 300 es + + layout(location = 0) in vec2 position; + out vec2 uv; + layout(location = 1) in vec2 texcoord0; + out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(position * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.0, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _sdtx_vs_source_glsl300es[301] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 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,0x32,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32, + 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,0x76,0x65,0x63,0x34,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e, + 0x30,0x29,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x31,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,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, +}; +/* + #version 300 es + precision mediump float; + precision highp int; + + uniform highp sampler2D tex_smp; + + layout(location = 0) out highp vec4 frag_color; + in highp vec2 uv; + in highp vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv).xxxx * color; + } +*/ +static const uint8_t _sdtx_fs_source_glsl300es[255] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x78,0x78, + 0x78,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]]) + { + main0_out out = {}; + out.gl_Position = float4(fma(in.position, float2(2.0, -2.0), float2(-1.0, 1.0)), 0.0, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sdtx_vs_bytecode_metal_macos[2892] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4c,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,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,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0x27,0xd2,0x9c,0x99,0x28,0x3d, + 0x28,0xcf,0x09,0x1c,0x55,0xac,0x1d,0xfa,0x23,0x34,0x9b,0xc3,0x82,0x58,0xe9,0xeb, + 0xdc,0x0c,0xa5,0xd3,0x0c,0xa1,0xbf,0xa0,0xe7,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,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,0x04,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,0x20,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x85,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x6c,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x18,0xc2,0xff,0xff,0xff,0xff,0x0f,0x80,0x04,0x50,0x1b,0x8c,0xe1,0xff, + 0xff,0xff,0xff,0x07,0x40,0x02,0x28,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, + 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, + 0x11,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84, + 0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c, + 0x10,0x34,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x42,0x4c, + 0x84,0x10,0x15,0x22,0x22,0x82,0x6c,0x20,0x60,0x8e,0x00,0x0c,0x52,0x20,0x87,0x11, + 0x88,0x64,0x04,0x00,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,0x29,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x18, + 0xc2,0x1c,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,0xca,0x12,0x18,0x01,0x28,0x82,0x42,0x28,0x08,0xd2,0xb1,0x84,0x07, + 0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xb1,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10, + 0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20, + 0x80,0x72,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24, + 0x02,0x22,0x24,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c, + 0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x46,0x06,0x46,0x66,0xc6,0x65, + 0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46, + 0x66,0xc6,0x65,0x66,0xa6,0x26,0x65,0x88,0x80,0x10,0x43,0x8c,0x44,0x48,0x8c,0x64, + 0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e,0x44,0x48,0x84,0x64,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,0x40,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,0x64,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,0x90,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,0x64, + 0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3, + 0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93, + 0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62, + 0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46, + 0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb, + 0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90, + 0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c, + 0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb, + 0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32, + 0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6, + 0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x64,0x40,0x3c,0xe4,0x4b,0x86,0x44,0x40,0xc0, + 0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47, + 0x06,0x33,0x84,0x4a,0x04,0xc4,0x43,0xbe,0x44,0x48,0x04,0x04,0x0c,0x90,0x08,0x91, + 0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c, + 0xe4,0x4b,0x88,0x44,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0x18,0x62,0x20, + 0x62,0x80,0x90,0x01,0x62,0x06,0x43,0x8c,0x02,0x40,0x3a,0xe4,0x0c,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,0x19,0x14,0xc6,0x19,0xa1,0x84,0x43,0x3a,0xc8,0x83, + 0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e,0x4c,0x09,0xd0,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3, + 0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c, + 0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19, + 0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5, + 0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0, + 0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81, + 0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d, + 0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39, + 0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77, + 0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef, + 0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07, + 0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73, + 0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x05,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x16,0xd0,0x00,0x48,0xe4,0x17, + 0x0c,0xe0,0x57,0x76,0x71,0xdb,0x00,0x00,0x61,0x20,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0xb4,0x63,0x11,0x40, + 0x60,0x1c,0x73,0x10,0x83,0xd0,0x34,0x94,0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82, + 0x20,0x18,0x80,0x20,0x08,0x82,0xe0,0x30,0x96,0x00,0x82,0x20,0x88,0xff,0x02,0x08, + 0x82,0x20,0xfe,0xcd,0x00,0x90,0xcc,0x41,0x54,0x15,0x35,0xd1,0xcc,0x00,0x10,0x8c, + 0x11,0x80,0x20,0x08,0xe2,0xdf,0x08,0xc0,0x0c,0x00,0x00,0x00,0x23,0x06,0xc6,0x10, + 0x54,0x0e,0x72,0x0c,0x32,0x04,0xc7,0x32,0xc8,0x10,0x1c,0xcd,0x6c,0xc3,0x01,0x01, + 0xb3,0x0d,0x01,0x14,0xcc,0x36,0x04,0x83,0x90,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv).xxxx * in.color; + return out; + } +*/ +static const uint8_t _sdtx_fs_bytecode_metal_macos[3033] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x60,0xb0,0xd5,0x97,0xc2,0xac,0x24, + 0x52,0x1f,0x4a,0x56,0x12,0x1a,0x52,0xd7,0xb5,0xc0,0x14,0xee,0xce,0x96,0x55,0xcf, + 0x7d,0x2c,0x16,0x4c,0x83,0x17,0xaf,0xc3,0x9d,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,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,0xec,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb8,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,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,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,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,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,0xe1,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xd2,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,0x46, + 0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,0x34,0xcc,0xd8,0xde,0xc2,0xe8, + 0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e, + 0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a, + 0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d, + 0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93, + 0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95, + 0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c, + 0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d, + 0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42, + 0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d, + 0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c, + 0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0, + 0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee, + 0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0x01,0xaf,0xb0,0x34, + 0xb9,0x96,0x30,0xb6,0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2,0x39,0x94,0xb6, + 0xb0,0x34,0x37,0x98,0x94,0x21,0xc4,0xd3,0x06,0x0f,0x1b,0x10,0x0b,0x4b,0x93,0x6b, + 0x09,0x63,0x4b,0x0b,0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x6b,0xa1,0x2b,0xc3,0xa3, + 0xab,0x93,0x2b,0x9b,0x1b,0x62,0x3c,0x6f,0xf0,0xb4,0xc1,0xe3,0x06,0xc4,0xc2,0xd2, + 0xe4,0x5a,0xc2,0xd8,0xd2,0xc2,0xe6,0x5a,0xe6,0xc6,0xde,0xe0,0xca,0x5a,0xe6,0xc2, + 0xda,0xe0,0xd8,0xca,0xe4,0xe6,0x86,0x18,0x4f,0x1c,0x3c,0x6d,0xf0,0xc0,0xc1,0x10, + 0xe2,0x79,0x83,0x27,0x0e,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,0x19,0x14, + 0xc6,0x19,0xc1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0x98,0x83,0x3c,0x84,0xc3,0x39,0xb4, + 0x43,0x39,0xb8,0x03,0x3d,0x4c,0x09,0xd6,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19, + 0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c, + 0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76, + 0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10, + 0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e, + 0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d, + 0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b, + 0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77, + 0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5, + 0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50, + 0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71, + 0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98, + 0x07,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,0x14,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00, + 0x00,0x14,0x47,0x00,0x88,0x8d,0x00,0x90,0x1a,0x01,0xa8,0x01,0x12,0x33,0x00,0x14, + 0x66,0x00,0x08,0x8c,0x00,0x00,0x00,0x00,0x00,0x23,0x06,0xca,0x10,0x4c,0x09,0xb2, + 0x10,0x46,0x11,0x0c,0x32,0x04,0x03,0x62,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, + 0x08,0x88,0x01,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x06,0xe0,0x90,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]]) + { + main0_out out = {}; + out.gl_Position = float4(fma(in.position, float2(2.0, -2.0), float2(-1.0, 1.0)), 0.0, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sdtx_vs_bytecode_metal_ios[2876] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3c,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,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,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0x27,0x6d,0x17,0x18,0x1b,0xd1, + 0x64,0x91,0xfe,0x8a,0x4c,0x49,0xdf,0xe3,0x15,0x74,0xcb,0x3e,0x43,0xde,0xeb,0xfa, + 0x8e,0xf0,0xf5,0xf8,0x4b,0xd2,0xac,0x23,0xe4,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,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,0x04,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,0x18,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x83,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x6c,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x18,0xc2,0xff,0xff,0xff,0xff,0x0f,0x80,0x04,0x50,0x1b,0x8c,0xe1,0xff, + 0xff,0xff,0xff,0x07,0x40,0x02,0x28,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, + 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, + 0x11,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84, + 0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c, + 0x10,0x34,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x42,0x4c, + 0x84,0x10,0x15,0x22,0x22,0x82,0x6c,0x20,0x60,0x8e,0x00,0x0c,0x52,0x20,0x87,0x11, + 0x88,0x64,0x04,0x00,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,0x02, + 0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0xcc,0x01,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,0xca,0x12,0x18,0x01,0x28, + 0x82,0x42,0x28,0x08,0xd2,0xb1,0x04,0x09,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0xb1,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x72,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x02,0x22,0x24,0x05,0xe3,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd, + 0x0d,0x64,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e, + 0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x26,0x65,0x88, + 0x80,0x10,0x43,0x8c,0x44,0x48,0x8c,0x64,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04, + 0x41,0x8e,0x44,0x48,0x84,0x64,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,0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d, + 0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21, + 0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61, + 0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88, + 0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d, + 0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb, + 0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47, + 0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e, + 0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a, + 0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf, + 0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9, + 0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x64,0x40, + 0x3c,0xe4,0x4b,0x86,0x44,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42, + 0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x04,0xc4,0x43,0xbe,0x44, + 0x48,0x04,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x44,0x40,0xc0,0x00,0x89,0x90, + 0x0b,0x99,0x90,0x32,0x18,0x62,0x20,0x62,0x80,0x90,0x01,0x62,0x06,0x43,0x8c,0x02, + 0x40,0x3a,0xe4,0x0c,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,0x19,0x14,0xc6, + 0x19,0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03, + 0x3e,0x4c,0x09,0xd0,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b, + 0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8, + 0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60, + 0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0, + 0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e, + 0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8, + 0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6, + 0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc, + 0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78, + 0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10, + 0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e, + 0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0, + 0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07, + 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x50,0x30,0x00, + 0xd2,0xd0,0x16,0xd0,0x00,0x48,0xe4,0x17,0x0c,0xe0,0x57,0x76,0x71,0xdb,0x00,0x00, + 0x61,0x20,0x00,0x00,0x1b,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0xb4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x83,0xd0,0x34,0x94, + 0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0x20,0x18,0x80,0x20,0x08,0x82,0xe0,0x30, + 0x96,0x00,0x82,0x20,0x88,0xff,0x02,0x08,0x82,0x20,0xfe,0xcd,0x00,0x90,0xcc,0x41, + 0x54,0x15,0x35,0xd1,0xcc,0x00,0x10,0x8c,0x11,0x80,0x20,0x08,0xe2,0xdf,0x08,0xc0, + 0x0c,0x00,0x00,0x00,0x23,0x06,0xc6,0x10,0x54,0x0e,0x72,0x0c,0x32,0x04,0xc7,0x32, + 0xc8,0x10,0x1c,0xcd,0x6c,0xc3,0x01,0x01,0xb3,0x0d,0x01,0x14,0xcc,0x36,0x04,0x83, + 0x90,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv).xxxx * in.color; + return out; + } + +*/ +static const uint8_t _sdtx_fs_bytecode_metal_ios[3033] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x64,0xff,0xce,0xd3,0x48,0x71,0xaf, + 0x68,0xf3,0x79,0x4e,0x9f,0x9e,0xbf,0x16,0xda,0xcc,0x0f,0x60,0xbc,0x56,0x43,0x8f, + 0x4b,0x45,0xa7,0x93,0x35,0x40,0xfe,0x60,0x45,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,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,0xe4,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb6,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,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,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,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,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,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x41,0x02,0x00, + 0x00,0x79,0x18,0x00,0x00,0xd2,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65, + 0x66,0xa6,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,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,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,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1, + 0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c, + 0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22, + 0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93, + 0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36, + 0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce, + 0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c, + 0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f, + 0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23, + 0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac, + 0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36, + 0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0, + 0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00, + 0x4f,0xf5,0xa8,0x01,0xaf,0xb0,0x34,0xb9,0x96,0x30,0xb6,0xb4,0xb0,0xb9,0x96,0xb9, + 0xb1,0x37,0xb8,0xb2,0x39,0x94,0xb6,0xb0,0x34,0x37,0x98,0x94,0x21,0xc4,0xd3,0x06, + 0x0f,0x1b,0x10,0x0b,0x4b,0x93,0x6b,0x09,0x63,0x4b,0x0b,0x9b,0x6b,0x99,0x1b,0x7b, + 0x83,0x2b,0x6b,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x9b,0x1b,0x62,0x3c,0x6f,0xf0, + 0xb4,0xc1,0xe3,0x06,0xc4,0xc2,0xd2,0xe4,0x5a,0xc2,0xd8,0xd2,0xc2,0xe6,0x5a,0xe6, + 0xc6,0xde,0xe0,0xca,0x5a,0xe6,0xc2,0xda,0xe0,0xd8,0xca,0xe4,0xe6,0x86,0x18,0x4f, + 0x1c,0x3c,0x6d,0xf0,0xc0,0xc1,0x10,0xe2,0x79,0x83,0x27,0x0e,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,0x19,0x14,0xc6,0x19,0xc1,0x84,0x43,0x3a,0xc8,0x83,0x1b, + 0x98,0x83,0x3c,0x84,0xc3,0x39,0xb4,0x43,0x39,0xb8,0x03,0x3d,0x4c,0x09,0xd6,0x00, + 0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84, + 0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03, + 0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87, + 0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f, + 0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3, + 0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2, + 0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21, + 0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83, + 0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87, + 0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e, + 0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90, + 0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85, + 0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,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,0x14,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x14,0x47,0x00,0x88,0x8d,0x00,0x90, + 0x1a,0x01,0xa8,0x01,0x12,0x33,0x00,0x14,0x66,0x00,0x08,0x8c,0x00,0x00,0x00,0x00, + 0x00,0x23,0x06,0xca,0x10,0x4c,0x09,0xb2,0x10,0x46,0x11,0x0c,0x32,0x04,0x03,0x62, + 0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x08,0x88,0x01,0x00,0x00,0x02,0x00,0x00, + 0x00,0x5b,0x06,0xe0,0x90,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]]) + { + main0_out out = {}; + out.gl_Position = float4(fma(in.position, float2(2.0, -2.0), float2(-1.0, 1.0)), 0.0, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } + +*/ +static const uint8_t _sdtx_vs_source_metal_sim[577] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28, + 0x6c,0x6f,0x63,0x6e,0x30,0x29,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,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, + 0x32,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,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,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,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x28,0x66,0x6d,0x61,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d, + 0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75, + 0x76,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,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, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv).xxxx * in.color; + return out; + } + +*/ +static const uint8_t _sdtx_fs_source_metal_sim[441] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,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,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,0x73,0x6d,0x70,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,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,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,0x29,0x2e,0x78,0x78,0x78,0x78,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 float4 gl_Position; + static float2 position; + static float2 uv; + static float2 texcoord0; + static float4 color; + static float4 color0; + + struct SPIRV_Cross_Input + { + float2 position : TEXCOORD0; + float2 texcoord0 : TEXCOORD1; + float4 color0 : TEXCOORD2; + }; + + struct SPIRV_Cross_Output + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = float4(mad(position, float2(2.0f, -2.0f), float2(-1.0f, 1.0f)), 0.0f, 1.0f); + uv = texcoord0; + color = color0; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + position = stage_input.position; + texcoord0 = stage_input.texcoord0; + color0 = stage_input.color0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.uv = uv; + stage_output.color = color; + return stage_output; + } +*/ +static const uint8_t _sdtx_vs_bytecode_hlsl4[692] = { + 0x44,0x58,0x42,0x43,0x07,0x05,0xa0,0xb3,0x53,0xc1,0x0a,0x0d,0x1e,0xf4,0xe4,0xa6, + 0x91,0xaf,0x4c,0xca,0x01,0x00,0x00,0x00,0xb4,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xe4,0x00,0x00,0x00,0x54,0x01,0x00,0x00, + 0x38,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0x1c,0x00,0x00,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, + 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,0x03,0x03,0x00,0x00,0x50,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,0x03,0x0c,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,0xdc,0x00,0x00,0x00,0x40,0x00,0x01,0x00, + 0x37,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,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,0x32,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, + 0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x32,0x00,0x00,0x0f,0x32,0x20,0x10,0x00, + 0x02,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00, + 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08,0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x01,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,0x03,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, +}; +/* + Texture2D tex : register(t0); + SamplerState smp : register(s0); + + static float4 frag_color; + static float2 uv; + static float4 color; + + struct SPIRV_Cross_Input + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 frag_color : SV_Target0; + }; + + void frag_main() + { + frag_color = tex.Sample(smp, uv).xxxx * color; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + uv = stage_input.uv; + color = stage_input.color; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.frag_color = frag_color; + return stage_output; + } +*/ +static const uint8_t _sdtx_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0xb7,0xcd,0xbd,0xb1,0x6f,0x85,0x5d,0x59,0x07,0x7e,0xa3,0x6e, + 0xe2,0x23,0x68,0xa0,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,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,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,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,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,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +/* + diagnostic(off, derivative_uniformity); + + var position_1 : vec2f; + + var uv : vec2f; + + var texcoord0 : vec2f; + + var color : vec4f; + + var color0 : vec4f; + + var gl_Position : vec4f; + + fn main_1() { + let x_27 = ((position_1 * vec2f(2.0f, -2.0f)) + vec2f(-1.0f, 1.0f)); + gl_Position = vec4f(x_27.x, x_27.y, 0.0f, 1.0f); + uv = texcoord0; + color = color0; + return; + } + + struct main_out { + @builtin(position) + gl_Position : vec4f, + @location(0) + uv_1 : vec2f, + @location(1) + color_1 : vec4f, + } + + @vertex + fn main(@location(0) position_1_param : vec2f, @location(1) texcoord0_param : vec2f, @location(2) color0_param : vec4f) -> main_out { + position_1 = position_1_param; + texcoord0 = texcoord0_param; + color0 = color0_param; + main_1(); + return main_out(gl_Position, uv, color); + } +*/ +static const uint8_t _sdtx_vs_source_wgsl[832] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70, + 0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74, + 0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a, + 0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20, + 0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x37,0x20,0x3d,0x20,0x28, + 0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x2a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x28,0x32,0x2e,0x30,0x66,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x66,0x29, + 0x29,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x2d,0x31,0x2e,0x30,0x66,0x2c, + 0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78, + 0x5f,0x32,0x37,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x37,0x2e,0x79,0x2c,0x20,0x30, + 0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x75,0x76, + 0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72, + 0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x40,0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x76,0x65,0x72,0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d, + 0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c, + 0x20,0x75,0x76,0x2c,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +/* + diagnostic(off, derivative_uniformity); + + var frag_color : vec4f; + + @binding(0) @group(1) var tex : texture_2d; + + @binding(32) @group(1) var smp : sampler; + + var uv : vec2f; + + var color : vec4f; + + fn main_1() { + let x_23 = uv; + let x_24 = textureSample(tex, smp, x_23); + frag_color = (x_24.xxxx * color); + return; + } + + struct main_out { + @location(0) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(0) uv_param : vec2f, @location(1) color_param : vec4f) -> main_out { + uv = uv_param; + color = color_param; + main_1(); + return main_out(frag_color); + } +*/ +static const uint8_t _sdtx_fs_source_wgsl[590] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64, + 0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29, + 0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76, + 0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b, + 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66, + 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73,0x6d, + 0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x34,0x2e,0x78, + 0x78,0x78,0x78,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x20,0x20, + 0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, + 0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, + 0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75, + 0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +#elif defined(SOKOL_VULKAN) +/* + #version 460 + + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4((position * vec2(2.0, -2.0)) + vec2(-1.0, 1.0), 0.0, 1.0); + uv = texcoord0; + color = color0; + } + +*/ +static const uint8_t _sdtx_vs_bytecode_spirv_vk[1248] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x2a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0b,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x03,0x00,0x03,0x00, + 0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00, + 0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x0b,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78,0x00,0x00,0x00,0x00, + 0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00,0x0b,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,0x0b,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,0x0b,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, + 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x23,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, + 0x26,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x28,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x23,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,0x26,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x28,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0b,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x40, + 0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0xc0, + 0x2c,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x00,0x00,0x80,0xbf,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x2c,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x22,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x20,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x27,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x27,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, + 0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x12,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x21,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x23,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x26,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +/* + #version 460 + + layout(set = 1, binding = 0) uniform texture2D tex; + layout(set = 1, binding = 32) uniform sampler smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(sampler2D(tex, smp), uv).xxxx * color; + } + +*/ +static const uint8_t _sdtx_fs_bytecode_spirv_vk[816] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0a,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x74,0x65,0x78,0x00, + 0x05,0x00,0x03,0x00,0x10,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x05,0x00,0x03,0x00, + 0x16,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,0x09,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,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,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0a,0x00,0x00,0x00, + 0x06,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,0x20,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, + 0x0e,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,0x1b,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x56,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x4f,0x00,0x09,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x1d,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x09,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sdtx_vs_src_dummy = ""; +static const char* _sdtx_fs_src_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +typedef struct { + uint32_t id; + sg_resource_state state; +} _sdtx_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sdtx_pool_t; + +typedef struct { + float x, y; +} _sdtx_float2_t; + +typedef struct { + float x, y; + uint16_t u, v; + 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; + 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; + _sdtx_float2_t pos; + float tab_width; + uint32_t color; +} _sdtx_context_t; + +typedef struct { + _sdtx_pool_t pool; + _sdtx_context_t* contexts; +} _sdtx_context_pool_t; + +typedef struct { + uint32_t init_cookie; + sdtx_desc_t desc; + sg_image font_img; + sg_view font_view; + sg_sampler font_smp; + sg_shader shader; + uint32_t fmt_buf_size; + char* fmt_buf; + sdtx_context def_ctx_id; + sdtx_context cur_ctx_id; + _sdtx_context_t* cur_ctx; // may be 0! + _sdtx_context_pool_t context_pool; + uint8_t font_pixels[SDTX_MAX_FONTS * 256 * 8 * 8]; +} _sdtx_t; +static _sdtx_t _sdtx; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>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, (uint32_t)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_fn) { + ptr = _sdtx.desc.allocator.alloc_fn(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_fn) { + _sdtx.desc.allocator.free_fn(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 + pool->size = num + 1; + 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*) _sdtx_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + 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; + } +} + +static void _sdtx_discard_pool(_sdtx_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sdtx_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sdtx_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sdtx_pool_alloc_index(_sdtx_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SDTX_INVALID_SLOT_INDEX; + } +} + +static void _sdtx_pool_free_index(_sdtx_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SDTX_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +static void _sdtx_setup_context_pool(const sdtx_desc_t* desc) { + SOKOL_ASSERT(desc); + // note: the pool will have an additional item, since slot 0 is reserved + 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*) _sdtx_malloc_clear(pool_byte_size); +} + +static void _sdtx_discard_context_pool(void) { + SOKOL_ASSERT(_sdtx.context_pool.contexts); + _sdtx_free(_sdtx.context_pool.contexts); + _sdtx.context_pool.contexts = 0; + _sdtx_discard_pool(&_sdtx.context_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 + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +static uint32_t _sdtx_slot_alloc(_sdtx_pool_t* pool, _sdtx_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SDTX_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SDTX_SLOT_SHIFT)|(slot_index & _SDTX_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _sdtx_slot_index(uint32_t id) { + int slot_index = (int) (id & _SDTX_SLOT_MASK); + SOKOL_ASSERT(_SDTX_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +// get context pointer without id-check +static _sdtx_context_t* _sdtx_context_at(uint32_t ctx_id) { + SOKOL_ASSERT(SG_INVALID_ID != ctx_id); + int slot_index = _sdtx_slot_index(ctx_id); + SOKOL_ASSERT((slot_index > _SDTX_INVALID_SLOT_INDEX) && (slot_index < _sdtx.context_pool.pool.size)); + return &_sdtx.context_pool.contexts[slot_index]; +} + +// get context pointer with id-check, returns 0 if no match +static _sdtx_context_t* _sdtx_lookup_context(uint32_t ctx_id) { + if (SG_INVALID_ID != ctx_id) { + _sdtx_context_t* ctx = _sdtx_context_at(ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +// make context handle from raw uint32_t id +static sdtx_context _sdtx_make_ctx_id(uint32_t ctx_id) { + sdtx_context ctx; + ctx.id = ctx_id; + return ctx; +} + +static sdtx_context _sdtx_alloc_context(void) { + sdtx_context ctx_id; + int slot_index = _sdtx_pool_alloc_index(&_sdtx.context_pool.pool); + if (_SDTX_INVALID_SLOT_INDEX != slot_index) { + ctx_id = _sdtx_make_ctx_id(_sdtx_slot_alloc(&_sdtx.context_pool.pool, &_sdtx.context_pool.contexts[slot_index].slot, slot_index)); + } else { + // pool is exhausted + ctx_id = _sdtx_make_ctx_id(SG_INVALID_ID); + } + return ctx_id; +} + +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); + res.tab_width = _sdtx_def(res.tab_width, _SDTX_DEFAULT_TAB_WIDTH); + // keep pixel format attrs are passed as is into pipeline creation + SOKOL_ASSERT(res.char_buf_size > 0); + SOKOL_ASSERT(!isnan(res.canvas_width)); + SOKOL_ASSERT(!isnan(res.canvas_height)); + SOKOL_ASSERT(res.canvas_width > 0.0f); + SOKOL_ASSERT(res.canvas_height > 0.0f); + 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"); + + SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc); + _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; + + 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; + _sdtx_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.size = vbuf_size; + vbuf_desc.usage.vertex_buffer = true; + vbuf_desc.usage.stream_update = true; + vbuf_desc.label = "sdtx-vbuf"; + ctx->vbuf = sg_make_buffer(&vbuf_desc); + SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id); + + sg_pipeline_desc 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; + pip_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N; + pip_desc.shader = _sdtx.shader; + pip_desc.index_type = SG_INDEXTYPE_NONE; + pip_desc.sample_count = ctx->desc.sample_count; + pip_desc.depth.pixel_format = ctx->desc.depth_format; + pip_desc.colors[0].pixel_format = ctx->desc.color_format; + 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; + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + pip_desc.label = "sdtx-pipeline"; + ctx->pip = sg_make_pipeline(&pip_desc); + SOKOL_ASSERT(SG_INVALID_ID != ctx->pip.id); + + ctx->canvas_size.x = ctx->desc.canvas_width; + ctx->canvas_size.y = ctx->desc.canvas_height; + ctx->glyph_size.x = 8.0f / ctx->canvas_size.x; + ctx->glyph_size.y = 8.0f / ctx->canvas_size.y; + 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.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(); + _sdtx_clear(ctx, sizeof(*ctx)); + _sdtx_pool_free_index(&_sdtx.context_pool.pool, _sdtx_slot_index(ctx_id.id)); + } +} + +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); + SOKOL_ASSERT((font_desc->data.size > 0) && ((font_desc->data.size % 8) == 0)); + SOKOL_ASSERT(font_desc->first_char <= font_desc->last_char); + SOKOL_ASSERT((size_t)(((font_desc->last_char - font_desc->first_char) + 1) * 8) == font_desc->data.size); + const uint8_t* ptr = (const uint8_t*) font_desc->data.ptr; + for (int chr = font_desc->first_char; chr <= font_desc->last_char; chr++) { + for (int line = 0; line < 8; line++) { + uint8_t bits = *ptr++; + for (int x = 0; x < 8; x++) { + out_pixels[line*256*8 + chr*8 + x] = ((bits>>(7-x)) & 1) ? 0xFF : 0x00; + } + } + } +} + +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*) _sdtx_malloc_clear(_sdtx.fmt_buf_size); + + sg_push_debug_group("sokol-debugtext"); + + // common shader for all contexts + sg_shader_desc shd_desc; + _sdtx_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.label = "sokol-debugtext-shader"; + shd_desc.attrs[0].glsl_name = "position"; + shd_desc.attrs[1].glsl_name = "texcoord0"; + shd_desc.attrs[2].glsl_name = "color0"; + shd_desc.attrs[0].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[0].hlsl_sem_index = 0; + shd_desc.attrs[1].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[1].hlsl_sem_index = 1; + shd_desc.attrs[2].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[2].hlsl_sem_index = 2; + shd_desc.attrs[0].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[1].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[2].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.views[0].texture.image_type = SG_IMAGETYPE_2D; + shd_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.views[0].texture.hlsl_register_t_n = 0; + shd_desc.views[0].texture.msl_texture_n = 0; + shd_desc.views[0].texture.wgsl_group1_binding_n = 0; + shd_desc.views[0].texture.spirv_set1_binding_n = 0; + shd_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.samplers[0].hlsl_register_s_n = 0; + shd_desc.samplers[0].msl_sampler_n = 0; + shd_desc.samplers[0].wgsl_group1_binding_n = 32; + shd_desc.samplers[0].spirv_set1_binding_n = 32; + shd_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.texture_sampler_pairs[0].view_slot = 0; + shd_desc.texture_sampler_pairs[0].sampler_slot = 0; + shd_desc.texture_sampler_pairs[0].glsl_name = "tex_smp"; + #if defined(SOKOL_GLCORE) + shd_desc.vertex_func.source = (const char*)_sdtx_vs_source_glsl410; + shd_desc.fragment_func.source = (const char*)_sdtx_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vertex_func.source = (const char*)_sdtx_vs_source_glsl300es; + shd_desc.fragment_func.source = (const char*)_sdtx_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vertex_func.entry = "main0"; + shd_desc.fragment_func.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sdtx_vs_bytecode_metal_macos); + shd_desc.fragment_func.bytecode = SG_RANGE(_sdtx_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sdtx_vs_bytecode_metal_ios); + shd_desc.fragment_func.bytecode = SG_RANGE(_sdtx_fs_bytecode_metal_ios); + break; + default: + shd_desc.vertex_func.source = (const char*)_sdtx_vs_source_metal_sim; + shd_desc.fragment_func.source = (const char*)_sdtx_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vertex_func.bytecode = SG_RANGE(_sdtx_vs_bytecode_hlsl4); + shd_desc.fragment_func.bytecode = SG_RANGE(_sdtx_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vertex_func.source = (const char*)_sdtx_vs_source_wgsl; + shd_desc.fragment_func.source = (const char*)_sdtx_fs_source_wgsl; + #elif defined(SOKOL_VULKAN) + shd_desc.vertex_func.bytecode = SG_RANGE(_sdtx_vs_bytecode_spirv_vk); + shd_desc.vertex_func.entry = "main"; + shd_desc.fragment_func.bytecode = SG_RANGE(_sdtx_fs_bytecode_spirv_vk); + shd_desc.fragment_func.entry = "main"; + #else + shd_desc.vertex_func.source = _sdtx_vs_src_dummy; + shd_desc.fragment_func.source = _sdtx_fs_src_dummy; + #endif + _sdtx.shader = sg_make_shader(&shd_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sdtx.shader.id); + + // unpack font data + memset(_sdtx.font_pixels, 0xFF, sizeof(_sdtx.font_pixels)); + 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]); + } + } + + // create font image, texture view and sampler + sg_image_desc 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; + img_desc.data.mip_levels[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); + + sg_view_desc view_desc; + _sdtx_clear(&view_desc, sizeof(view_desc)); + view_desc.texture.image = _sdtx.font_img; + view_desc.label = "sdtx-font-texture-view"; + _sdtx.font_view = sg_make_view(&view_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_view.id); + + sg_sampler_desc smp_desc; + _sdtx_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_NEAREST; + smp_desc.mag_filter = SG_FILTER_NEAREST; + smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + smp_desc.label = "sdtx-font-sampler"; + _sdtx.font_smp = sg_make_sampler(&smp_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_smp.id); + + sg_pop_debug_group(); +} + +static void _sdtx_discard_common(void) { + sg_push_debug_group("sokol-debugtext"); + sg_destroy_sampler(_sdtx.font_smp); + sg_destroy_view(_sdtx.font_view); + sg_destroy_image(_sdtx.font_img); + sg_destroy_shader(_sdtx.shader); + if (_sdtx.fmt_buf) { + _sdtx_free(_sdtx.fmt_buf); + _sdtx.fmt_buf = 0; + } + sg_pop_debug_group(); +} + +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 float _sdtx_clamp(float v, float lo, float hi) { + if (v < lo) return lo; + else if (v > hi) return hi; + else return v; +} + +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); + uint8_t a_u8 = (uint8_t) (_sdtx_clamp(a, 0.0f, 1.0f) * 255.0f); + return _sdtx_pack_rgbab(r_u8, g_u8, b_u8, a_u8); +} + +static void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) { + switch (c) { + case '\r': + ctx->pos.x = 0.0f; + break; + case '\n': + ctx->pos.x = 0.0f; + ctx->pos.y += 1.0f; + break; + case '\t': + ctx->pos.x = (ctx->pos.x - fmodf(ctx->pos.x, ctx->tab_width)) + ctx->tab_width; + break; + case ' ': + ctx->pos.x += 1.0f; + break; + } +} + +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 height 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) + 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 + 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++; + + 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->pos.x += 1.0f; +} + +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_render_char(ctx, c_u8); + } +} + +SOKOL_API_IMPL void _sdtx_draw_layer(_sdtx_context_t* ctx, int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(ctx); + if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-debugtext"); + + if (ctx->update_frame_id != ctx->frame_id) { + ctx->update_frame_id = ctx->frame_id; + const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sdtx_vertex_t) }; + sg_update_buffer(ctx->vbuf, &range); + } + + sg_apply_pipeline(ctx->pip); + sg_bindings bindings; + _sdtx_clear(&bindings, sizeof(bindings)); + bindings.vertex_buffers[0] = ctx->vbuf; + bindings.views[0] = _sdtx.font_view; + bindings.samplers[0] = _sdtx.font_smp; + 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_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + 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 (res.fonts[i].data.ptr) { + res.fonts[i].last_char = _sdtx_def(res.fonts[i].last_char, 255); + } + } + 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 +SOKOL_API_IMPL void sdtx_setup(const sdtx_desc_t* desc) { + SOKOL_ASSERT(desc); + _sdtx_clear(&_sdtx, sizeof(_sdtx)); + _sdtx.init_cookie = _SDTX_INIT_COOKIE; + _sdtx.desc = _sdtx_desc_defaults(desc); + _sdtx_setup_context_pool(&_sdtx.desc); + _sdtx_setup_common(); + _sdtx.def_ctx_id = sdtx_make_context(&_sdtx.desc.context); + SOKOL_ASSERT(SDTX_DEFAULT_CONTEXT.id == _sdtx.def_ctx_id.id); + sdtx_set_context(_sdtx.def_ctx_id); +} + +SOKOL_API_IMPL void sdtx_shutdown(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + for (int i = 0; i < _sdtx.context_pool.pool.size; i++) { + _sdtx_context_t* ctx = &_sdtx.context_pool.contexts[i]; + _sdtx_destroy_context(_sdtx_make_ctx_id(ctx->slot.id)); + } + _sdtx_discard_common(); + _sdtx_discard_context_pool(); + _sdtx.init_cookie = 0; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_kc853(void) { + sdtx_font_desc_t desc = { { _sdtx_font_kc853, sizeof(_sdtx_font_kc853) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_kc854(void) { + sdtx_font_desc_t desc = { { _sdtx_font_kc854, sizeof(_sdtx_font_kc854) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_z1013(void) { + sdtx_font_desc_t desc = { { _sdtx_font_z1013, sizeof(_sdtx_font_z1013) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_cpc(void) { + sdtx_font_desc_t desc = { { _sdtx_font_cpc, sizeof(_sdtx_font_cpc) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_c64(void) { + sdtx_font_desc_t desc = { { _sdtx_font_c64, sizeof(_sdtx_font_c64) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_oric(void) { + sdtx_font_desc_t desc = { { _sdtx_font_oric, sizeof(_sdtx_font_oric) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_context sdtx_make_context(const sdtx_context_desc_t* desc) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(desc); + sdtx_context ctx_id = _sdtx_alloc_context(); + if (ctx_id.id != SG_INVALID_ID) { + _sdtx_init_context(ctx_id, desc); + } else { + _SDTX_ERROR(CONTEXT_POOL_EXHAUSTED); + } + return ctx_id; +} + +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)) { + _SDTX_ERROR(CANNOT_DESTROY_DEFAULT_CONTEXT); + return; + } + _sdtx_destroy_context(ctx_id); + // re-validate the current context pointer (this will return a nullptr + // if we just destroyed the current context) + _sdtx.cur_ctx = _sdtx_lookup_context(_sdtx.cur_ctx_id.id); +} + +SOKOL_API_IMPL void sdtx_set_context(sdtx_context ctx_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + if (_sdtx_is_default_context(ctx_id)) { + _sdtx.cur_ctx_id = _sdtx.def_ctx_id; + } else { + _sdtx.cur_ctx_id = ctx_id; + } + // this may return a nullptr if the ctx_id handle is invalid + _sdtx.cur_ctx = _sdtx_lookup_context(_sdtx.cur_ctx_id.id); +} + +SOKOL_API_IMPL sdtx_context sdtx_get_context(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + return _sdtx.cur_ctx_id; +} + +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)); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->cur_font = font_index; + } +} + +SOKOL_API_IMPL void sdtx_canvas(float w, float h) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(!isnan(w)); + SOKOL_ASSERT(!isnan(h)); + SOKOL_ASSERT((w > 0.0f) && (h > 0.0f)); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->canvas_size.x = w; + ctx->canvas_size.y = h; + ctx->glyph_size.x = (8.0f / ctx->canvas_size.x); + ctx->glyph_size.y = (8.0f / ctx->canvas_size.y); + ctx->origin.x = 0.0f; + ctx->origin.y = 0.0f; + ctx->pos.x = 0.0f; + ctx->pos.y = 0.0f; + } +} + +SOKOL_API_IMPL void sdtx_origin(float x, float y) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->origin.x = x; + ctx->origin.y = y; + } +} + +SOKOL_API_IMPL void sdtx_home(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = 0.0f; + ctx->pos.y = 0.0f; + } +} + +SOKOL_API_IMPL void sdtx_pos(float x, float y) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = x; + ctx->pos.y = y; + } +} + +SOKOL_API_IMPL void sdtx_pos_x(float x) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = x; + } +} + +SOKOL_API_IMPL void sdtx_pos_y(float y) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.y = y; + } +} + +SOKOL_API_IMPL void sdtx_move(float dx, float dy) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x += dx; + ctx->pos.y += dy; + } +} + +SOKOL_API_IMPL void sdtx_move_x(float dx) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x += dx; + } +} + +SOKOL_API_IMPL void sdtx_move_y(float dy) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.y += dy; + } +} + +SOKOL_API_IMPL void sdtx_crlf(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = 0.0f; + ctx->pos.y += 1.0f; + } +} + +SOKOL_API_IMPL void sdtx_color3b(uint8_t r, uint8_t g, uint8_t b) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbab(r, g, b, 255); + } +} + +SOKOL_API_IMPL void sdtx_color3f(float r, float g, float b) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbaf(r, g, b, 1.0f); + } +} + +SOKOL_API_IMPL void sdtx_color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbab(r, g, b, a); + } +} + +SOKOL_API_IMPL void sdtx_color4f(float r, float g, float b, float a) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbaf(r, g, b, a); + } +} + +SOKOL_API_IMPL void sdtx_color1i(uint32_t rgba) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = rgba; + } +} + +SOKOL_DEBUGTEXT_API_DECL void sdtx_putc(char chr) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_put_char(ctx, chr); + } +} + +SOKOL_DEBUGTEXT_API_DECL void sdtx_puts(const char* str) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + char chr; + while (0 != (chr = *str++)) { + _sdtx_put_char(ctx, chr); + } + } +} + +SOKOL_DEBUGTEXT_API_DECL void sdtx_putr(const char* str, int len) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + for (int i = 0; i < len; i++) { + char chr = str[i]; + if (0 == chr) { + break; + } + _sdtx_put_char(ctx, chr); + } + } +} + +SOKOL_DEBUGTEXT_API_DECL int sdtx_vprintf(const char* fmt, va_list args) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(_sdtx.fmt_buf && (_sdtx.fmt_buf_size >= 2)); + int res = SOKOL_VSNPRINTF(_sdtx.fmt_buf, _sdtx.fmt_buf_size, fmt, args); + // make sure we're 0-terminated in case we're on an old MSVC + _sdtx.fmt_buf[_sdtx.fmt_buf_size-1] = 0; + sdtx_puts(_sdtx.fmt_buf); + return res; +} + +SOKOL_DEBUGTEXT_API_DECL int sdtx_printf(const char* fmt, ...) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(_sdtx.fmt_buf && (_sdtx.fmt_buf_size >= 2)); + va_list args; + va_start(args, fmt); + int res = SOKOL_VSNPRINTF(_sdtx.fmt_buf, _sdtx.fmt_buf_size, fmt, args); + va_end(args); + // make sure we're 0-terminated in case we're on an old MSVC + _sdtx.fmt_buf[_sdtx.fmt_buf_size-1] = 0; + sdtx_puts(_sdtx.fmt_buf); + return res; +} + +SOKOL_DEBUGTEXT_API_DECL sdtx_range sdtx_get_cleared_fmt_buffer(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(_sdtx.fmt_buf && (_sdtx.fmt_buf_size >= 2)); + memset(_sdtx.fmt_buf, 0, _sdtx.fmt_buf_size); + sdtx_range res; _sdtx_clear(&res, sizeof(res)); + res.ptr = _sdtx.fmt_buf; + res.size = _sdtx.fmt_buf_size - 1; + return res; +} + +SOKOL_API_IMPL void sdtx_draw(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _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/thirdparty/sokol/util/sokol_fontstash.h b/thirdparty/sokol/util/sokol_fontstash.h new file mode 100644 index 0000000..4672b50 --- /dev/null +++ b/thirdparty/sokol/util/sokol_fontstash.h @@ -0,0 +1,2492 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_FONTSTASH_IMPL) +#define SOKOL_FONTSTASH_IMPL +#endif +#ifndef SOKOL_FONTSTASH_INCLUDED +/* + sokol_fontstash.h -- renderer for https://github.com/memononen/fontstash + on top of sokol_gl.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_FONTSTASH_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + SOKOL_VULKAN + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + Include the following headers before including sokol_fontstash.h: + + sokol_gfx.h + + Additionally include the following headers for including the sokol_fontstash.h + implementation: + + sokol_gl.h + + HOW TO + ====== + --- First initialize sokol-gfx and sokol-gl as usual: + + sg_setup(&(sg_desc){...}); + sgl_setup(&(sgl_desc){...}); + + --- Create at least one fontstash context with sfons_create() (this replaces + glfonsCreate() from fontstash.h's example GL renderer: + + 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. + + --- From here on, use fontstash.h's functions "as usual" to add TTF + font data and draw text. Note that (just like with sokol-gl), text + rendering can happen anywhere in the frame, not only inside + a sokol-gfx rendering pass. + + --- You can use the helper function + + uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + + To convert a 0..255 RGBA color into a packed uint32_t color value + expected by fontstash.h. + + --- Once per frame before calling sgl_draw(), call: + + sfons_flush(FONScontext* ctx) + + ...this will update the dynamic sokol-gfx texture with the latest font + atlas content. + + --- To actually render the text (and any other sokol-gl draw commands), + call sgl_draw() inside a sokol-gfx frame. + + --- NOTE that you can mix fontstash.h calls with sokol-gl calls to mix + text rendering with sokol-gl rendering. You can also use + sokol-gl's matrix stack to position fontstash.h text in 3D. + + --- finally on application shutdown, call: + + sfons_destroy(FONScontext* ctx) + + before sgl_shutdown() and sg_shutdown() + + + WHAT HAPPENS UNDER THE HOOD: + ============================ + + 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 + - creates a 1-byte-per-pixel font atlas texture via sokol-gfx + (pixel format SG_PIXELFORMAT_R8) + + fonsDrawText(): + - this will call the following sequence of sokol-gl functions: + + sgl_enable_texture(); + sgl_texture(...); + sgl_push_pipeline(); + sgl_load_pipeline(...); + sgl_begin_triangles(); + for each vertex: + sgl_v2f_t2f_c1i(...); + sgl_end(); + sgl_pop_pipeline(); + sgl_disable_texture(); + + - note that sokol-gl will merge several sgl_*_begin/sgl_end pairs + into a single draw call if no relevant state has changed, typically + all calls to fonsDrawText() will be merged into a single draw call + as long as all calls use the same FONScontext + + 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_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_fn = my_alloc, + .free_fn = 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 + + Copyright (c) 2018 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_FONTSTASH_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_fontstash.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_FONTSTASH_API_DECL) +#define SOKOL_FONTSTASH_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_FONTSTASH_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_FONTSTASH_IMPL) +#define SOKOL_FONTSTASH_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_FONTSTASH_API_DECL __declspec(dllimport) +#else +#define SOKOL_FONTSTASH_API_DECL extern +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif + +/* + 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_fn and free_fn 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_fn)(size_t size, void* user_data); + void (*free_fn)(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); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* SOKOL_FONTSTASH_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_FONTSTASH_IMPL +#define SOKOL_FONTSTASH_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 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" +#endif +#if !defined(FONS_H) +#error "Please include fontstash.h before sokol_fontstash.h" +#endif + +#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_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +/* + Embedded source code compiled with: + + sokol-shdc -i sfons.glsl -o sfons.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + layout(binding=0) uniform vs_params { + uniform mat4 mvp; + uniform mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + in float psize; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + #ifndef SOKOL_WGSL + gl_PointSize = psize; + #endif + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + layout(binding=0) uniform texture2D tex; + layout(binding=0) uniform sampler smp; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = vec4(1.0, 1.0, 1.0, texture(sampler2D(tex, smp), uv.xy).r) * color; + } + @end + + @program sfontstash vs fs +*/ +#if defined(SOKOL_GLCORE) +/* + #version 410 + + uniform vec4 vs_params[8]; + 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; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = mat4(vs_params[0], vs_params[1], vs_params[2], vs_params[3]) * position; + gl_PointSize = psize; + uv = mat4(vs_params[4], vs_params[5], vs_params[6], vs_params[7]) * vec4(texcoord0, 0.0, 1.0); + color = color0; + } +*/ +static const uint8_t _sfons_vs_source_glsl410[520] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,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, + 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,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,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,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,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, +}; +/* + #version 410 + + uniform sampler2D tex_smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec4 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = vec4(1.0, 1.0, 1.0, texture(tex_smp, uv.xy).x) * color; + } +*/ +static const uint8_t _sfons_fs_source_glsl410[245] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,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,0x30,0x29,0x20,0x69,0x6e,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, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e, + 0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76, + 0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +/* + #version 300 es + + uniform vec4 vs_params[8]; + layout(location = 0) in vec4 position; + layout(location = 3) in float psize; + out vec4 uv; + layout(location = 1) in vec2 texcoord0; + out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = mat4(vs_params[0], vs_params[1], vs_params[2], vs_params[3]) * position; + gl_PointSize = psize; + uv = mat4(vs_params[4], vs_params[5], vs_params[6], vs_params[7]) * vec4(texcoord0, 0.0, 1.0); + color = color0; + } +*/ +static const uint8_t _sfons_vs_source_glsl300es[481] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,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, +}; +/* + #version 300 es + precision mediump float; + precision highp int; + + uniform highp sampler2D tex_smp; + + layout(location = 0) out highp vec4 frag_color; + in highp vec4 uv; + in highp vec4 color; + + void main() + { + frag_color = vec4(1.0, 1.0, 1.0, texture(tex_smp, uv.xy).x) * color; + } + +*/ +static const uint8_t _sfons_fs_source_glsl300es[276] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e,0x30, + 0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e, + 0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + float4x4 tm; + }; + + struct main0_out + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; + }; + + struct main0_in + { + float4 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + float psize [[attribute(3)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = in.psize; + out.uv = _19.tm * float4(in.texcoord0, 0.0, 1.0); + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sfons_vs_bytecode_metal_macos[3381] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x35,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,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,0x59,0xf1,0xe4,0x22,0x0f,0x75,0x42, + 0x53,0x9c,0x2a,0x05,0xe4,0xbd,0x91,0x28,0x82,0x06,0x3f,0x0d,0xb9,0xef,0x34,0xbd, + 0xe8,0xfa,0x46,0x5c,0x66,0x1f,0xc6,0xde,0x3a,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,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,0x04,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xfe,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,0x68,0x00,0x00, + 0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00,0x8a,0x08,0x07,0x78, + 0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73,0xa8,0x07,0x77,0x18,0x87,0x36,0x30, + 0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca, + 0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8,0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1, + 0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c, + 0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00,0x06,0x77,0x78,0x87,0x36,0x10,0x87, + 0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87,0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea, + 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,0xda,0xc0,0x1d,0xde,0xc1,0x1d,0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc, + 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc, + 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,0x83,0x79,0x48,0x87,0x73,0x70, + 0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90,0x87,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,0x80,0x1e,0xe4,0x21,0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d, + 0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07, + 0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87,0x36,0x80,0x07,0x79,0x78,0x07,0x7a, + 0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36,0x10,0x87,0x7a,0x30,0x07,0x73,0x28, + 0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28,0x07,0x00,0x0f,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,0x36,0x18,0x42,0x01,0x2c,0x40, + 0x05,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,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65, + 0x66,0xa6,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,0x62,0x72,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,0x79,0x18,0x00, + 0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43, + 0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4, + 0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6, + 0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0, + 0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1, + 0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f, + 0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc, + 0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19, + 0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3, + 0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87, + 0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77, + 0xa8,0x07,0x78,0x98,0x07,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,0xf4,0xc6,0x22, + 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0x46,0x00,0xa8, + 0x95,0x40,0x19,0x14,0x01,0x8d,0x19,0x00,0x12,0x33,0x00,0x14,0x66,0x00,0x66,0x00, + 0x00,0xe3,0x15,0x8c,0xa4,0x69,0x12,0x05,0x65,0x90,0x41,0x22,0x14,0x13,0x02,0xf9, + 0x8c,0x57,0x40,0x96,0xe7,0x2d,0x14,0x94,0x41,0x06,0xeb,0x78,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x8a,0x6a,0x1b,0x83,0x31,0x70,0x28,0x28,0x83,0x0c,0x1b,0x53, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0x1a,0x18,0xa0,0x01,0x1a,0x50,0x14, + 0x94,0x41,0x06,0x30,0x88,0x36,0x13,0x02,0xf9,0x58,0x11,0xc0,0x67,0xbc,0xe2,0x2b, + 0x03,0x37,0x68,0x83,0x32,0xa0,0xa0,0x0c,0x32,0x90,0x41,0xd6,0x99,0x10,0xc8,0x67, + 0xbc,0x62,0x0c,0xd2,0x40,0x0e,0xe2,0xc0,0xa3,0xa0,0x0c,0x32,0xa0,0x41,0x27,0x06, + 0x26,0x04,0xf2,0xb1,0xa0,0x80,0xcf,0x78,0x05,0x1a,0xb8,0xc1,0x1d,0xd8,0x81,0x18, + 0x50,0x50,0x6c,0x08,0xe0,0x33,0xdb,0x20,0x06,0x01,0x30,0xdb,0x10,0xb8,0x41,0x30, + 0xdb,0x10,0x3c,0xc2,0x6c,0x43,0xf0,0x06,0x43,0x06,0x01,0x31,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, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = float4(1.0, 1.0, 1.0, tex.sample(smp, in.uv.xy).x) * in.color; + return out; + } +*/ +static const uint8_t _sfons_fs_bytecode_metal_macos[3065] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x00,0x93,0xa3,0x32,0x70,0x27,0x04, + 0x4a,0x88,0x6f,0x1e,0x08,0x67,0xd0,0x5a,0xba,0xd2,0x72,0x48,0xca,0xe7,0x64,0x0b, + 0xdf,0x26,0xd3,0x77,0xf1,0x3d,0xde,0x93,0x55,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,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,0x00,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xbd,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,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,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,0x30,0x02,0xb1,0x8c,0x00,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,0xe1,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xd1,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,0x46, + 0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,0x0e,0x73, + 0x6d,0x70,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,0x03,0x5e,0x61,0x69,0x72,0x2d,0x61,0x6c,0x69,0x61,0x73,0x2d, + 0x73,0x63,0x6f,0x70,0x65,0x73,0x28,0x6d,0x61,0x69,0x6e,0x30,0x29,0x43,0x88,0x87, + 0x0d,0x9e,0x35,0x20,0x16,0x96,0x26,0xd7,0x12,0xc6,0x96,0x16,0x36,0xd7,0x32,0x37, + 0xf6,0x06,0x57,0xd6,0x42,0x57,0x86,0x47,0x57,0x27,0x57,0x36,0x37,0xc4,0x78,0xdc, + 0xe0,0x61,0x83,0xa7,0x0d,0x88,0x85,0xa5,0xc9,0xb5,0x84,0xb1,0xa5,0x85,0xcd,0xb5, + 0xcc,0x8d,0xbd,0xc1,0x95,0xb5,0xcc,0x85,0xb5,0xc1,0xb1,0x95,0xc9,0xcd,0x0d,0x31, + 0x1e,0x38,0x78,0xd8,0xe0,0x79,0x83,0x21,0xc4,0xe3,0x06,0x0f,0x1c,0x8c,0x88,0xd8, + 0x81,0x1d,0xec,0xa1,0x1d,0xdc,0xa0,0x1d,0xde,0x81,0x1c,0xea,0x81,0x1d,0xca,0xc1, + 0x0d,0xcc,0x81,0x1d,0xc2,0xe1,0x1c,0xe6,0x61,0x8a,0x10,0x0c,0x23,0x14,0x76,0x60, + 0x07,0x7b,0x68,0x07,0x37,0x48,0x07,0x72,0x28,0x07,0x77,0xa0,0x87,0x29,0x41,0x31, + 0x62,0x09,0x87,0x74,0x90,0x07,0x37,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77, + 0x70,0x87,0x29,0x81,0x31,0x82,0x0a,0x87,0x74,0x90,0x07,0x37,0x60,0x87,0x70,0x70, + 0x87,0x73,0xa8,0x87,0x70,0x38,0x87,0x72,0xf8,0x05,0x7b,0x28,0x07,0x79,0x98,0x87, + 0x74,0x78,0x07,0x77,0x98,0x12,0x20,0x23,0xa6,0x70,0x48,0x07,0x79,0x70,0x83,0x71, + 0x78,0x87,0x76,0x80,0x87,0x74,0x60,0x87,0x72,0xf8,0x85,0x77,0x80,0x07,0x7a,0x48, + 0x87,0x77,0x70,0x87,0x79,0x98,0x32,0x28,0x8c,0x33,0x82,0x09,0x87,0x74,0x90,0x07, + 0x37,0x30,0x07,0x79,0x08,0x87,0x73,0x68,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0xa8, + 0x01,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8, + 0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08, + 0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20, + 0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e, + 0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2, + 0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4, + 0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8, + 0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08, + 0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10, + 0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30, + 0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78, + 0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,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,0x1a,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,0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18, + 0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28, + 0x43,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66, + 0x1b,0x94,0x00,0xc8,0x20,0x20,0x06,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0xe0, + 0x88,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + float4x4 tm; + }; + + struct main0_out + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; + }; + + struct main0_in + { + float4 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + float psize [[attribute(3)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = in.psize; + out.uv = _19.tm * float4(in.texcoord0, 0.0, 1.0); + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sfons_vs_bytecode_metal_ios[3493] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa5,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x90,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,0x81,0x80,0x75,0xbc,0xa9,0xd0,0xb9, + 0x4f,0xee,0x63,0x10,0x8f,0xfe,0x66,0xa5,0x3b,0x40,0xb7,0x43,0x44,0x12,0xba,0x69, + 0x7d,0xf3,0x83,0x9a,0xda,0x47,0x00,0x29,0xec,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,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,0x70,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0x19,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,0x70,0x00,0x00, + 0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00,0x8a,0x08,0x07,0x78, + 0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73,0xa8,0x07,0x77,0x18,0x87,0x36,0x30, + 0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca, + 0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8,0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1, + 0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c, + 0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00,0x06,0x77,0x78,0x87,0x36,0x10,0x87, + 0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87,0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea, + 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,0xda,0xc0,0x1d,0xde,0xc1,0x1d,0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc, + 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc, + 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,0x83,0x79,0x48,0x87,0x73,0x70, + 0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90,0x87,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,0x80,0x1e,0xe4,0x21,0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d, + 0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07, + 0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87,0x36,0x80,0x07,0x79,0x78,0x07,0x7a, + 0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36,0x10,0x87,0x7a,0x30,0x07,0x73,0x28, + 0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28,0x07,0x00,0x0f,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,0x36,0x6c,0x42,0x01,0x2c,0x40, + 0x35,0x84,0x43,0x3a,0xc8,0x43,0x1b,0x88,0x43,0x3d,0x98,0x83,0x39,0x94,0x83,0x3c, + 0xb4,0x81,0x3b,0xbc,0x43,0x1b,0x84,0x03,0x3b,0xa4,0x43,0x38,0xcc,0x03,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,0x48,0x00,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x12,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,0x46, + 0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,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,0x62,0x72,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,0xe1,0x15,0x96,0x26,0xd7,0x12,0xc6,0x96,0x16,0x36,0xd7,0x32,0x37,0xf6, + 0x06,0x57,0x36,0x87,0xd2,0x16,0x96,0xe6,0x06,0x93,0x32,0x84,0x50,0x44,0x41,0x09, + 0x05,0x5a,0x61,0x69,0x72,0x2d,0x61,0x6c,0x69,0x61,0x73,0x2d,0x73,0x63,0x6f,0x70, + 0x65,0x2d,0x61,0x72,0x67,0x28,0x34,0x29,0x43,0x0c,0x85,0x14,0x14,0x51,0x50,0x46, + 0x61,0x88,0xa0,0x90,0xc2,0x88,0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda,0xe1, + 0x1d,0xc8,0xa1,0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61,0x1e, + 0xa6,0x08,0xc1,0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87, + 0x72,0x70,0x07,0x7a,0x98,0x12,0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b, + 0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x18,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,0x01,0x32,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,0x83,0xc2, + 0x38,0x23,0x94,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0xa0,0x87,0x72, + 0xc0,0x87,0x29,0xc1,0x1e,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19, + 0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c, + 0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76, + 0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10, + 0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e, + 0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d, + 0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b, + 0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77, + 0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5, + 0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50, + 0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71, + 0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98, + 0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x42,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,0x46,0x00,0xa8,0x95,0x40,0x19,0x14, + 0x01,0x8d,0x19,0x00,0x12,0x33,0x00,0x14,0x66,0x00,0x66,0x00,0x00,0xe3,0x15,0x8c, + 0xa4,0x69,0x12,0x05,0x65,0x90,0x41,0x22,0x14,0x13,0x02,0xf9,0x8c,0x57,0x40,0x96, + 0xe7,0x2d,0x14,0x94,0x41,0x06,0xeb,0x78,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x8a,0x6a,0x1b,0x83,0x31,0x70,0x28,0x28,0x83,0x0c,0x1b,0x53,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0x1a,0x18,0xa0,0x01,0x1a,0x50,0x14,0x94,0x41,0x06,0x30, + 0x88,0x36,0x13,0x02,0xf9,0x58,0x11,0xc0,0x67,0xbc,0xe2,0x2b,0x03,0x37,0x68,0x83, + 0x32,0xa0,0xa0,0x0c,0x32,0x90,0x41,0xd6,0x99,0x10,0xc8,0x67,0xbc,0x62,0x0c,0xd2, + 0x40,0x0e,0xe2,0xc0,0xa3,0xa0,0x0c,0x32,0xa0,0x41,0x27,0x06,0x26,0x04,0xf2,0xb1, + 0xa0,0x80,0xcf,0x78,0x05,0x1a,0xb8,0xc1,0x1d,0xd8,0x81,0x18,0x50,0x50,0x6c,0x08, + 0xe0,0x33,0xdb,0x20,0x06,0x01,0x30,0xdb,0x10,0xb8,0x41,0x30,0xdb,0x10,0x3c,0xc2, + 0x6c,0x43,0xf0,0x06,0x43,0x06,0x01,0x31,0x00,0x0d,0x00,0x00,0x00,0x5b,0x8a,0x20, + 0x00,0x85,0xa3,0x14,0xb6,0x14,0x45,0x00,0x0a,0x47,0x29,0x6c,0x29,0x94,0x00,0x14, + 0x8e,0x52,0xd8,0x52,0x3c,0x01,0x28,0x1c,0xa5,0xb0,0xa5,0xa0,0x02,0x50,0x38,0x4a, + 0x61,0x4b,0x81,0x05,0xa0,0x70,0x94,0xc2,0x96,0xa2,0x0b,0x40,0xe1,0x28,0x05,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = float4(1.0, 1.0, 1.0, tex.sample(smp, in.uv.xy).x) * in.color; + return out; + } +*/ +static const uint8_t _sfons_fs_bytecode_metal_ios[3049] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x1c,0x34,0xc1,0x68,0x6e,0xbc,0x1a, + 0x30,0x92,0x74,0x28,0x57,0x7d,0x71,0xda,0x98,0x7b,0x20,0xf9,0x6e,0x40,0x73,0x89, + 0x90,0x99,0xa5,0x20,0xbe,0x88,0x63,0xc5,0x97,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,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,0xf4,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xba,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,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,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,0x30,0x02,0xb1,0x8c,0x00,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,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x41,0x02,0x00, + 0x00,0x79,0x18,0x00,0x00,0xd0,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65, + 0x66,0xa6,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,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,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,0x0e,0x73,0x6d,0x70,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,0x03,0x5e,0x61,0x69,0x72, + 0x2d,0x61,0x6c,0x69,0x61,0x73,0x2d,0x73,0x63,0x6f,0x70,0x65,0x73,0x28,0x6d,0x61, + 0x69,0x6e,0x30,0x29,0x43,0x88,0x87,0x0d,0x9e,0x35,0x20,0x16,0x96,0x26,0xd7,0x12, + 0xc6,0x96,0x16,0x36,0xd7,0x32,0x37,0xf6,0x06,0x57,0xd6,0x42,0x57,0x86,0x47,0x57, + 0x27,0x57,0x36,0x37,0xc4,0x78,0xdc,0xe0,0x61,0x83,0xa7,0x0d,0x88,0x85,0xa5,0xc9, + 0xb5,0x84,0xb1,0xa5,0x85,0xcd,0xb5,0xcc,0x8d,0xbd,0xc1,0x95,0xb5,0xcc,0x85,0xb5, + 0xc1,0xb1,0x95,0xc9,0xcd,0x0d,0x31,0x1e,0x38,0x78,0xd8,0xe0,0x79,0x83,0x21,0xc4, + 0xe3,0x06,0x0f,0x1c,0x8c,0x88,0xd8,0x81,0x1d,0xec,0xa1,0x1d,0xdc,0xa0,0x1d,0xde, + 0x81,0x1c,0xea,0x81,0x1d,0xca,0xc1,0x0d,0xcc,0x81,0x1d,0xc2,0xe1,0x1c,0xe6,0x61, + 0x8a,0x10,0x0c,0x23,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x48,0x07,0x72,0x28, + 0x07,0x77,0xa0,0x87,0x29,0x41,0x31,0x62,0x09,0x87,0x74,0x90,0x07,0x37,0xb0,0x87, + 0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x81,0x31,0x82,0x0a,0x87,0x74, + 0x90,0x07,0x37,0x60,0x87,0x70,0x70,0x87,0x73,0xa8,0x87,0x70,0x38,0x87,0x72,0xf8, + 0x05,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x20,0x23,0xa6, + 0x70,0x48,0x07,0x79,0x70,0x83,0x71,0x78,0x87,0x76,0x80,0x87,0x74,0x60,0x87,0x72, + 0xf8,0x85,0x77,0x80,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x79,0x98,0x32,0x28,0x8c, + 0x33,0x82,0x09,0x87,0x74,0x90,0x07,0x37,0x30,0x07,0x79,0x08,0x87,0x73,0x68,0x87, + 0x72,0x70,0x07,0x7a,0x98,0x12,0xa8,0x01,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19, + 0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c, + 0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76, + 0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10, + 0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e, + 0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d, + 0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b, + 0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77, + 0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5, + 0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50, + 0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71, + 0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98, + 0x07,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,0x1a,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,0x06,0x80,0xc0,0x0c, + 0xc0,0x08,0xc0,0x18,0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x83,0x0c,0x0f, + 0x91,0x8c,0x18,0x28,0x43,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d, + 0x05,0x91,0x7c,0x66,0x1b,0x94,0x00,0xc8,0x20,0x20,0x06,0x00,0x00,0x02,0x00,0x00, + 0x00,0x5b,0x86,0xe0,0x88,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + float4x4 tm; + }; + + struct main0_out + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; + }; + + struct main0_in + { + float4 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + float psize [[attribute(3)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = in.psize; + out.uv = _19.tm * float4(in.texcoord0, 0.0, 1.0); + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sfons_vs_source_metal_sim[756] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,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,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,0x31,0x39,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,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a, + 0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,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,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x31,0x39,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,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, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = float4(1.0, 1.0, 1.0, tex.sample(smp, in.uv.xy).x) * in.color; + return out; + } +*/ +static const uint8_t _sfons_fs_source_metal_sim[464] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,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, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,0x73,0x6d,0x70,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,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,0x73,0x6d, + 0x70,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) +/* + cbuffer vs_params : register(b0) + { + row_major float4x4 _19_mvp : packoffset(c0); + row_major float4x4 _19_tm : packoffset(c4); + }; + + + static float4 gl_Position; + static float gl_PointSize; + static float4 position; + static float psize; + static float4 uv; + static float2 texcoord0; + static float4 color; + static float4 color0; + + struct SPIRV_Cross_Input + { + float4 position : TEXCOORD0; + float2 texcoord0 : TEXCOORD1; + float4 color0 : TEXCOORD2; + float psize : TEXCOORD3; + }; + + struct SPIRV_Cross_Output + { + float4 uv : TEXCOORD0; + float4 color : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = mul(position, _19_mvp); + gl_PointSize = psize; + uv = mul(float4(texcoord0, 0.0f, 1.0f), _19_tm); + color = color0; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + position = stage_input.position; + psize = stage_input.psize; + texcoord0 = stage_input.texcoord0; + color0 = stage_input.color0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.uv = uv; + stage_output.color = color; + return stage_output; + } +*/ +static const uint8_t _sfons_vs_bytecode_hlsl4[1032] = { + 0x44,0x58,0x42,0x43,0x74,0x7f,0x01,0xd9,0xf4,0xd5,0xed,0x1d,0x74,0xc1,0x30,0x27, + 0xd8,0xe9,0x9d,0x50,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 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,0x31,0x39,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,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,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,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, + 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, +}; +/* + Texture2D tex : register(t0); + SamplerState smp : register(s0); + + static float4 frag_color; + static float4 uv; + static float4 color; + + struct SPIRV_Cross_Input + { + float4 uv : TEXCOORD0; + float4 color : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 frag_color : SV_Target0; + }; + + void frag_main() + { + frag_color = float4(1.0f, 1.0f, 1.0f, tex.Sample(smp, uv.xy).x) * color; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + uv = stage_input.uv; + color = stage_input.color; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.frag_color = frag_color; + return stage_output; + } +*/ +static const uint8_t _sfons_fs_bytecode_hlsl4[628] = { + 0x44,0x58,0x42,0x43,0xb6,0x66,0xf0,0xfc,0x09,0x54,0x2a,0x35,0x84,0x1d,0x27,0xd2, + 0xff,0xb3,0x2e,0xdb,0x01,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xf8,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,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,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0xa8,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x96,0x73,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0x12,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x01,0x40,0x00,0x00,0x00,0x00,0x80,0x3f,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x06,0x0c,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00, + 0x01,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,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,0x01,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,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) +/* + diagnostic(off, derivative_uniformity); + + struct vs_params { + /_ @offset(0) _/ + mvp : mat4x4f, + /_ @offset(64) _/ + tm : mat4x4f, + } + + @binding(0) @group(0) var x_19 : vs_params; + + var position_1 : vec4f; + + var uv : vec4f; + + var texcoord0 : vec2f; + + var color : vec4f; + + var color0 : vec4f; + + var psize : f32; + + var gl_Position : vec4f; + + fn main_1() { + gl_Position = (x_19.mvp * position_1); + uv = (x_19.tm * vec4f(texcoord0.x, texcoord0.y, 0.0f, 1.0f)); + color = color0; + return; + } + + struct main_out { + @builtin(position) + gl_Position : vec4f, + @location(0) + uv_1 : vec4f, + @location(1) + color_1 : vec4f, + } + + @vertex + fn main(@location(0) position_1_param : vec4f, @location(1) texcoord0_param : vec2f, @location(2) color0_param : vec4f, @location(3) psize_param : f32) -> main_out { + position_1 = position_1_param; + texcoord0 = texcoord0_param; + color0 = color0_param; + psize = psize_param; + main_1(); + return main_out(gl_Position, uv, color); + } +*/ +static const uint8_t _sfons_vs_source_wgsl[1027] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x6d,0x76,0x70,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78,0x34,0x66,0x2c,0x0a, + 0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x36,0x34,0x29, + 0x20,0x2a,0x2f,0x0a,0x20,0x20,0x74,0x6d,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78, + 0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28, + 0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x76,0x61,0x72, + 0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76,0x61,0x72, + 0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x0a, + 0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b, + 0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x28,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x29,0x3b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d, + 0x20,0x28,0x78,0x5f,0x31,0x39,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2e,0x78,0x2c,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x66, + 0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75, + 0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a, + 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74, + 0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x28,0x33,0x29,0x20,0x70,0x73,0x69,0x7a,0x65,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x20,0x3a,0x20,0x66,0x33,0x32,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64, + 0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x3b,0x0a,0x20,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, + 0x65,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a, + 0x7d,0x0a,0x00, +}; +/* + diagnostic(off, derivative_uniformity); + + var frag_color : vec4f; + + @binding(0) @group(1) var tex : texture_2d; + + @binding(32) @group(1) var smp : sampler; + + var uv : vec4f; + + var color : vec4f; + + fn main_1() { + let x_24 = uv; + let x_26 = textureSample(tex, smp, x_24.xy); + frag_color = (vec4f(1.0f, 1.0f, 1.0f, x_26.x) * color); + return; + } + + struct main_out { + @location(0) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(0) uv_param : vec4f, @location(1) color_param : vec4f) -> main_out { + uv = uv_param; + color = color_param; + main_1(); + return main_out(frag_color); + } +*/ +static const uint8_t _sfons_fs_source_wgsl[615] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64, + 0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29, + 0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76, + 0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b, + 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66, + 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x36,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73,0x6d, + 0x70,0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x79,0x29,0x3b,0x0a,0x20,0x20,0x66, + 0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x76,0x65,0x63, + 0x34,0x66,0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31, + 0x2e,0x30,0x66,0x2c,0x20,0x78,0x5f,0x32,0x36,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b, + 0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a, + 0x40,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69, + 0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75, + 0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c, + 0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b, + 0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +#elif defined(SOKOL_VULKAN) +/* + #version 460 + + layout(set = 0, binding = 0, std140) uniform vs_params + { + mat4 mvp; + mat4 tm; + } _19; + + 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; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = _19.mvp * position; + gl_PointSize = psize; + uv = _19.tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + +*/ +static const uint8_t _sfons_vs_bytecode_spirv_vk[1668] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x33,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65, + 0x72,0x74,0x65,0x78,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00, + 0x06,0x00,0x07,0x00,0x0b,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, + 0x0b,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,0x0b,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,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x76,0x70,0x00,0x06,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x74,0x6d,0x00,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00,0x5f,0x31,0x39,0x00, + 0x05,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1f,0x00,0x00,0x00,0x70,0x73,0x69,0x7a, + 0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x23,0x00,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x05,0x00,0x28,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64, + 0x30,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x00,0x47,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x28,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x30,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x31,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00, + 0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x06,0x00, + 0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x1c,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x21,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x22,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x14,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x26,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0x91,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x23,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x30,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, +}; +/* + #version 460 + + layout(set = 1, binding = 0) uniform texture2D tex; + layout(set = 1, binding = 32) uniform sampler smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec4 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = vec4(1.0, 1.0, 1.0, texture(sampler2D(tex, smp), uv.xy).x) * color; + } + +*/ +static const uint8_t _sfons_fs_bytecode_spirv_vk[888] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0a,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x74,0x65,0x78,0x00, + 0x05,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x05,0x00,0x03,0x00, + 0x16,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1f,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1f,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00, + 0x06,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,0x20,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, + 0x0f,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x13,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x56,0x00,0x05,0x00, + 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x4f,0x00,0x07,0x00,0x17,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sfons_vs_source_dummy = ""; +static const char* _sfons_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +typedef struct _sfons_t { + sfons_desc_t desc; + sg_shader shd; + sgl_pipeline pip; + sg_image img; + sg_view tex_view; + sg_sampler smp; + 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_fn) { + ptr = allocator->alloc_fn(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_fn) { + allocator->free_fn(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; + + // sokol-gl compatible shader which treats RED channel as alpha + if (sfons->shd.id == SG_INVALID_ID) { + sg_shader_desc shd_desc; + _sfons_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].glsl_name = "position"; + shd_desc.attrs[1].glsl_name = "texcoord0"; + shd_desc.attrs[2].glsl_name = "color0"; + shd_desc.attrs[3].glsl_name = "psize"; + shd_desc.attrs[0].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[0].hlsl_sem_index = 0; + shd_desc.attrs[1].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[1].hlsl_sem_index = 1; + shd_desc.attrs[2].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[2].hlsl_sem_index = 2; + shd_desc.attrs[3].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[3].hlsl_sem_index = 3; + shd_desc.attrs[0].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[1].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[2].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[3].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX; + shd_desc.uniform_blocks[0].size = 128; + shd_desc.uniform_blocks[0].hlsl_register_b_n = 0; + shd_desc.uniform_blocks[0].msl_buffer_n = 0; + shd_desc.uniform_blocks[0].wgsl_group0_binding_n = 0; + shd_desc.uniform_blocks[0].spirv_set0_binding_n = 0; + shd_desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "vs_params"; + shd_desc.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.uniform_blocks[0].glsl_uniforms[0].array_count = 8; + shd_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.views[0].texture.image_type = SG_IMAGETYPE_2D; + shd_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.views[0].texture.hlsl_register_t_n = 0; + shd_desc.views[0].texture.msl_texture_n = 0; + shd_desc.views[0].texture.wgsl_group1_binding_n = 0; + shd_desc.views[0].texture.spirv_set1_binding_n = 0; + shd_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.samplers[0].hlsl_register_s_n = 0; + shd_desc.samplers[0].msl_sampler_n = 0; + shd_desc.samplers[0].wgsl_group1_binding_n = 32; + shd_desc.samplers[0].spirv_set1_binding_n = 32; + shd_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.texture_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.texture_sampler_pairs[0].view_slot = 0; + shd_desc.texture_sampler_pairs[0].sampler_slot = 0; + shd_desc.label = "sokol-fontstash-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vertex_func.source = (const char*)_sfons_vs_source_glsl410; + shd_desc.fragment_func.source = (const char*)_sfons_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vertex_func.source = (const char*)_sfons_vs_source_glsl300es; + shd_desc.fragment_func.source = (const char*)_sfons_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vertex_func.entry = "main0"; + shd_desc.fragment_func.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sfons_vs_bytecode_metal_macos); + shd_desc.fragment_func.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sfons_vs_bytecode_metal_ios); + shd_desc.fragment_func.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_ios); + break; + default: + shd_desc.vertex_func.source = (const char*)_sfons_vs_source_metal_sim; + shd_desc.fragment_func.source = (const char*)_sfons_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vertex_func.bytecode = SG_RANGE(_sfons_vs_bytecode_hlsl4); + shd_desc.fragment_func.bytecode = SG_RANGE(_sfons_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vertex_func.source = (const char*)_sfons_vs_source_wgsl; + shd_desc.fragment_func.source = (const char*)_sfons_fs_source_wgsl; + #elif defined(SOKOL_VULKAN) + shd_desc.vertex_func.bytecode = SG_RANGE(_sfons_vs_bytecode_spirv_vk); + shd_desc.vertex_func.entry = "main"; + shd_desc.fragment_func.bytecode = SG_RANGE(_sfons_fs_bytecode_spirv_vk); + shd_desc.fragment_func.entry = "main"; + #else + shd_desc.vertex_func.source = _sfons_vs_source_dummy; + shd_desc.fragment_func.source = _sfons_fs_source_dummy; + #endif + shd_desc.label = "sfons-shader"; + sfons->shd = sg_make_shader(&shd_desc); + } + + // sokol-gl pipeline object + if (sfons->pip.id == SG_INVALID_ID) { + sg_pipeline_desc 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; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + pip_desc.label = "fontstash-pipeline"; + sfons->pip = sgl_make_pipeline(&pip_desc); + } + + // a sampler object + if (sfons->smp.id == SG_INVALID_ID) { + sg_sampler_desc smp_desc; + _sfons_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_LINEAR; + smp_desc.mag_filter = SG_FILTER_LINEAR; + smp_desc.label = "fontstash-sampler"; + sfons->smp = sg_make_sampler(&smp_desc); + } + + // create or re-create font atlas image and texture view + if (sfons->img.id != SG_INVALID_ID) { + sg_destroy_image(sfons->img); + sfons->img.id = SG_INVALID_ID; + } + if (sfons->tex_view.id != SG_INVALID_ID) { + sg_destroy_view(sfons->tex_view); + sfons->tex_view.id = SG_INVALID_ID; + } + SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID); + SOKOL_ASSERT(sfons->tex_view.id == SG_INVALID_ID); + sfons->cur_width = width; + sfons->cur_height = height; + sg_image_desc img_desc; + _sfons_clear(&img_desc, sizeof(img_desc)); + img_desc.width = sfons->cur_width; + img_desc.height = sfons->cur_height; + img_desc.usage.dynamic_update = true; + img_desc.pixel_format = SG_PIXELFORMAT_R8; + img_desc.label = "fontstash-image"; + sfons->img = sg_make_image(&img_desc); + sg_view_desc view_desc; + _sfons_clear(&view_desc, sizeof(view_desc)); + view_desc.texture.image = sfons->img; + view_desc.label = "fontstash-texview"; + sfons->tex_view = sg_make_view(&view_desc); + return 1; +} + +static int _sfons_render_resize(void* user_ptr, int width, int height) { + return _sfons_render_create(user_ptr, width, height); +} + +static void _sfons_render_update(void* user_ptr, int* rect, const unsigned char* data) { + SOKOL_ASSERT(user_ptr && rect && data); + _SOKOL_UNUSED(rect); + _SOKOL_UNUSED(data); + _sfons_t* sfons = (_sfons_t*) user_ptr; + sfons->img_dirty = true; +} + +static void _sfons_render_draw(void* user_ptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts) { + SOKOL_ASSERT(user_ptr && verts && tcoords && colors && (nverts > 0)); + _sfons_t* sfons = (_sfons_t*) user_ptr; + sgl_enable_texture(); + sgl_texture(sfons->tex_view, sfons->smp); + sgl_push_pipeline(); + sgl_load_pipeline(sfons->pip); + sgl_begin_triangles(); + for (int i = 0; i < nverts; i++) { + sgl_v2f_t2f_c1i(verts[2*i+0], verts[2*i+1], tcoords[2*i+0], tcoords[2*i+1], colors[i]); + } + sgl_end(); + sgl_pop_pipeline(); + sgl_disable_texture(); +} + +static void _sfons_render_delete(void* user_ptr) { + SOKOL_ASSERT(user_ptr); + _sfons_t* sfons = (_sfons_t*) user_ptr; + if (sfons->img.id != SG_INVALID_ID) { + sg_destroy_image(sfons->img); + sfons->img.id = SG_INVALID_ID; + } + if (sfons->tex_view.id != SG_INVALID_ID) { + sg_destroy_view(sfons->tex_view); + sfons->tex_view.id = SG_INVALID_ID; + } + if (sfons->smp.id != SG_INVALID_ID) { + sg_destroy_sampler(sfons->smp); + sfons->smp.id = SG_INVALID_ID; + } + if (sfons->pip.id != SG_INVALID_ID) { + sgl_destroy_pipeline(sfons->pip); + sfons->pip.id = SG_INVALID_ID; + } + if (sfons->shd.id != SG_INVALID_ID) { + sg_destroy_shader(sfons->shd); + sfons->shd.id = SG_INVALID_ID; + } +} + +#define _sfons_def(val, def) (((val) == 0) ? (def) : (val)) + +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_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t)); + sfons->desc = _sfons_desc_defaults(desc); + FONSparams params; + _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; + 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) { + SOKOL_ASSERT(ctx && ctx->params.userPtr); + _sfons_t* sfons = (_sfons_t*) ctx->params.userPtr; + if (sfons->img_dirty) { + sfons->img_dirty = false; + sg_image_data data; + _sfons_clear(&data, sizeof(data)); + data.mip_levels[0].ptr = ctx->texData; + data.mip_levels[0].size = (size_t) (sfons->cur_width * sfons->cur_height); + sg_update_image(sfons->img, &data); + } +} + +SOKOL_API_IMPL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return ((uint32_t)r) | ((uint32_t)g<<8) | ((uint32_t)b<<16) | ((uint32_t)a<<24); +} + +#endif // SOKOL_FONTSTASH_IMPL diff --git a/thirdparty/sokol/util/sokol_gfx_imgui.h b/thirdparty/sokol/util/sokol_gfx_imgui.h new file mode 100644 index 0000000..72a6c6c --- /dev/null +++ b/thirdparty/sokol/util/sokol_gfx_imgui.h @@ -0,0 +1,5168 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMGUI_IMPL) +#define SOKOL_GFX_IMGUI_IMPL +#endif +#ifndef SOKOL_GFX_IMGUI_INCLUDED +/* + sokol_gfx_imgui.h -- debug-inspection UI for sokol_gfx.h using Dear ImGui + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMGUI_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + NOTE that the implementation can be compiled either as C++ or as C. + When compiled as C++, sokol_gfx_imgui.h will directly call into the + Dear ImGui C++ API. When compiled as C, sokol_gfx_imgui.h will call + cimgui.h functions instead. + + Include the following file(s) before including sokol_gfx_imgui.h: + + sokol_gfx.h + + Additionally, include the following headers before including the + implementation: + + If the implementation is compiled as C++: + imgui.h + + If the implementation is compiled as C: + cimgui.h + + The sokol_gfx.h implementation must be compiled with debug trace hooks + enabled by defining: + + SOKOL_TRACE_HOOKS + + ...before including the sokol_gfx.h implementation. + + Before including the sokol_gfx_imgui.h implementation, optionally + override the following macros: + + SOKOL_ASSERT(c) -- your own assert macro, default: assert(c) + SOKOL_UNREACHABLE -- your own macro to annotate unreachable code, + default: SOKOL_ASSERT(false) + SOKOL_GFX_IMGUI_API_DECL - public function declaration prefix (default: extern) + SOKOL_GFX_IMGUI_CPREFIX - defines the function prefix for the Dear ImGui C bindings (default: ig) + SOKOL_API_DECL - same as SOKOL_GFX_IMGUI_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_gfx_imgui.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_IMGUI_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + STEP BY STEP: + ============= + --- call sgimgui_init() with optional allocator overrides: + + sgimgui_init(&(sgimgui_desc_t){ + .allocator = { + .alloc_fn = my_malloc, + .free_fn = my_free, + } + }); + + --- somewhere in the per-frame code call: + + sgimgui_draw() + + this won't draw anything yet, since no windows are open. + + --- call the convenience function sgimgui_draw_menu(ctx, title) + to render a menu which allows to open/close the provided debug windows + + sgimgui_draw_menu("sokol-gfx"); + + --- alternatively the individual single menu items via: + + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("sokol-gfx")) { + sgimgui_draw_buffer_window_menu_item("Buffers"); + sgimgui_draw_image_window_menu_item("Images"); + sgimgui_draw_sampler_window_menu_item("Samplers"); + sgimgui_draw_shader_window_menu_item("Shaders"); + sgimgui_draw_pipeline_window_menu_item("Pipelines"); + sgimgui_draw_view_window_menu_item("Views"); + sgimgui_draw_capture_window_menu_item("Calls"); + sgimgui_draw_capabilities_window_menu_item("Capabilities"); + sgimgui_draw_frame_stats_window_menu_item("Frame Stats"); + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + + --- before application shutdown, call: + + sgimgui_discard(); + + ...this is not strictly necessary because the application exits + anyway, but not doing this may trigger memory leak detection tools. + + --- finally, your application needs an ImGui renderer, you can either + provide your own, or drop in the sokol_imgui.h utility header + + ALTERNATIVE DRAWING FUNCTIONS: + ============================== + Instead of the convenient but all-in-one sgimgui_draw() function, + you can also use the following granular functions which might allow + better integration with your existing UI: + + The following functions only render the window *content* (so you + can integrate the UI into you own windows): + + void sgimgui_draw_buffer_window_content(); + void sgimgui_draw_image_window_content(); + void sgimgui_draw_sampler_window_content(); + void sgimgui_draw_shader_window_content(); + void sgimgui_draw_pipeline_window_content(); + void sgimgui_draw_view_window_content(); + void sgimgui_draw_capture_window_content(); + void sgimgui_draw_capabilities_window_content(); + void sgimgui_draw_frame_stats_window_content(); + + And these are the 'full window' drawing functions: + + void sgimgui_draw_buffer_window("Buffers"); + void sgimgui_draw_image_window("Images"); + void sgimgui_draw_sampler_window("Samplers"); + void sgimgui_draw_shader_window("Shaders"); + void sgimgui_draw_pipeline_window("Pipelines"); + void sgimgui_draw_view_window("Views"); + void sgimgui_draw_capture_window("Frame Capture"); + void sgimgui_draw_capabilities_window("Capabilities"); + void sgimgui_draw_frame_stats_window("Frame Stats"); + + Finer-grained drawing functions may be moved to the public API + in the future as needed. + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 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_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" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_IMGUI_API_DECL) +#define SOKOL_GFX_IMGUI_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_IMGUI_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMGUI_IMPL) +#define SOKOL_GFX_IMGUI_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_IMGUI_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_IMGUI_API_DECL extern +#endif +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + sgimgui_allocator_t + + Used in sgimgui_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 sgimgui_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sgimgui_allocator_t; + +/* + sgimgui_desc_t + + Initialization options for sgimgui_init(). +*/ +typedef struct sgimgui_desc_t { + sgimgui_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) +} sgimgui_desc_t; + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_setup(const sgimgui_desc_t* desc); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_shutdown(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_menu(const char* title); + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_buffer_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_image_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_sampler_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_shader_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_pipeline_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_view_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capture_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capabilities_window_content(void); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_frame_stats_window_content(void); + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_buffer_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_image_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_sampler_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_shader_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_pipeline_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_view_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capture_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capabilities_window(const char* title); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_frame_stats_window(const char* title); + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_buffer_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_image_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_sampler_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_shader_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_pipeline_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_view_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capture_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capabilities_menu_item(const char* label); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_frame_stats_menu_item(const char* label); + +#if defined(__cplusplus) +} // extern "C" + +// reference-based equivalents for c++ +inline void sgimgui_setup(const sgimgui_desc_t& desc) { return sgimgui_setup(&desc); } + +#endif +#endif /* SOKOL_GFX_IMGUI_INCLUDED */ + +/*=== 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 sgimgui_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" + #endif +#else + #if !defined(CIMGUI_API) + #error "Please include cimgui.h before the sokol_imgui.h implementation" + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#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 +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif + +#include +#include // snprintf +#include // malloc, free + +#define _SGIMGUI_STRBUF_LEN (96) +/* max number of captured calls per frame */ +#define _SGIMGUI_MAX_FRAMECAPTURE_ITEMS (4096) + +typedef struct { + char buf[_SGIMGUI_STRBUF_LEN]; +} _sgimgui_str_t; + +typedef struct { + sg_buffer res_id; + _sgimgui_str_t label; + sg_buffer_desc desc; +} _sgimgui_buffer_t; + +typedef struct { + sg_image res_id; + float ui_scale; + _sgimgui_str_t label; + sg_image_desc desc; +} _sgimgui_image_t; + +typedef struct { + sg_sampler res_id; + _sgimgui_str_t label; + sg_sampler_desc desc; +} _sgimgui_sampler_t; + +typedef struct { + sg_shader res_id; + _sgimgui_str_t label; + _sgimgui_str_t vs_entry; + _sgimgui_str_t vs_d3d11_target; + _sgimgui_str_t fs_entry; + _sgimgui_str_t fs_d3d11_target; + _sgimgui_str_t glsl_texture_sampler_name[SG_MAX_TEXTURE_SAMPLER_PAIRS]; + _sgimgui_str_t glsl_uniform_name[SG_MAX_UNIFORMBLOCK_BINDSLOTS][SG_MAX_UNIFORMBLOCK_MEMBERS]; + _sgimgui_str_t attr_glsl_name[SG_MAX_VERTEX_ATTRIBUTES]; + _sgimgui_str_t attr_hlsl_sem_name[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_desc desc; +} _sgimgui_shader_t; + +typedef struct { + sg_pipeline res_id; + _sgimgui_str_t label; + sg_pipeline_desc desc; +} _sgimgui_pipeline_t; + +typedef struct { + sg_view res_id; + float ui_scale; + _sgimgui_str_t label; + sg_view_desc desc; +} _sgimgui_view_t; + +typedef struct { + bool open; + sg_buffer sel_buf; + int num_slots; + _sgimgui_buffer_t* slots; +} _sgimgui_buffer_window_t; + +typedef struct { + bool open; + sg_image sel_img; + int num_slots; + _sgimgui_image_t* slots; +} _sgimgui_image_window_t; + +typedef struct { + bool open; + sg_sampler sel_smp; + int num_slots; + _sgimgui_sampler_t* slots; +} _sgimgui_sampler_window_t; + +typedef struct { + bool open; + sg_shader sel_shd; + int num_slots; + _sgimgui_shader_t* slots; +} _sgimgui_shader_window_t; + +typedef struct { + bool open; + sg_pipeline sel_pip; + int num_slots; + _sgimgui_pipeline_t* slots; +} _sgimgui_pipeline_window_t; + +typedef struct { + bool open; + sg_view sel_view; + int num_slots; + _sgimgui_view_t* slots; +} _sgimgui_view_window_t; + +typedef enum { + _SGIMGUI_CMD_INVALID, + _SGIMGUI_CMD_RESET_STATE_CACHE, + _SGIMGUI_CMD_MAKE_BUFFER, + _SGIMGUI_CMD_MAKE_IMAGE, + _SGIMGUI_CMD_MAKE_SAMPLER, + _SGIMGUI_CMD_MAKE_SHADER, + _SGIMGUI_CMD_MAKE_PIPELINE, + _SGIMGUI_CMD_MAKE_VIEW, + _SGIMGUI_CMD_DESTROY_BUFFER, + _SGIMGUI_CMD_DESTROY_IMAGE, + _SGIMGUI_CMD_DESTROY_SAMPLER, + _SGIMGUI_CMD_DESTROY_SHADER, + _SGIMGUI_CMD_DESTROY_PIPELINE, + _SGIMGUI_CMD_DESTROY_VIEW, + _SGIMGUI_CMD_UPDATE_BUFFER, + _SGIMGUI_CMD_UPDATE_IMAGE, + _SGIMGUI_CMD_APPEND_BUFFER, + _SGIMGUI_CMD_BEGIN_PASS, + _SGIMGUI_CMD_APPLY_VIEWPORT, + _SGIMGUI_CMD_APPLY_SCISSOR_RECT, + _SGIMGUI_CMD_APPLY_PIPELINE, + _SGIMGUI_CMD_APPLY_BINDINGS, + _SGIMGUI_CMD_APPLY_UNIFORMS, + _SGIMGUI_CMD_DRAW, + _SGIMGUI_CMD_DRAW_EX, + _SGIMGUI_CMD_DISPATCH, + _SGIMGUI_CMD_END_PASS, + _SGIMGUI_CMD_COMMIT, + _SGIMGUI_CMD_ALLOC_BUFFER, + _SGIMGUI_CMD_ALLOC_IMAGE, + _SGIMGUI_CMD_ALLOC_SAMPLER, + _SGIMGUI_CMD_ALLOC_SHADER, + _SGIMGUI_CMD_ALLOC_PIPELINE, + _SGIMGUI_CMD_ALLOC_VIEW, + _SGIMGUI_CMD_DEALLOC_BUFFER, + _SGIMGUI_CMD_DEALLOC_IMAGE, + _SGIMGUI_CMD_DEALLOC_SAMPLER, + _SGIMGUI_CMD_DEALLOC_SHADER, + _SGIMGUI_CMD_DEALLOC_PIPELINE, + _SGIMGUI_CMD_DEALLOC_VIEW, + _SGIMGUI_CMD_INIT_BUFFER, + _SGIMGUI_CMD_INIT_IMAGE, + _SGIMGUI_CMD_INIT_SAMPLER, + _SGIMGUI_CMD_INIT_SHADER, + _SGIMGUI_CMD_INIT_PIPELINE, + _SGIMGUI_CMD_INIT_VIEW, + _SGIMGUI_CMD_UNINIT_BUFFER, + _SGIMGUI_CMD_UNINIT_IMAGE, + _SGIMGUI_CMD_UNINIT_SAMPLER, + _SGIMGUI_CMD_UNINIT_SHADER, + _SGIMGUI_CMD_UNINIT_PIPELINE, + _SGIMGUI_CMD_UNINIT_VIEW, + _SGIMGUI_CMD_FAIL_BUFFER, + _SGIMGUI_CMD_FAIL_IMAGE, + _SGIMGUI_CMD_FAIL_SAMPLER, + _SGIMGUI_CMD_FAIL_SHADER, + _SGIMGUI_CMD_FAIL_PIPELINE, + _SGIMGUI_CMD_FAIL_VIEW, + _SGIMGUI_CMD_PUSH_DEBUG_GROUP, + _SGIMGUI_CMD_POP_DEBUG_GROUP, +} _sgimgui_cmd_t; + +typedef struct { + sg_buffer result; +} _sgimgui_args_make_buffer_t; + +typedef struct { + sg_image result; +} _sgimgui_args_make_image_t; + +typedef struct { + sg_sampler result; +} _sgimgui_args_make_sampler_t; + +typedef struct { + sg_shader result; +} _sgimgui_args_make_shader_t; + +typedef struct { + sg_pipeline result; +} _sgimgui_args_make_pipeline_t; + +typedef struct { + sg_view result; +} _sgimgui_args_make_view_t; + +typedef struct { + sg_buffer buffer; +} _sgimgui_args_destroy_buffer_t; + +typedef struct { + sg_image image; +} _sgimgui_args_destroy_image_t; + +typedef struct { + sg_sampler sampler; +} _sgimgui_args_destroy_sampler_t; + +typedef struct { + sg_shader shader; +} _sgimgui_args_destroy_shader_t; + +typedef struct { + sg_pipeline pipeline; +} _sgimgui_args_destroy_pipeline_t; + +typedef struct { + sg_view view; +} _sgimgui_args_destroy_view_t; + +typedef struct { + sg_buffer buffer; + size_t data_size; +} _sgimgui_args_update_buffer_t; + +typedef struct { + sg_image image; +} _sgimgui_args_update_image_t; + +typedef struct { + sg_buffer buffer; + size_t data_size; + int result; +} _sgimgui_args_append_buffer_t; + +typedef struct { + sg_pass pass; +} _sgimgui_args_begin_pass_t; + +typedef struct { + int x, y, width, height; + bool origin_top_left; +} _sgimgui_args_apply_viewport_t; + +typedef struct { + int x, y, width, height; + bool origin_top_left; +} _sgimgui_args_apply_scissor_rect_t; + +typedef struct { + sg_pipeline pipeline; +} _sgimgui_args_apply_pipeline_t; + +typedef struct { + sg_bindings bindings; +} _sgimgui_args_apply_bindings_t; + +typedef struct { + int ub_slot; + size_t data_size; + sg_pipeline pipeline; /* the pipeline which was active at this call */ + size_t ubuf_pos; /* start of copied data in capture buffer */ +} _sgimgui_args_apply_uniforms_t; + +typedef struct { + int base_element; + int num_elements; + int num_instances; +} _sgimgui_args_draw_t; + +typedef struct { + int base_element; + int num_elements; + int num_instances; + int base_vertex; + int base_instance; +} _sgimgui_args_draw_ex_t; + +typedef struct { + int num_groups_x; + int num_groups_y; + int num_groups_z; +} _sgimgui_args_dispatch_t; + +typedef struct { + sg_buffer result; +} _sgimgui_args_alloc_buffer_t; + +typedef struct { + sg_image result; +} _sgimgui_args_alloc_image_t; + +typedef struct { + sg_sampler result; +} _sgimgui_args_alloc_sampler_t; + +typedef struct { + sg_shader result; +} _sgimgui_args_alloc_shader_t; + +typedef struct { + sg_pipeline result; +} _sgimgui_args_alloc_pipeline_t; + +typedef struct { + sg_view result; +} _sgimgui_args_alloc_view_t; + +typedef struct { + sg_buffer buffer; +} _sgimgui_args_dealloc_buffer_t; + +typedef struct { + sg_image image; +} _sgimgui_args_dealloc_image_t; + +typedef struct { + sg_sampler sampler; +} _sgimgui_args_dealloc_sampler_t; + +typedef struct { + sg_shader shader; +} _sgimgui_args_dealloc_shader_t; + +typedef struct { + sg_pipeline pipeline; +} _sgimgui_args_dealloc_pipeline_t; + +typedef struct { + sg_view view; +} _sgimgui_args_dealloc_view_t; + +typedef struct { + sg_buffer buffer; +} _sgimgui_args_init_buffer_t; + +typedef struct { + sg_image image; +} _sgimgui_args_init_image_t; + +typedef struct { + sg_sampler sampler; +} _sgimgui_args_init_sampler_t; + +typedef struct { + sg_shader shader; +} _sgimgui_args_init_shader_t; + +typedef struct { + sg_pipeline pipeline; +} _sgimgui_args_init_pipeline_t; + +typedef struct { + sg_view view; +} _sgimgui_args_init_view_t; + +typedef struct { + sg_buffer buffer; +} _sgimgui_args_uninit_buffer_t; + +typedef struct { + sg_image image; +} _sgimgui_args_uninit_image_t; + +typedef struct { + sg_sampler sampler; +} _sgimgui_args_uninit_sampler_t; + +typedef struct { + sg_shader shader; +} _sgimgui_args_uninit_shader_t; + +typedef struct { + sg_pipeline pipeline; +} _sgimgui_args_uninit_pipeline_t; + +typedef struct { + sg_view view; +} _sgimgui_args_uninit_view_t; + +typedef struct { + sg_buffer buffer; +} _sgimgui_args_fail_buffer_t; + +typedef struct { + sg_image image; +} _sgimgui_args_fail_image_t; + +typedef struct { + sg_sampler sampler; +} _sgimgui_args_fail_sampler_t; + +typedef struct { + sg_shader shader; +} _sgimgui_args_fail_shader_t; + +typedef struct { + sg_pipeline pipeline; +} _sgimgui_args_fail_pipeline_t; + +typedef struct { + sg_view view; +} _sgimgui_args_fail_view_t; + +typedef struct { + _sgimgui_str_t name; +} _sgimgui_args_push_debug_group_t; + +typedef union { + _sgimgui_args_make_buffer_t make_buffer; + _sgimgui_args_make_image_t make_image; + _sgimgui_args_make_sampler_t make_sampler; + _sgimgui_args_make_shader_t make_shader; + _sgimgui_args_make_pipeline_t make_pipeline; + _sgimgui_args_make_view_t make_view; + _sgimgui_args_destroy_buffer_t destroy_buffer; + _sgimgui_args_destroy_image_t destroy_image; + _sgimgui_args_destroy_sampler_t destroy_sampler; + _sgimgui_args_destroy_shader_t destroy_shader; + _sgimgui_args_destroy_pipeline_t destroy_pipeline; + _sgimgui_args_destroy_view_t destroy_view; + _sgimgui_args_update_buffer_t update_buffer; + _sgimgui_args_update_image_t update_image; + _sgimgui_args_append_buffer_t append_buffer; + _sgimgui_args_begin_pass_t begin_pass; + _sgimgui_args_apply_viewport_t apply_viewport; + _sgimgui_args_apply_scissor_rect_t apply_scissor_rect; + _sgimgui_args_apply_pipeline_t apply_pipeline; + _sgimgui_args_apply_bindings_t apply_bindings; + _sgimgui_args_apply_uniforms_t apply_uniforms; + _sgimgui_args_draw_t draw; + _sgimgui_args_draw_ex_t draw_ex; + _sgimgui_args_dispatch_t dispatch; + _sgimgui_args_alloc_buffer_t alloc_buffer; + _sgimgui_args_alloc_image_t alloc_image; + _sgimgui_args_alloc_sampler_t alloc_sampler; + _sgimgui_args_alloc_shader_t alloc_shader; + _sgimgui_args_alloc_pipeline_t alloc_pipeline; + _sgimgui_args_alloc_view_t alloc_view; + _sgimgui_args_dealloc_buffer_t dealloc_buffer; + _sgimgui_args_dealloc_image_t dealloc_image; + _sgimgui_args_dealloc_sampler_t dealloc_sampler; + _sgimgui_args_dealloc_shader_t dealloc_shader; + _sgimgui_args_dealloc_pipeline_t dealloc_pipeline; + _sgimgui_args_dealloc_view_t dealloc_view; + _sgimgui_args_init_buffer_t init_buffer; + _sgimgui_args_init_image_t init_image; + _sgimgui_args_init_sampler_t init_sampler; + _sgimgui_args_init_shader_t init_shader; + _sgimgui_args_init_pipeline_t init_pipeline; + _sgimgui_args_init_view_t init_view; + _sgimgui_args_uninit_buffer_t uninit_buffer; + _sgimgui_args_uninit_image_t uninit_image; + _sgimgui_args_uninit_sampler_t uninit_sampler; + _sgimgui_args_uninit_shader_t uninit_shader; + _sgimgui_args_uninit_pipeline_t uninit_pipeline; + _sgimgui_args_uninit_view_t uninit_view; + _sgimgui_args_fail_buffer_t fail_buffer; + _sgimgui_args_fail_image_t fail_image; + _sgimgui_args_fail_sampler_t fail_sampler; + _sgimgui_args_fail_shader_t fail_shader; + _sgimgui_args_fail_pipeline_t fail_pipeline; + _sgimgui_args_fail_view_t fail_view; + _sgimgui_args_push_debug_group_t push_debug_group; +} _sgimgui_args_t; + +typedef struct { + _sgimgui_cmd_t cmd; + uint32_t color; + _sgimgui_args_t args; +} _sgimgui_capture_item_t; + +typedef struct { + size_t ubuf_size; /* size of uniform capture buffer in bytes */ + size_t ubuf_pos; /* current uniform buffer pos */ + uint8_t* ubuf; /* buffer for capturing uniform updates */ + int num_items; + _sgimgui_capture_item_t items[_SGIMGUI_MAX_FRAMECAPTURE_ITEMS]; +} _sgimgui_capture_bucket_t; + +/* double-buffered call-capture buckets, one bucket is currently recorded, + the previous bucket is displayed +*/ +typedef struct { + bool open; + int bucket_index; /* which bucket to record to, 0 or 1 */ + int sel_item; /* currently selected capture item by index */ + _sgimgui_capture_bucket_t bucket[2]; +} _sgimgui_capture_window_t; + +typedef struct { + bool open; +} _sgimgui_caps_window_t; + +typedef struct { + bool open; + bool disable_sokol_imgui_stats; + bool in_sokol_imgui; + sg_stats stats; + // FIXME: add a ringbuffer for a stats history here +} _sgimgui_frame_stats_window_t; + +typedef struct { + uint32_t init_tag; + sgimgui_desc_t desc; + _sgimgui_buffer_window_t buffer_window; + _sgimgui_image_window_t image_window; + _sgimgui_sampler_window_t sampler_window; + _sgimgui_shader_window_t shader_window; + _sgimgui_pipeline_window_t pipeline_window; + _sgimgui_view_window_t view_window; + _sgimgui_capture_window_t capture_window; + _sgimgui_caps_window_t caps_window; + _sgimgui_frame_stats_window_t frame_stats_window; + sg_pipeline cur_pipeline; + sg_trace_hooks hooks; +} _sgimgui_t; +static _sgimgui_t _sgimgui; + +#define _SGIMGUI_SLOT_MASK (0xFFFF) +#define _SGIMGUI_LIST_WIDTH (192) +#define _SGIMGUI_COLOR_OTHER 0xFFCCCCCC +#define _SGIMGUI_COLOR_RSRC 0xFF00FFFF +#define _SGIMGUI_COLOR_PASS 0xFFFFFF00 +#define _SGIMGUI_COLOR_APPLY 0xFFCCCC00 +#define _SGIMGUI_COLOR_DRAW 0xFF00FF00 +#define _SGIMGUI_COLOR_ERR 0xFF8888FF + +/*--- C => C++ layer ---------------------------------------------------------*/ + +#if defined(__cplusplus) +#define _SGIMGUI_IMGUI_FUNC(name) ImGui::name +#else +#ifndef SOKOL_GFX_IMGUI_CPREFIX +#define SOKOL_GFX_IMGUI_CPREFIX ig +#endif +#define _SGIMGUI_CONCAT2(prefix, name) prefix ## name +#define _SGIMGUI_CONCAT(prefix, name) _SGIMGUI_CONCAT2(prefix, name) +#define _SGIMGUI_IMGUI_FUNC(name) _SGIMGUI_CONCAT(SOKOL_GFX_IMGUI_CPREFIX, name) +#endif + +#if defined(__cplusplus) +#define IMVEC2(x,y) ImVec2(x,y) +#define IMVEC4(x,y,z,w) ImVec4(x,y,z,w) +#else +#define IMVEC2(x,y) (ImVec2){x,y} +#define IMVEC4(x,y,z,w) (ImVec4){x,y,z,w} +#endif + +_SOKOL_PRIVATE void _sgimgui_igtext(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + _SGIMGUI_IMGUI_FUNC(TextV)(fmt, args); + va_end(args); +} + +_SOKOL_PRIVATE void _sgimgui_igseparator(void) { + _SGIMGUI_IMGUI_FUNC(Separator)(); +} + +_SOKOL_PRIVATE void _sgimgui_igsameline(void) { + _SGIMGUI_IMGUI_FUNC(SameLine)(); +} + +_SOKOL_PRIVATE void _sgimgui_igpushidint(int int_id) { + #if defined(__cplusplus) + ImGui::PushID(int_id); + #else + _SGIMGUI_IMGUI_FUNC(PushIDInt)(int_id); + #endif +} + +_SOKOL_PRIVATE void _sgimgui_igpushid(const char* str_id) { + _SGIMGUI_IMGUI_FUNC(PushID)(str_id); +} + +_SOKOL_PRIVATE void _sgimgui_igpopid(void) { + _SGIMGUI_IMGUI_FUNC(PopID)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igselectableex(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2 size) { + #if defined(__cplusplus) + return ImGui::Selectable(label, selected, flags, size); + #else + return _SGIMGUI_IMGUI_FUNC(SelectableEx)(label, selected, flags, size); + #endif +} + +_SOKOL_PRIVATE bool _sgimgui_igsmallbutton(const char* label) { + return _SGIMGUI_IMGUI_FUNC(SmallButton)(label); +} + +_SOKOL_PRIVATE bool _sgimgui_igbeginchild(const char* str_id, const ImVec2 size, bool border, ImGuiWindowFlags flags) { + return _SGIMGUI_IMGUI_FUNC(BeginChild)(str_id, size, border, flags); +} + +_SOKOL_PRIVATE void _sgimgui_igendchild(void) { + _SGIMGUI_IMGUI_FUNC(EndChild)(); +} + +_SOKOL_PRIVATE void _sgimgui_igpushstylecolor(ImGuiCol idx, ImU32 col) { + _SGIMGUI_IMGUI_FUNC(PushStyleColor)(idx, col); +} + +_SOKOL_PRIVATE void _sgimgui_igpopstylecolor(void) { + _SGIMGUI_IMGUI_FUNC(PopStyleColor)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igtreenodestr(const char* str_id, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + bool ret = _SGIMGUI_IMGUI_FUNC(TreeNodeV)(str_id, fmt, args); + va_end(args); + return ret; +} + +_SOKOL_PRIVATE bool _sgimgui_igtreenode(const char* label) { + return _SGIMGUI_IMGUI_FUNC(TreeNode)(label); +} + +_SOKOL_PRIVATE void _sgimgui_igtreepop(void) { + _SGIMGUI_IMGUI_FUNC(TreePop)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igisitemhovered(ImGuiHoveredFlags flags) { + return _SGIMGUI_IMGUI_FUNC(IsItemHovered)(flags); +} + +_SOKOL_PRIVATE void _sgimgui_igsettooltip(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + _SGIMGUI_IMGUI_FUNC(SetTooltipV)(fmt, args); + va_end(args); +} + +_SOKOL_PRIVATE bool _sgimgui_igbegintooltip(void) { + return _SGIMGUI_IMGUI_FUNC(BeginTooltip)(); +} + +_SOKOL_PRIVATE void _sgimgui_igendtooltip(void) { + _SGIMGUI_IMGUI_FUNC(EndTooltip)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igsliderfloatex(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { + #if defined(__cplusplus) + return ImGui::SliderFloat(label, v, v_min, v_max, format, flags); + #else + return _SGIMGUI_IMGUI_FUNC(SliderFloatEx)(label, v, v_min, v_max, format, flags); + #endif +} + +_SOKOL_PRIVATE void _sgimgui_igsetnextwindowsize(const ImVec2 size, ImGuiCond cond) { + _SGIMGUI_IMGUI_FUNC(SetNextWindowSize)(size, cond); +} + +_SOKOL_PRIVATE bool _sgimgui_igbegin(const char* name, bool* p_open, ImGuiWindowFlags flags) { + return _SGIMGUI_IMGUI_FUNC(Begin)(name, p_open, flags); +} + +_SOKOL_PRIVATE void _sgimgui_igend(void) { + _SGIMGUI_IMGUI_FUNC(End)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igbeginmenu(const char* label) { + return _SGIMGUI_IMGUI_FUNC(BeginMenu)(label); +} + +_SOKOL_PRIVATE void _sgimgui_igendmenu(void) { + _SGIMGUI_IMGUI_FUNC(EndMenu)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igmenuitemboolptr(const char* label, const char* shortcut, bool* p_selected, bool enabled) { + #if defined(__cplusplus) + return ImGui::MenuItem(label, shortcut, p_selected, enabled); + #else + return _SGIMGUI_IMGUI_FUNC(MenuItemBoolPtr)(label, shortcut, p_selected, enabled); + #endif +} + +_SOKOL_PRIVATE bool _sgimgui_igbegintable(const char* str_id, int column, ImGuiTableFlags flags) { + return _SGIMGUI_IMGUI_FUNC(BeginTable)(str_id, column, flags); +} + +_SOKOL_PRIVATE void _sgimgui_igendtable(void) { + _SGIMGUI_IMGUI_FUNC(EndTable)(); +} + +_SOKOL_PRIVATE void _sgimgui_igtablesetupscrollfreeze(int cols, int rows) { + _SGIMGUI_IMGUI_FUNC(TableSetupScrollFreeze)(cols, rows); +} + +_SOKOL_PRIVATE void _sgimgui_igtablesetupcolumn(const char* label, ImGuiTableColumnFlags flags) { + _SGIMGUI_IMGUI_FUNC(TableSetupColumn)(label, flags); +} + +_SOKOL_PRIVATE void _sgimgui_igtableheadersrow(void) { + _SGIMGUI_IMGUI_FUNC(TableHeadersRow)(); +} + +_SOKOL_PRIVATE void _sgimgui_igtablenextrow(void) { + _SGIMGUI_IMGUI_FUNC(TableNextRow)(); +} + +_SOKOL_PRIVATE bool _sgimgui_igtablesetcolumnindex(int column_n) { + return _SGIMGUI_IMGUI_FUNC(TableSetColumnIndex)(column_n); +} + +_SOKOL_PRIVATE bool _sgimgui_igcheckbox(const char* label, bool* v) { + return _SGIMGUI_IMGUI_FUNC(Checkbox)(label, v); +} + +_SOKOL_PRIVATE void _sgimgui_igimage(ImTextureID user_texture_id, const ImVec2 size) { + #if defined(__cplusplus) + ImGui::Image(ImTextureRef(user_texture_id), size); + #else + // FIXME: Dear Bindings is currently missing a constructor wrapper for ImTextureRef + ImTextureRef tex_ref = {0}; + tex_ref._TexID = user_texture_id; + _SGIMGUI_IMGUI_FUNC(Image)(tex_ref, size); + #endif +} + +/*--- UTILS ------------------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sgimgui_malloc(const sgimgui_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(allocator && (size > 0)); + void* ptr; + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +_SOKOL_PRIVATE void* _sgimgui_malloc_clear(const sgimgui_allocator_t* allocator, size_t size) { + void* ptr = _sgimgui_malloc(allocator, size); + _sgimgui_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sgimgui_free(const sgimgui_allocator_t* allocator, void* ptr) { + SOKOL_ASSERT(allocator); + if (allocator->free_fn) { + allocator->free_fn(ptr, allocator->user_data); + } else { + free(ptr); + } +} + + _SOKOL_PRIVATE void* _sgimgui_realloc(const sgimgui_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 = _sgimgui_malloc(allocator, new_size); + if (old_ptr) { + if (old_size > 0) { + memcpy(new_ptr, old_ptr, old_size); + } + _sgimgui_free(allocator, old_ptr); + } + return new_ptr; +} + +_SOKOL_PRIVATE int _sgimgui_slot_index(uint32_t id) { + int slot_index = (int) (id & _SGIMGUI_SLOT_MASK); + SOKOL_ASSERT(0 != slot_index); + return slot_index; +} + +_SOKOL_PRIVATE uint32_t _sgimgui_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); +} + +_SOKOL_PRIVATE uint32_t _sgimgui_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 _sgimgui_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; + } + } +} + +_SOKOL_PRIVATE void _sgimgui_strcpy(_sgimgui_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, _SGIMGUI_STRBUF_LEN, src, (_SGIMGUI_STRBUF_LEN-1)); + #else + strncpy(dst->buf, src, _SGIMGUI_STRBUF_LEN); + #endif + dst->buf[_SGIMGUI_STRBUF_LEN-1] = 0; + } else { + _sgimgui_clear(dst->buf, _SGIMGUI_STRBUF_LEN); + } +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_make_str(const char* str) { + _sgimgui_str_t res; + _sgimgui_strcpy(&res, str); + return res; +} + +_SOKOL_PRIVATE const char* _sgimgui_str_dup(const sgimgui_allocator_t* allocator, const char* src) { + SOKOL_ASSERT(allocator && src); + size_t len = strlen(src) + 1; + char* dst = (char*) _sgimgui_malloc(allocator, len); + memcpy(dst, src, len); + return (const char*) dst; +} + +_SOKOL_PRIVATE const void* _sgimgui_bin_dup(const sgimgui_allocator_t* allocator, const void* src, size_t num_bytes) { + SOKOL_ASSERT(allocator && src && (num_bytes > 0)); + void* dst = _sgimgui_malloc(allocator, num_bytes); + memcpy(dst, src, num_bytes); + return (const void*) dst; +} + +_SOKOL_PRIVATE void _sgimgui_snprintf(_sgimgui_str_t* dst, const char* fmt, ...) { + SOKOL_ASSERT(dst); + va_list args; + va_start(args, fmt); + vsnprintf(dst->buf, sizeof(dst->buf), fmt, args); + dst->buf[sizeof(dst->buf)-1] = 0; + va_end(args); +} + +/*--- STRING CONVERSION ------------------------------------------------------*/ +_SOKOL_PRIVATE const char* _sgimgui_resourcestate_string(sg_resource_state s) { + switch (s) { + case SG_RESOURCESTATE_INITIAL: return "SG_RESOURCESTATE_INITIAL"; + case SG_RESOURCESTATE_ALLOC: return "SG_RESOURCESTATE_ALLOC"; + case SG_RESOURCESTATE_VALID: return "SG_RESOURCESTATE_VALID"; + case SG_RESOURCESTATE_FAILED: return "SG_RESOURCESTATE_FAILED"; + default: return "SG_RESOURCESTATE_INVALID"; + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_resource_slot(const sg_slot_info* slot) { + _sgimgui_igtext("ResId: %08X", slot->res_id); + _sgimgui_igtext("State: %s", _sgimgui_resourcestate_string(slot->state)); + _sgimgui_igtext("Uninit Count: %d", slot->uninit_count); +} + +_SOKOL_PRIVATE const char* _sgimgui_backend_string(sg_backend b) { + switch (b) { + case SG_BACKEND_GLCORE: return "SG_BACKEND_GLCORE"; + case SG_BACKEND_GLES3: return "SG_BACKEND_GLES3"; + case SG_BACKEND_D3D11: return "SG_BACKEND_D3D11"; + case SG_BACKEND_METAL_IOS: return "SG_BACKEND_METAL_IOS"; + case SG_BACKEND_METAL_MACOS: return "SG_BACKEND_METAL_MACOS"; + case SG_BACKEND_METAL_SIMULATOR: return "SG_BACKEND_METAL_SIMULATOR"; + case SG_BACKEND_WGPU: return "SG_BACKEND_WGPU"; + case SG_BACKEND_VULKAN: return "SG_BACKEND_VULKAN"; + case SG_BACKEND_DUMMY: return "SG_BACKEND_DUMMY"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_imagetype_string(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return "SG_IMAGETYPE_2D"; + case SG_IMAGETYPE_CUBE: return "SG_IMAGETYPE_CUBE"; + case SG_IMAGETYPE_3D: return "SG_IMAGETYPE_3D"; + case SG_IMAGETYPE_ARRAY: return "SG_IMAGETYPE_ARRAY"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_imagesampletype_string(sg_image_sample_type t) { + switch (t) { + case SG_IMAGESAMPLETYPE_FLOAT: return "SG_IMAGESAMPLETYPE_FLOAT"; + case SG_IMAGESAMPLETYPE_DEPTH: return "SG_IMAGESAMPLETYPE_DEPTH"; + case SG_IMAGESAMPLETYPE_SINT: return "SG_IMAGESAMPLETYPE_SINT"; + case SG_IMAGESAMPLETYPE_UINT: return "SG_IMAGESAMPLETYPE_UINT"; + case SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT: return "SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_samplertype_string(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FILTERING: return "SG_SAMPLERTYPE_FILTERING"; + case SG_SAMPLERTYPE_COMPARISON: return "SG_SAMPLERTYPE_COMPARISON"; + case SG_SAMPLERTYPE_NONFILTERING: return "SG_SAMPLERTYPE_NONFILTERING"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_uniformlayout_string(sg_uniform_layout l) { + switch (l) { + case SG_UNIFORMLAYOUT_NATIVE: return "SG_UNIFORMLAYOUT_NATIVE"; + case SG_UNIFORMLAYOUT_STD140: return "SG_UNIFORMLAYOUT_STD140"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_pixelformat_string(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_NONE: return "SG_PIXELFORMAT_NONE"; + case SG_PIXELFORMAT_R8: return "SG_PIXELFORMAT_R8"; + case SG_PIXELFORMAT_R8SN: return "SG_PIXELFORMAT_R8SN"; + case SG_PIXELFORMAT_R8UI: return "SG_PIXELFORMAT_R8UI"; + case SG_PIXELFORMAT_R8SI: return "SG_PIXELFORMAT_R8SI"; + case SG_PIXELFORMAT_R16: return "SG_PIXELFORMAT_R16"; + case SG_PIXELFORMAT_R16SN: return "SG_PIXELFORMAT_R16SN"; + case SG_PIXELFORMAT_R16UI: return "SG_PIXELFORMAT_R16UI"; + case SG_PIXELFORMAT_R16SI: return "SG_PIXELFORMAT_R16SI"; + case SG_PIXELFORMAT_R16F: return "SG_PIXELFORMAT_R16F"; + case SG_PIXELFORMAT_RG8: return "SG_PIXELFORMAT_RG8"; + case SG_PIXELFORMAT_RG8SN: return "SG_PIXELFORMAT_RG8SN"; + case SG_PIXELFORMAT_RG8UI: return "SG_PIXELFORMAT_RG8UI"; + case SG_PIXELFORMAT_RG8SI: return "SG_PIXELFORMAT_RG8SI"; + case SG_PIXELFORMAT_R32UI: return "SG_PIXELFORMAT_R32UI"; + case SG_PIXELFORMAT_R32SI: return "SG_PIXELFORMAT_R32SI"; + case SG_PIXELFORMAT_R32F: return "SG_PIXELFORMAT_R32F"; + case SG_PIXELFORMAT_RG16: return "SG_PIXELFORMAT_RG16"; + case SG_PIXELFORMAT_RG16SN: return "SG_PIXELFORMAT_RG16SN"; + case SG_PIXELFORMAT_RG16UI: return "SG_PIXELFORMAT_RG16UI"; + 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"; + case SG_PIXELFORMAT_BGRA8: return "SG_PIXELFORMAT_BGRA8"; + case SG_PIXELFORMAT_RGB10A2: return "SG_PIXELFORMAT_RGB10A2"; + case SG_PIXELFORMAT_RG11B10F: return "SG_PIXELFORMAT_RG11B10F"; + case SG_PIXELFORMAT_RG32UI: return "SG_PIXELFORMAT_RG32UI"; + case SG_PIXELFORMAT_RG32SI: return "SG_PIXELFORMAT_RG32SI"; + case SG_PIXELFORMAT_RG32F: return "SG_PIXELFORMAT_RG32F"; + case SG_PIXELFORMAT_RGBA16: return "SG_PIXELFORMAT_RGBA16"; + case SG_PIXELFORMAT_RGBA16SN: return "SG_PIXELFORMAT_RGBA16SN"; + case SG_PIXELFORMAT_RGBA16UI: return "SG_PIXELFORMAT_RGBA16UI"; + case SG_PIXELFORMAT_RGBA16SI: return "SG_PIXELFORMAT_RGBA16SI"; + case SG_PIXELFORMAT_RGBA16F: return "SG_PIXELFORMAT_RGBA16F"; + case SG_PIXELFORMAT_RGBA32UI: return "SG_PIXELFORMAT_RGBA32UI"; + case SG_PIXELFORMAT_RGBA32SI: return "SG_PIXELFORMAT_RGBA32SI"; + case SG_PIXELFORMAT_RGBA32F: return "SG_PIXELFORMAT_RGBA32F"; + case SG_PIXELFORMAT_DEPTH: return "SG_PIXELFORMAT_DEPTH"; + case SG_PIXELFORMAT_DEPTH_STENCIL: return "SG_PIXELFORMAT_DEPTH_STENCIL"; + case SG_PIXELFORMAT_BC1_RGBA: return "SG_PIXELFORMAT_BC1_RGBA"; + case SG_PIXELFORMAT_BC2_RGBA: return "SG_PIXELFORMAT_BC2_RGBA"; + case SG_PIXELFORMAT_BC3_RGBA: return "SG_PIXELFORMAT_BC3_RGBA"; + case SG_PIXELFORMAT_BC4_R: return "SG_PIXELFORMAT_BC4_R"; + case SG_PIXELFORMAT_BC4_RSN: return "SG_PIXELFORMAT_BC4_RSN"; + case SG_PIXELFORMAT_BC5_RG: return "SG_PIXELFORMAT_BC5_RG"; + case SG_PIXELFORMAT_BC5_RGSN: return "SG_PIXELFORMAT_BC5_RGSN"; + case SG_PIXELFORMAT_BC6H_RGBF: return "SG_PIXELFORMAT_BC6H_RGBF"; + case SG_PIXELFORMAT_BC6H_RGBUF: return "SG_PIXELFORMAT_BC6H_RGBUF"; + case SG_PIXELFORMAT_BC7_RGBA: return "SG_PIXELFORMAT_BC7_RGBA"; + case SG_PIXELFORMAT_ETC2_RGB8: return "SG_PIXELFORMAT_ETC2_RGB8"; + case SG_PIXELFORMAT_ETC2_RGB8A1: return "SG_PIXELFORMAT_ETC2_RGB8A1"; + case SG_PIXELFORMAT_ETC2_RGBA8: return "SG_PIXELFORMAT_ETC2_RGBA8"; + case SG_PIXELFORMAT_EAC_R11: return "SG_PIXELFORMAT_EAC_R11"; + case SG_PIXELFORMAT_EAC_R11SN: return "SG_PIXELFORMAT_EAC_R11SN"; + case SG_PIXELFORMAT_EAC_RG11: return "SG_PIXELFORMAT_EAC_RG11"; + case SG_PIXELFORMAT_EAC_RG11SN: return "SG_PIXELFORMAT_EAC_RG11SN"; + case SG_PIXELFORMAT_RGB9E5: return "SG_PIXELFORMAT_RGB9E5"; + case SG_PIXELFORMAT_BC3_SRGBA: return "SG_PIXELFORMAT_BC3_SRGBA"; + case SG_PIXELFORMAT_BC7_SRGBA: return "SG_PIXELFORMAT_BC7_SRGBA"; + case SG_PIXELFORMAT_ETC2_SRGB8: return "SG_PIXELFORMAT_ETC2_SRGB8"; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return "SG_PIXELFORMAT_ETC2_SRGB8A8"; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return "SG_PIXELFORMAT_ASTC_4x4_RGBA"; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return "SG_PIXELFORMAT_ASTC_4x4_SRGBA"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_filter_string(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: return "SG_FILTER_NEAREST"; + case SG_FILTER_LINEAR: return "SG_FILTER_LINEAR"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_wrap_string(sg_wrap w) { + switch (w) { + case SG_WRAP_REPEAT: return "SG_WRAP_REPEAT"; + case SG_WRAP_CLAMP_TO_EDGE: return "SG_WRAP_CLAMP_TO_EDGE"; + case SG_WRAP_CLAMP_TO_BORDER: return "SG_WRAP_CLAMP_TO_BORDER"; + case SG_WRAP_MIRRORED_REPEAT: return "SG_WRAP_MIRRORED_REPEAT"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_bordercolor_string(sg_border_color bc) { + switch (bc) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return "SG_BORDERCOLOR_TRANSPARENT_BLACK"; + case SG_BORDERCOLOR_OPAQUE_BLACK: return "SG_BORDERCOLOR_OPAQUE_BLACK"; + case SG_BORDERCOLOR_OPAQUE_WHITE: return "SG_BORDERCOLOR_OPAQUE_WHITE"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_uniformtype_string(sg_uniform_type t) { + switch (t) { + case SG_UNIFORMTYPE_FLOAT: return "SG_UNIFORMTYPE_FLOAT"; + 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 "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_vertexstep_string(sg_vertex_step s) { + switch (s) { + case SG_VERTEXSTEP_PER_VERTEX: return "SG_VERTEXSTEP_PER_VERTEX"; + case SG_VERTEXSTEP_PER_INSTANCE: return "SG_VERTEXSTEP_PER_INSTANCE"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_vertexformat_string(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return "SG_VERTEXFORMAT_FLOAT"; + case SG_VERTEXFORMAT_FLOAT2: return "SG_VERTEXFORMAT_FLOAT2"; + case SG_VERTEXFORMAT_FLOAT3: return "SG_VERTEXFORMAT_FLOAT3"; + case SG_VERTEXFORMAT_FLOAT4: return "SG_VERTEXFORMAT_FLOAT4"; + case SG_VERTEXFORMAT_INT: return "SG_VERTEXFORMAT_INT"; + case SG_VERTEXFORMAT_INT2: return "SG_VERTEXFORMAT_INT2"; + case SG_VERTEXFORMAT_INT3: return "SG_VERTEXFORMAT_INT3"; + case SG_VERTEXFORMAT_INT4: return "SG_VERTEXFORMAT_INT4"; + case SG_VERTEXFORMAT_UINT: return "SG_VERTEXFORMAT_UINT"; + case SG_VERTEXFORMAT_UINT2: return "SG_VERTEXFORMAT_UINT2"; + case SG_VERTEXFORMAT_UINT3: return "SG_VERTEXFORMAT_UINT3"; + case SG_VERTEXFORMAT_UINT4: return "SG_VERTEXFORMAT_UINT4"; + case SG_VERTEXFORMAT_BYTE4: return "SG_VERTEXFORMAT_BYTE4"; + case SG_VERTEXFORMAT_BYTE4N: return "SG_VERTEXFORMAT_BYTE4N"; + case SG_VERTEXFORMAT_UBYTE4: return "SG_VERTEXFORMAT_UBYTE4"; + case SG_VERTEXFORMAT_UBYTE4N: return "SG_VERTEXFORMAT_UBYTE4N"; + case SG_VERTEXFORMAT_SHORT2: return "SG_VERTEXFORMAT_SHORT2"; + case SG_VERTEXFORMAT_SHORT2N: return "SG_VERTEXFORMAT_SHORT2N"; + case SG_VERTEXFORMAT_USHORT2: return "SG_VERTEXFORMAT_USHORT2"; + case SG_VERTEXFORMAT_USHORT2N: return "SG_VERTEXFORMAT_USHORT2N"; + case SG_VERTEXFORMAT_SHORT4: return "SG_VERTEXFORMAT_SHORT4"; + case SG_VERTEXFORMAT_SHORT4N: return "SG_VERTEXFORMAT_SHORT4N"; + case SG_VERTEXFORMAT_USHORT4: return "SG_VERTEXFORMAT_USHORT4"; + case SG_VERTEXFORMAT_USHORT4N: return "SG_VERTEXFORMAT_USHORT4N"; + case SG_VERTEXFORMAT_UINT10_N2: return "SG_VERTEXFORMAT_UINT10_N2"; + case SG_VERTEXFORMAT_HALF2: return "SG_VERTEXFORMAT_HALF2"; + case SG_VERTEXFORMAT_HALF4: return "SG_VERTEXFORMAT_HALF4"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_primitivetype_string(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return "SG_PRIMITIVETYPE_POINTS"; + case SG_PRIMITIVETYPE_LINES: return "SG_PRIMITIVETYPE_LINES"; + case SG_PRIMITIVETYPE_LINE_STRIP: return "SG_PRIMITIVETYPE_LINE_STRIP"; + case SG_PRIMITIVETYPE_TRIANGLES: return "SG_PRIMITIVETYPE_TRIANGLES"; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return "SG_PRIMITIVETYPE_TRIANGLE_STRIP"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_indextype_string(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return "SG_INDEXTYPE_NONE"; + case SG_INDEXTYPE_UINT16: return "SG_INDEXTYPE_UINT16"; + case SG_INDEXTYPE_UINT32: return "SG_INDEXTYPE_UINT32"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_stencilop_string(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return "SG_STENCILOP_KEEP"; + case SG_STENCILOP_ZERO: return "SG_STENCILOP_ZERO"; + case SG_STENCILOP_REPLACE: return "SG_STENCILOP_REPLACE"; + case SG_STENCILOP_INCR_CLAMP: return "SG_STENCILOP_INCR_CLAMP"; + case SG_STENCILOP_DECR_CLAMP: return "SG_STENCILOP_DECR_CLAMP"; + case SG_STENCILOP_INVERT: return "SG_STENCILOP_INVERT"; + case SG_STENCILOP_INCR_WRAP: return "SG_STENCILOP_INCR_WRAP"; + case SG_STENCILOP_DECR_WRAP: return "SG_STENCILOP_DECR_WRAP"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_comparefunc_string(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return "SG_COMPAREFUNC_NEVER"; + case SG_COMPAREFUNC_LESS: return "SG_COMPAREFUNC_LESS"; + case SG_COMPAREFUNC_EQUAL: return "SG_COMPAREFUNC_EQUAL"; + case SG_COMPAREFUNC_LESS_EQUAL: return "SG_COMPAREFUNC_LESS_EQUAL"; + case SG_COMPAREFUNC_GREATER: return "SG_COMPAREFUNC_GREATER"; + case SG_COMPAREFUNC_NOT_EQUAL: return "SG_COMPAREFUNC_NOT_EQUAL"; + case SG_COMPAREFUNC_GREATER_EQUAL: return "SG_COMPAREFUNC_GREATER_EQUAL"; + case SG_COMPAREFUNC_ALWAYS: return "SG_COMPAREFUNC_ALWAYS"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_blendfactor_string(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return "SG_BLENDFACTOR_ZERO"; + case SG_BLENDFACTOR_ONE: return "SG_BLENDFACTOR_ONE"; + case SG_BLENDFACTOR_SRC_COLOR: return "SG_BLENDFACTOR_SRC_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR"; + case SG_BLENDFACTOR_SRC_ALPHA: return "SG_BLENDFACTOR_SRC_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA"; + case SG_BLENDFACTOR_DST_COLOR: return "SG_BLENDFACTOR_DST_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_DST_COLOR"; + case SG_BLENDFACTOR_DST_ALPHA: return "SG_BLENDFACTOR_DST_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA"; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return "SG_BLENDFACTOR_SRC_ALPHA_SATURATED"; + case SG_BLENDFACTOR_BLEND_COLOR: return "SG_BLENDFACTOR_BLEND_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR"; + case SG_BLENDFACTOR_BLEND_ALPHA: return "SG_BLENDFACTOR_BLEND_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA"; + case SG_BLENDFACTOR_SRC1_COLOR: return "SG_BLENDFACTOR_SRC1_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR"; + case SG_BLENDFACTOR_SRC1_ALPHA: return "SG_BLENDFACTOR_SRC1_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_blendop_string(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return "SG_BLENDOP_ADD"; + case SG_BLENDOP_SUBTRACT: return "SG_BLENDOP_SUBTRACT"; + case SG_BLENDOP_REVERSE_SUBTRACT: return "SG_BLENDOP_REVERSE_SUBTRACT"; + case SG_BLENDOP_MIN: return "SG_BLENDOP_MIN"; + case SG_BLENDOP_MAX: return "SG_BLENDOP_MAX"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_colormask_string(sg_color_mask m) { + static const char* str[] = { + "NONE", + "R", + "G", + "RG", + "B", + "RB", + "GB", + "RGB", + "A", + "RA", + "GA", + "RGA", + "BA", + "RBA", + "GBA", + "RGBA", + }; + return str[m & 0xF]; +} + +_SOKOL_PRIVATE const char* _sgimgui_cullmode_string(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return "SG_CULLMODE_NONE"; + case SG_CULLMODE_FRONT: return "SG_CULLMODE_FRONT"; + case SG_CULLMODE_BACK: return "SG_CULLMODE_BACK"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_facewinding_string(sg_face_winding fw) { + switch (fw) { + case SG_FACEWINDING_CCW: return "SG_FACEWINDING_CCW"; + case SG_FACEWINDING_CW: return "SG_FACEWINDING_CW"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_shaderstage_string(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VERTEX: return "SG_SHADERSTAGE_VERTEX"; + case SG_SHADERSTAGE_FRAGMENT: return "SG_SHADERSTAGE_FRAGMENT"; + case SG_SHADERSTAGE_COMPUTE: return "SG_SHADERSTAGE_COMPUTE"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_shaderattrbasetype_string(sg_shader_attr_base_type b) { + switch (b) { + case SG_SHADERATTRBASETYPE_UNDEFINED: return "SG_SHADERATTRBASETYPE_UNDEFINED"; + case SG_SHADERATTRBASETYPE_FLOAT: return "SG_SHADERATTRBASETYPE_FLOAT"; + case SG_SHADERATTRBASETYPE_SINT: return "SG_SHADERATTRBASETYPE_SINT"; + case SG_SHADERATTRBASETYPE_UINT: return "SG_SHADERATTRBASETYPE_UINT"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_bool_string(bool b) { + return b ? "true" : "false"; +} + +_SOKOL_PRIVATE const char* _sgimgui_color_string(_sgimgui_str_t* dst_str, sg_color color) { + _sgimgui_snprintf(dst_str, "%.3f %.3f %.3f %.3f", color.r, color.g, color.b, color.a); + return dst_str->buf; +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_res_id_string(uint32_t res_id, const char* label) { + SOKOL_ASSERT(label); + _sgimgui_str_t res; + if (label[0]) { + _sgimgui_snprintf(&res, "'%s'", label); + } else { + _sgimgui_snprintf(&res, "0x%08X", res_id); + } + return res; +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_buffer_id_string(_sgimgui_t* ctx, sg_buffer buf_id) { + if (buf_id.id != SG_INVALID_ID) { + const _sgimgui_buffer_t* buf_ui = &ctx->buffer_window.slots[_sgimgui_slot_index(buf_id.id)]; + return _sgimgui_res_id_string(buf_id.id, buf_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_image_id_string(_sgimgui_t* ctx, sg_image img_id) { + if (img_id.id != SG_INVALID_ID) { + const _sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img_id.id)]; + return _sgimgui_res_id_string(img_id.id, img_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_sampler_id_string(_sgimgui_t* ctx, sg_sampler smp_id) { + if (smp_id.id != SG_INVALID_ID) { + const _sgimgui_sampler_t* smp_ui = &ctx->sampler_window.slots[_sgimgui_slot_index(smp_id.id)]; + return _sgimgui_res_id_string(smp_id.id, smp_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_shader_id_string(_sgimgui_t* ctx, sg_shader shd_id) { + if (shd_id.id != SG_INVALID_ID) { + const _sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(shd_id.id)]; + return _sgimgui_res_id_string(shd_id.id, shd_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_pipeline_id_string(_sgimgui_t* ctx, sg_pipeline pip_id) { + if (pip_id.id != SG_INVALID_ID) { + const _sgimgui_pipeline_t* pip_ui = &ctx->pipeline_window.slots[_sgimgui_slot_index(pip_id.id)]; + return _sgimgui_res_id_string(pip_id.id, pip_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_view_id_string(_sgimgui_t* ctx, sg_view view_id) { + if (view_id.id != SG_INVALID_ID) { + const _sgimgui_view_t* view_ui = &ctx->view_window.slots[_sgimgui_slot_index(view_id.id)]; + return _sgimgui_res_id_string(view_id.id, view_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +/*--- RESOURCE HELPERS -------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_buffer_created(_sgimgui_t* ctx, sg_buffer res_id, int slot_index, const sg_buffer_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->buffer_window.num_slots)); + _sgimgui_buffer_t* buf = &ctx->buffer_window.slots[slot_index]; + buf->res_id = res_id; + buf->desc = *desc; + buf->label = _sgimgui_make_str(desc->label); +} + +_SOKOL_PRIVATE void _sgimgui_buffer_destroyed(_sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->buffer_window.num_slots)); + _sgimgui_buffer_t* buf = &ctx->buffer_window.slots[slot_index]; + buf->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_image_created(_sgimgui_t* ctx, sg_image res_id, int slot_index, const sg_image_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->image_window.num_slots)); + _sgimgui_image_t* img = &ctx->image_window.slots[slot_index]; + img->res_id = res_id; + img->desc = *desc; + img->ui_scale = 1.0f; + img->label = _sgimgui_make_str(desc->label); +} + +_SOKOL_PRIVATE void _sgimgui_image_destroyed(_sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->image_window.num_slots)); + _sgimgui_image_t* img = &ctx->image_window.slots[slot_index]; + img->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_sampler_created(_sgimgui_t* ctx, sg_sampler res_id, int slot_index, const sg_sampler_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->sampler_window.num_slots)); + _sgimgui_sampler_t* smp = &ctx->sampler_window.slots[slot_index]; + smp->res_id = res_id; + smp->desc = *desc; + smp->label = _sgimgui_make_str(desc->label); +} + +_SOKOL_PRIVATE void _sgimgui_sampler_destroyed(_sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->sampler_window.num_slots)); + _sgimgui_sampler_t* smp = &ctx->sampler_window.slots[slot_index]; + smp->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_shader_created(_sgimgui_t* ctx, sg_shader res_id, int slot_index, const sg_shader_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->shader_window.num_slots)); + _sgimgui_shader_t* shd = &ctx->shader_window.slots[slot_index]; + shd->res_id = res_id; + shd->desc = *desc; + shd->label = _sgimgui_make_str(desc->label); + if (shd->desc.vertex_func.entry) { + shd->vs_entry = _sgimgui_make_str(shd->desc.vertex_func.entry); + shd->desc.vertex_func.entry = shd->vs_entry.buf; + } + if (shd->desc.fragment_func.entry) { + shd->fs_entry = _sgimgui_make_str(shd->desc.fragment_func.entry); + shd->desc.fragment_func.entry = shd->fs_entry.buf; + } + if (shd->desc.vertex_func.d3d11_target) { + shd->vs_d3d11_target = _sgimgui_make_str(shd->desc.vertex_func.d3d11_target); + shd->desc.vertex_func.d3d11_target = shd->vs_d3d11_target.buf; + } + if (shd->desc.fragment_func.d3d11_target) { + shd->fs_d3d11_target = _sgimgui_make_str(shd->desc.fragment_func.d3d11_target); + shd->desc.fragment_func.d3d11_target = shd->fs_d3d11_target.buf; + } + for (int i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + for (int j = 0; j < SG_MAX_UNIFORMBLOCK_MEMBERS; j++) { + sg_glsl_shader_uniform* su = &shd->desc.uniform_blocks[i].glsl_uniforms[j]; + if (su->glsl_name) { + shd->glsl_uniform_name[i][j] = _sgimgui_make_str(su->glsl_name); + su->glsl_name = shd->glsl_uniform_name[i][j].buf; + } + } + } + for (int i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) { + if (shd->desc.texture_sampler_pairs[i].glsl_name) { + shd->glsl_texture_sampler_name[i] = _sgimgui_make_str(shd->desc.texture_sampler_pairs[i].glsl_name); + shd->desc.texture_sampler_pairs[i].glsl_name = shd->glsl_texture_sampler_name[i].buf; + } + } + if (shd->desc.vertex_func.source) { + shd->desc.vertex_func.source = _sgimgui_str_dup(&ctx->desc.allocator, shd->desc.vertex_func.source); + } + if (shd->desc.vertex_func.bytecode.ptr) { + shd->desc.vertex_func.bytecode.ptr = _sgimgui_bin_dup(&ctx->desc.allocator, shd->desc.vertex_func.bytecode.ptr, shd->desc.vertex_func.bytecode.size); + } + if (shd->desc.fragment_func.source) { + shd->desc.fragment_func.source = _sgimgui_str_dup(&ctx->desc.allocator, shd->desc.fragment_func.source); + } + if (shd->desc.fragment_func.bytecode.ptr) { + shd->desc.fragment_func.bytecode.ptr = _sgimgui_bin_dup(&ctx->desc.allocator, shd->desc.fragment_func.bytecode.ptr, shd->desc.fragment_func.bytecode.size); + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + sg_shader_vertex_attr* va = &shd->desc.attrs[i]; + if (va->glsl_name) { + shd->attr_glsl_name[i] = _sgimgui_make_str(va->glsl_name); + va->glsl_name = shd->attr_glsl_name[i].buf; + } + if (va->hlsl_sem_name) { + shd->attr_hlsl_sem_name[i] = _sgimgui_make_str(va->hlsl_sem_name); + va->hlsl_sem_name = shd->attr_hlsl_sem_name[i].buf; + } + } +} + +_SOKOL_PRIVATE void _sgimgui_shader_destroyed(_sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->shader_window.num_slots)); + _sgimgui_shader_t* shd = &ctx->shader_window.slots[slot_index]; + shd->res_id.id = SG_INVALID_ID; + if (shd->desc.vertex_func.source) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.vertex_func.source); + shd->desc.vertex_func.source = 0; + } + if (shd->desc.vertex_func.bytecode.ptr) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.vertex_func.bytecode.ptr); + shd->desc.vertex_func.bytecode.ptr = 0; + } + if (shd->desc.fragment_func.source) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.fragment_func.source); + shd->desc.fragment_func.source = 0; + } + if (shd->desc.fragment_func.bytecode.ptr) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.fragment_func.bytecode.ptr); + shd->desc.fragment_func.bytecode.ptr = 0; + } +} + +_SOKOL_PRIVATE void _sgimgui_pipeline_created(_sgimgui_t* ctx, sg_pipeline res_id, int slot_index, const sg_pipeline_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->pipeline_window.num_slots)); + _sgimgui_pipeline_t* pip = &ctx->pipeline_window.slots[slot_index]; + pip->res_id = res_id; + pip->label = _sgimgui_make_str(desc->label); + pip->desc = *desc; + +} + +_SOKOL_PRIVATE void _sgimgui_pipeline_destroyed(_sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->pipeline_window.num_slots)); + _sgimgui_pipeline_t* pip = &ctx->pipeline_window.slots[slot_index]; + pip->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_view_created(_sgimgui_t* ctx, sg_view res_id, int slot_index, const sg_view_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->view_window.num_slots)); + _sgimgui_view_t* view = &ctx->view_window.slots[slot_index]; + view->res_id = res_id; + view->ui_scale = 1.0f; + view->label = _sgimgui_make_str(desc->label); + view->desc = *desc; +} + +_SOKOL_PRIVATE void _sgimgui_view_destroyed(_sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->view_window.num_slots)); + _sgimgui_view_t* view = &ctx->view_window.slots[slot_index]; + view->res_id.id = SG_INVALID_ID; +} + +/*--- COMMAND CAPTURING ------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_capture_init(_sgimgui_t* ctx) { + const size_t ubuf_initial_size = 256 * 1024; + for (int i = 0; i < 2; i++) { + _sgimgui_capture_bucket_t* bucket = &ctx->capture_window.bucket[i]; + bucket->ubuf_size = ubuf_initial_size; + bucket->ubuf = (uint8_t*) _sgimgui_malloc(&ctx->desc.allocator, bucket->ubuf_size); + } +} + +_SOKOL_PRIVATE void _sgimgui_capture_discard(_sgimgui_t* ctx) { + for (int i = 0; i < 2; i++) { + _sgimgui_capture_bucket_t* bucket = &ctx->capture_window.bucket[i]; + SOKOL_ASSERT(bucket->ubuf); + _sgimgui_free(&ctx->desc.allocator, bucket->ubuf); + bucket->ubuf = 0; + } +} + +_SOKOL_PRIVATE _sgimgui_capture_bucket_t* _sgimgui_capture_get_write_bucket(_sgimgui_t* ctx) { + return &ctx->capture_window.bucket[ctx->capture_window.bucket_index & 1]; +} + +_SOKOL_PRIVATE _sgimgui_capture_bucket_t* _sgimgui_capture_get_read_bucket(_sgimgui_t* ctx) { + return &ctx->capture_window.bucket[(ctx->capture_window.bucket_index + 1) & 1]; +} + +_SOKOL_PRIVATE void _sgimgui_capture_next_frame(_sgimgui_t* ctx) { + ctx->capture_window.bucket_index = (ctx->capture_window.bucket_index + 1) & 1; + _sgimgui_capture_bucket_t* bucket = &ctx->capture_window.bucket[ctx->capture_window.bucket_index]; + bucket->num_items = 0; + bucket->ubuf_pos = 0; +} + +_SOKOL_PRIVATE void _sgimgui_capture_grow_ubuf(_sgimgui_t* ctx, size_t required_size) { + _sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_write_bucket(ctx); + SOKOL_ASSERT(required_size > bucket->ubuf_size); + 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*) _sgimgui_realloc(&ctx->desc.allocator, bucket->ubuf, old_size, new_size); +} + +_SOKOL_PRIVATE _sgimgui_capture_item_t* _sgimgui_capture_next_write_item(_sgimgui_t* ctx) { + _sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_write_bucket(ctx); + if (bucket->num_items < _SGIMGUI_MAX_FRAMECAPTURE_ITEMS) { + _sgimgui_capture_item_t* item = &bucket->items[bucket->num_items++]; + return item; + } else { + return 0; + } +} + +_SOKOL_PRIVATE int _sgimgui_capture_num_read_items(_sgimgui_t* ctx) { + _sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_read_bucket(ctx); + return bucket->num_items; +} + +_SOKOL_PRIVATE _sgimgui_capture_item_t* _sgimgui_capture_read_item_at(_sgimgui_t* ctx, int index) { + _sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_read_bucket(ctx); + SOKOL_ASSERT(index < bucket->num_items); + return &bucket->items[index]; +} + +_SOKOL_PRIVATE size_t _sgimgui_capture_uniforms(_sgimgui_t* ctx, const sg_range* data) { + _sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_write_bucket(ctx); + const size_t required_size = bucket->ubuf_pos + data->size; + if (required_size > bucket->ubuf_size) { + _sgimgui_capture_grow_ubuf(ctx, required_size); + } + SOKOL_ASSERT(required_size <= bucket->ubuf_size); + memcpy(bucket->ubuf + bucket->ubuf_pos, data->ptr, data->size); + const size_t pos = bucket->ubuf_pos; + bucket->ubuf_pos += data->size; + SOKOL_ASSERT(bucket->ubuf_pos <= bucket->ubuf_size); + return pos; +} + +_SOKOL_PRIVATE _sgimgui_str_t _sgimgui_capture_item_string(_sgimgui_t* ctx, int index, const _sgimgui_capture_item_t* item) { + _sgimgui_str_t str = _sgimgui_make_str(0); + switch (item->cmd) { + case _SGIMGUI_CMD_RESET_STATE_CACHE: + _sgimgui_snprintf(&str, "%d: sg_reset_state_cache()", index); + break; + + case _SGIMGUI_CMD_MAKE_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.make_buffer.result); + _sgimgui_snprintf(&str, "%d: sg_make_buffer(desc=..) => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_MAKE_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.make_image.result); + _sgimgui_snprintf(&str, "%d: sg_make_image(desc=..) => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_MAKE_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.make_sampler.result); + _sgimgui_snprintf(&str, "%d: sg_make_sampler(desc=..) => %s", index, res_id.buf); + } + break; + case _SGIMGUI_CMD_MAKE_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.make_shader.result); + _sgimgui_snprintf(&str, "%d: sg_make_shader(desc=..) => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_MAKE_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.make_pipeline.result); + _sgimgui_snprintf(&str, "%d: sg_make_pipeline(desc=..) => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_MAKE_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.make_view.result); + _sgimgui_snprintf(&str, "%d: sg_make_views(desc=..) => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DESTROY_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.destroy_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_destroy_buffer(buf=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DESTROY_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.destroy_image.image); + _sgimgui_snprintf(&str, "%d: sg_destroy_image(img=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DESTROY_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.destroy_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_destroy_sampler(smp=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DESTROY_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.destroy_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_destroy_shader(shd=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DESTROY_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.destroy_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_destroy_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DESTROY_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.destroy_view.view); + _sgimgui_snprintf(&str, "%d: sg_destroy_view(view=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UPDATE_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.update_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_update_buffer(buf=%s, data.size=%d)", + index, res_id.buf, + item->args.update_buffer.data_size); + } + break; + + case _SGIMGUI_CMD_UPDATE_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.update_image.image); + _sgimgui_snprintf(&str, "%d: sg_update_image(img=%s, data=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_APPEND_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.append_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_append_buffer(buf=%s, data.size=%d) => %d", + index, res_id.buf, + item->args.append_buffer.data_size, + item->args.append_buffer.result); + } + break; + + case _SGIMGUI_CMD_BEGIN_PASS: + { + _sgimgui_snprintf(&str, "%d: sg_begin_pass(pass=...)", index); + } + break; + + case _SGIMGUI_CMD_APPLY_VIEWPORT: + _sgimgui_snprintf(&str, "%d: sg_apply_viewport(x=%d, y=%d, width=%d, height=%d, origin_top_left=%s)", + index, + item->args.apply_viewport.x, + item->args.apply_viewport.y, + item->args.apply_viewport.width, + item->args.apply_viewport.height, + _sgimgui_bool_string(item->args.apply_viewport.origin_top_left)); + break; + + case _SGIMGUI_CMD_APPLY_SCISSOR_RECT: + _sgimgui_snprintf(&str, "%d: sg_apply_scissor_rect(x=%d, y=%d, width=%d, height=%d, origin_top_left=%s)", + index, + item->args.apply_scissor_rect.x, + item->args.apply_scissor_rect.y, + item->args.apply_scissor_rect.width, + item->args.apply_scissor_rect.height, + _sgimgui_bool_string(item->args.apply_scissor_rect.origin_top_left)); + break; + + case _SGIMGUI_CMD_APPLY_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.apply_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_apply_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_APPLY_BINDINGS: + _sgimgui_snprintf(&str, "%d: sg_apply_bindings(bindings=..)", index); + break; + + case _SGIMGUI_CMD_APPLY_UNIFORMS: + _sgimgui_snprintf(&str, "%d: sg_apply_uniforms(ub_slot=%d, data.size=%d)", + index, + item->args.apply_uniforms.ub_slot, + item->args.apply_uniforms.data_size); + break; + + case _SGIMGUI_CMD_DRAW: + _sgimgui_snprintf(&str, "%d: sg_draw(base_element=%d, num_elements=%d, num_instances=%d)", + index, + item->args.draw.base_element, + item->args.draw.num_elements, + item->args.draw.num_instances); + break; + + case _SGIMGUI_CMD_DRAW_EX: + _sgimgui_snprintf(&str, "%d: sg_draw_ex(base_element=%d, num_elements=%d, num_instances=%d, base_vertex=%d, base_instance=%d)", + index, + item->args.draw_ex.base_element, + item->args.draw_ex.num_elements, + item->args.draw_ex.num_instances, + item->args.draw_ex.base_vertex, + item->args.draw_ex.base_instance); + break; + + case _SGIMGUI_CMD_DISPATCH: + _sgimgui_snprintf(&str, "%d: sg_dispatch(num_groups_x=%d, num_groups_y=%d, num_groups_z=%d)", + index, + item->args.dispatch.num_groups_x, + item->args.dispatch.num_groups_y, + item->args.dispatch.num_groups_z); + break; + + case _SGIMGUI_CMD_END_PASS: + _sgimgui_snprintf(&str, "%d: sg_end_pass()", index); + break; + + case _SGIMGUI_CMD_COMMIT: + _sgimgui_snprintf(&str, "%d: sg_commit()", index); + break; + + case _SGIMGUI_CMD_ALLOC_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.alloc_buffer.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_buffer() => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_ALLOC_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.alloc_image.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_image() => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_ALLOC_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.alloc_sampler.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_sampler() => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_ALLOC_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.alloc_shader.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_shader() => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_ALLOC_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.alloc_pipeline.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_pipeline() => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_ALLOC_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.alloc_view.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_view() => %s", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DEALLOC_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.dealloc_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_dealloc_buffer(buf=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DEALLOC_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.dealloc_image.image); + _sgimgui_snprintf(&str, "%d: sg_dealloc_image(img=%d)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DEALLOC_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.dealloc_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_dealloc_sampler(smp=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DEALLOC_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.dealloc_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_dealloc_shader(shd=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DEALLOC_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.dealloc_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_dealloc_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_DEALLOC_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.dealloc_view.view); + _sgimgui_snprintf(&str, "%d: sg_dealloc_view(view=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_INIT_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.init_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_init_buffer(buf=%s, desc=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_INIT_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.init_image.image); + _sgimgui_snprintf(&str, "%d: sg_init_image(img=%s, desc=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_INIT_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.init_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_init_sampler(smp=%s, desc=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_INIT_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.init_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_init_shader(shd=%s, desc=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_INIT_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.init_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_init_pipeline(pip=%s, desc=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_INIT_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.init_view.view); + _sgimgui_snprintf(&str, "%d: sg_init_view(view=%s, desc=..)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UNINIT_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.uninit_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_uninit_buffer(buf=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UNINIT_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.uninit_image.image); + _sgimgui_snprintf(&str, "%d: sg_uninit_image(img=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UNINIT_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.uninit_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_uninit_sampler(smp=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UNINIT_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.uninit_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_uninit_shader(shd=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UNINIT_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.uninit_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_uninit_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_UNINIT_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.uninit_view.view); + _sgimgui_snprintf(&str, "%d: sg_uninit_view(view=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_FAIL_BUFFER: + { + _sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.fail_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_fail_buffer(buf=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_FAIL_IMAGE: + { + _sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.fail_image.image); + _sgimgui_snprintf(&str, "%d: sg_fail_image(img=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_FAIL_SAMPLER: + { + _sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.fail_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_fail_sampler(smp=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_FAIL_SHADER: + { + _sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.fail_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_fail_shader(shd=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_FAIL_PIPELINE: + { + _sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.fail_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_fail_pipeline(shd=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_FAIL_VIEW: + { + _sgimgui_str_t res_id = _sgimgui_view_id_string(ctx, item->args.fail_view.view); + _sgimgui_snprintf(&str, "%d: sg_fail_view(view=%s)", index, res_id.buf); + } + break; + + case _SGIMGUI_CMD_PUSH_DEBUG_GROUP: + _sgimgui_snprintf(&str, "%d: sg_push_debug_group(name=%s)", index, + item->args.push_debug_group.name.buf); + break; + + case _SGIMGUI_CMD_POP_DEBUG_GROUP: + _sgimgui_snprintf(&str, "%d: sg_pop_debug_group()", index); + break; + + default: + _sgimgui_snprintf(&str, "%d: ???", index); + break; + } + return str; +} + +/*--- CAPTURE CALLBACKS ------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_reset_state_cache(void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_RESET_STATE_CACHE; + item->color = _SGIMGUI_COLOR_OTHER; + } + if (ctx->hooks.reset_state_cache) { + ctx->hooks.reset_state_cache(ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_buffer(const sg_buffer_desc* desc, sg_buffer buf_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_MAKE_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_buffer.result = buf_id; + } + if (ctx->hooks.make_buffer) { + ctx->hooks.make_buffer(desc, buf_id, ctx->hooks.user_data); + } + if (buf_id.id != SG_INVALID_ID) { + _sgimgui_buffer_created(ctx, buf_id, _sgimgui_slot_index(buf_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_image(const sg_image_desc* desc, sg_image img_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_MAKE_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_image.result = img_id; + } + if (ctx->hooks.make_image) { + ctx->hooks.make_image(desc, img_id, ctx->hooks.user_data); + } + if (img_id.id != SG_INVALID_ID) { + _sgimgui_image_created(ctx, img_id, _sgimgui_slot_index(img_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_sampler(const sg_sampler_desc* desc, sg_sampler smp_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_MAKE_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_sampler.result = smp_id; + } + if (ctx->hooks.make_sampler) { + ctx->hooks.make_sampler(desc, smp_id, ctx->hooks.user_data); + } + if (smp_id.id != SG_INVALID_ID) { + _sgimgui_sampler_created(ctx, smp_id, _sgimgui_slot_index(smp_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_shader(const sg_shader_desc* desc, sg_shader shd_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_MAKE_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_shader.result = shd_id; + } + if (ctx->hooks.make_shader) { + ctx->hooks.make_shader(desc, shd_id, ctx->hooks.user_data); + } + if (shd_id.id != SG_INVALID_ID) { + _sgimgui_shader_created(ctx, shd_id, _sgimgui_slot_index(shd_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_pipeline(const sg_pipeline_desc* desc, sg_pipeline pip_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_MAKE_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_pipeline.result = pip_id; + } + if (ctx->hooks.make_pipeline) { + ctx->hooks.make_pipeline(desc, pip_id, ctx->hooks.user_data); + } + if (pip_id.id != SG_INVALID_ID) { + _sgimgui_pipeline_created(ctx, pip_id, _sgimgui_slot_index(pip_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_view(const sg_view_desc* desc, sg_view view_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_MAKE_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_view.result = view_id; + } + if (ctx->hooks.make_view) { + ctx->hooks.make_view(desc, view_id, ctx->hooks.user_data); + } + if (view_id.id != SG_INVALID_ID) { + _sgimgui_view_created(ctx, view_id, _sgimgui_slot_index(view_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_buffer(sg_buffer buf, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DESTROY_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_buffer.buffer = buf; + } + if (ctx->hooks.destroy_buffer) { + ctx->hooks.destroy_buffer(buf, ctx->hooks.user_data); + } + if (buf.id != SG_INVALID_ID) { + _sgimgui_buffer_destroyed(ctx, _sgimgui_slot_index(buf.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_image(sg_image img, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DESTROY_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_image.image = img; + } + if (ctx->hooks.destroy_image) { + ctx->hooks.destroy_image(img, ctx->hooks.user_data); + } + if (img.id != SG_INVALID_ID) { + _sgimgui_image_destroyed(ctx, _sgimgui_slot_index(img.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_sampler(sg_sampler smp, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DESTROY_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_sampler.sampler = smp; + } + if (ctx->hooks.destroy_sampler) { + ctx->hooks.destroy_sampler(smp, ctx->hooks.user_data); + } + if (smp.id != SG_INVALID_ID) { + _sgimgui_sampler_destroyed(ctx, _sgimgui_slot_index(smp.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_shader(sg_shader shd, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DESTROY_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_shader.shader = shd; + } + if (ctx->hooks.destroy_shader) { + ctx->hooks.destroy_shader(shd, ctx->hooks.user_data); + } + if (shd.id != SG_INVALID_ID) { + _sgimgui_shader_destroyed(ctx, _sgimgui_slot_index(shd.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_pipeline(sg_pipeline pip, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DESTROY_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_pipeline.pipeline = pip; + } + if (ctx->hooks.destroy_pipeline) { + ctx->hooks.destroy_pipeline(pip, ctx->hooks.user_data); + } + if (pip.id != SG_INVALID_ID) { + _sgimgui_pipeline_destroyed(ctx, _sgimgui_slot_index(pip.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_view(sg_view view, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DESTROY_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_view.view = view; + } + if (ctx->hooks.destroy_view) { + ctx->hooks.destroy_view(view, ctx->hooks.user_data); + } + if (view.id != SG_INVALID_ID) { + _sgimgui_view_destroyed(ctx, _sgimgui_slot_index(view.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_update_buffer(sg_buffer buf, const sg_range* data, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UPDATE_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.update_buffer.buffer = buf; + item->args.update_buffer.data_size = data->size; + } + if (ctx->hooks.update_buffer) { + ctx->hooks.update_buffer(buf, data, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_update_image(sg_image img, const sg_image_data* data, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UPDATE_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.update_image.image = img; + } + if (ctx->hooks.update_image) { + ctx->hooks.update_image(img, data, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_append_buffer(sg_buffer buf, const sg_range* data, int result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_APPEND_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.append_buffer.buffer = buf; + item->args.append_buffer.data_size = data->size; + item->args.append_buffer.result = result; + } + if (ctx->hooks.append_buffer) { + ctx->hooks.append_buffer(buf, data, result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_begin_pass(const sg_pass* pass, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + SOKOL_ASSERT(pass); + item->cmd = _SGIMGUI_CMD_BEGIN_PASS; + item->color = _SGIMGUI_COLOR_PASS; + item->args.begin_pass.pass = *pass; + } + if (ctx->hooks.begin_pass) { + ctx->hooks.begin_pass(pass, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_viewport(int x, int y, int width, int height, bool origin_top_left, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_APPLY_VIEWPORT; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_viewport.x = x; + item->args.apply_viewport.y = y; + item->args.apply_viewport.width = width; + item->args.apply_viewport.height = height; + item->args.apply_viewport.origin_top_left = origin_top_left; + } + if (ctx->hooks.apply_viewport) { + ctx->hooks.apply_viewport(x, y, width, height, origin_top_left, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_APPLY_SCISSOR_RECT; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_scissor_rect.x = x; + item->args.apply_scissor_rect.y = y; + item->args.apply_scissor_rect.width = width; + item->args.apply_scissor_rect.height = height; + item->args.apply_scissor_rect.origin_top_left = origin_top_left; + } + if (ctx->hooks.apply_scissor_rect) { + ctx->hooks.apply_scissor_rect(x, y, width, height, origin_top_left, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_pipeline(sg_pipeline pip, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + ctx->cur_pipeline = pip; /* stored for _sgimgui_apply_uniforms */ + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_APPLY_PIPELINE; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_pipeline.pipeline = pip; + } + if (ctx->hooks.apply_pipeline) { + ctx->hooks.apply_pipeline(pip, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_bindings(const sg_bindings* bindings, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + SOKOL_ASSERT(bindings); + item->cmd = _SGIMGUI_CMD_APPLY_BINDINGS; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_bindings.bindings = *bindings; + } + if (ctx->hooks.apply_bindings) { + ctx->hooks.apply_bindings(bindings, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_uniforms(int ub_slot, const sg_range* data, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(data); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_APPLY_UNIFORMS; + item->color = _SGIMGUI_COLOR_APPLY; + _sgimgui_args_apply_uniforms_t* args = &item->args.apply_uniforms; + args->ub_slot = ub_slot; + args->data_size = data->size; + args->pipeline = ctx->cur_pipeline; + args->ubuf_pos = _sgimgui_capture_uniforms(ctx, data); + } + if (ctx->hooks.apply_uniforms) { + ctx->hooks.apply_uniforms(ub_slot, data, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw(int base_element, int num_elements, int num_instances, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DRAW; + item->color = _SGIMGUI_COLOR_DRAW; + item->args.draw.base_element = base_element; + item->args.draw.num_elements = num_elements; + item->args.draw.num_instances = num_instances; + } + if (ctx->hooks.draw) { + ctx->hooks.draw(base_element, num_elements, num_instances, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*)user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DRAW_EX; + item->color = _SGIMGUI_COLOR_DRAW; + item->args.draw_ex.base_element = base_element; + item->args.draw_ex.num_elements = num_elements; + item->args.draw_ex.num_instances = num_instances; + item->args.draw_ex.base_vertex = base_vertex; + item->args.draw_ex.base_instance = base_instance; + } + if (ctx->hooks.draw_ex) { + ctx->hooks.draw_ex(base_element, num_elements, num_instances, base_vertex, base_instance, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dispatch(int num_groups_x, int num_groups_y, int num_groups_z, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DISPATCH; + item->color = _SGIMGUI_COLOR_DRAW; + item->args.dispatch.num_groups_x = num_groups_x; + item->args.dispatch.num_groups_y = num_groups_y; + item->args.dispatch.num_groups_z = num_groups_z; + } + if (ctx->hooks.dispatch) { + ctx->hooks.dispatch(num_groups_x, num_groups_y, num_groups_z, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_end_pass(void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + ctx->cur_pipeline.id = SG_INVALID_ID; + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_END_PASS; + item->color = _SGIMGUI_COLOR_PASS; + } + if (ctx->hooks.end_pass) { + ctx->hooks.end_pass(ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_commit(void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_COMMIT; + item->color = _SGIMGUI_COLOR_OTHER; + } + _sgimgui_capture_next_frame(ctx); + if (ctx->hooks.commit) { + ctx->hooks.commit(ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_buffer(sg_buffer result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_ALLOC_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_buffer.result = result; + } + if (ctx->hooks.alloc_buffer) { + ctx->hooks.alloc_buffer(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_image(sg_image result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_ALLOC_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_image.result = result; + } + if (ctx->hooks.alloc_image) { + ctx->hooks.alloc_image(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_sampler(sg_sampler result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_ALLOC_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_sampler.result = result; + } + if (ctx->hooks.alloc_sampler) { + ctx->hooks.alloc_sampler(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_shader(sg_shader result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_ALLOC_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_shader.result = result; + } + if (ctx->hooks.alloc_shader) { + ctx->hooks.alloc_shader(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_pipeline(sg_pipeline result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_ALLOC_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_pipeline.result = result; + } + if (ctx->hooks.alloc_pipeline) { + ctx->hooks.alloc_pipeline(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_view(sg_view result, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_ALLOC_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_view.result = result; + } + if (ctx->hooks.alloc_view) { + ctx->hooks.alloc_view(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_buffer(sg_buffer buf_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DEALLOC_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_buffer.buffer = buf_id; + } + if (ctx->hooks.dealloc_buffer) { + ctx->hooks.dealloc_buffer(buf_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_image(sg_image img_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DEALLOC_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_image.image = img_id; + } + if (ctx->hooks.dealloc_image) { + ctx->hooks.dealloc_image(img_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_sampler(sg_sampler smp_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DEALLOC_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_sampler.sampler = smp_id; + } + if (ctx->hooks.dealloc_sampler) { + ctx->hooks.dealloc_sampler(smp_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_shader(sg_shader shd_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DEALLOC_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_shader.shader = shd_id; + } + if (ctx->hooks.dealloc_shader) { + ctx->hooks.dealloc_shader(shd_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_pipeline(sg_pipeline pip_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DEALLOC_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_pipeline.pipeline = pip_id; + } + if (ctx->hooks.dealloc_pipeline) { + ctx->hooks.dealloc_pipeline(pip_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_view(sg_view view_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_DEALLOC_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_view.view = view_id; + } + if (ctx->hooks.dealloc_view) { + ctx->hooks.dealloc_view(view_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_INIT_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_buffer.buffer = buf_id; + } + if (ctx->hooks.init_buffer) { + ctx->hooks.init_buffer(buf_id, desc, ctx->hooks.user_data); + } + if (buf_id.id != SG_INVALID_ID) { + _sgimgui_buffer_created(ctx, buf_id, _sgimgui_slot_index(buf_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_image(sg_image img_id, const sg_image_desc* desc, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_INIT_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_image.image = img_id; + } + if (ctx->hooks.init_image) { + ctx->hooks.init_image(img_id, desc, ctx->hooks.user_data); + } + if (img_id.id != SG_INVALID_ID) { + _sgimgui_image_created(ctx, img_id, _sgimgui_slot_index(img_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_INIT_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_sampler.sampler = smp_id; + } + if (ctx->hooks.init_sampler) { + ctx->hooks.init_sampler(smp_id, desc, ctx->hooks.user_data); + } + if (smp_id.id != SG_INVALID_ID) { + _sgimgui_sampler_created(ctx, smp_id, _sgimgui_slot_index(smp_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_shader(sg_shader shd_id, const sg_shader_desc* desc, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_INIT_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_shader.shader = shd_id; + } + if (ctx->hooks.init_shader) { + ctx->hooks.init_shader(shd_id, desc, ctx->hooks.user_data); + } + if (shd_id.id != SG_INVALID_ID) { + _sgimgui_shader_created(ctx, shd_id, _sgimgui_slot_index(shd_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_INIT_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_pipeline.pipeline = pip_id; + } + if (ctx->hooks.init_pipeline) { + ctx->hooks.init_pipeline(pip_id, desc, ctx->hooks.user_data); + } + if (pip_id.id != SG_INVALID_ID) { + _sgimgui_pipeline_created(ctx, pip_id, _sgimgui_slot_index(pip_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_view(sg_view view_id, const sg_view_desc* desc, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_INIT_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_view.view = view_id; + } + if (ctx->hooks.init_view) { + ctx->hooks.init_view(view_id, desc, ctx->hooks.user_data); + } + if (view_id.id != SG_INVALID_ID) { + _sgimgui_view_created(ctx, view_id, _sgimgui_slot_index(view_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_buffer(sg_buffer buf, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UNINIT_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_buffer.buffer = buf; + } + if (ctx->hooks.uninit_buffer) { + ctx->hooks.uninit_buffer(buf, ctx->hooks.user_data); + } + if (buf.id != SG_INVALID_ID) { + _sgimgui_buffer_destroyed(ctx, _sgimgui_slot_index(buf.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_image(sg_image img, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UNINIT_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_image.image = img; + } + if (ctx->hooks.uninit_image) { + ctx->hooks.uninit_image(img, ctx->hooks.user_data); + } + if (img.id != SG_INVALID_ID) { + _sgimgui_image_destroyed(ctx, _sgimgui_slot_index(img.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_sampler(sg_sampler smp, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UNINIT_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_sampler.sampler = smp; + } + if (ctx->hooks.uninit_sampler) { + ctx->hooks.uninit_sampler(smp, ctx->hooks.user_data); + } + if (smp.id != SG_INVALID_ID) { + _sgimgui_sampler_destroyed(ctx, _sgimgui_slot_index(smp.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_shader(sg_shader shd, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UNINIT_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_shader.shader = shd; + } + if (ctx->hooks.uninit_shader) { + ctx->hooks.uninit_shader(shd, ctx->hooks.user_data); + } + if (shd.id != SG_INVALID_ID) { + _sgimgui_shader_destroyed(ctx, _sgimgui_slot_index(shd.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_pipeline(sg_pipeline pip, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UNINIT_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_pipeline.pipeline = pip; + } + if (ctx->hooks.uninit_pipeline) { + ctx->hooks.uninit_pipeline(pip, ctx->hooks.user_data); + } + if (pip.id != SG_INVALID_ID) { + _sgimgui_pipeline_destroyed(ctx, _sgimgui_slot_index(pip.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_view(sg_view view, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_UNINIT_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_view.view = view; + } + if (ctx->hooks.uninit_view) { + ctx->hooks.uninit_view(view, ctx->hooks.user_data); + } + if (view.id != SG_INVALID_ID) { + _sgimgui_view_destroyed(ctx, _sgimgui_slot_index(view.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_buffer(sg_buffer buf_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_FAIL_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_buffer.buffer = buf_id; + } + if (ctx->hooks.fail_buffer) { + ctx->hooks.fail_buffer(buf_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_image(sg_image img_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_FAIL_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_image.image = img_id; + } + if (ctx->hooks.fail_image) { + ctx->hooks.fail_image(img_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_sampler(sg_sampler smp_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_FAIL_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_sampler.sampler = smp_id; + } + if (ctx->hooks.fail_sampler) { + ctx->hooks.fail_sampler(smp_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_shader(sg_shader shd_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_FAIL_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_shader.shader = shd_id; + } + if (ctx->hooks.fail_shader) { + ctx->hooks.fail_shader(shd_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_pipeline(sg_pipeline pip_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_FAIL_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_pipeline.pipeline = pip_id; + } + if (ctx->hooks.fail_pipeline) { + ctx->hooks.fail_pipeline(pip_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_view(sg_view view_id, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_FAIL_VIEW; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_view.view = view_id; + } + if (ctx->hooks.fail_view) { + ctx->hooks.fail_view(view_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_push_debug_group(const char* name, void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + if (0 == strcmp(name, "sokol-imgui")) { + ctx->frame_stats_window.in_sokol_imgui = true; + if (ctx->frame_stats_window.disable_sokol_imgui_stats) { + sg_disable_stats(); + } + } + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_PUSH_DEBUG_GROUP; + item->color = _SGIMGUI_COLOR_OTHER; + item->args.push_debug_group.name = _sgimgui_make_str(name); + } + if (ctx->hooks.push_debug_group) { + ctx->hooks.push_debug_group(name, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_pop_debug_group(void* user_data) { + _sgimgui_t* ctx = (_sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + if (ctx->frame_stats_window.in_sokol_imgui) { + ctx->frame_stats_window.in_sokol_imgui = false; + if (ctx->frame_stats_window.disable_sokol_imgui_stats) { + sg_enable_stats(); + } + } + _sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = _SGIMGUI_CMD_POP_DEBUG_GROUP; + item->color = _SGIMGUI_COLOR_OTHER; + } + if (ctx->hooks.pop_debug_group) { + ctx->hooks.pop_debug_group(ctx->hooks.user_data); + } +} + +/*--- IMGUI HELPERS ----------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_draw_image(_sgimgui_t* ctx, sg_image img, float* opt_scale_ptr, float max_width) { + if (sg_query_image_state(img) != SG_RESOURCESTATE_VALID) { + _sgimgui_igtext("Image not in valid state."); + return; + } + // try to find a texture view for the image + sg_view view = {SG_INVALID_ID}; + for (int i = 0; i < ctx->view_window.num_slots; i++) { + const _sgimgui_view_t* view_ui = &ctx->view_window.slots[i]; + view = view_ui->res_id; + if (sg_query_view_type(view) == SG_VIEWTYPE_TEXTURE) { + sg_image view_img = sg_query_view_image(view); + if (view_img.id == img.id) { + // FIXME: once texture views can have a separate image type, check this instead + const bool image_renderable = (sg_query_image_type(view_img) == SG_IMAGETYPE_2D) && (sg_query_image_sample_count(view_img) == 1); + if (image_renderable) { + break; + } + } + } + } + if (view.id != SG_INVALID_ID) { + _sgimgui_igpushidint((int)view.id); + float scale = 1.0f; + if (opt_scale_ptr) { + _sgimgui_igsliderfloatex("Scale", opt_scale_ptr, 0.125f, 8.0f, "%.3f", ImGuiSliderFlags_Logarithmic); + scale = *opt_scale_ptr; + } + float w = (float)sg_query_image_width(img) * scale; + float h = (float)sg_query_image_height(img) * scale; + if ((max_width > 1.0f) && (w > max_width)) { + h *= max_width / w; + w = max_width; + } + _sgimgui_igimage(simgui_imtextureid(view), IMVEC2(w, h)); + _sgimgui_igpopid(); + } else { + _sgimgui_igtext("Image has no renderable texture view.", img.id); + } +} + +_SOKOL_PRIVATE bool _sgimgui_draw_resid_list_item(uint32_t res_id, const char* label, bool selected) { + _sgimgui_igpushidint((int)res_id); + bool res; + if (label[0]) { + res = _sgimgui_igselectableex(label, selected, 0, IMVEC2(0,0)); + } else { + _sgimgui_str_t str; + _sgimgui_snprintf(&str, "0x%08X", res_id); + res = _sgimgui_igselectableex(str.buf, selected, 0, IMVEC2(0,0)); + } + _sgimgui_igpopid(); + return res; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_resid_link(uint32_t res_type, uint32_t res_id, const char* label) { + SOKOL_ASSERT(label); + _sgimgui_str_t str_buf; + const char* str; + if (label[0]) { + str = label; + } else { + _sgimgui_snprintf(&str_buf, "0x%08X", res_id); + str = str_buf.buf; + } + _sgimgui_igpushidint((int)((res_type<<24)|res_id)); + bool res = _sgimgui_igsmallbutton(str); + _sgimgui_igpopid(); + return res; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_buffer_link(_sgimgui_t* ctx, sg_buffer buf) { + bool retval = false; + if (buf.id != SG_INVALID_ID) { + const _sgimgui_buffer_t* buf_ui = &ctx->buffer_window.slots[_sgimgui_slot_index(buf.id)]; + retval = _sgimgui_draw_resid_link(1, buf.id, buf_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_image_link(_sgimgui_t* ctx, sg_image img) { + bool retval = false; + if (img.id != SG_INVALID_ID) { + const _sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img.id)]; + retval = _sgimgui_draw_resid_link(2, img.id, img_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_sampler_link(_sgimgui_t* ctx, sg_sampler smp) { + bool retval = false; + if (smp.id != SG_INVALID_ID) { + const _sgimgui_sampler_t* smp_ui = &ctx->sampler_window.slots[_sgimgui_slot_index(smp.id)]; + retval = _sgimgui_draw_resid_link(3, smp.id, smp_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_shader_link(_sgimgui_t* ctx, sg_shader shd) { + bool retval = false; + if (shd.id != SG_INVALID_ID) { + const _sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(shd.id)]; + retval = _sgimgui_draw_resid_link(4, shd.id, shd_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_view_link(_sgimgui_t* ctx, sg_view view) { + bool retval = false; + if (view.id != SG_INVALID_ID) { + const _sgimgui_view_t* view_ui = &ctx->view_window.slots[_sgimgui_slot_index(view.id)]; + retval = _sgimgui_draw_resid_link(5, view.id, view_ui->label.buf); + if (_sgimgui_igisitemhovered(0)) { + sg_image img = sg_query_view_image(view); + if (img.id != SG_INVALID_ID) { + if (_sgimgui_igbegintooltip()) { + _sgimgui_draw_image(ctx, img, 0, 128.0f); + _sgimgui_igendtooltip(); + } + } + } + } + return retval; +} + +_SOKOL_PRIVATE void _sgimgui_show_buffer(_sgimgui_t* ctx, sg_buffer buf) { + ctx->buffer_window.open = true; + ctx->buffer_window.sel_buf = buf; +} + +_SOKOL_PRIVATE void _sgimgui_show_image(_sgimgui_t* ctx, sg_image img) { + ctx->image_window.open = true; + ctx->image_window.sel_img = img; +} + +_SOKOL_PRIVATE void _sgimgui_show_sampler(_sgimgui_t* ctx, sg_sampler smp) { + ctx->sampler_window.open = true; + ctx->sampler_window.sel_smp = smp; +} + +_SOKOL_PRIVATE void _sgimgui_show_shader(_sgimgui_t* ctx, sg_shader shd) { + ctx->shader_window.open = true; + ctx->shader_window.sel_shd = shd; +} + +_SOKOL_PRIVATE void _sgimgui_show_view(_sgimgui_t* ctx, sg_view view) { + ctx->view_window.open = true; + ctx->view_window.sel_view = view; +} + +_SOKOL_PRIVATE void _sgimgui_draw_buffer_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("buffer_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->buffer_window.num_slots; i++) { + sg_buffer buf = ctx->buffer_window.slots[i].res_id; + sg_resource_state state = sg_query_buffer_state(buf); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->buffer_window.sel_buf.id == buf.id; + if (_sgimgui_draw_resid_list_item(buf.id, ctx->buffer_window.slots[i].label.buf, selected)) { + ctx->buffer_window.sel_buf.id = buf.id; + } + } + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_image_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("image_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->image_window.num_slots; i++) { + sg_image img = ctx->image_window.slots[i].res_id; + sg_resource_state state = sg_query_image_state(img); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->image_window.sel_img.id == img.id; + if (_sgimgui_draw_resid_list_item(img.id, ctx->image_window.slots[i].label.buf, selected)) { + ctx->image_window.sel_img.id = img.id; + } + } + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_sampler_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("sampler_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->sampler_window.num_slots; i++) { + sg_sampler smp = ctx->sampler_window.slots[i].res_id; + sg_resource_state state = sg_query_sampler_state(smp); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->sampler_window.sel_smp.id == smp.id; + if (_sgimgui_draw_resid_list_item(smp.id, ctx->sampler_window.slots[i].label.buf, selected)) { + ctx->sampler_window.sel_smp.id = smp.id; + } + } + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_shader_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("shader_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->shader_window.num_slots; i++) { + sg_shader shd = ctx->shader_window.slots[i].res_id; + sg_resource_state state = sg_query_shader_state(shd); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->shader_window.sel_shd.id == shd.id; + if (_sgimgui_draw_resid_list_item(shd.id, ctx->shader_window.slots[i].label.buf, selected)) { + ctx->shader_window.sel_shd.id = shd.id; + } + } + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_pipeline_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("pipeline_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 1; i < ctx->pipeline_window.num_slots; i++) { + sg_pipeline pip = ctx->pipeline_window.slots[i].res_id; + sg_resource_state state = sg_query_pipeline_state(pip); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->pipeline_window.sel_pip.id == pip.id; + if (_sgimgui_draw_resid_list_item(pip.id, ctx->pipeline_window.slots[i].label.buf, selected)) { + ctx->pipeline_window.sel_pip.id = pip.id; + } + } + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_view_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("view_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 1; i < ctx->view_window.num_slots; i++) { + sg_view view = ctx->view_window.slots[i].res_id; + sg_resource_state state = sg_query_view_state(view); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->view_window.sel_view.id == view.id; + if (_sgimgui_draw_resid_list_item(view.id, ctx->view_window.slots[i].label.buf, selected)) { + ctx->view_window.sel_view.id = view.id; + } + } + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_capture_list(_sgimgui_t* ctx) { + _sgimgui_igbeginchild("capture_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + const int num_items = _sgimgui_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 _sgimgui_capture_item_t* item = _sgimgui_capture_read_item_at(ctx, i); + _sgimgui_str_t item_string = _sgimgui_capture_item_string(ctx, i, item); + _sgimgui_igpushstylecolor(ImGuiCol_Text, item->color); + _sgimgui_igpushidint(i); + if (item->cmd == _SGIMGUI_CMD_PUSH_DEBUG_GROUP) { + if (group_stack & 1) { + group_stack <<= 1; + const char* group_name = item->args.push_debug_group.name.buf; + if (_sgimgui_igtreenodestr(group_name, "Group: %s", group_name)) { + group_stack |= 1; + } + } else { + group_stack <<= 1; + } + } else if (item->cmd == _SGIMGUI_CMD_POP_DEBUG_GROUP) { + if (group_stack & 1) { + _sgimgui_igtreepop(); + } + group_stack >>= 1; + } else if (group_stack & 1) { + if (_sgimgui_igselectableex(item_string.buf, ctx->capture_window.sel_item == i, 0, IMVEC2(0,0))) { + ctx->capture_window.sel_item = i; + } + if (_sgimgui_igisitemhovered(0)) { + _sgimgui_igsettooltip("%s", item_string.buf); + } + } + _sgimgui_igpopid(); + _sgimgui_igpopstylecolor(); + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_buffer_panel(_sgimgui_t* ctx, sg_buffer buf) { + if (buf.id != SG_INVALID_ID) { + _sgimgui_igbeginchild("buffer", IMVEC2(0,0), false, 0); + sg_buffer_info info = sg_query_buffer_info(buf); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + const _sgimgui_buffer_t* buf_ui = &ctx->buffer_window.slots[_sgimgui_slot_index(buf.id)]; + _sgimgui_igtext("Label: %s", buf_ui->label.buf[0] ? buf_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + _sgimgui_igseparator(); + _sgimgui_igtext("Usage:\n"); + _sgimgui_igtext(" vertex_buffer: %s", _sgimgui_bool_string(buf_ui->desc.usage.vertex_buffer)); + _sgimgui_igtext(" index_buffer: %s", _sgimgui_bool_string(buf_ui->desc.usage.index_buffer)); + _sgimgui_igtext(" storage_buffer: %s", _sgimgui_bool_string(buf_ui->desc.usage.storage_buffer)); + _sgimgui_igtext(" immutable: %s", _sgimgui_bool_string(buf_ui->desc.usage.immutable)); + _sgimgui_igtext(" dynamic_update: %s", _sgimgui_bool_string(buf_ui->desc.usage.dynamic_update)); + _sgimgui_igtext(" stream_update: %s", _sgimgui_bool_string(buf_ui->desc.usage.stream_update)); + _sgimgui_igtext("Size: %d", (int)buf_ui->desc.size); + if (!buf_ui->desc.usage.immutable) { + _sgimgui_igseparator(); + _sgimgui_igtext("Num Slots: %d", info.num_slots); + _sgimgui_igtext("Active Slot: %d", info.active_slot); + _sgimgui_igtext("Update Frame Index: %d", info.update_frame_index); + _sgimgui_igtext("Append Frame Index: %d", info.append_frame_index); + _sgimgui_igtext("Append Pos: %d", info.append_pos); + _sgimgui_igtext("Append Overflow: %s", _sgimgui_bool_string(info.append_overflow)); + } + } else { + _sgimgui_igtext("Buffer 0x%08X not valid.", buf.id); + } + _sgimgui_igendchild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_image_panel(_sgimgui_t* ctx, sg_image img) { + if (img.id != SG_INVALID_ID) { + _sgimgui_igbeginchild("image", IMVEC2(0,0), false, 0); + sg_image_info info = sg_query_image_info(img); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + _sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img.id)]; + const sg_image_desc* desc = &img_ui->desc; + _sgimgui_igtext("Label: %s", img_ui->label.buf[0] ? img_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + _sgimgui_igseparator(); + _sgimgui_draw_image(ctx, img, &img_ui->ui_scale, 4096.0f); + _sgimgui_igseparator(); + _sgimgui_igtext("Type: %s", _sgimgui_imagetype_string(desc->type)); + _sgimgui_igtext("Usage:\n"); + _sgimgui_igtext(" storage_image: %s", _sgimgui_bool_string(desc->usage.storage_image)); + _sgimgui_igtext(" color_attachment: %s", _sgimgui_bool_string(desc->usage.color_attachment)); + _sgimgui_igtext(" resolve_attachment: %s", _sgimgui_bool_string(desc->usage.resolve_attachment)); + _sgimgui_igtext(" depth_stencil_attachment: %s", _sgimgui_bool_string(desc->usage.depth_stencil_attachment)); + _sgimgui_igtext(" immutable: %s", _sgimgui_bool_string(desc->usage.immutable)); + _sgimgui_igtext(" dynamic_update: %s", _sgimgui_bool_string(desc->usage.dynamic_update)); + _sgimgui_igtext(" stream_update: %s", _sgimgui_bool_string(desc->usage.stream_update)); + _sgimgui_igtext("Width: %d", desc->width); + _sgimgui_igtext("Height: %d", desc->height); + _sgimgui_igtext("Num Slices: %d", desc->num_slices); + _sgimgui_igtext("Num Mipmaps: %d", desc->num_mipmaps); + _sgimgui_igtext("Pixel Format: %s", _sgimgui_pixelformat_string(desc->pixel_format)); + _sgimgui_igtext("Sample Count: %d", desc->sample_count); + if (!desc->usage.immutable) { + _sgimgui_igseparator(); + _sgimgui_igtext("Num Slots: %d", info.num_slots); + _sgimgui_igtext("Active Slot: %d", info.active_slot); + _sgimgui_igtext("Update Frame Index: %d", info.upd_frame_index); + } + } else { + _sgimgui_igtext("Image 0x%08X not valid.", img.id); + } + _sgimgui_igendchild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_sampler_panel(_sgimgui_t* ctx, sg_sampler smp) { + if (smp.id != SG_INVALID_ID) { + _sgimgui_igbeginchild("sampler", IMVEC2(0,0), false, 0); + sg_sampler_info info = sg_query_sampler_info(smp); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + _sgimgui_sampler_t* smp_ui = &ctx->sampler_window.slots[_sgimgui_slot_index(smp.id)]; + const sg_sampler_desc* desc = &smp_ui->desc; + _sgimgui_igtext("Label: %s", smp_ui->label.buf[0] ? smp_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + _sgimgui_igseparator(); + _sgimgui_igtext("Min Filter: %s", _sgimgui_filter_string(desc->min_filter)); + _sgimgui_igtext("Mag Filter: %s", _sgimgui_filter_string(desc->mag_filter)); + _sgimgui_igtext("Mipmap Filter: %s", _sgimgui_filter_string(desc->mipmap_filter)); + _sgimgui_igtext("Wrap U: %s", _sgimgui_wrap_string(desc->wrap_u)); + _sgimgui_igtext("Wrap V: %s", _sgimgui_wrap_string(desc->wrap_v)); + _sgimgui_igtext("Wrap W: %s", _sgimgui_wrap_string(desc->wrap_w)); + _sgimgui_igtext("Min LOD: %.3f", desc->min_lod); + _sgimgui_igtext("Max LOD: %.3f", desc->max_lod); + _sgimgui_igtext("Border Color: %s", _sgimgui_bordercolor_string(desc->border_color)); + _sgimgui_igtext("Compare: %s", _sgimgui_comparefunc_string(desc->compare)); + _sgimgui_igtext("Max Anisotropy: %d", desc->max_anisotropy); + } else { + _sgimgui_igtext("Sampler 0x%08X not valid.", smp.id); + } + _sgimgui_igendchild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_shader_func(const char* title, const sg_shader_function* func) { + SOKOL_ASSERT(func); + if ((func->source == 0) && (func->bytecode.ptr == 0)) { + return; + } + _sgimgui_igpushid(title); + _sgimgui_igtext("%s", title); + if (func->entry) { + _sgimgui_igtext(" entry: %s", func->entry); + } + if (func->d3d11_target) { + _sgimgui_igtext(" d3d11_target: %s", func->d3d11_target); + } + if (func->source) { + if (_sgimgui_igtreenode("source:")) { + _sgimgui_igtext("%s", func->source); + _sgimgui_igtreepop(); + } + } else if (func->bytecode.ptr) { + if (_sgimgui_igtreenode("bytecode")) { + _sgimgui_igtext("Byte-code display currently not supported."); + _sgimgui_igtreepop(); + } + } + _sgimgui_igpopid(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_shader_panel(_sgimgui_t* ctx, sg_shader shd) { + if (shd.id != SG_INVALID_ID) { + _sgimgui_igbeginchild("shader", IMVEC2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); + sg_shader_info info = sg_query_shader_info(shd); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + const _sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(shd.id)]; + _sgimgui_igtext("Label: %s", shd_ui->label.buf[0] ? shd_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + _sgimgui_igseparator(); + if (_sgimgui_igtreenode("Attrs")) { + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + const sg_shader_vertex_attr* a_desc = &shd_ui->desc.attrs[i]; + if ((a_desc->base_type != SG_SHADERATTRBASETYPE_UNDEFINED) || a_desc->glsl_name || a_desc->hlsl_sem_name) { + _sgimgui_igtext("#%d:", i); + if (a_desc->base_type != SG_SHADERATTRBASETYPE_UNDEFINED) { + _sgimgui_igtext(" Base Type: %s", _sgimgui_shaderattrbasetype_string(a_desc->base_type)); + } + if (a_desc->glsl_name) { + _sgimgui_igtext(" GLSL Name: %s", a_desc->glsl_name); + } + if (a_desc->hlsl_sem_name) { + _sgimgui_igtext(" HLSL Sem Name: %s", a_desc->hlsl_sem_name); + _sgimgui_igtext(" HLSL Sem Index: %d", a_desc->hlsl_sem_index); + } + } + } + _sgimgui_igtreepop(); + } + int num_valid_ubs = 0; + for (int i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* ub = &shd_ui->desc.uniform_blocks[i]; + if (ub->stage != SG_SHADERSTAGE_NONE) { + num_valid_ubs++; + } + } + int num_valid_views = 0; + for (int i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &shd_ui->desc.views[i]; + if ((view->texture.stage != SG_SHADERSTAGE_NONE) || + (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) || + (view->storage_image.stage != SG_SHADERSTAGE_NONE)) + { + num_valid_views++; + } + } + int num_valid_samplers = 0; + for (int i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + if (shd_ui->desc.samplers[i].stage != SG_SHADERSTAGE_NONE) { + num_valid_samplers++; + } + } + int num_valid_texture_sampler_pairs = 0; + for (int i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) { + if (shd_ui->desc.texture_sampler_pairs[i].stage != SG_SHADERSTAGE_NONE) { + num_valid_texture_sampler_pairs++; + } + } + if (num_valid_ubs > 0) { + if (_sgimgui_igtreenode("Uniform Blocks")) { + for (int i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) { + const sg_shader_uniform_block* ub = &shd_ui->desc.uniform_blocks[i]; + if (ub->stage == SG_SHADERSTAGE_NONE) { + continue; + } + _sgimgui_igtext("- slot: %d", i); + _sgimgui_igtext(" stage: %s", _sgimgui_shaderstage_string(ub->stage)); + _sgimgui_igtext(" size: %d", ub->size); + _sgimgui_igtext(" layout: %s", _sgimgui_uniformlayout_string(ub->layout)); + _sgimgui_igtext(" hlsl_register_b_n: %d", ub->hlsl_register_b_n); + _sgimgui_igtext(" msl_buffer_n: %d", ub->msl_buffer_n); + _sgimgui_igtext(" wgsl_group0_binding_n: %d", ub->wgsl_group0_binding_n); + _sgimgui_igtext(" spirv_set0_binding_n: %d", ub->spirv_set0_binding_n); + _sgimgui_igtext(" glsl_uniforms:"); + for (int j = 0; j < SG_MAX_UNIFORMBLOCK_MEMBERS; j++) { + const sg_glsl_shader_uniform* u = &ub->glsl_uniforms[j]; + if (SG_UNIFORMTYPE_INVALID != u->type) { + if (u->array_count <= 1) { + _sgimgui_igtext(" %s %s", _sgimgui_uniformtype_string(u->type), u->glsl_name ? u->glsl_name : ""); + } else { + _sgimgui_igtext(" %s[%d] %s", _sgimgui_uniformtype_string(u->type), u->array_count, u->glsl_name ? u->glsl_name : ""); + } + } + } + } + _sgimgui_igtreepop(); + } + } + if (num_valid_views > 0) { + if (_sgimgui_igtreenode("Views")) { + for (int i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + const sg_shader_view* view = &shd_ui->desc.views[i]; + if (view->texture.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_texture_view* tex = &view->texture; + _sgimgui_igtext("- slot: %d", i); + _sgimgui_igtext(" stage: %s", _sgimgui_shaderstage_string(tex->stage)); + _sgimgui_igtext(" type: SG_VIEWTYPE_TEXTURE"); + _sgimgui_igtext(" image_type: %s", _sgimgui_imagetype_string(tex->image_type)); + _sgimgui_igtext(" sample_type: %s", _sgimgui_imagesampletype_string(tex->sample_type)); + _sgimgui_igtext(" multisampled: %s", _sgimgui_bool_string(tex->multisampled)); + _sgimgui_igtext(" hlsl_register_t_n: %d", tex->hlsl_register_t_n); + _sgimgui_igtext(" msl_texture_n: %d", tex->msl_texture_n); + _sgimgui_igtext(" wgsl_group1_binding_n: %d", tex->wgsl_group1_binding_n); + _sgimgui_igtext(" spirv_set1_binding_n: %d", tex->spirv_set1_binding_n); + } else if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_storage_buffer_view* sbuf = &view->storage_buffer; + _sgimgui_igtext("- slot: %d", i); + _sgimgui_igtext(" stage: %s", _sgimgui_shaderstage_string(sbuf->stage)); + _sgimgui_igtext(" type: SG_VIEWTYPE_STORAGEBUFFER"); + _sgimgui_igtext(" readonly: %s", _sgimgui_bool_string(sbuf->readonly)); + if (sbuf->readonly) { + _sgimgui_igtext(" hlsl_register_t_n: %d", sbuf->hlsl_register_t_n); + } else { + _sgimgui_igtext(" hlsl_register_u_n: %d", sbuf->hlsl_register_u_n); + } + _sgimgui_igtext(" msl_buffer_n: %d", sbuf->msl_buffer_n); + _sgimgui_igtext(" wgsl_group1_binding_n: %d", sbuf->wgsl_group1_binding_n); + _sgimgui_igtext(" spirv_group1_binding_n: %d\n", sbuf->spirv_set1_binding_n); + _sgimgui_igtext(" glsl_binding_n: %d", sbuf->glsl_binding_n); + } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) { + const sg_shader_storage_image_view* simg = &view->storage_image; + _sgimgui_igtext("- slot: %d", i); + _sgimgui_igtext(" stage: %s", _sgimgui_shaderstage_string(simg->stage)); + _sgimgui_igtext(" type: SG_VIEWTYPE_STORAGEIMAGE"); + _sgimgui_igtext(" image_type: %s", _sgimgui_imagetype_string(simg->image_type)); + _sgimgui_igtext(" access_format: %s", _sgimgui_pixelformat_string(simg->access_format)); + _sgimgui_igtext(" writeonly: %s", _sgimgui_bool_string(simg->writeonly)); + _sgimgui_igtext(" hlsl_register_u_n: %d", simg->hlsl_register_u_n); + _sgimgui_igtext(" msl_texture_n: %d", simg->msl_texture_n); + _sgimgui_igtext(" wgsl_group2_binding_n: %d", simg->wgsl_group1_binding_n); + _sgimgui_igtext(" spirv_set1_binding_n: %d", simg->spirv_set1_binding_n); + _sgimgui_igtext(" glsl_binding_n: %d", simg->glsl_binding_n); + } + } + _sgimgui_igtreepop(); + } + } + if (num_valid_samplers > 0) { + if (_sgimgui_igtreenode("Samplers")) { + for (int i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + const sg_shader_sampler* ssd = &shd_ui->desc.samplers[i]; + if (ssd->stage == SG_SHADERSTAGE_NONE) { + continue; + } + _sgimgui_igtext("- slot: %d", i); + _sgimgui_igtext(" stage: %s", _sgimgui_shaderstage_string(ssd->stage)); + _sgimgui_igtext(" sampler_type: %s", _sgimgui_samplertype_string(ssd->sampler_type)); + _sgimgui_igtext(" hlsl_register_s_n: %d", ssd->hlsl_register_s_n); + _sgimgui_igtext(" msl_sampler_n: %d", ssd->msl_sampler_n); + _sgimgui_igtext(" wgsl_group1_binding_n: %d", ssd->wgsl_group1_binding_n); + _sgimgui_igtext(" spirv_set1_binding_1: %d", ssd->spirv_set1_binding_n); + } + _sgimgui_igtreepop(); + } + } + if (num_valid_texture_sampler_pairs > 0) { + if (_sgimgui_igtreenode("Texture Sampler Pairs")) { + for (int i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) { + const sg_shader_texture_sampler_pair* stspd = &shd_ui->desc.texture_sampler_pairs[i]; + if (stspd->stage == SG_SHADERSTAGE_NONE) { + continue; + } + _sgimgui_igtext("- slot: %d", i); + _sgimgui_igtext(" stage: %s", _sgimgui_shaderstage_string(stspd->stage)); + _sgimgui_igtext(" view_slot: %d", stspd->view_slot); + _sgimgui_igtext(" sampler_slot: %d", stspd->sampler_slot); + _sgimgui_igtext(" glsl_name: %s", stspd->glsl_name ? stspd->glsl_name : "---"); + } + _sgimgui_igtreepop(); + } + } + _sgimgui_draw_shader_func("Vertex Function", &shd_ui->desc.vertex_func); + _sgimgui_draw_shader_func("Fragment Function", &shd_ui->desc.fragment_func); + _sgimgui_draw_shader_func("Compute Function", &shd_ui->desc.compute_func); + } else { + _sgimgui_igtext("Shader 0x%08X not valid!", shd.id); + } + _sgimgui_igendchild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_vertex_layout_state(const sg_vertex_layout_state* layout) { + if (_sgimgui_igtreenode("Buffers")) { + for (int i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + const sg_vertex_buffer_layout_state* l_state = &layout->buffers[i]; + if (l_state->stride > 0) { + _sgimgui_igtext("#%d:", i); + _sgimgui_igtext(" Stride: %d", l_state->stride); + _sgimgui_igtext(" Step Func: %s", _sgimgui_vertexstep_string(l_state->step_func)); + _sgimgui_igtext(" Step Rate: %d", l_state->step_rate); + } + } + _sgimgui_igtreepop(); + } + if (_sgimgui_igtreenode("Attrs")) { + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + const sg_vertex_attr_state* a_state = &layout->attrs[i]; + if (a_state->format != SG_VERTEXFORMAT_INVALID) { + _sgimgui_igtext("#%d:", i); + _sgimgui_igtext(" Format: %s", _sgimgui_vertexformat_string(a_state->format)); + _sgimgui_igtext(" Offset: %d", a_state->offset); + _sgimgui_igtext(" Buffer Index: %d", a_state->buffer_index); + } + } + _sgimgui_igtreepop(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_stencil_face_state(const sg_stencil_face_state* sfs) { + _sgimgui_igtext("Fail Op: %s", _sgimgui_stencilop_string(sfs->fail_op)); + _sgimgui_igtext("Depth Fail Op: %s", _sgimgui_stencilop_string(sfs->depth_fail_op)); + _sgimgui_igtext("Pass Op: %s", _sgimgui_stencilop_string(sfs->pass_op)); + _sgimgui_igtext("Compare: %s", _sgimgui_comparefunc_string(sfs->compare)); +} + +_SOKOL_PRIVATE void _sgimgui_draw_stencil_state(const sg_stencil_state* ss) { + _sgimgui_igtext("Enabled: %s", _sgimgui_bool_string(ss->enabled)); + _sgimgui_igtext("Read Mask: 0x%02X", ss->read_mask); + _sgimgui_igtext("Write Mask: 0x%02X", ss->write_mask); + _sgimgui_igtext("Ref: 0x%02X", ss->ref); + if (_sgimgui_igtreenode("Front")) { + _sgimgui_draw_stencil_face_state(&ss->front); + _sgimgui_igtreepop(); + } + if (_sgimgui_igtreenode("Back")) { + _sgimgui_draw_stencil_face_state(&ss->back); + _sgimgui_igtreepop(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_depth_state(const sg_depth_state* ds) { + _sgimgui_igtext("Pixel Format: %s", _sgimgui_pixelformat_string(ds->pixel_format)); + _sgimgui_igtext("Compare: %s", _sgimgui_comparefunc_string(ds->compare)); + _sgimgui_igtext("Write Enabled: %s", _sgimgui_bool_string(ds->write_enabled)); + _sgimgui_igtext("Bias: %f", ds->bias); + _sgimgui_igtext("Bias Slope: %f", ds->bias_slope_scale); + _sgimgui_igtext("Bias Clamp: %f", ds->bias_clamp); +} + +_SOKOL_PRIVATE void _sgimgui_draw_blend_state(const sg_blend_state* bs) { + _sgimgui_igtext("Blend Enabled: %s", _sgimgui_bool_string(bs->enabled)); + _sgimgui_igtext("Src Factor RGB: %s", _sgimgui_blendfactor_string(bs->src_factor_rgb)); + _sgimgui_igtext("Dst Factor RGB: %s", _sgimgui_blendfactor_string(bs->dst_factor_rgb)); + _sgimgui_igtext("Op RGB: %s", _sgimgui_blendop_string(bs->op_rgb)); + _sgimgui_igtext("Src Factor Alpha: %s", _sgimgui_blendfactor_string(bs->src_factor_alpha)); + _sgimgui_igtext("Dst Factor Alpha: %s", _sgimgui_blendfactor_string(bs->dst_factor_alpha)); + _sgimgui_igtext("Op Alpha: %s", _sgimgui_blendop_string(bs->op_alpha)); +} + +_SOKOL_PRIVATE void _sgimgui_draw_color_target_state(const sg_color_target_state* cs) { + _sgimgui_igtext("Pixel Format: %s", _sgimgui_pixelformat_string(cs->pixel_format)); + _sgimgui_igtext("Write Mask: %s", _sgimgui_colormask_string(cs->write_mask)); + if (_sgimgui_igtreenode("Blend State:")) { + _sgimgui_draw_blend_state(&cs->blend); + _sgimgui_igtreepop(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_pipeline_panel(_sgimgui_t* ctx, sg_pipeline pip) { + if (pip.id != SG_INVALID_ID) { + _sgimgui_igbeginchild("pipeline", IMVEC2(0,0), false, 0); + sg_pipeline_info info = sg_query_pipeline_info(pip); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + const _sgimgui_pipeline_t* pip_ui = &ctx->pipeline_window.slots[_sgimgui_slot_index(pip.id)]; + _sgimgui_igtext("Label: %s", pip_ui->label.buf[0] ? pip_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + _sgimgui_igseparator(); + _sgimgui_igtext("Compute: %s", _sgimgui_bool_string(pip_ui->desc.compute)); + _sgimgui_igtext("Shader: "); _sgimgui_igsameline(); + if (_sgimgui_draw_shader_link(ctx, pip_ui->desc.shader)) { + _sgimgui_show_shader(ctx, pip_ui->desc.shader); + } + if (!pip_ui->desc.compute) { + if (_sgimgui_igtreenode("Vertex Layout State")) { + _sgimgui_draw_vertex_layout_state(&pip_ui->desc.layout); + _sgimgui_igtreepop(); + } + if (_sgimgui_igtreenode("Depth State")) { + _sgimgui_draw_depth_state(&pip_ui->desc.depth); + _sgimgui_igtreepop(); + } + if (_sgimgui_igtreenode("Stencil State")) { + _sgimgui_draw_stencil_state(&pip_ui->desc.stencil); + _sgimgui_igtreepop(); + } + _sgimgui_igtext("Color Count: %d", pip_ui->desc.color_count); + for (int i = 0; i < pip_ui->desc.color_count; i++) { + _sgimgui_str_t str; + _sgimgui_snprintf(&str, "Color Target %d", i); + if (_sgimgui_igtreenode(str.buf)) { + _sgimgui_draw_color_target_state(&pip_ui->desc.colors[i]); + _sgimgui_igtreepop(); + } + } + _sgimgui_igtext("Prim Type: %s", _sgimgui_primitivetype_string(pip_ui->desc.primitive_type)); + _sgimgui_igtext("Index Type: %s", _sgimgui_indextype_string(pip_ui->desc.index_type)); + _sgimgui_igtext("Cull Mode: %s", _sgimgui_cullmode_string(pip_ui->desc.cull_mode)); + _sgimgui_igtext("Face Winding: %s", _sgimgui_facewinding_string(pip_ui->desc.face_winding)); + _sgimgui_igtext("Sample Count: %d", pip_ui->desc.sample_count); + _sgimgui_str_t blend_color_str; + _sgimgui_igtext("Blend Color: %s", _sgimgui_color_string(&blend_color_str, pip_ui->desc.blend_color)); + _sgimgui_igtext("Alpha To Coverage: %s", _sgimgui_bool_string(pip_ui->desc.alpha_to_coverage_enabled)); + } + } else { + _sgimgui_igtext("Pipeline 0x%08X not valid.", pip.id); + } + _sgimgui_igendchild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_buffer_view(_sgimgui_t* ctx, const char* title, const sg_buffer_view_desc* desc) { + _sgimgui_igtext("%s: ", title); + _sgimgui_igtext(" Buffer: "); _sgimgui_igsameline(); + if (_sgimgui_draw_buffer_link(ctx, desc->buffer)) { + _sgimgui_show_buffer(ctx, desc->buffer); + } + _sgimgui_igtext(" Offset: %d", desc->offset); +} + +_SOKOL_PRIVATE void _sgimgui_draw_image_view(_sgimgui_t* ctx, const char* title, sg_view view, const sg_image_view_desc* desc) { + _sgimgui_igtext("%s: ", title); + _sgimgui_igtext(" Image: "); _sgimgui_igsameline(); + if (_sgimgui_draw_image_link(ctx, desc->image)) { + _sgimgui_show_image(ctx, desc->image); + } + _sgimgui_igtext(" Mip Level: %d", desc->mip_level); + _sgimgui_igtext(" Slice: %d", desc->slice); + _sgimgui_igseparator(); + _sgimgui_view_t* view_ui = &ctx->view_window.slots[_sgimgui_slot_index(view.id)]; + _sgimgui_draw_image(ctx, desc->image, &view_ui->ui_scale, 4096.0f); +} + +_SOKOL_PRIVATE void _sgimgui_draw_texture_view(_sgimgui_t* ctx, const char* title, sg_view view, const sg_texture_view_desc* desc) { + _sgimgui_igtext("%s: ", title); + _sgimgui_igtext(" Image: "); _sgimgui_igsameline(); + if (_sgimgui_draw_image_link(ctx, desc->image)) { + _sgimgui_show_image(ctx, desc->image); + } + _sgimgui_igtext(" Mip Levels Base: %d", desc->mip_levels.base); + _sgimgui_igtext(" Mip Levels Count: %d", desc->mip_levels.count); + _sgimgui_igtext(" Slices Base: %d", desc->slices.base); + _sgimgui_igtext(" Slices Count: %d", desc->slices.count); + _sgimgui_igseparator(); + _sgimgui_view_t* view_ui = &ctx->view_window.slots[_sgimgui_slot_index(view.id)]; + _sgimgui_draw_image(ctx, desc->image, &view_ui->ui_scale, 4096.0f); +} + +_SOKOL_PRIVATE void _sgimgui_draw_view_panel(_sgimgui_t* ctx, sg_view view) { + if (view.id != SG_INVALID_ID) { + _sgimgui_igbeginchild("view", IMVEC2(0,0), false, 0); + sg_view_info info = sg_query_view_info(view); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + _sgimgui_view_t* view_ui = &ctx->view_window.slots[_sgimgui_slot_index(view.id)]; + _sgimgui_igtext("Label: %s", view_ui->label.buf[0] ? view_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + _sgimgui_igseparator(); + const sg_view_desc desc = sg_query_view_desc(view); + const sg_view_type type = sg_query_view_type(view); + switch (type) { + case SG_VIEWTYPE_STORAGEBUFFER: + _sgimgui_draw_buffer_view(ctx, "Storage Buffer", &desc.storage_buffer); + break; + case SG_VIEWTYPE_STORAGEIMAGE: + _sgimgui_draw_image_view(ctx, "Storage Image", view, &desc.storage_image); + break; + case SG_VIEWTYPE_TEXTURE: + _sgimgui_draw_texture_view(ctx, "Texture", view, &desc.texture); + break; + case SG_VIEWTYPE_COLORATTACHMENT: + _sgimgui_draw_image_view(ctx, "Color Attachment", view, &desc.color_attachment); + break; + case SG_VIEWTYPE_RESOLVEATTACHMENT: + _sgimgui_draw_image_view(ctx, "Resolve Attachment", view, &desc.resolve_attachment); + break; + case SG_VIEWTYPE_DEPTHSTENCILATTACHMENT: + _sgimgui_draw_image_view(ctx, "Depth Stencil Attachment", view, &desc.depth_stencil_attachment); + break; + default: + break; + } + } else { + _sgimgui_igtext("View 0x%08X not valid.", view.id); + } + _sgimgui_igendchild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_bindings_panel(_sgimgui_t* ctx, const sg_bindings* bnd) { + _sgimgui_igpushid("bnd_vbufs"); + for (int i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { + sg_buffer buf = bnd->vertex_buffers[i]; + if (buf.id != SG_INVALID_ID) { + _sgimgui_igtext("Vertex Buffer #%d:", i); _sgimgui_igsameline(); + if (_sgimgui_draw_buffer_link(ctx, buf)) { + _sgimgui_show_buffer(ctx, buf); + } + _sgimgui_igsameline(); + _sgimgui_igtext("offset: %d", bnd->vertex_buffer_offsets[i]); + } + } + _sgimgui_igpopid(); + _sgimgui_igpushid("bnd_ibuf"); + if (bnd->index_buffer.id != SG_INVALID_ID) { + sg_buffer buf = bnd->index_buffer; + if (buf.id != SG_INVALID_ID) { + _sgimgui_igtext("Index Buffer:"); _sgimgui_igsameline(); + if (_sgimgui_draw_buffer_link(ctx, buf)) { + _sgimgui_show_buffer(ctx, buf); + } + _sgimgui_igsameline(); + _sgimgui_igtext("offset: %d", bnd->index_buffer_offset); + } + } + _sgimgui_igpopid(); + _sgimgui_igpushid("bnd_views"); + for (int i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) { + sg_view view = bnd->views[i]; + if (view.id != SG_INVALID_ID) { + _sgimgui_igtext("View #%d:", i); _sgimgui_igsameline(); + if (_sgimgui_draw_view_link(ctx, view)) { + _sgimgui_show_view(ctx, view); + } + } + } + _sgimgui_igpopid(); + _sgimgui_igpushid("bnd_smps"); + for (int i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) { + sg_sampler smp = bnd->samplers[i]; + if (smp.id != SG_INVALID_ID) { + _sgimgui_igtext("Sampler Slot #%d:", i); _sgimgui_igsameline(); + if (_sgimgui_draw_sampler_link(ctx, smp)) { + _sgimgui_show_sampler(ctx, smp); + } + } + } + _sgimgui_igpopid(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_uniforms_panel(_sgimgui_t* ctx, const _sgimgui_args_apply_uniforms_t* args) { + SOKOL_ASSERT(args->ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS); + + /* check if all the required information for drawing the structured uniform block content + is available, otherwise just render a generic hexdump + */ + if (sg_query_pipeline_state(args->pipeline) != SG_RESOURCESTATE_VALID) { + _sgimgui_igtext("Pipeline object not valid!"); + return; + } + _sgimgui_pipeline_t* pip_ui = &ctx->pipeline_window.slots[_sgimgui_slot_index(args->pipeline.id)]; + if (sg_query_shader_state(pip_ui->desc.shader) != SG_RESOURCESTATE_VALID) { + _sgimgui_igtext("Shader object not valid!"); + return; + } + _sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(pip_ui->desc.shader.id)]; + SOKOL_ASSERT(shd_ui->res_id.id == pip_ui->desc.shader.id); + const sg_shader_uniform_block* ub_desc = &shd_ui->desc.uniform_blocks[args->ub_slot]; + SOKOL_ASSERT(args->data_size <= ub_desc->size); + bool draw_dump = false; + if (ub_desc->glsl_uniforms[0].type == SG_UNIFORMTYPE_INVALID) { + draw_dump = true; + } + + _sgimgui_capture_bucket_t* bucket = _sgimgui_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_UNIFORMBLOCK_MEMBERS; i++) { + const sg_glsl_shader_uniform* ud = &ub_desc->glsl_uniforms[i]; + if (ud->type == SG_UNIFORMTYPE_INVALID) { + break; + } + int num_items = (ud->array_count > 1) ? ud->array_count : 1; + if (num_items > 1) { + _sgimgui_igtext("%d: %s %s[%d] =", i, _sgimgui_uniformtype_string(ud->type), ud->glsl_name?ud->glsl_name:"", ud->array_count); + } else { + _sgimgui_igtext("%d: %s %s =", i, _sgimgui_uniformtype_string(ud->type), ud->glsl_name?ud->glsl_name:""); + } + for (int item_index = 0; item_index < num_items; item_index++) { + const uint32_t u_size = _sgimgui_std140_uniform_size(ud->type, ud->array_count) / 4; + const uint32_t u_align = _sgimgui_std140_uniform_alignment(ud->type, ud->array_count) / 4; + u_off = _sgimgui_align_u32(u_off, u_align); + switch (ud->type) { + case SG_UNIFORMTYPE_FLOAT: + _sgimgui_igtext(" %.3f", uptrf[u_off]); + break; + case SG_UNIFORMTYPE_INT: + _sgimgui_igtext(" %d", uptri32[u_off]); + break; + case SG_UNIFORMTYPE_FLOAT2: + _sgimgui_igtext(" %.3f, %.3f", uptrf[u_off], uptrf[u_off+1]); + break; + case SG_UNIFORMTYPE_INT2: + _sgimgui_igtext(" %d, %d", uptri32[u_off], uptri32[u_off+1]); + break; + case SG_UNIFORMTYPE_FLOAT3: + _sgimgui_igtext(" %.3f, %.3f, %.3f", uptrf[u_off], uptrf[u_off+1], uptrf[u_off+2]); + break; + case SG_UNIFORMTYPE_INT3: + _sgimgui_igtext(" %d, %d, %d", uptri32[u_off], uptri32[u_off+1], uptri32[u_off+2]); + break; + case SG_UNIFORMTYPE_FLOAT4: + _sgimgui_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: + _sgimgui_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: + _sgimgui_igtext(" %.3f, %.3f, %.3f, %.3f\n" + " %.3f, %.3f, %.3f, %.3f\n" + " %.3f, %.3f, %.3f, %.3f\n" + " %.3f, %.3f, %.3f, %.3f", + 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: + _sgimgui_igtext("???"); + break; + } + 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++) { + _sgimgui_igtext("%.3f, ", uptrf[i]); + if (((i + 1) % 4) != 0) { + _sgimgui_igsameline(); + } + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_passaction_panel(const sg_pass_action* action, int num_color_atts) { + _sgimgui_igtext("Pass Action:"); + for (int i = 0; i < num_color_atts; i++) { + const sg_color_attachment_action* c_att = &action->colors[i]; + _sgimgui_igtext(" Color Attachment %d:", i); + _sgimgui_str_t color_str; + switch (c_att->load_action) { + case SG_LOADACTION_LOAD: _sgimgui_igtext(" SG_LOADACTION_LOAD"); break; + case SG_LOADACTION_DONTCARE: _sgimgui_igtext(" SG_LOADACTION_DONTCARE"); break; + case SG_LOADACTION_CLEAR: + _sgimgui_igtext(" SG_LOADACTION_CLEAR: %s", _sgimgui_color_string(&color_str, c_att->clear_value)); + break; + default: _sgimgui_igtext(" ???"); break; + } + switch (c_att->store_action) { + case SG_STOREACTION_STORE: _sgimgui_igtext(" SG_STOREACTION_STORE"); break; + case SG_STOREACTION_DONTCARE: _sgimgui_igtext(" SG_STOREACTION_DONTCARE"); break; + default: _sgimgui_igtext(" ???"); break; + } + } + const sg_depth_attachment_action* d_att = &action->depth; + _sgimgui_igtext(" Depth Attachment:"); + switch (d_att->load_action) { + case SG_LOADACTION_LOAD: _sgimgui_igtext(" SG_LOADACTION_LOAD"); break; + case SG_LOADACTION_DONTCARE: _sgimgui_igtext(" SG_LOADACTION_DONTCARE"); break; + case SG_LOADACTION_CLEAR: _sgimgui_igtext(" SG_LOADACTION_CLEAR: %.3f", d_att->clear_value); break; + default: _sgimgui_igtext(" ???"); break; + } + switch (d_att->store_action) { + case SG_STOREACTION_STORE: _sgimgui_igtext(" SG_STOREACTION_STORE"); break; + case SG_STOREACTION_DONTCARE: _sgimgui_igtext(" SG_STOREACTION_DONTCARE"); break; + default: _sgimgui_igtext(" ???"); break; + } + const sg_stencil_attachment_action* s_att = &action->stencil; + _sgimgui_igtext(" Stencil Attachment"); + switch (s_att->load_action) { + case SG_LOADACTION_LOAD: _sgimgui_igtext(" SG_LOADACTION_LOAD"); break; + case SG_LOADACTION_DONTCARE: _sgimgui_igtext(" SG_LOADACTION_DONTCARE"); break; + case SG_LOADACTION_CLEAR: _sgimgui_igtext(" SG_LOADACTION_CLEAR: 0x%02X", s_att->clear_value); break; + default: _sgimgui_igtext(" ???"); break; + } + switch (d_att->store_action) { + case SG_STOREACTION_STORE: _sgimgui_igtext(" SG_STOREACTION_STORE"); break; + case SG_STOREACTION_DONTCARE: _sgimgui_igtext(" SG_STOREACTION_DONTCARE"); break; + default: _sgimgui_igtext(" ???"); break; + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_attachments_panel(_sgimgui_t* ctx, const sg_attachments* atts, int num_color_atts) { + _sgimgui_igtext("Attachments:"); + for (int i = 0; i < num_color_atts; i++) { + if (atts->colors[i].id!= SG_INVALID_ID) { + sg_view view = atts->colors[i]; + _sgimgui_igtext(" Color Attachment #%d:", i); _sgimgui_igsameline(); + if (_sgimgui_draw_view_link(ctx, view)) { + _sgimgui_show_view(ctx, view); + } + } + } + for (int i = 0; i < num_color_atts; i++) { + if (atts->resolves[i].id != SG_INVALID_ID) { + sg_view view = atts->resolves[i]; + _sgimgui_igtext(" Resolve Attachment #%d:", i); _sgimgui_igsameline(); + if (_sgimgui_draw_view_link(ctx, view)) { + _sgimgui_show_view(ctx, view); + } + } + } + if (atts->depth_stencil.id != SG_INVALID_ID) { + sg_view view = atts->depth_stencil; + _sgimgui_igtext(" Depth Stencil Attachment:"); _sgimgui_igsameline(); + if (_sgimgui_draw_view_link(ctx, view)) { + _sgimgui_show_view(ctx, view); + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_swapchain_panel(sg_swapchain* swapchain) { + _sgimgui_igtext("Swapchain:"); + _sgimgui_igtext(" Width: %d", swapchain->width); + _sgimgui_igtext(" Height: %d", swapchain->height); + _sgimgui_igtext(" Sample Count: %d", swapchain->sample_count); + _sgimgui_igtext(" Color Format: %s", _sgimgui_pixelformat_string(swapchain->color_format)); + _sgimgui_igtext(" Depth Format: %s", _sgimgui_pixelformat_string(swapchain->depth_format)); + _sgimgui_igseparator(); + switch (sg_query_backend()) { + case SG_BACKEND_D3D11: + _sgimgui_igtext("D3D11 Objects:"); + _sgimgui_igtext(" Render View: %p", swapchain->d3d11.render_view); + _sgimgui_igtext(" Resolve View: %p", swapchain->d3d11.resolve_view); + _sgimgui_igtext(" Depth Stencil View: %p", swapchain->d3d11.depth_stencil_view); + break; + case SG_BACKEND_WGPU: + _sgimgui_igtext("WGPU Objects:"); + _sgimgui_igtext(" Render View: %p", swapchain->wgpu.render_view); + _sgimgui_igtext(" Resolve View: %p", swapchain->wgpu.resolve_view); + _sgimgui_igtext(" Depth Stencil View: %p", swapchain->wgpu.depth_stencil_view); + break; + case SG_BACKEND_METAL_MACOS: + case SG_BACKEND_METAL_IOS: + case SG_BACKEND_METAL_SIMULATOR: + _sgimgui_igtext("Metal Objects:"); + _sgimgui_igtext(" Current Drawable: %p", swapchain->metal.current_drawable); + _sgimgui_igtext(" Depth Stencil Texture: %p", swapchain->metal.depth_stencil_texture); + _sgimgui_igtext(" MSAA Color Texture: %p", swapchain->metal.msaa_color_texture); + break; + case SG_BACKEND_GLCORE: + case SG_BACKEND_GLES3: + _sgimgui_igtext("GL Objects:"); + _sgimgui_igtext(" Framebuffer: %d", swapchain->gl.framebuffer); + break; + case SG_BACKEND_VULKAN: + _sgimgui_igtext("Vulkan Objects:"); + _sgimgui_igtext(" Render Image: %p", swapchain->vulkan.render_image); + _sgimgui_igtext(" Render View: %p", swapchain->vulkan.render_view); + _sgimgui_igtext(" Resolve Image: %p", swapchain->vulkan.resolve_image); + _sgimgui_igtext(" Resolve View: %p", swapchain->vulkan.resolve_view); + _sgimgui_igtext(" Depth Stencil Image: %p", swapchain->vulkan.depth_stencil_image); + _sgimgui_igtext(" Depth Stencil View: %p", swapchain->vulkan.depth_stencil_view); + _sgimgui_igtext(" Render Finished Semaphore: %p", swapchain->vulkan.render_finished_semaphore); + _sgimgui_igtext(" Present Complete Semaphore: %p", swapchain->vulkan.present_complete_semaphore); + break; + default: + _sgimgui_igtext(" UNKNOWN BACKEND!"); + break; + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_pass_panel(_sgimgui_t* ctx, sg_pass* pass) { + bool is_compute_pass = pass->compute; + bool is_attachments_pass = false; + bool is_swapchain_pass = false; + int num_color_atts = 0; + if (!is_compute_pass) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->attachments.colors[i].id != SG_INVALID_ID) { + num_color_atts++; + is_attachments_pass = true; + } + } + if (pass->attachments.depth_stencil.id != SG_INVALID_ID) { + is_attachments_pass = true; + } + if (!is_attachments_pass) { + num_color_atts = 1; + is_swapchain_pass = true; + } + } + _sgimgui_igtext("Compute: %s", _sgimgui_bool_string(is_compute_pass)); + _sgimgui_igseparator(); + if (!is_compute_pass) { + _sgimgui_draw_passaction_panel(&pass->action, num_color_atts); + _sgimgui_igseparator(); + if (is_attachments_pass) { + _sgimgui_draw_attachments_panel(ctx, &pass->attachments, num_color_atts); + } else if (is_swapchain_pass) { + _sgimgui_draw_swapchain_panel(&pass->swapchain); + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_capture_panel(_sgimgui_t* ctx) { + int sel_item_index = ctx->capture_window.sel_item; + if (sel_item_index >= _sgimgui_capture_num_read_items(ctx)) { + return; + } + _sgimgui_capture_item_t* item = _sgimgui_capture_read_item_at(ctx, sel_item_index); + _sgimgui_igbeginchild("capture_item", IMVEC2(0, 0), false, 0); + _sgimgui_igpushstylecolor(ImGuiCol_Text, item->color); + _sgimgui_igtext("%s", _sgimgui_capture_item_string(ctx, sel_item_index, item).buf); + _sgimgui_igpopstylecolor(); + _sgimgui_igseparator(); + switch (item->cmd) { + case _SGIMGUI_CMD_RESET_STATE_CACHE: + break; + case _SGIMGUI_CMD_MAKE_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.make_buffer.result); + break; + case _SGIMGUI_CMD_MAKE_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.make_image.result); + break; + case _SGIMGUI_CMD_MAKE_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.make_sampler.result); + break; + case _SGIMGUI_CMD_MAKE_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.make_shader.result); + break; + case _SGIMGUI_CMD_MAKE_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.make_pipeline.result); + break; + case _SGIMGUI_CMD_MAKE_VIEW: + _sgimgui_draw_view_panel(ctx, item->args.make_view.result); + break; + case _SGIMGUI_CMD_DESTROY_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.destroy_buffer.buffer); + break; + case _SGIMGUI_CMD_DESTROY_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.destroy_image.image); + break; + case _SGIMGUI_CMD_DESTROY_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.destroy_sampler.sampler); + break; + case _SGIMGUI_CMD_DESTROY_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.destroy_shader.shader); + break; + case _SGIMGUI_CMD_DESTROY_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.destroy_pipeline.pipeline); + break; + case _SGIMGUI_CMD_DESTROY_VIEW: + _sgimgui_draw_view_panel(ctx, item->args.destroy_view.view); + break; + case _SGIMGUI_CMD_UPDATE_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.update_buffer.buffer); + break; + case _SGIMGUI_CMD_UPDATE_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.update_image.image); + break; + case _SGIMGUI_CMD_APPEND_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.update_buffer.buffer); + break; + case _SGIMGUI_CMD_BEGIN_PASS: + _sgimgui_draw_pass_panel(ctx, &item->args.begin_pass.pass); + break; + case _SGIMGUI_CMD_APPLY_VIEWPORT: + case _SGIMGUI_CMD_APPLY_SCISSOR_RECT: + break; + case _SGIMGUI_CMD_APPLY_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.apply_pipeline.pipeline); + break; + case _SGIMGUI_CMD_APPLY_BINDINGS: + _sgimgui_draw_bindings_panel(ctx, &item->args.apply_bindings.bindings); + break; + case _SGIMGUI_CMD_APPLY_UNIFORMS: + _sgimgui_draw_uniforms_panel(ctx, &item->args.apply_uniforms); + break; + case _SGIMGUI_CMD_DRAW: + case _SGIMGUI_CMD_DRAW_EX: + case _SGIMGUI_CMD_DISPATCH: + case _SGIMGUI_CMD_END_PASS: + case _SGIMGUI_CMD_COMMIT: + break; + case _SGIMGUI_CMD_ALLOC_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.alloc_buffer.result); + break; + case _SGIMGUI_CMD_ALLOC_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.alloc_image.result); + break; + case _SGIMGUI_CMD_ALLOC_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.alloc_sampler.result); + break; + case _SGIMGUI_CMD_ALLOC_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.alloc_shader.result); + break; + case _SGIMGUI_CMD_ALLOC_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.alloc_pipeline.result); + break; + case _SGIMGUI_CMD_ALLOC_VIEW: + _sgimgui_draw_view_panel(ctx, item->args.alloc_view.result); + break; + case _SGIMGUI_CMD_INIT_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.init_buffer.buffer); + break; + case _SGIMGUI_CMD_INIT_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.init_image.image); + break; + case _SGIMGUI_CMD_INIT_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.init_sampler.sampler); + break; + case _SGIMGUI_CMD_INIT_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.init_shader.shader); + break; + case _SGIMGUI_CMD_INIT_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.init_pipeline.pipeline); + break; + case _SGIMGUI_CMD_INIT_VIEW: + _sgimgui_draw_view_panel(ctx, item->args.init_view.view); + break; + case _SGIMGUI_CMD_FAIL_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.fail_buffer.buffer); + break; + case _SGIMGUI_CMD_FAIL_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.fail_image.image); + break; + case _SGIMGUI_CMD_FAIL_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.fail_sampler.sampler); + break; + case _SGIMGUI_CMD_FAIL_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.fail_shader.shader); + break; + case _SGIMGUI_CMD_FAIL_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.fail_pipeline.pipeline); + break; + case _SGIMGUI_CMD_FAIL_VIEW: + _sgimgui_draw_view_panel(ctx, item->args.fail_view.view); + break; + default: + break; + } + _sgimgui_igendchild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_caps_panel(void) { + _sgimgui_igtext("Backend: %s\n", _sgimgui_backend_string(sg_query_backend())); + _sgimgui_igtext("Dear ImGui Version: %s\n\n", IMGUI_VERSION); + sg_features f = sg_query_features(); + _sgimgui_igtext("Features:"); + _sgimgui_igtext(" origin_top_left: %s", _sgimgui_bool_string(f.origin_top_left)); + _sgimgui_igtext(" image_clamp_to_border: %s", _sgimgui_bool_string(f.image_clamp_to_border)); + _sgimgui_igtext(" mrt_independent_blend_state: %s", _sgimgui_bool_string(f.mrt_independent_blend_state)); + _sgimgui_igtext(" mrt_independent_write_mask: %s", _sgimgui_bool_string(f.mrt_independent_write_mask)); + _sgimgui_igtext(" compute: %s", _sgimgui_bool_string(f.compute)); + _sgimgui_igtext(" msaa_texture_bindings: %s", _sgimgui_bool_string(f.msaa_texture_bindings)); + _sgimgui_igtext(" separate_buffer_types: %s", _sgimgui_bool_string(f.separate_buffer_types)); + _sgimgui_igtext(" draw_base_vertex: %s", _sgimgui_bool_string(f.draw_base_vertex)); + _sgimgui_igtext(" draw_base_instance: %s", _sgimgui_bool_string(f.draw_base_instance)); + _sgimgui_igtext(" dual_source_blending: %s", _sgimgui_bool_string(f.dual_source_blending)); + _sgimgui_igtext(" gl_texture_views: %s", _sgimgui_bool_string(f.gl_texture_views)); + sg_limits l = sg_query_limits(); + _sgimgui_igtext("\nLimits:\n"); + _sgimgui_igtext(" max_image_size_2d: %d", l.max_image_size_2d); + _sgimgui_igtext(" max_image_size_cube: %d", l.max_image_size_cube); + _sgimgui_igtext(" max_image_size_3d: %d", l.max_image_size_3d); + _sgimgui_igtext(" max_image_size_array: %d", l.max_image_size_array); + _sgimgui_igtext(" max_image_array_layers: %d", l.max_image_array_layers); + _sgimgui_igtext(" max_vertex_attrs: %d", l.max_vertex_attrs); + _sgimgui_igtext(" max_color_attachments: %d", l.max_color_attachments); + _sgimgui_igtext(" max_texture_bindings_per_stage: %d", l.max_texture_bindings_per_stage); + _sgimgui_igtext(" max_storage_buffer_bindings_per_stage: %d", l.max_storage_buffer_bindings_per_stage); + _sgimgui_igtext(" max_storage_image_bindings_per_stage: %d", l.max_storage_image_bindings_per_stage); + _sgimgui_igtext(" gl_max_vertex_uniform_components: %d", l.gl_max_vertex_uniform_components); + _sgimgui_igtext(" gl_max_combined_texture_image_units: %d", l.gl_max_combined_texture_image_units); + _sgimgui_igtext(" d3d11_max_unordered_access_views: %d", l.d3d11_max_unordered_access_views); + _sgimgui_igtext(" vk_min_uniform_buffer_offset_alignment: %d", l.vk_min_uniform_buffer_offset_alignment); + _sgimgui_igtext("\nStruct Sizes:\n"); + _sgimgui_igtext(" sg_desc: %d bytes\n", (int)sizeof(sg_desc)); + _sgimgui_igtext(" sg_buffer_desc: %d bytes\n", (int)sizeof(sg_buffer_desc)); + _sgimgui_igtext(" sg_image_desc: %d bytes\n", (int)sizeof(sg_image_desc)); + _sgimgui_igtext(" sg_view_desc: %d bytes\n", (int)sizeof(sg_view_desc)); + _sgimgui_igtext(" sg_sampler_desc: %d bytes\n", (int)sizeof(sg_sampler_desc)); + _sgimgui_igtext(" sg_shader_desc: %d bytes\n", (int)sizeof(sg_shader_desc)); + _sgimgui_igtext(" sg_pipeline_desc: %d bytes\n", (int)sizeof(sg_pipeline_desc)); + _sgimgui_igtext(" sg_pass: %d bytes\n", (int)sizeof(sg_pass)); + _sgimgui_igtext(" sg_bindings: %d bytes\n", (int)sizeof(sg_bindings)); + _sgimgui_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; + sg_pixelformat_info info = sg_query_pixelformat(fmt); + if (info.sample) { + _sgimgui_igtext(" %s: %s%s%s%s%s%s%s%s%s", + _sgimgui_pixelformat_string(fmt), + info.sample ? "SAMPLE ":"", + info.filter ? "FILTER ":"", + info.blend ? "BLEND ":"", + info.render ? "RENDER ":"", + info.msaa ? "MSAA ":"", + info.depth ? "DEPTH ":"", + info.compressed ? "COMPRESSED ":"", + info.read ? "READ ":"", + info.write ? "WRITE ":""); + } + } +} + +_SOKOL_PRIVATE void _sgimgui_frame_add_stats_row(const char* key, uint32_t value) { + _sgimgui_igtablenextrow(); + _sgimgui_igtablesetcolumnindex(0); + _sgimgui_igtext("%s", key); + _sgimgui_igtablesetcolumnindex(1); + _sgimgui_igtext("%d", value); +} + +#define _sgimgui_frame_stats(key) _sgimgui_frame_add_stats_row(#key, stats->key) + +_SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(_sgimgui_t* ctx) { + _SOKOL_UNUSED(ctx); + _sgimgui_igcheckbox("Ignore sokol_imgui.h", &ctx->frame_stats_window.disable_sokol_imgui_stats); + const sg_stats* stats = &ctx->frame_stats_window.stats; + const ImGuiTableFlags flags = + ImGuiTableFlags_Resizable | + ImGuiTableFlags_ScrollY | + ImGuiTableFlags_SizingFixedFit | + ImGuiTableFlags_Borders; + if (_sgimgui_igbegintable("#frame_stats_table", 2, flags)) { + _sgimgui_igtablesetupscrollfreeze(0, 1); + _sgimgui_igtablesetupcolumn("key", ImGuiTableColumnFlags_None); + _sgimgui_igtablesetupcolumn("value", ImGuiTableColumnFlags_None); + _sgimgui_igtableheadersrow(); + _sgimgui_frame_stats(prev_frame.frame_index); + _sgimgui_frame_stats(prev_frame.num_passes); + _sgimgui_frame_stats(prev_frame.num_apply_viewport); + _sgimgui_frame_stats(prev_frame.num_apply_scissor_rect); + _sgimgui_frame_stats(prev_frame.num_apply_pipeline); + _sgimgui_frame_stats(prev_frame.num_apply_bindings); + _sgimgui_frame_stats(prev_frame.num_apply_uniforms); + _sgimgui_frame_stats(prev_frame.num_draw); + _sgimgui_frame_stats(prev_frame.num_draw_ex); + _sgimgui_frame_stats(prev_frame.num_dispatch); + _sgimgui_frame_stats(prev_frame.num_update_buffer); + _sgimgui_frame_stats(prev_frame.num_append_buffer); + _sgimgui_frame_stats(prev_frame.num_update_image); + _sgimgui_frame_stats(prev_frame.size_apply_uniforms); + _sgimgui_frame_stats(prev_frame.size_update_buffer); + _sgimgui_frame_stats(prev_frame.size_append_buffer); + _sgimgui_frame_stats(prev_frame.size_update_image); + _sgimgui_frame_stats(prev_frame.buffers.allocated); + _sgimgui_frame_stats(prev_frame.buffers.deallocated); + _sgimgui_frame_stats(prev_frame.buffers.inited); + _sgimgui_frame_stats(prev_frame.buffers.uninited); + _sgimgui_frame_stats(prev_frame.images.allocated); + _sgimgui_frame_stats(prev_frame.images.deallocated); + _sgimgui_frame_stats(prev_frame.images.inited); + _sgimgui_frame_stats(prev_frame.images.uninited); + _sgimgui_frame_stats(prev_frame.views.allocated); + _sgimgui_frame_stats(prev_frame.views.deallocated); + _sgimgui_frame_stats(prev_frame.views.inited); + _sgimgui_frame_stats(prev_frame.views.uninited); + _sgimgui_frame_stats(prev_frame.shaders.allocated); + _sgimgui_frame_stats(prev_frame.shaders.deallocated); + _sgimgui_frame_stats(prev_frame.shaders.inited); + _sgimgui_frame_stats(prev_frame.shaders.uninited); + _sgimgui_frame_stats(prev_frame.pipelines.allocated); + _sgimgui_frame_stats(prev_frame.pipelines.deallocated); + _sgimgui_frame_stats(prev_frame.pipelines.inited); + _sgimgui_frame_stats(prev_frame.pipelines.uninited); + switch (sg_query_backend()) { + case SG_BACKEND_GLCORE: + case SG_BACKEND_GLES3: + _sgimgui_frame_stats(prev_frame.gl.num_bind_buffer); + _sgimgui_frame_stats(prev_frame.gl.num_active_texture); + _sgimgui_frame_stats(prev_frame.gl.num_bind_texture); + _sgimgui_frame_stats(prev_frame.gl.num_bind_image_texture); + _sgimgui_frame_stats(prev_frame.gl.num_bind_sampler); + _sgimgui_frame_stats(prev_frame.gl.num_use_program); + _sgimgui_frame_stats(prev_frame.gl.num_render_state); + _sgimgui_frame_stats(prev_frame.gl.num_vertex_attrib_pointer); + _sgimgui_frame_stats(prev_frame.gl.num_vertex_attrib_divisor); + _sgimgui_frame_stats(prev_frame.gl.num_enable_vertex_attrib_array); + _sgimgui_frame_stats(prev_frame.gl.num_disable_vertex_attrib_array); + _sgimgui_frame_stats(prev_frame.gl.num_uniform); + _sgimgui_frame_stats(prev_frame.gl.num_memory_barriers); + break; + case SG_BACKEND_WGPU: + _sgimgui_frame_stats(prev_frame.wgpu.uniforms.num_set_bindgroup); + _sgimgui_frame_stats(prev_frame.wgpu.uniforms.size_write_buffer); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_set_vertex_buffer); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_skip_redundant_vertex_buffer); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_set_index_buffer); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_skip_redundant_index_buffer); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_create_bindgroup); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_discard_bindgroup); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_set_bindgroup); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_skip_redundant_bindgroup); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_bindgroup_cache_hits); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_bindgroup_cache_misses); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_bindgroup_cache_collisions); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_bindgroup_cache_invalidates); + _sgimgui_frame_stats(prev_frame.wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch); + break; + case SG_BACKEND_METAL_MACOS: + case SG_BACKEND_METAL_IOS: + case SG_BACKEND_METAL_SIMULATOR: + _sgimgui_frame_stats(prev_frame.metal.idpool.num_added); + _sgimgui_frame_stats(prev_frame.metal.idpool.num_released); + _sgimgui_frame_stats(prev_frame.metal.idpool.num_garbage_collected); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_blend_color); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_cull_mode); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_front_facing_winding); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_stencil_reference_value); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_depth_bias); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_render_pipeline_state); + _sgimgui_frame_stats(prev_frame.metal.pipeline.num_set_depth_stencil_state); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_vertex_buffer); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_fragment_buffer); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_compute_buffer); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_vertex_buffer_offset); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_fragment_buffer_offset); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_compute_buffer_offset); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_vertex_texture); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_fragment_texture); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_compute_texture); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_vertex_sampler_state); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_fragment_sampler_state); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_set_compute_sampler_state); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_vertex_buffer); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_fragment_buffer); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_compute_buffer); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_vertex_texture); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_fragment_texture); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_compute_texture); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_vertex_sampler_state); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_fragment_sampler_state); + _sgimgui_frame_stats(prev_frame.metal.bindings.num_skip_redundant_compute_sampler_state); + _sgimgui_frame_stats(prev_frame.metal.uniforms.num_set_vertex_buffer_offset); + _sgimgui_frame_stats(prev_frame.metal.uniforms.num_set_fragment_buffer_offset); + _sgimgui_frame_stats(prev_frame.metal.uniforms.num_set_compute_buffer_offset); + break; + case SG_BACKEND_D3D11: + _sgimgui_frame_stats(prev_frame.d3d11.pass.num_om_set_render_targets); + _sgimgui_frame_stats(prev_frame.d3d11.pass.num_clear_render_target_view); + _sgimgui_frame_stats(prev_frame.d3d11.pass.num_clear_depth_stencil_view); + _sgimgui_frame_stats(prev_frame.d3d11.pass.num_resolve_subresource); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_rs_set_state); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_om_set_depth_stencil_state); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_om_set_blend_state); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_ia_set_primitive_topology); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_ia_set_input_layout); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_vs_set_shader); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_vs_set_constant_buffers); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_ps_set_shader); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_ps_set_constant_buffers); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_cs_set_shader); + _sgimgui_frame_stats(prev_frame.d3d11.pipeline.num_cs_set_constant_buffers); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_ia_set_vertex_buffers); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_ia_set_index_buffer); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_vs_set_shader_resources); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_ps_set_shader_resources); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_cs_set_shader_resources); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_vs_set_samplers); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_ps_set_samplers); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_cs_set_samplers); + _sgimgui_frame_stats(prev_frame.d3d11.bindings.num_cs_set_unordered_access_views); + _sgimgui_frame_stats(prev_frame.d3d11.uniforms.num_update_subresource); + _sgimgui_frame_stats(prev_frame.d3d11.draw.num_draw_indexed_instanced); + _sgimgui_frame_stats(prev_frame.d3d11.draw.num_draw_indexed); + _sgimgui_frame_stats(prev_frame.d3d11.draw.num_draw_instanced); + _sgimgui_frame_stats(prev_frame.d3d11.draw.num_draw); + _sgimgui_frame_stats(prev_frame.d3d11.num_map); + _sgimgui_frame_stats(prev_frame.d3d11.num_unmap); + break; + case SG_BACKEND_VULKAN: + _sgimgui_frame_stats(prev_frame.vk.num_cmd_pipeline_barrier); + _sgimgui_frame_stats(prev_frame.vk.num_allocate_memory); + _sgimgui_frame_stats(prev_frame.vk.num_free_memory); + _sgimgui_frame_stats(prev_frame.vk.size_allocate_memory); + _sgimgui_frame_stats(prev_frame.vk.num_delete_queue_added); + _sgimgui_frame_stats(prev_frame.vk.num_delete_queue_collected); + _sgimgui_frame_stats(prev_frame.vk.num_cmd_copy_buffer); + _sgimgui_frame_stats(prev_frame.vk.num_cmd_copy_buffer_to_image); + _sgimgui_frame_stats(prev_frame.vk.num_cmd_set_descriptor_buffer_offsets); + _sgimgui_frame_stats(prev_frame.vk.size_descriptor_buffer_writes); + break; + default: break; + } + _sgimgui_frame_stats(total.buffers.alive); + _sgimgui_frame_stats(total.buffers.free); + _sgimgui_frame_stats(total.buffers.allocated); + _sgimgui_frame_stats(total.buffers.deallocated); + _sgimgui_frame_stats(total.buffers.inited); + _sgimgui_frame_stats(total.buffers.uninited); + _sgimgui_frame_stats(total.images.alive); + _sgimgui_frame_stats(total.images.free); + _sgimgui_frame_stats(total.images.allocated); + _sgimgui_frame_stats(total.images.deallocated); + _sgimgui_frame_stats(total.images.inited); + _sgimgui_frame_stats(total.images.uninited); + _sgimgui_frame_stats(total.samplers.alive); + _sgimgui_frame_stats(total.samplers.free); + _sgimgui_frame_stats(total.samplers.allocated); + _sgimgui_frame_stats(total.samplers.deallocated); + _sgimgui_frame_stats(total.samplers.inited); + _sgimgui_frame_stats(total.samplers.uninited); + _sgimgui_frame_stats(total.views.alive); + _sgimgui_frame_stats(total.views.free); + _sgimgui_frame_stats(total.views.allocated); + _sgimgui_frame_stats(total.views.deallocated); + _sgimgui_frame_stats(total.views.inited); + _sgimgui_frame_stats(total.views.uninited); + _sgimgui_frame_stats(total.pipelines.alive); + _sgimgui_frame_stats(total.pipelines.free); + _sgimgui_frame_stats(total.pipelines.allocated); + _sgimgui_frame_stats(total.pipelines.deallocated); + _sgimgui_frame_stats(total.pipelines.inited); + _sgimgui_frame_stats(total.pipelines.uninited); + _sgimgui_igendtable(); + } +} + +#define _sgimgui_def(val, def) (((val) == 0) ? (def) : (val)) + +_SOKOL_PRIVATE sgimgui_desc_t _sgimgui_desc_defaults(const sgimgui_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sgimgui_desc_t res = *desc; + // FIXME: any additional default overrides would go here + return res; +} + +/*--- PUBLIC FUNCTIONS -------------------------------------------------------*/ +SOKOL_API_IMPL void sgimgui_setup(const sgimgui_desc_t* desc) { + SOKOL_ASSERT(desc); + _sgimgui_clear(&_sgimgui, sizeof(_sgimgui_t)); + _sgimgui.init_tag = 0xABCDABCD; + _sgimgui.desc = _sgimgui_desc_defaults(desc); + _sgimgui_capture_init(&_sgimgui); + + /* hook into sokol_gfx functions */ + sg_trace_hooks hooks; + _sgimgui_clear(&hooks, sizeof(hooks)); + hooks.user_data = (void*)&_sgimgui; + hooks.reset_state_cache = _sgimgui_reset_state_cache; + hooks.make_buffer = _sgimgui_make_buffer; + hooks.make_image = _sgimgui_make_image; + hooks.make_sampler = _sgimgui_make_sampler; + hooks.make_shader = _sgimgui_make_shader; + hooks.make_pipeline = _sgimgui_make_pipeline; + hooks.make_view = _sgimgui_make_view; + hooks.destroy_buffer = _sgimgui_destroy_buffer; + hooks.destroy_image = _sgimgui_destroy_image; + hooks.destroy_sampler = _sgimgui_destroy_sampler; + hooks.destroy_shader = _sgimgui_destroy_shader; + hooks.destroy_pipeline = _sgimgui_destroy_pipeline; + hooks.destroy_view = _sgimgui_destroy_view; + hooks.update_buffer = _sgimgui_update_buffer; + hooks.update_image = _sgimgui_update_image; + hooks.append_buffer = _sgimgui_append_buffer; + hooks.begin_pass = _sgimgui_begin_pass; + hooks.apply_viewport = _sgimgui_apply_viewport; + hooks.apply_scissor_rect = _sgimgui_apply_scissor_rect; + hooks.apply_pipeline = _sgimgui_apply_pipeline; + hooks.apply_bindings = _sgimgui_apply_bindings; + hooks.apply_uniforms = _sgimgui_apply_uniforms; + hooks.draw = _sgimgui_draw; + hooks.draw_ex = _sgimgui_draw_ex; + hooks.dispatch = _sgimgui_dispatch; + hooks.end_pass = _sgimgui_end_pass; + hooks.commit = _sgimgui_commit; + hooks.alloc_buffer = _sgimgui_alloc_buffer; + hooks.alloc_image = _sgimgui_alloc_image; + hooks.alloc_sampler = _sgimgui_alloc_sampler; + hooks.alloc_shader = _sgimgui_alloc_shader; + hooks.alloc_pipeline = _sgimgui_alloc_pipeline; + hooks.alloc_view = _sgimgui_alloc_view; + hooks.dealloc_buffer = _sgimgui_dealloc_buffer; + hooks.dealloc_image = _sgimgui_dealloc_image; + hooks.dealloc_sampler = _sgimgui_dealloc_sampler; + hooks.dealloc_shader = _sgimgui_dealloc_shader; + hooks.dealloc_pipeline = _sgimgui_dealloc_pipeline; + hooks.dealloc_view = _sgimgui_dealloc_view; + hooks.init_buffer = _sgimgui_init_buffer; + hooks.init_image = _sgimgui_init_image; + hooks.init_sampler = _sgimgui_init_sampler; + hooks.init_shader = _sgimgui_init_shader; + hooks.init_pipeline = _sgimgui_init_pipeline; + hooks.init_view = _sgimgui_init_view; + hooks.uninit_buffer = _sgimgui_uninit_buffer; + hooks.uninit_image = _sgimgui_uninit_image; + hooks.uninit_sampler = _sgimgui_uninit_sampler; + hooks.uninit_shader = _sgimgui_uninit_shader; + hooks.uninit_pipeline = _sgimgui_uninit_pipeline; + hooks.uninit_view = _sgimgui_uninit_view; + hooks.fail_buffer = _sgimgui_fail_buffer; + hooks.fail_image = _sgimgui_fail_image; + hooks.fail_sampler = _sgimgui_fail_sampler; + hooks.fail_shader = _sgimgui_fail_shader; + hooks.fail_pipeline = _sgimgui_fail_pipeline; + hooks.fail_view = _sgimgui_fail_view; + hooks.push_debug_group = _sgimgui_push_debug_group; + hooks.pop_debug_group = _sgimgui_pop_debug_group; + _sgimgui.hooks = sg_install_trace_hooks(&hooks); + + /* allocate resource debug-info slots */ + const sg_desc sgdesc = sg_query_desc(); + _sgimgui.buffer_window.num_slots = sgdesc.buffer_pool_size; + _sgimgui.image_window.num_slots = sgdesc.image_pool_size; + _sgimgui.sampler_window.num_slots = sgdesc.sampler_pool_size; + _sgimgui.shader_window.num_slots = sgdesc.shader_pool_size; + _sgimgui.pipeline_window.num_slots = sgdesc.pipeline_pool_size; + _sgimgui.view_window.num_slots = sgdesc.view_pool_size; + + const size_t buffer_pool_size = (size_t)_sgimgui.buffer_window.num_slots * sizeof(_sgimgui_buffer_t); + _sgimgui.buffer_window.slots = (_sgimgui_buffer_t*) _sgimgui_malloc_clear(&_sgimgui.desc.allocator, buffer_pool_size); + + const size_t image_pool_size = (size_t)_sgimgui.image_window.num_slots * sizeof(_sgimgui_image_t); + _sgimgui.image_window.slots = (_sgimgui_image_t*) _sgimgui_malloc_clear(&_sgimgui.desc.allocator, image_pool_size); + + const size_t sampler_pool_size = (size_t)_sgimgui.sampler_window.num_slots * sizeof(_sgimgui_sampler_t); + _sgimgui.sampler_window.slots = (_sgimgui_sampler_t*) _sgimgui_malloc_clear(&_sgimgui.desc.allocator, sampler_pool_size); + + const size_t shader_pool_size = (size_t)_sgimgui.shader_window.num_slots * sizeof(_sgimgui_shader_t); + _sgimgui.shader_window.slots = (_sgimgui_shader_t*) _sgimgui_malloc_clear(&_sgimgui.desc.allocator, shader_pool_size); + + const size_t pipeline_pool_size = (size_t)_sgimgui.pipeline_window.num_slots * sizeof(_sgimgui_pipeline_t); + _sgimgui.pipeline_window.slots = (_sgimgui_pipeline_t*) _sgimgui_malloc_clear(&_sgimgui.desc.allocator, pipeline_pool_size); + + const size_t view_pool_size = (size_t)_sgimgui.view_window.num_slots * sizeof(_sgimgui_view_t); + _sgimgui.view_window.slots = (_sgimgui_view_t*) _sgimgui_malloc_clear(&_sgimgui.desc.allocator, view_pool_size); +} + +SOKOL_API_IMPL void sgimgui_shutdown(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + /* restore original trace hooks */ + sg_install_trace_hooks(&_sgimgui.hooks); + _sgimgui.init_tag = 0; + _sgimgui_capture_discard(&_sgimgui); + if (_sgimgui.buffer_window.slots) { + for (int i = 0; i < _sgimgui.buffer_window.num_slots; i++) { + if (_sgimgui.buffer_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_buffer_destroyed(&_sgimgui, i); + } + } + _sgimgui_free(&_sgimgui.desc.allocator, (void*)_sgimgui.buffer_window.slots); + _sgimgui.buffer_window.slots = 0; + } + if (_sgimgui.image_window.slots) { + for (int i = 0; i < _sgimgui.image_window.num_slots; i++) { + if (_sgimgui.image_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_image_destroyed(&_sgimgui, i); + } + } + _sgimgui_free(&_sgimgui.desc.allocator, (void*)_sgimgui.image_window.slots); + _sgimgui.image_window.slots = 0; + } + if (_sgimgui.sampler_window.slots) { + for (int i = 0; i < _sgimgui.sampler_window.num_slots; i++) { + if (_sgimgui.sampler_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_sampler_destroyed(&_sgimgui, i); + } + } + _sgimgui_free(&_sgimgui.desc.allocator, (void*)_sgimgui.sampler_window.slots); + _sgimgui.sampler_window.slots = 0; + } + if (_sgimgui.shader_window.slots) { + for (int i = 0; i < _sgimgui.shader_window.num_slots; i++) { + if (_sgimgui.shader_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_shader_destroyed(&_sgimgui, i); + } + } + _sgimgui_free(&_sgimgui.desc.allocator, (void*)_sgimgui.shader_window.slots); + _sgimgui.shader_window.slots = 0; + } + if (_sgimgui.pipeline_window.slots) { + for (int i = 0; i < _sgimgui.pipeline_window.num_slots; i++) { + if (_sgimgui.pipeline_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_pipeline_destroyed(&_sgimgui, i); + } + } + _sgimgui_free(&_sgimgui.desc.allocator, (void*)_sgimgui.pipeline_window.slots); + _sgimgui.pipeline_window.slots = 0; + } + if (_sgimgui.view_window.slots) { + for (int i = 0; i < _sgimgui.view_window.num_slots; i++) { + if (_sgimgui.view_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_view_destroyed(&_sgimgui, i); + } + } + _sgimgui_free(&_sgimgui.desc.allocator, (void*)_sgimgui.view_window.slots); + _sgimgui.view_window.slots = 0; + } +} + +SOKOL_API_IMPL void sgimgui_draw(void) { + sgimgui_draw_buffer_window("Buffers"); + sgimgui_draw_image_window("Images"); + sgimgui_draw_sampler_window("Samplers"); + sgimgui_draw_shader_window("Shaders"); + sgimgui_draw_pipeline_window("Pipelines"); + sgimgui_draw_view_window("Views"); + sgimgui_draw_capture_window("Frame Capture"); + sgimgui_draw_capabilities_window("Capabilities"); + sgimgui_draw_frame_stats_window("Frame Stats"); +} + +SOKOL_API_IMPL void sgimgui_draw_menu(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (_sgimgui_igbeginmenu(title)) { + sgimgui_draw_capabilities_menu_item("Capabilities"); + sgimgui_draw_frame_stats_menu_item("Frame Stats"); + sgimgui_draw_buffer_menu_item("Buffers"); + sgimgui_draw_image_menu_item("Images"); + sgimgui_draw_view_menu_item("Views"); + sgimgui_draw_sampler_menu_item("Samplers"); + sgimgui_draw_shader_menu_item("Shaders"); + sgimgui_draw_pipeline_menu_item("Pipelines"); + sgimgui_draw_capture_menu_item("Calls"); + _sgimgui_igendmenu(); + } +} + +SOKOL_API_IMPL void sgimgui_draw_buffer_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.buffer_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_image_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.image_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_sampler_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.sampler_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_shader_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.shader_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_pipeline_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.pipeline_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_view_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.view_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_capture_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.capture_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_capabilities_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.caps_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_frame_stats_menu_item(const char* label) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(label); + _sgimgui_igmenuitemboolptr(label, 0, &_sgimgui.frame_stats_window.open, true); +} + +SOKOL_API_IMPL void sgimgui_draw_buffer_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.buffer_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(440, 280), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.buffer_window.open, 0)) { + sgimgui_draw_buffer_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_image_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.image_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(440, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.image_window.open, 0)) { + sgimgui_draw_image_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_sampler_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.sampler_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(440, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.sampler_window.open, 0)) { + sgimgui_draw_sampler_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_shader_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.shader_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(440, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.shader_window.open, 0)) { + sgimgui_draw_shader_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_pipeline_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.pipeline_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(540, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.pipeline_window.open, 0)) { + sgimgui_draw_pipeline_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_view_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.view_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(440, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.view_window.open, 0)) { + sgimgui_draw_view_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_capture_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.capture_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(640, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.capture_window.open, 0)) { + sgimgui_draw_capture_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_capabilities_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.caps_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(440, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.caps_window.open, 0)) { + sgimgui_draw_capabilities_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_frame_stats_window(const char* title) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + SOKOL_ASSERT(title); + if (!_sgimgui.frame_stats_window.open) { + return; + } + _sgimgui_igsetnextwindowsize(IMVEC2(640, 400), ImGuiCond_Once); + if (_sgimgui_igbegin(title, &_sgimgui.frame_stats_window.open, 0)) { + sgimgui_draw_frame_stats_window_content(); + } + _sgimgui_igend(); +} + +SOKOL_API_IMPL void sgimgui_draw_buffer_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_buffer_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_buffer_panel(&_sgimgui, _sgimgui.buffer_window.sel_buf); +} + +SOKOL_API_IMPL void sgimgui_draw_image_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_image_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_image_panel(&_sgimgui, _sgimgui.image_window.sel_img); +} + +SOKOL_API_IMPL void sgimgui_draw_sampler_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_sampler_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_sampler_panel(&_sgimgui, _sgimgui.sampler_window.sel_smp); +} + +SOKOL_API_IMPL void sgimgui_draw_shader_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_shader_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_shader_panel(&_sgimgui, _sgimgui.shader_window.sel_shd); +} + +SOKOL_API_IMPL void sgimgui_draw_pipeline_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_pipeline_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_pipeline_panel(&_sgimgui, _sgimgui.pipeline_window.sel_pip); +} + +SOKOL_API_IMPL void sgimgui_draw_view_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_view_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_view_panel(&_sgimgui, _sgimgui.view_window.sel_view); +} + +SOKOL_API_IMPL void sgimgui_draw_capture_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_capture_list(&_sgimgui); + _sgimgui_igsameline(); + _sgimgui_draw_capture_panel(&_sgimgui); +} + +SOKOL_API_IMPL void sgimgui_draw_capabilities_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui_draw_caps_panel(); +} + +SOKOL_API_IMPL void sgimgui_draw_frame_stats_window_content(void) { + SOKOL_ASSERT(_sgimgui.init_tag == 0xABCDABCD); + _sgimgui.frame_stats_window.stats = sg_query_stats(); + _sgimgui_draw_frame_stats_panel(&_sgimgui); +} + +#endif /* SOKOL_GFX_IMGUI_IMPL */ diff --git a/thirdparty/sokol/util/sokol_gl.h b/thirdparty/sokol/util/sokol_gl.h new file mode 100644 index 0000000..8c6d966 --- /dev/null +++ b/thirdparty/sokol/util/sokol_gl.h @@ -0,0 +1,5018 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GL_IMPL) +#define SOKOL_GL_IMPL +#endif +#ifndef SOKOL_GL_INCLUDED +/* + sokol_gl.h -- OpenGL 1.x style rendering on top of sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GL_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + SOKOL_VULKAN + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_gl.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GL_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_gl.h: + + sokol_gfx.h + + Matrix functions have been taken from MESA and Regal. + + FEATURE OVERVIEW: + ================= + sokol_gl.h implements a subset of the OpenGLES 1.x feature set useful for + when you just want to quickly render a bunch of triangles or + lines without having to mess with buffers and shaders. + + The current feature set is mostly useful for debug visualizations + and simple UI-style 2D rendering: + + What's implemented: + - vertex components: + - position (x, y, z) + - 2D texture coords (u, v) + - color (r, g, b, a) + - primitive types: + - triangle list and strip + - line list and strip + - quad list (TODO: quad strips) + - 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 + functions for gluPerspective and gluLookat + + Notable GLES 1.x features that are *NOT* implemented: + - vertex lighting (this is the most likely GL feature that might be added later) + - vertex arrays (although providing whole chunks of vertex data at once + might be a useful feature for a later version) + - texture coordinate generation + - line width + - all pixel store functions + - no ALPHA_TEST + - no clear functions (clearing is handled by the sokol-gfx render pass) + - fog + + Notable differences to GL: + - No "enum soup" for render states etc, instead there's a + 'pipeline stack', this is similar to GL's matrix stack, + but for pipeline-state-objects. The pipeline object at + the top of the pipeline stack defines the active set of render states + - All angles are in radians, not degrees (note the sgl_rad() and + sgl_deg() conversion functions) + - No enable/disable state for scissor test, this is always enabled + + STEP BY STEP: + ============= + --- To initialize sokol-gl, call: + + sgl_setup(const sgl_desc_t* desc) + + NOTE that sgl_setup() must be called *after* initializing sokol-gfx + (via sg_setup). This is because sgl_setup() needs to create + sokol-gfx resource objects. + + If you're intending to render to the default pass, and also don't + 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. + + 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*){ + .color_format = SG_PIXELFORMAT_..., + .depth_format = SG_PIXELFORMAT_..., + .sample_count = ..., + }); + + To reduce memory usage, or if you need to create more then the default number of + contexts, pipelines, vertices or draw commands, set the following sgl_desc_t + members: + + .context_pool_size (default: 4) + .pipeline_pool_size (default: 64) + .max_vertices (default: 64k) + .max_commands (default: 16k) + + Finally you can change the face winding for front-facing triangles + and quads: + + .face_winding - default is SG_FACEWINDING_CCW + + The default winding for front faces is counter-clock-wise. This is + the same as OpenGL's default, but different from sokol-gfx. + + --- Optionally create additional context objects if you want to render into + multiple sokol-gfx render passes (or generally if you want to + use multiple independent sokol-gl "state buckets") + + sgl_context ctx = sgl_make_context(const sgl_context_desc_t* desc) + + For details on rendering with sokol-gl contexts, search below for + WORKING WITH CONTEXTS. + + --- Optionally create pipeline-state-objects if you need render state + that differs from sokol-gl's default state: + + sgl_pipeline pip = sgl_make_pipeline(const sg_pipeline_desc* desc) + + ...this creates a pipeline object that's compatible with the currently + active context, alternatively call: + + sgl_pipeline_pip = sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc) + + ...to create a pipeline object that's compatible with an explicitly + provided context. + + The similarity with sokol_gfx.h's sg_pipeline type and sg_make_pipeline() + function is intended. sgl_make_pipeline() also takes a standard + sokol-gfx sg_pipeline_desc object to describe the render state, but + without: + - shader + - vertex layout + - color- and depth-pixel-formats + - primitive type (lines, triangles, ...) + - MSAA sample count + Those will be filled in by sgl_make_pipeline(). Note that each + call to sgl_make_pipeline() needs to create several sokol-gfx + pipeline objects (one for each primitive type). + + 'depth.write_enabled' will be forced to 'false' if the context this + pipeline object is intended for has its depth pixel format set to + SG_PIXELFORMAT_NONE (which means the framebuffer this context is used + with doesn't have a depth-stencil surface). + + --- if you need to destroy sgl_pipeline objects before sgl_shutdown(): + + sgl_destroy_pipeline(sgl_pipeline pip) + + --- After sgl_setup() you can call any of the sokol-gl functions anywhere + in a frame, *except* sgl_draw(). The 'vanilla' functions + will only change internal sokol-gl state, and not call any sokol-gfx + functions. + + --- Unlike OpenGL, sokol-gl has a function to reset internal state to + a known default. This is useful at the start of a sequence of + rendering operations: + + void sgl_defaults(void) + + This will set the following default state: + + - 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' + into the top of the pipeline stack + + The current matrix- and pipeline-stack-depths will not be changed by + sgl_defaults(). + + --- change the currently active renderstate through the + pipeline-stack functions, this works similar to the + traditional GL matrix stack: + + ...load the default pipeline state on the top of the pipeline stack: + + sgl_load_default_pipeline() + + ...load a specific pipeline on the top of the pipeline stack: + + sgl_load_pipeline(sgl_pipeline pip) + + ...push and pop the pipeline stack: + sgl_push_pipeline() + sgl_pop_pipeline() + + --- control texturing with: + + sgl_enable_texture() + sgl_disable_texture() + sgl_texture(sg_view tex_view, sg_sampler smp) + + NOTE: the tex_view and smp handles can be invalid (SG_INVALID_ID), in this + case, sokol-gl will fall back to the internal default (white) texture + and sampler. + + --- set the current viewport and scissor rect with: + + sgl_viewport(int x, int y, int w, int h, bool origin_top_left) + sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left) + + ...or call these alternatives which take float arguments (this might allow + to avoid casting between float and integer in more strongly typed languages + when floating point pixel coordinates are used): + + sgl_viewportf(float x, float y, float w, float h, bool origin_top_left) + sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left) + + ...these calls add a new command to the internal command queue, so + that the viewport or scissor rect are set at the right time relative + to other sokol-gl calls. + + --- adjust the transform matrices, matrix manipulation works just like + the OpenGL matrix stack: + + ...set the current matrix mode: + + sgl_matrix_mode_modelview() + sgl_matrix_mode_projection() + sgl_matrix_mode_texture() + + ...load the identity matrix into the current matrix: + + sgl_load_identity() + + ...translate, rotate and scale the current matrix: + + sgl_translate(float x, float y, float z) + sgl_rotate(float angle_rad, float x, float y, float z) + sgl_scale(float x, float y, float z) + + NOTE that all angles in sokol-gl are in radians, not in degree. + Convert between radians and degree with the helper functions: + + float sgl_rad(float deg) - degrees to radians + float sgl_deg(float rad) - radians to degrees + + ...directly load the current matrix from a float[16] array: + + sgl_load_matrix(const float m[16]) + sgl_load_transpose_matrix(const float m[16]) + + ...directly multiply the current matrix from a float[16] array: + + sgl_mult_matrix(const float m[16]) + sgl_mult_transpose_matrix(const float m[16]) + + The memory layout of those float[16] arrays is the same as in OpenGL. + + ...more matrix functions: + + sgl_frustum(float left, float right, float bottom, float top, float near, float far) + sgl_ortho(float left, float right, float bottom, float top, float near, float far) + sgl_perspective(float fov_y, float aspect, float near, float far) + sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z) + + These functions work the same as glFrustum(), glOrtho(), gluPerspective() + and gluLookAt(). + + ...and finally to push / pop the current matrix stack: + + sgl_push_matrix(void) + sgl_pop_matrix(void) + + Again, these work the same as glPushMatrix() and glPopMatrix(). + + --- perform primitive rendering: + + ...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 + and without alpha). + + 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() + sgl_begin_lines() + sgl_begin_line_strip() + sgl_begin_triangles() + sgl_begin_triangle_strip() + sgl_begin_quads() + + ...after sgl_begin_*() specify vertices: + + sgl_v*(...) + sgl_v*_t*(...) + sgl_v*_c*(...) + sgl_v*_t*_c*(...) + + These functions write a new vertex to sokol-gl's internal vertex buffer, + optionally with texture-coords and color. If the texture coordinate + and/or color is missing, it will be taken from the current texture-coord + and color 'register'. + + ...finally, after specifying vertices, call: + + sgl_end() + + This will record a new draw command in sokol-gl's internal command + list, or it will extend the previous draw command if no relevant + state has changed since the last sgl_begin/end pair. + + --- inside a sokol-gfx rendering pass, call the sgl_draw() function + to render the currently active context: + + sgl_draw() + + ...or alternatively call: + + sgl_context_draw(ctx) + + ...to render an explicitly provided context. + + This will render everything that has been recorded in the context since + the last call to sgl_draw() through sokol-gfx, and will 'rewind' the internal + vertex-, uniform- and command-buffers. + + --- each sokol-gl context tracks internal error states which can + be obtains via: + + sgl_error_t sgl_error() + + ...alternatively with an explicit context argument: + + sgl_error_t sgl_context_error(ctx); + + ...this returns a struct with the following booleans: + + .any - true if any of the below errors is true + .vertices_full - internal vertex buffer is full (checked in sgl_end()) + .uniforms_full - the internal uniforms buffer is full (checked in sgl_end()) + .commands_full - the internal command buffer is full (checked in sgl_end()) + .stack_overflow - matrix- or pipeline-stack overflow + .stack_underflow - matrix- or pipeline-stack underflow + .no_context - the active context no longer exists + + ...depending on the above error state, sgl_draw() may skip rendering + completely, or only draw partial geometry + + --- you can get the number of recorded vertices and draw commands in the current + frame and active sokol-gl context via: + + int sgl_num_vertices() + int sgl_num_commands() + + ...this allows you to check whether the vertex or command pools are running + full before the overflow actually happens (in this case you could also + check the error booleans in the result of sgl_error()). + + RENDER LAYERS + ============= + 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: + ====================== + If you want to render to more than one sokol-gfx render pass you need to + work with additional sokol-gl context objects (one context object for + each offscreen rendering pass, in addition to the implicitly created + 'default context'. + + All sokol-gl state is tracked per context, and there is always a "current + context" (with the notable exception that the currently set context is + destroyed, more on that later). + + Using multiple contexts can also be useful if you only render in + a single pass, but want to maintain multiple independent "state buckets". + + To create new context object, call: + + sgl_context ctx = sgl_make_context(&(sgl_context_desc){ + .max_vertices = ..., // default: 64k + .max_commands = ..., // default: 16k + .color_format = ..., + .depth_format = ..., + .sample_count = ..., + }); + + The color_format, depth_format and sample_count items must be compatible + with the render pass the sgl_draw() or sgL_context_draw() function + will be called in. + + Creating a context does *not* make the context current. To do this, call: + + sgl_set_context(ctx); + + The currently active context will implicitly be used by most sokol-gl functions + which don't take an explicit context handle as argument. + + To switch back to the default context, pass the global constant SGL_DEFAULT_CONTEXT: + + sgl_set_context(SGL_DEFAULT_CONTEXT); + + ...or alternatively use the function sgl_default_context() instead of the + global constant: + + sgl_set_context(sgl_default_context()); + + To get the currently active context, call: + + sgl_context cur_ctx = sgl_get_context(); + + The following functions exist in two variants, one which use the currently + active context (set with sgl_set_context()), and another version which + takes an explicit context handle instead: + + sgl_make_pipeline() vs sgl_context_make_pipeline() + sgl_error() vs sgl_context_error(); + sgl_draw() vs sgl_context_draw(); + + Except for using the currently active context versus a provided context + handle, the two variants are exactlyidentical, e.g. the following + code sequences do the same thing: + + sgl_set_context(ctx); + sgl_pipeline pip = sgl_make_pipeline(...); + sgl_error_t err = sgl_error(); + sgl_draw(); + + vs + + sgl_pipeline pip = sgl_context_make_pipeline(ctx, ...); + sgl_error_t err = sgl_context_error(ctx); + sgl_context_draw(ctx); + + Destroying the currently active context is a 'soft error'. All following + calls which require a currently active context will silently fail, + and sgl_error() will return SGL_ERROR_NO_CONTEXT. + + UNDER THE HOOD: + =============== + sokol_gl.h works by recording vertex data and rendering commands into + memory buffers, and then drawing the recorded commands via sokol_gfx.h + + The only functions which call into sokol_gfx.h are: + - sgl_setup() + - sgl_shutdown() + - sgl_draw() (and variants) + + sgl_setup() must be called after initializing sokol-gfx. + sgl_shutdown() must be called before shutting down sokol-gfx. + sgl_draw() must be called once per frame inside a sokol-gfx render pass. + + All other sokol-gl function can be called anywhere in a frame, since + they just record data into memory buffers owned by sokol-gl. + + What happens in: + + sgl_setup(): + Unique resources shared by all contexts are created: + - a shader object (using embedded shader source or byte code) + - an 8x8 white default texture + The default context is created, which involves: + - 3 memory buffers are created, one for vertex data, + one for uniform data, and one for commands + - a dynamic vertex buffer is created + - the default sgl_pipeline object is created, which involves + creating 5 sg_pipeline objects + + One vertex is 24 bytes: + - float3 position + - float2 texture coords + - uint32_t color + + One uniform block is 128 bytes: + - mat4 model-view-projection matrix + - mat4 texture matrix + + One draw command is ca. 24 bytes for the actual + command code plus command arguments. + + Each sgl_end() consumes one command, and one uniform block + (only when the matrices have changed). + The required size for one sgl_begin/end pair is (at most): + + (152 + 24 * num_verts) bytes + + sgl_shutdown(): + - all sokol-gfx resources (buffer, shader, default-texture and + all pipeline objects) are destroyed + - the 3 memory buffers are freed + + 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: + - depending on what has changed since the last draw command, + call sg_apply_pipeline(), sg_apply_bindings() and + sg_apply_uniforms() + - finally call sg_draw() + + All other functions only modify the internally tracked state, add + data to the vertex, uniform and command buffers, or manipulate + the matrix stack. + + ON DRAW COMMAND MERGING + ======================= + Not every call to sgl_end() will automatically record a new draw command. + If possible, the previous draw command will simply be extended, + resulting in fewer actual draw calls later in sgl_draw(). + + 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_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 + + Merging a draw command simply means that the number of vertices + 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_fn = my_alloc, + .free_fn = 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 + + Copyright (c) 2018 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_GL_INCLUDED (1) +#include +#include +#include // size_t, offsetof + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_gl.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_GL_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GL_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GL_IMPL) +#define SOKOL_GL_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GL_API_DECL __declspec(dllimport) +#else +#define SOKOL_GL_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +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; + +/* a context handle (created with sgl_make_context()) */ +typedef struct sgl_context { uint32_t id; } sgl_context; + +/* + sgl_error_t + + Errors are reset each frame after calling sgl_draw(), + get the last error code with sgl_error() +*/ +typedef struct sgl_error_t { + bool any; + bool vertices_full; + bool uniforms_full; + bool commands_full; + bool stack_overflow; + bool stack_underflow; + bool no_context; +} sgl_error_t; + +/* + sgl_context_desc_t + + Describes the initialization parameters of a rendering context. + Creating additional contexts is useful if you want to render + in separate sokol-gfx passes. +*/ +typedef struct sgl_context_desc_t { + int max_vertices; // default: 64k + int max_commands; // default: 16k + sg_pixel_format color_format; + sg_pixel_format depth_format; + 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_fn)(size_t size, void* user_data); + void (*free_fn)(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 + int context_pool_size; // max number of contexts (including default context), default: 4 + int pipeline_pool_size; // size of internal pipeline pool, default: 64 + sg_pixel_format color_format; + 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 */ +static const sgl_context SGL_DEFAULT_CONTEXT = { 0x00010001 }; + +/* setup/shutdown/misc */ +SOKOL_GL_API_DECL void sgl_setup(const sgl_desc_t* desc); +SOKOL_GL_API_DECL void sgl_shutdown(void); +SOKOL_GL_API_DECL float sgl_rad(float deg); +SOKOL_GL_API_DECL float sgl_deg(float rad); +SOKOL_GL_API_DECL sgl_error_t sgl_error(void); +SOKOL_GL_API_DECL sgl_error_t sgl_context_error(sgl_context ctx); + +/* context functions */ +SOKOL_GL_API_DECL sgl_context sgl_make_context(const sgl_context_desc_t* desc); +SOKOL_GL_API_DECL void sgl_destroy_context(sgl_context ctx); +SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx); +SOKOL_GL_API_DECL sgl_context sgl_get_context(void); +SOKOL_GL_API_DECL sgl_context sgl_default_context(void); + +/* get information about recorded vertices and commands in current context */ +SOKOL_GL_API_DECL int sgl_num_vertices(void); +SOKOL_GL_API_DECL int sgl_num_commands(void); + +/* draw recorded commands (call inside a sokol-gfx render pass) */ +SOKOL_GL_API_DECL void sgl_draw(void); +SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx); +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); +SOKOL_GL_API_DECL void sgl_destroy_pipeline(sgl_pipeline pip); + +/* render state functions */ +SOKOL_GL_API_DECL void sgl_defaults(void); +SOKOL_GL_API_DECL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left); +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_view tex_view, sg_sampler smp); +SOKOL_GL_API_DECL void sgl_layer(int layer_id); + +/* pipeline stack functions */ +SOKOL_GL_API_DECL void sgl_load_default_pipeline(void); +SOKOL_GL_API_DECL void sgl_load_pipeline(sgl_pipeline pip); +SOKOL_GL_API_DECL void sgl_push_pipeline(void); +SOKOL_GL_API_DECL void sgl_pop_pipeline(void); + +/* matrix stack functions */ +SOKOL_GL_API_DECL void sgl_matrix_mode_modelview(void); +SOKOL_GL_API_DECL void sgl_matrix_mode_projection(void); +SOKOL_GL_API_DECL void sgl_matrix_mode_texture(void); +SOKOL_GL_API_DECL void sgl_load_identity(void); +SOKOL_GL_API_DECL void sgl_load_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_load_transpose_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_mult_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_mult_transpose_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_rotate(float angle_rad, float x, float y, float z); +SOKOL_GL_API_DECL void sgl_scale(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_translate(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_frustum(float l, float r, float b, float t, float n, float f); +SOKOL_GL_API_DECL void sgl_ortho(float l, float r, float b, float t, float n, float f); +SOKOL_GL_API_DECL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far); +SOKOL_GL_API_DECL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z); +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 / 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); +SOKOL_GL_API_DECL void sgl_begin_lines(void); +SOKOL_GL_API_DECL void sgl_begin_line_strip(void); +SOKOL_GL_API_DECL void sgl_begin_triangles(void); +SOKOL_GL_API_DECL void sgl_begin_triangle_strip(void); +SOKOL_GL_API_DECL void sgl_begin_quads(void); +SOKOL_GL_API_DECL void sgl_v2f(float x, float y); +SOKOL_GL_API_DECL void sgl_v3f(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_v2f_t2f(float x, float y, float u, float v); +SOKOL_GL_API_DECL void sgl_v3f_t2f(float x, float y, float z, float u, float v); +SOKOL_GL_API_DECL void sgl_v2f_c3f(float x, float y, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v2f_c1i(float x, float y, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +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); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sgl_setup(const sgl_desc_t& desc) { return sgl_setup(&desc); } +inline sgl_context sgl_make_context(const sgl_context_desc_t& desc) { return sgl_make_context(&desc); } +inline sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc& desc) { return sgl_make_pipeline(&desc); } +inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc& desc) { return sgl_context_make_pipeline(ctx, &desc); } +#endif +#endif /* SOKOL_GL_INCLUDED */ + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_GL_IMPL +#define SOKOL_GL_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 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 +#endif + +#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 + +#define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) +#define _SGL_INIT_COOKIE (0xABCDABCD) + +/* + Embedded source code compiled with: + + sokol-shdc -i sgl.glsl -o sgl.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl:spirv_vk -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + layout(binding=0) uniform vs_params { + mat4 mvp; + mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + in float psize; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + #ifndef SOKOL_WGSL + gl_PointSize = psize; + #endif + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + layout(binding=0) uniform texture2D tex; + layout(binding=0) uniform sampler smp; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv.xy) * color; + } + @end + + @program sgl vs fs +*/ + +#if defined(SOKOL_GLCORE) +/* + #version 410 + + uniform vec4 vs_params[8]; + 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; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = mat4(vs_params[0], vs_params[1], vs_params[2], vs_params[3]) * position; + gl_PointSize = psize; + uv = mat4(vs_params[4], vs_params[5], vs_params[6], vs_params[7]) * vec4(texcoord0, 0.0, 1.0); + color = color0; + } +*/ +static const uint8_t _sgl_vs_source_glsl410[520] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,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, + 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,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,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,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,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, +}; +/* + #version 410 + + uniform sampler2D tex_smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec4 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv.xy) * color; + } +*/ +static const uint8_t _sgl_fs_source_glsl410[222] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,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,0x30,0x29,0x20,0x69,0x6e,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, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29, + 0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +/* + #version 300 es + + uniform vec4 vs_params[8]; + layout(location = 0) in vec4 position; + layout(location = 3) in float psize; + out vec4 uv; + layout(location = 1) in vec2 texcoord0; + out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = mat4(vs_params[0], vs_params[1], vs_params[2], vs_params[3]) * position; + gl_PointSize = psize; + uv = mat4(vs_params[4], vs_params[5], vs_params[6], vs_params[7]) * vec4(texcoord0, 0.0, 1.0); + color = color0; + } +*/ +static const uint8_t _sgl_vs_source_glsl300es[481] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,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, +}; +/* + #version 300 es + precision mediump float; + precision highp int; + + uniform highp sampler2D tex_smp; + + layout(location = 0) out highp vec4 frag_color; + in highp vec4 uv; + in highp vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv.xy) * color; + } +*/ +static const uint8_t _sgl_fs_source_glsl300es[253] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x20, + 0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + float4x4 tm; + }; + + struct main0_out + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; + }; + + struct main0_in + { + float4 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + float psize [[attribute(3)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = in.psize; + out.uv = _19.tm * float4(in.texcoord0, 0.0, 1.0); + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sgl_vs_bytecode_metal_macos[3381] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x35,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,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,0x59,0xf1,0xe4,0x22,0x0f,0x75,0x42, + 0x53,0x9c,0x2a,0x05,0xe4,0xbd,0x91,0x28,0x82,0x06,0x3f,0x0d,0xb9,0xef,0x34,0xbd, + 0xe8,0xfa,0x46,0x5c,0x66,0x1f,0xc6,0xde,0x3a,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,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,0x04,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xfe,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,0x68,0x00,0x00, + 0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00,0x8a,0x08,0x07,0x78, + 0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73,0xa8,0x07,0x77,0x18,0x87,0x36,0x30, + 0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca, + 0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8,0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1, + 0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c, + 0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00,0x06,0x77,0x78,0x87,0x36,0x10,0x87, + 0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87,0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea, + 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,0xda,0xc0,0x1d,0xde,0xc1,0x1d,0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc, + 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc, + 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,0x83,0x79,0x48,0x87,0x73,0x70, + 0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90,0x87,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,0x80,0x1e,0xe4,0x21,0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d, + 0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07, + 0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87,0x36,0x80,0x07,0x79,0x78,0x07,0x7a, + 0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36,0x10,0x87,0x7a,0x30,0x07,0x73,0x28, + 0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28,0x07,0x00,0x0f,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,0x36,0x18,0x42,0x01,0x2c,0x40, + 0x05,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,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65, + 0x66,0xa6,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,0x62,0x72,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,0x79,0x18,0x00, + 0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43, + 0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4, + 0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6, + 0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0, + 0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1, + 0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f, + 0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc, + 0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19, + 0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3, + 0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87, + 0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77, + 0xa8,0x07,0x78,0x98,0x07,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,0xf4,0xc6,0x22, + 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0x46,0x00,0xa8, + 0x95,0x40,0x19,0x14,0x01,0x8d,0x19,0x00,0x12,0x33,0x00,0x14,0x66,0x00,0x66,0x00, + 0x00,0xe3,0x15,0x8c,0xa4,0x69,0x12,0x05,0x65,0x90,0x41,0x22,0x14,0x13,0x02,0xf9, + 0x8c,0x57,0x40,0x96,0xe7,0x2d,0x14,0x94,0x41,0x06,0xeb,0x78,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x8a,0x6a,0x1b,0x83,0x31,0x70,0x28,0x28,0x83,0x0c,0x1b,0x53, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0x1a,0x18,0xa0,0x01,0x1a,0x50,0x14, + 0x94,0x41,0x06,0x30,0x88,0x36,0x13,0x02,0xf9,0x58,0x11,0xc0,0x67,0xbc,0xe2,0x2b, + 0x03,0x37,0x68,0x83,0x32,0xa0,0xa0,0x0c,0x32,0x90,0x41,0xd6,0x99,0x10,0xc8,0x67, + 0xbc,0x62,0x0c,0xd2,0x40,0x0e,0xe2,0xc0,0xa3,0xa0,0x0c,0x32,0xa0,0x41,0x27,0x06, + 0x26,0x04,0xf2,0xb1,0xa0,0x80,0xcf,0x78,0x05,0x1a,0xb8,0xc1,0x1d,0xd8,0x81,0x18, + 0x50,0x50,0x6c,0x08,0xe0,0x33,0xdb,0x20,0x06,0x01,0x30,0xdb,0x10,0xb8,0x41,0x30, + 0xdb,0x10,0x3c,0xc2,0x6c,0x43,0xf0,0x06,0x43,0x06,0x01,0x31,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, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv.xy) * in.color; + return out; + } +*/ +static const uint8_t _sgl_fs_bytecode_metal_macos[3033] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x43,0x4f,0xc0,0x79,0x15,0x0f,0x7e, + 0x56,0x86,0x83,0xab,0x09,0x97,0xeb,0xad,0x1f,0xad,0xc5,0x99,0xa0,0x69,0x5d,0x31, + 0x4f,0x5e,0x5b,0x06,0x1b,0x6c,0x91,0x10,0xa7,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,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,0xe4,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb6,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,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,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,0xe1,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xd1,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,0x46,0x06,0x46,0x66,0xc6, + 0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06, + 0x46,0x66,0xc6,0x65,0x66,0xa6,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,0x0e,0x73,0x6d,0x70,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, + 0x03,0x5e,0x61,0x69,0x72,0x2d,0x61,0x6c,0x69,0x61,0x73,0x2d,0x73,0x63,0x6f,0x70, + 0x65,0x73,0x28,0x6d,0x61,0x69,0x6e,0x30,0x29,0x43,0x88,0x87,0x0d,0x9e,0x35,0x20, + 0x16,0x96,0x26,0xd7,0x12,0xc6,0x96,0x16,0x36,0xd7,0x32,0x37,0xf6,0x06,0x57,0xd6, + 0x42,0x57,0x86,0x47,0x57,0x27,0x57,0x36,0x37,0xc4,0x78,0xdc,0xe0,0x61,0x83,0xa7, + 0x0d,0x88,0x85,0xa5,0xc9,0xb5,0x84,0xb1,0xa5,0x85,0xcd,0xb5,0xcc,0x8d,0xbd,0xc1, + 0x95,0xb5,0xcc,0x85,0xb5,0xc1,0xb1,0x95,0xc9,0xcd,0x0d,0x31,0x1e,0x38,0x78,0xd8, + 0xe0,0x79,0x83,0x21,0xc4,0xe3,0x06,0x0f,0x1c,0x8c,0x88,0xd8,0x81,0x1d,0xec,0xa1, + 0x1d,0xdc,0xa0,0x1d,0xde,0x81,0x1c,0xea,0x81,0x1d,0xca,0xc1,0x0d,0xcc,0x81,0x1d, + 0xc2,0xe1,0x1c,0xe6,0x61,0x8a,0x10,0x0c,0x23,0x14,0x76,0x60,0x07,0x7b,0x68,0x07, + 0x37,0x48,0x07,0x72,0x28,0x07,0x77,0xa0,0x87,0x29,0x41,0x31,0x62,0x09,0x87,0x74, + 0x90,0x07,0x37,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x81, + 0x31,0x82,0x0a,0x87,0x74,0x90,0x07,0x37,0x60,0x87,0x70,0x70,0x87,0x73,0xa8,0x87, + 0x70,0x38,0x87,0x72,0xf8,0x05,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77, + 0x98,0x12,0x20,0x23,0xa6,0x70,0x48,0x07,0x79,0x70,0x83,0x71,0x78,0x87,0x76,0x80, + 0x87,0x74,0x60,0x87,0x72,0xf8,0x85,0x77,0x80,0x07,0x7a,0x48,0x87,0x77,0x70,0x87, + 0x79,0x98,0x32,0x28,0x8c,0x33,0x82,0x09,0x87,0x74,0x90,0x07,0x37,0x30,0x07,0x79, + 0x08,0x87,0x73,0x68,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0xa8,0x01,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84, + 0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03, + 0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87, + 0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f, + 0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3, + 0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2, + 0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21, + 0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83, + 0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87, + 0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e, + 0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90, + 0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85, + 0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,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,0x14,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,0x43,0x40,0x29,0x49,0x50,0x20,0x86,0x60, + 0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x08,0x88,0x01,0x00,0x00,0x02,0x00,0x00, + 0x00,0x5b,0x86,0xe0,0x88,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + float4x4 tm; + }; + + struct main0_out + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; + }; + + struct main0_in + { + float4 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + float psize [[attribute(3)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = in.psize; + out.uv = _19.tm * float4(in.texcoord0, 0.0, 1.0); + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sgl_vs_bytecode_metal_ios[3493] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa5,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x90,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,0x81,0x80,0x75,0xbc,0xa9,0xd0,0xb9, + 0x4f,0xee,0x63,0x10,0x8f,0xfe,0x66,0xa5,0x3b,0x40,0xb7,0x43,0x44,0x12,0xba,0x69, + 0x7d,0xf3,0x83,0x9a,0xda,0x47,0x00,0x29,0xec,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,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,0x70,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0x19,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,0x70,0x00,0x00, + 0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00,0x8a,0x08,0x07,0x78, + 0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73,0xa8,0x07,0x77,0x18,0x87,0x36,0x30, + 0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca, + 0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8,0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1, + 0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c, + 0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00,0x06,0x77,0x78,0x87,0x36,0x10,0x87, + 0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87,0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea, + 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,0xda,0xc0,0x1d,0xde,0xc1,0x1d,0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc, + 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc, + 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,0x83,0x79,0x48,0x87,0x73,0x70, + 0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90,0x87,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,0x80,0x1e,0xe4,0x21,0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d, + 0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07, + 0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87,0x36,0x80,0x07,0x79,0x78,0x07,0x7a, + 0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36,0x10,0x87,0x7a,0x30,0x07,0x73,0x28, + 0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28,0x07,0x00,0x0f,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,0x36,0x6c,0x42,0x01,0x2c,0x40, + 0x35,0x84,0x43,0x3a,0xc8,0x43,0x1b,0x88,0x43,0x3d,0x98,0x83,0x39,0x94,0x83,0x3c, + 0xb4,0x81,0x3b,0xbc,0x43,0x1b,0x84,0x03,0x3b,0xa4,0x43,0x38,0xcc,0x03,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,0x48,0x00,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x12,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,0x46, + 0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,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,0x62,0x72,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,0xe1,0x15,0x96,0x26,0xd7,0x12,0xc6,0x96,0x16,0x36,0xd7,0x32,0x37,0xf6, + 0x06,0x57,0x36,0x87,0xd2,0x16,0x96,0xe6,0x06,0x93,0x32,0x84,0x50,0x44,0x41,0x09, + 0x05,0x5a,0x61,0x69,0x72,0x2d,0x61,0x6c,0x69,0x61,0x73,0x2d,0x73,0x63,0x6f,0x70, + 0x65,0x2d,0x61,0x72,0x67,0x28,0x34,0x29,0x43,0x0c,0x85,0x14,0x14,0x51,0x50,0x46, + 0x61,0x88,0xa0,0x90,0xc2,0x88,0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda,0xe1, + 0x1d,0xc8,0xa1,0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61,0x1e, + 0xa6,0x08,0xc1,0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87, + 0x72,0x70,0x07,0x7a,0x98,0x12,0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b, + 0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x18,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,0x01,0x32,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,0x83,0xc2, + 0x38,0x23,0x94,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0xa0,0x87,0x72, + 0xc0,0x87,0x29,0xc1,0x1e,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19, + 0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c, + 0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76, + 0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10, + 0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e, + 0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d, + 0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b, + 0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77, + 0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5, + 0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50, + 0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71, + 0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98, + 0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x42,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,0x46,0x00,0xa8,0x95,0x40,0x19,0x14, + 0x01,0x8d,0x19,0x00,0x12,0x33,0x00,0x14,0x66,0x00,0x66,0x00,0x00,0xe3,0x15,0x8c, + 0xa4,0x69,0x12,0x05,0x65,0x90,0x41,0x22,0x14,0x13,0x02,0xf9,0x8c,0x57,0x40,0x96, + 0xe7,0x2d,0x14,0x94,0x41,0x06,0xeb,0x78,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x8a,0x6a,0x1b,0x83,0x31,0x70,0x28,0x28,0x83,0x0c,0x1b,0x53,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0x1a,0x18,0xa0,0x01,0x1a,0x50,0x14,0x94,0x41,0x06,0x30, + 0x88,0x36,0x13,0x02,0xf9,0x58,0x11,0xc0,0x67,0xbc,0xe2,0x2b,0x03,0x37,0x68,0x83, + 0x32,0xa0,0xa0,0x0c,0x32,0x90,0x41,0xd6,0x99,0x10,0xc8,0x67,0xbc,0x62,0x0c,0xd2, + 0x40,0x0e,0xe2,0xc0,0xa3,0xa0,0x0c,0x32,0xa0,0x41,0x27,0x06,0x26,0x04,0xf2,0xb1, + 0xa0,0x80,0xcf,0x78,0x05,0x1a,0xb8,0xc1,0x1d,0xd8,0x81,0x18,0x50,0x50,0x6c,0x08, + 0xe0,0x33,0xdb,0x20,0x06,0x01,0x30,0xdb,0x10,0xb8,0x41,0x30,0xdb,0x10,0x3c,0xc2, + 0x6c,0x43,0xf0,0x06,0x43,0x06,0x01,0x31,0x00,0x0d,0x00,0x00,0x00,0x5b,0x8a,0x20, + 0x00,0x85,0xa3,0x14,0xb6,0x14,0x45,0x00,0x0a,0x47,0x29,0x6c,0x29,0x94,0x00,0x14, + 0x8e,0x52,0xd8,0x52,0x3c,0x01,0x28,0x1c,0xa5,0xb0,0xa5,0xa0,0x02,0x50,0x38,0x4a, + 0x61,0x4b,0x81,0x05,0xa0,0x70,0x94,0xc2,0x96,0xa2,0x0b,0x40,0xe1,0x28,0x05,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv.xy) * in.color; + return out; + } +*/ +static const uint8_t _sgl_fs_bytecode_metal_ios[3017] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,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,0x40,0xd6,0xeb,0xe8,0x54,0x51,0x57, + 0x09,0x04,0x8e,0xcb,0x4c,0x44,0x2c,0x92,0x69,0x71,0x54,0xbd,0xb6,0xe0,0x7e,0x6b, + 0x97,0x01,0x63,0xb9,0x24,0x88,0x76,0x15,0x8c,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,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,0xd8,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb3,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,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,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x41,0x02,0x00,0x00,0x79,0x18,0x00, + 0x00,0xd0,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,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,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,0x0e,0x73,0x6d,0x70,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,0x03,0x5e,0x61,0x69,0x72,0x2d,0x61,0x6c,0x69, + 0x61,0x73,0x2d,0x73,0x63,0x6f,0x70,0x65,0x73,0x28,0x6d,0x61,0x69,0x6e,0x30,0x29, + 0x43,0x88,0x87,0x0d,0x9e,0x35,0x20,0x16,0x96,0x26,0xd7,0x12,0xc6,0x96,0x16,0x36, + 0xd7,0x32,0x37,0xf6,0x06,0x57,0xd6,0x42,0x57,0x86,0x47,0x57,0x27,0x57,0x36,0x37, + 0xc4,0x78,0xdc,0xe0,0x61,0x83,0xa7,0x0d,0x88,0x85,0xa5,0xc9,0xb5,0x84,0xb1,0xa5, + 0x85,0xcd,0xb5,0xcc,0x8d,0xbd,0xc1,0x95,0xb5,0xcc,0x85,0xb5,0xc1,0xb1,0x95,0xc9, + 0xcd,0x0d,0x31,0x1e,0x38,0x78,0xd8,0xe0,0x79,0x83,0x21,0xc4,0xe3,0x06,0x0f,0x1c, + 0x8c,0x88,0xd8,0x81,0x1d,0xec,0xa1,0x1d,0xdc,0xa0,0x1d,0xde,0x81,0x1c,0xea,0x81, + 0x1d,0xca,0xc1,0x0d,0xcc,0x81,0x1d,0xc2,0xe1,0x1c,0xe6,0x61,0x8a,0x10,0x0c,0x23, + 0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x48,0x07,0x72,0x28,0x07,0x77,0xa0,0x87, + 0x29,0x41,0x31,0x62,0x09,0x87,0x74,0x90,0x07,0x37,0xb0,0x87,0x72,0x90,0x87,0x79, + 0x48,0x87,0x77,0x70,0x87,0x29,0x81,0x31,0x82,0x0a,0x87,0x74,0x90,0x07,0x37,0x60, + 0x87,0x70,0x70,0x87,0x73,0xa8,0x87,0x70,0x38,0x87,0x72,0xf8,0x05,0x7b,0x28,0x07, + 0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x20,0x23,0xa6,0x70,0x48,0x07,0x79, + 0x70,0x83,0x71,0x78,0x87,0x76,0x80,0x87,0x74,0x60,0x87,0x72,0xf8,0x85,0x77,0x80, + 0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x79,0x98,0x32,0x28,0x8c,0x33,0x82,0x09,0x87, + 0x74,0x90,0x07,0x37,0x30,0x07,0x79,0x08,0x87,0x73,0x68,0x87,0x72,0x70,0x07,0x7a, + 0x98,0x12,0xa8,0x01,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8, + 0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08, + 0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20, + 0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e, + 0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2, + 0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4, + 0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8, + 0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08, + 0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10, + 0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30, + 0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78, + 0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,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,0x14,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,0x43,0x40,0x29,0x49, + 0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x08,0x88,0x01,0x00, + 0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0xe0,0x88,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + float4x4 tm; + }; + + struct main0_out + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; + }; + + struct main0_in + { + float4 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + float psize [[attribute(3)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = in.psize; + out.uv = _19.tm * float4(in.texcoord0, 0.0, 1.0); + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sgl_vs_source_metal_sim[756] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,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,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,0x31,0x39,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,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a, + 0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,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,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x31,0x39,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,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, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float4 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv.xy) * in.color; + return out; + } +*/ +static const uint8_t _sgl_fs_source_metal_sim[439] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,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, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,0x73,0x6d,0x70,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,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,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,0x2e,0x78,0x79,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) +/* + cbuffer vs_params : register(b0) + { + row_major float4x4 _19_mvp : packoffset(c0); + row_major float4x4 _19_tm : packoffset(c4); + }; + + + static float4 gl_Position; + static float gl_PointSize; + static float4 position; + static float psize; + static float4 uv; + static float2 texcoord0; + static float4 color; + static float4 color0; + + struct SPIRV_Cross_Input + { + float4 position : TEXCOORD0; + float2 texcoord0 : TEXCOORD1; + float4 color0 : TEXCOORD2; + float psize : TEXCOORD3; + }; + + struct SPIRV_Cross_Output + { + float4 uv : TEXCOORD0; + float4 color : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = mul(position, _19_mvp); + gl_PointSize = psize; + uv = mul(float4(texcoord0, 0.0f, 1.0f), _19_tm); + color = color0; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + position = stage_input.position; + psize = stage_input.psize; + texcoord0 = stage_input.texcoord0; + color0 = stage_input.color0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.uv = uv; + stage_output.color = color; + return stage_output; + } +*/ +static const uint8_t _sgl_vs_bytecode_hlsl4[1032] = { + 0x44,0x58,0x42,0x43,0x74,0x7f,0x01,0xd9,0xf4,0xd5,0xed,0x1d,0x74,0xc1,0x30,0x27, + 0xd8,0xe9,0x9d,0x50,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 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,0x31,0x39,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,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,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,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, + 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, +}; +/* + Texture2D tex : register(t0); + SamplerState smp : register(s0); + + static float4 frag_color; + static float4 uv; + static float4 color; + + struct SPIRV_Cross_Input + { + float4 uv : TEXCOORD0; + float4 color : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 frag_color : SV_Target0; + }; + + void frag_main() + { + frag_color = tex.Sample(smp, uv.xy) * color; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + uv = stage_input.uv; + color = stage_input.color; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.frag_color = frag_color; + return stage_output; + } +*/ +static const uint8_t _sgl_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0xc8,0x9b,0x66,0x64,0x80,0x2f,0xbe,0x14,0xd9,0x88,0xa0,0x97, + 0x64,0x14,0x66,0xff,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,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,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,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,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,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +/* + diagnostic(off, derivative_uniformity); + + struct vs_params { + /_ @offset(0) _/ + mvp : mat4x4f, + /_ @offset(64) _/ + tm : mat4x4f, + } + + @binding(0) @group(0) var x_19 : vs_params; + + var position_1 : vec4f; + + var uv : vec4f; + + var texcoord0 : vec2f; + + var color : vec4f; + + var color0 : vec4f; + + var psize : f32; + + var gl_Position : vec4f; + + fn main_1() { + gl_Position = (x_19.mvp * position_1); + uv = (x_19.tm * vec4f(texcoord0.x, texcoord0.y, 0.0f, 1.0f)); + color = color0; + return; + } + + struct main_out { + @builtin(position) + gl_Position : vec4f, + @location(0) + uv_1 : vec4f, + @location(1) + color_1 : vec4f, + } + + @vertex + fn main(@location(0) position_1_param : vec4f, @location(1) texcoord0_param : vec2f, @location(2) color0_param : vec4f, @location(3) psize_param : f32) -> main_out { + position_1 = position_1_param; + texcoord0 = texcoord0_param; + color0 = color0_param; + psize = psize_param; + main_1(); + return main_out(gl_Position, uv, color); + } +*/ +static const uint8_t _sgl_vs_source_wgsl[1027] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x6d,0x76,0x70,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78,0x34,0x66,0x2c,0x0a, + 0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x36,0x34,0x29, + 0x20,0x2a,0x2f,0x0a,0x20,0x20,0x74,0x6d,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78, + 0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28, + 0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x76,0x61,0x72, + 0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76,0x61,0x72, + 0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x0a, + 0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b, + 0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x28,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x29,0x3b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d, + 0x20,0x28,0x78,0x5f,0x31,0x39,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2e,0x78,0x2c,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x66, + 0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75, + 0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a, + 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74, + 0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x28,0x33,0x29,0x20,0x70,0x73,0x69,0x7a,0x65,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x20,0x3a,0x20,0x66,0x33,0x32,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64, + 0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x3b,0x0a,0x20,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, + 0x65,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a, + 0x7d,0x0a,0x00, +}; +/* + diagnostic(off, derivative_uniformity); + + var frag_color : vec4f; + + @binding(0) @group(1) var tex : texture_2d; + + @binding(32) @group(1) var smp : sampler; + + var uv : vec4f; + + var color : vec4f; + + fn main_1() { + let x_23 = uv; + let x_25 = textureSample(tex, smp, x_23.xy); + frag_color = (x_25 * color); + return; + } + + struct main_out { + @location(0) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(0) uv_param : vec4f, @location(1) color_param : vec4f) -> main_out { + uv = uv_param; + color = color_param; + main_1(); + return main_out(frag_color); + } +*/ +static const uint8_t _sgl_fs_source_wgsl[588] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64, + 0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29, + 0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76, + 0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b, + 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66, + 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73,0x6d, + 0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x2e,0x78,0x79,0x29,0x3b,0x0a,0x20,0x20,0x66, + 0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32, + 0x35,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66, + 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75, + 0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +#elif defined(SOKOL_VULKAN) +/* + #version 460 + + layout(set = 0, binding = 0, std140) uniform vs_params + { + mat4 mvp; + mat4 tm; + } _19; + + 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; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = _19.mvp * position; + gl_PointSize = psize; + uv = _19.tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + +*/ +static const uint8_t _sgl_vs_bytecode_spirv_vk[1668] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x33,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65, + 0x72,0x74,0x65,0x78,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00, + 0x06,0x00,0x07,0x00,0x0b,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, + 0x0b,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,0x0b,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,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x76,0x70,0x00,0x06,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x74,0x6d,0x00,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00,0x5f,0x31,0x39,0x00, + 0x05,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1f,0x00,0x00,0x00,0x70,0x73,0x69,0x7a, + 0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x23,0x00,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x05,0x00,0x28,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64, + 0x30,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x00,0x47,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x28,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x30,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x31,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00, + 0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x06,0x00, + 0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x1c,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x21,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x22,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x14,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x26,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0x91,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x23,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x30,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, +}; +/* + #version 460 + + layout(set = 1, binding = 0) uniform texture2D tex; + layout(set = 1, binding = 32) uniform sampler smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec4 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(sampler2D(tex, smp), uv.xy) * color; + } + +*/ +static const uint8_t _sgl_fs_bytecode_spirv_vk[792] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x1d,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0a,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x74,0x65,0x78,0x00, + 0x05,0x00,0x03,0x00,0x10,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x05,0x00,0x03,0x00, + 0x15,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0a,0x00,0x00,0x00, + 0x06,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,0x20,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, + 0x0e,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,0x1b,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x56,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x4f,0x00,0x07,0x00,0x16,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; + +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sgl_vs_source_dummy = ""; +static const char* _sgl_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +// ████████ ██ ██ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ +// +// >>types +typedef enum { + SGL_PRIMITIVETYPE_POINTS = 0, + SGL_PRIMITIVETYPE_LINES, + SGL_PRIMITIVETYPE_LINE_STRIP, + SGL_PRIMITIVETYPE_TRIANGLES, + SGL_PRIMITIVETYPE_TRIANGLE_STRIP, + SGL_PRIMITIVETYPE_QUADS, + SGL_NUM_PRIMITIVE_TYPES, +} _sgl_primitive_type_t; + +typedef struct { + uint32_t id; + sg_resource_state state; +} _sgl_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sgl_pool_t; + +typedef struct { + _sgl_slot_t slot; + sg_pipeline pip[SGL_NUM_PRIMITIVE_TYPES]; +} _sgl_pipeline_t; + +typedef struct { + _sgl_pool_t pool; + _sgl_pipeline_t* pips; +} _sgl_pipeline_pool_t; + +typedef enum { + SGL_MATRIXMODE_MODELVIEW, + SGL_MATRIXMODE_PROJECTION, + SGL_MATRIXMODE_TEXTURE, + SGL_NUM_MATRIXMODES +} _sgl_matrix_mode_t; + +typedef struct { + float pos[3]; + float uv[2]; + uint32_t rgba; + float psize; +} _sgl_vertex_t; + +typedef struct { + float v[4][4]; +} _sgl_matrix_t; + +typedef struct { + _sgl_matrix_t mvp; /* model-view-projection matrix */ + _sgl_matrix_t tm; /* texture matrix */ +} _sgl_uniform_t; + +typedef enum { + SGL_COMMAND_DRAW, + SGL_COMMAND_VIEWPORT, + SGL_COMMAND_SCISSOR_RECT, +} _sgl_command_type_t; + +typedef struct { + sg_pipeline pip; + sg_view view; + sg_sampler smp; + int base_vertex; + int num_vertices; + int uniform_index; +} _sgl_draw_args_t; + +typedef struct { + int x, y, w, h; + bool origin_top_left; +} _sgl_viewport_args_t; + +typedef struct { + int x, y, w, h; + bool origin_top_left; +} _sgl_scissor_rect_args_t; + +typedef union { + _sgl_draw_args_t draw; + _sgl_viewport_args_t viewport; + _sgl_scissor_rect_args_t scissor_rect; +} _sgl_args_t; + +typedef struct { + _sgl_command_type_t cmd; + int layer_id; + _sgl_args_t args; +} _sgl_command_t; + +#define _SGL_INVALID_SLOT_INDEX (0) +#define _SGL_MAX_STACK_DEPTH (64) +#define _SGL_DEFAULT_CONTEXT_POOL_SIZE (4) +#define _SGL_DEFAULT_PIPELINE_POOL_SIZE (64) +#define _SGL_DEFAULT_MAX_VERTICES (1<<16) +#define _SGL_DEFAULT_MAX_COMMANDS (1<<14) +#define _SGL_SLOT_SHIFT (16) +#define _SGL_MAX_POOL_SIZE (1<<_SGL_SLOT_SHIFT) +#define _SGL_SLOT_MASK (_SGL_MAX_POOL_SIZE-1) + +typedef struct { + _sgl_slot_t slot; + sgl_context_desc_t desc; + 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 quad_vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ + sgl_error_t error; + bool in_begin; + int layer_id; + float u, v; + uint32_t rgba; + float point_size; + _sgl_primitive_type_t cur_prim_type; + sg_view cur_view; + sg_sampler cur_smp; + bool texturing_enabled; + bool matrix_dirty; /* reset in sgl_end(), set in any of the matrix stack functions */ + + /* sokol-gfx resources */ + sg_buffer vbuf; + sgl_pipeline def_pip; + sg_bindings bind; + + /* pipeline stack */ + int pip_tos; + sgl_pipeline pip_stack[_SGL_MAX_STACK_DEPTH]; + + /* matrix stacks */ + _sgl_matrix_mode_t cur_matrix_mode; + int matrix_tos[SGL_NUM_MATRIXMODES]; + _sgl_matrix_t matrix_stack[SGL_NUM_MATRIXMODES][_SGL_MAX_STACK_DEPTH]; +} _sgl_context_t; + +typedef struct { + _sgl_pool_t pool; + _sgl_context_t* contexts; +} _sgl_context_pool_t; + +typedef struct { + uint32_t init_cookie; + sgl_desc_t desc; + sg_image def_img; // a default white texture + sg_view def_view; // ...and the texture view for the default image + sg_sampler def_smp; // a default sampler + sg_shader shd; // same shader for all contexts + sgl_context def_ctx_id; + sgl_context cur_ctx_id; + _sgl_context_t* cur_ctx; // may be 0! + _sgl_pipeline_pool_t pip_pool; + _sgl_context_pool_t context_pool; +} _sgl_t; +static _sgl_t _sgl; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>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, (uint32_t)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_fn) { + ptr = _sgl.desc.allocator.alloc_fn(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_fn) { + _sgl.desc.allocator.free_fn(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 */ + pool->size = num + 1; + 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*) _sgl_malloc_clear(gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + 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; + } +} + +static void _sgl_discard_pool(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sgl_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sgl_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sgl_pool_alloc_index(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SGL_INVALID_SLOT_INDEX; + } +} + +static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +static uint32_t _sgl_slot_alloc(_sgl_pool_t* pool, _sgl_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SGL_SLOT_SHIFT)|(slot_index & _SGL_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +static int _sgl_slot_index(uint32_t id) { + int slot_index = (int) (id & _SGL_SLOT_MASK); + SOKOL_ASSERT(_SGL_INVALID_SLOT_INDEX != slot_index); + 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); + int slot_index = _sgl_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.pip_pool.pool.size)); + return &_sgl.pip_pool.pips[slot_index]; +} + +/* get pipeline pointer with id-check, returns 0 if no match */ +static _sgl_pipeline_t* _sgl_lookup_pipeline(uint32_t pip_id) { + if (SG_INVALID_ID != pip_id) { + _sgl_pipeline_t* pip = _sgl_pipeline_at(pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +/* make pipeline id from uint32_t id */ +static sgl_pipeline _sgl_make_pip_id(uint32_t pip_id) { + sgl_pipeline pip = { pip_id }; + return pip; +} + +static sgl_pipeline _sgl_alloc_pipeline(void) { + sgl_pipeline res; + int slot_index = _sgl_pool_alloc_index(&_sgl.pip_pool.pool); + if (_SGL_INVALID_SLOT_INDEX != slot_index) { + res = _sgl_make_pip_id(_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index)); + } else { + /* pool is exhausted */ + res = _sgl_make_pip_id(SG_INVALID_ID); + } + return res; +} + +static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_desc, const sgl_context_desc_t* ctx_desc) { + SOKOL_ASSERT((pip_id.id != SG_INVALID_ID) && in_desc && ctx_desc); + + /* create a new desc with 'patched' shader and pixel format state */ + sg_pipeline_desc desc = *in_desc; + desc.layout.buffers[0].stride = sizeof(_sgl_vertex_t); + { + sg_vertex_attr_state* pos = &desc.layout.attrs[0]; + pos->offset = offsetof(_sgl_vertex_t, pos); + pos->format = SG_VERTEXFORMAT_FLOAT3; + } + { + sg_vertex_attr_state* uv = &desc.layout.attrs[1]; + uv->offset = offsetof(_sgl_vertex_t, uv); + uv->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* rgba = &desc.layout.attrs[2]; + rgba->offset = offsetof(_sgl_vertex_t, rgba); + rgba->format = SG_VERTEXFORMAT_UBYTE4N; + } + { + sg_vertex_attr_state* 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; + } + desc.index_type = SG_INDEXTYPE_NONE; + desc.sample_count = ctx_desc->sample_count; + if (desc.face_winding == _SG_FACEWINDING_DEFAULT) { + desc.face_winding = _sgl.desc.face_winding; + } + desc.depth.pixel_format = ctx_desc->depth_format; + if (ctx_desc->depth_format == SG_PIXELFORMAT_NONE) { + desc.depth.write_enabled = false; + } + desc.colors[0].pixel_format = ctx_desc->color_format; + if (desc.colors[0].write_mask == _SG_COLORMASK_DEFAULT) { + desc.colors[0].write_mask = SG_COLORMASK_RGB; + } + + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + pip->slot.state = SG_RESOURCESTATE_VALID; + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + switch (i) { + case SGL_PRIMITIVETYPE_POINTS: + desc.primitive_type = SG_PRIMITIVETYPE_POINTS; + break; + case SGL_PRIMITIVETYPE_LINES: + desc.primitive_type = SG_PRIMITIVETYPE_LINES; + break; + case SGL_PRIMITIVETYPE_LINE_STRIP: + desc.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP; + break; + case SGL_PRIMITIVETYPE_TRIANGLES: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES; + break; + case SGL_PRIMITIVETYPE_TRIANGLE_STRIP: + case SGL_PRIMITIVETYPE_QUADS: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP; + break; + } + if (SGL_PRIMITIVETYPE_QUADS == i) { + /* quads are emulated via triangles, use the same pipeline object */ + pip->pip[i] = pip->pip[SGL_PRIMITIVETYPE_TRIANGLES]; + } else { + pip->pip[i] = sg_make_pipeline(&desc); + if (pip->pip[i].id == SG_INVALID_ID) { + _SGL_ERROR(MAKE_PIPELINE_FAILED); + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } + } +} + +static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc, const sgl_context_desc_t* ctx_desc) { + SOKOL_ASSERT(desc && ctx_desc); + sgl_pipeline pip_id = _sgl_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sgl_init_pipeline(pip_id, desc, ctx_desc); + } else { + _SGL_ERROR(PIPELINE_POOL_EXHAUSTED); + } + return pip_id; +} + +static void _sgl_destroy_pipeline(sgl_pipeline pip_id) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + sg_push_debug_group("sokol-gl"); + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + if (i != SGL_PRIMITIVETYPE_QUADS) { + sg_destroy_pipeline(pip->pip[i]); + } + } + sg_pop_debug_group(); + _sgl_reset_pipeline(pip); + _sgl_pool_free_index(&_sgl.pip_pool.pool, _sgl_slot_index(pip_id.id)); + } +} + +static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t prim_type) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + return pip->pip[prim_type]; + } else { + sg_pipeline dummy_id = { SG_INVALID_ID }; + return dummy_id; + } +} + +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ ███████ +// +// >>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); + int slot_index = _sgl_slot_index(ctx_id); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.context_pool.pool.size)); + return &_sgl.context_pool.contexts[slot_index]; +} + +// get context pointer with id-check, returns 0 if no match +static _sgl_context_t* _sgl_lookup_context(uint32_t ctx_id) { + if (SG_INVALID_ID != ctx_id) { + _sgl_context_t* ctx = _sgl_context_at(ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +// make context id from uint32_t id +static sgl_context _sgl_make_ctx_id(uint32_t ctx_id) { + sgl_context ctx = { ctx_id }; + return ctx; +} + +static sgl_context _sgl_alloc_context(void) { + sgl_context res; + int slot_index = _sgl_pool_alloc_index(&_sgl.context_pool.pool); + if (_SGL_INVALID_SLOT_INDEX != slot_index) { + res = _sgl_make_ctx_id(_sgl_slot_alloc(&_sgl.context_pool.pool, &_sgl.context_pool.contexts[slot_index].slot, slot_index)); + } else { + // pool is exhausted + res = _sgl_make_ctx_id(SG_INVALID_ID); + } + return res; +} + +// return sgl_context_desc_t with patched defaults +static sgl_context_desc_t _sgl_context_desc_defaults(const sgl_context_desc_t* desc) { + sgl_context_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); + return res; +} + +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_view = _sgl.def_view; + ctx->cur_smp = _sgl.def_smp; + + // allocate buffers and pools + 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; + _sgl_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.size = (size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t); + vbuf_desc.usage.vertex_buffer = true; + vbuf_desc.usage.stream_update = true; + 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; + _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]); + } + ctx->pip_stack[0] = ctx->def_pip; + ctx->matrix_dirty = true; +} + +static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) { + SOKOL_ASSERT(desc); + sgl_context ctx_id = _sgl_alloc_context(); + if (ctx_id.id != SG_INVALID_ID) { + _sgl_init_context(ctx_id, desc); + } else { + _SGL_ERROR(CONTEXT_POOL_EXHAUSTED); + } + return ctx_id; +} + +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.ptr); + SOKOL_ASSERT(ctx->uniforms.ptr); + SOKOL_ASSERT(ctx->commands.ptr); + + _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); + _sgl_pool_free_index(&_sgl.context_pool.pool, _sgl_slot_index(ctx_id.id)); + } +} + +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc + +static sgl_error_t _sgl_error_defaults(void) { + sgl_error_t defaults; + _sgl_clear(&defaults, sizeof(defaults)); + return defaults; +} + +static int _sgl_num_vertices(_sgl_context_t* ctx) { + return ctx->vertices.next; +} + +static int _sgl_num_commands(_sgl_context_t* ctx) { + return ctx->commands.next; +} + +static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) { + ctx->in_begin = true; + ctx->base_vertex = ctx->vertices.next; + ctx->quad_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->error = _sgl_error_defaults(); + ctx->layer_id = 0; + ctx->matrix_dirty = true; +} + +// 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.vertices_full = true; + ctx->error.any = true; + return 0; + } +} + +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.uniforms_full = true; + ctx->error.any = true; + return 0; + } +} + +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 _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.commands_full = true; + ctx->error.any = true; + return 0; + } +} + +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 float _sgl_clamp(float v, float lo, float hi) { + if (v < lo) return lo; + else if (v > hi) return hi; + else return v; +} + +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); + uint8_t a_u8 = (uint8_t) (_sgl_clamp(a, 0.0f, 1.0f) * 255.0f); + return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8); +} + +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 */ + if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->quad_vtx_count & 3) == 3)) { + /* for quads, before writing the last quad vertex, reuse + the first and third vertex to start the second triangle in the quad + */ + vtx = _sgl_next_vertex(ctx); + if (vtx) { *vtx = *(vtx - 3); } + vtx = _sgl_next_vertex(ctx); + if (vtx) { *vtx = *(vtx - 2); } + } + vtx = _sgl_next_vertex(ctx); + if (vtx) { + 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->quad_vtx_count++; +} + +static void _sgl_identity(_sgl_matrix_t* m) { + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + m->v[c][r] = (r == c) ? 1.0f : 0.0f; + } + } +} + +static void _sgl_transpose(_sgl_matrix_t* dst, const _sgl_matrix_t* m) { + SOKOL_ASSERT(dst != m); + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + dst->v[r][c] = m->v[c][r]; + } + } +} + +/* _sgl_rotate, _sgl_frustum, _sgl_ortho from MESA m_matric.c */ +static void _sgl_matmul4(_sgl_matrix_t* p, const _sgl_matrix_t* a, const _sgl_matrix_t* b) { + for (int r = 0; r < 4; r++) { + float ai0=a->v[0][r], ai1=a->v[1][r], ai2=a->v[2][r], ai3=a->v[3][r]; + p->v[0][r] = ai0*b->v[0][0] + ai1*b->v[0][1] + ai2*b->v[0][2] + ai3*b->v[0][3]; + p->v[1][r] = ai0*b->v[1][0] + ai1*b->v[1][1] + ai2*b->v[1][2] + ai3*b->v[1][3]; + p->v[2][r] = ai0*b->v[2][0] + ai1*b->v[2][1] + ai2*b->v[2][2] + ai3*b->v[2][3]; + p->v[3][r] = ai0*b->v[3][0] + ai1*b->v[3][1] + ai2*b->v[3][2] + ai3*b->v[3][3]; + } +} + +static void _sgl_mul(_sgl_matrix_t* dst, const _sgl_matrix_t* m) { + _sgl_matmul4(dst, dst, m); +} + +static void _sgl_rotate(_sgl_matrix_t* dst, float a, float x, float y, float z) { + + float s = sinf(a); + float c = cosf(a); + + float mag = sqrtf(x*x + y*y + z*z); + if (mag < 1.0e-4F) { + return; + } + x /= mag; + y /= mag; + z /= mag; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xy = x * y; + float yz = y * z; + float zx = z * x; + float xs = x * s; + float ys = y * s; + float zs = z * s; + float one_c = 1.0f - c; + + _sgl_matrix_t m; + m.v[0][0] = (one_c * xx) + c; + m.v[1][0] = (one_c * xy) - zs; + m.v[2][0] = (one_c * zx) + ys; + m.v[3][0] = 0.0f; + m.v[0][1] = (one_c * xy) + zs; + m.v[1][1] = (one_c * yy) + c; + m.v[2][1] = (one_c * yz) - xs; + m.v[3][1] = 0.0f; + m.v[0][2] = (one_c * zx) - ys; + m.v[1][2] = (one_c * yz) + xs; + m.v[2][2] = (one_c * zz) + c; + m.v[3][2] = 0.0f; + m.v[0][3] = 0.0f; + m.v[1][3] = 0.0f; + m.v[2][3] = 0.0f; + m.v[3][3] = 1.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_scale(_sgl_matrix_t* dst, float x, float y, float z) { + for (int r = 0; r < 4; r++) { + dst->v[0][r] *= x; + dst->v[1][r] *= y; + dst->v[2][r] *= z; + } +} + +static void _sgl_translate(_sgl_matrix_t* dst, float x, float y, float z) { + for (int r = 0; r < 4; r++) { + dst->v[3][r] = dst->v[0][r]*x + dst->v[1][r]*y + dst->v[2][r]*z + dst->v[3][r]; + } +} + +static void _sgl_frustum(_sgl_matrix_t* dst, float left, float right, float bottom, float top, float znear, float zfar) { + float x = (2.0f * znear) / (right - left); + float y = (2.0f * znear) / (top - bottom); + float a = (right + left) / (right - left); + float b = (top + bottom) / (top - bottom); + float c = -(zfar + znear) / (zfar - znear); + float d = -(2.0f * zfar * znear) / (zfar - znear); + _sgl_matrix_t m; + m.v[0][0] = x; m.v[0][1] = 0.0f; m.v[0][2] = 0.0f; m.v[0][3] = 0.0f; + m.v[1][0] = 0.0f; m.v[1][1] = y; m.v[1][2] = 0.0f; m.v[1][3] = 0.0f; + m.v[2][0] = a; m.v[2][1] = b; m.v[2][2] = c; m.v[2][3] = -1.0f; + m.v[3][0] = 0.0f; m.v[3][1] = 0.0f; m.v[3][2] = d; m.v[3][3] = 0.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_ortho(_sgl_matrix_t* dst, float left, float right, float bottom, float top, float znear, float zfar) { + _sgl_matrix_t m; + m.v[0][0] = 2.0f / (right - left); + m.v[1][0] = 0.0f; + m.v[2][0] = 0.0f; + m.v[3][0] = -(right + left) / (right - left); + m.v[0][1] = 0.0f; + m.v[1][1] = 2.0f / (top - bottom); + m.v[2][1] = 0.0f; + m.v[3][1] = -(top + bottom) / (top - bottom); + m.v[0][2] = 0.0f; + m.v[1][2] = 0.0f; + m.v[2][2] = -2.0f / (zfar - znear); + m.v[3][2] = -(zfar + znear) / (zfar - znear); + m.v[0][3] = 0.0f; + m.v[1][3] = 0.0f; + m.v[2][3] = 0.0f; + m.v[3][3] = 1.0f; + + _sgl_mul(dst, &m); +} + +/* _sgl_perspective, _sgl_lookat from Regal project.c */ +static void _sgl_perspective(_sgl_matrix_t* dst, float fovy, float aspect, float znear, float zfar) { + float sine = sinf(fovy / 2.0f); + float delta_z = zfar - znear; + if ((delta_z == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) { + return; + } + float cotan = cosf(fovy / 2.0f) / sine; + _sgl_matrix_t m; + _sgl_identity(&m); + m.v[0][0] = cotan / aspect; + m.v[1][1] = cotan; + m.v[2][2] = -(zfar + znear) / delta_z; + m.v[2][3] = -1.0f; + m.v[3][2] = -2.0f * znear * zfar / delta_z; + m.v[3][3] = 0.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_normalize(float v[3]) { + float r = sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (r == 0.0f) { + return; + } + v[0] /= r; + v[1] /= r; + v[2] /= r; +} + +static void _sgl_cross(float v1[3], float v2[3], float res[3]) { + res[0] = v1[1]*v2[2] - v1[2]*v2[1]; + res[1] = v1[2]*v2[0] - v1[0]*v2[2]; + res[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +static void _sgl_lookat(_sgl_matrix_t* dst, + float eye_x, float eye_y, float eye_z, + float center_x, float center_y, float center_z, + float up_x, float up_y, float up_z) +{ + float fwd[3], side[3], up[3]; + + fwd[0] = center_x - eye_x; fwd[1] = center_y - eye_y; fwd[2] = center_z - eye_z; + up[0] = up_x; up[1] = up_y; up[2] = up_z; + _sgl_normalize(fwd); + _sgl_cross(fwd, up, side); + _sgl_normalize(side); + _sgl_cross(side, fwd, up); + + _sgl_matrix_t m; + _sgl_identity(&m); + m.v[0][0] = side[0]; + m.v[1][0] = side[1]; + m.v[2][0] = side[2]; + m.v[0][1] = up[0]; + m.v[1][1] = up[1]; + m.v[2][1] = up[2]; + m.v[0][2] = -fwd[0]; + m.v[1][2] = -fwd[1]; + m.v[2][2] = -fwd[2]; + _sgl_mul(dst, &m); + _sgl_translate(dst, -eye_x, -eye_y, -eye_z); +} + +/* current top-of-stack projection matrix */ +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 _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 _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 _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) { + return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]]; +} + +static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + 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); + res.context_pool_size = _sgl_def(desc->context_pool_size, _SGL_DEFAULT_CONTEXT_POOL_SIZE); + res.pipeline_pool_size = _sgl_def(desc->pipeline_pool_size, _SGL_DEFAULT_PIPELINE_POOL_SIZE); + res.face_winding = _sgl_def(desc->face_winding, SG_FACEWINDING_CCW); + return res; +} + +// create resources which are shared between all contexts +static void _sgl_setup_common(void) { + sg_push_debug_group("sokol-gl"); + + uint32_t pixels[64]; + for (int i = 0; i < 64; i++) { + pixels[i] = 0xFFFFFFFF; + } + sg_image_desc img_desc; + _sgl_clear(&img_desc, sizeof(img_desc)); + img_desc.type = SG_IMAGETYPE_2D; + img_desc.width = 8; + img_desc.height = 8; + img_desc.num_mipmaps = 1; + img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + img_desc.data.mip_levels[0] = SG_RANGE(pixels); + img_desc.label = "sgl-default-texture"; + _sgl.def_img = sg_make_image(&img_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_img.id); + + sg_view_desc view_desc; + _sgl_clear(&view_desc, sizeof(view_desc)); + view_desc.texture.image = _sgl.def_img; + view_desc.label = "sgl-default-texture-view"; + _sgl.def_view = sg_make_view(&view_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_view.id); + + sg_sampler_desc smp_desc; + _sgl_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_NEAREST; + smp_desc.mag_filter = SG_FILTER_NEAREST; + smp_desc.label = "sgl-default-sampler"; + _sgl.def_smp = sg_make_sampler(&smp_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_smp.id); + + // one shader for all contexts + sg_shader_desc shd_desc; + _sgl_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].glsl_name = "position"; + shd_desc.attrs[1].glsl_name = "texcoord0"; + shd_desc.attrs[2].glsl_name = "color0"; + shd_desc.attrs[3].glsl_name = "psize"; + shd_desc.attrs[0].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[0].hlsl_sem_index = 0; + shd_desc.attrs[1].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[1].hlsl_sem_index = 1; + shd_desc.attrs[2].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[2].hlsl_sem_index = 2; + shd_desc.attrs[3].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[3].hlsl_sem_index = 3; + shd_desc.attrs[0].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[1].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[2].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[3].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX; + shd_desc.uniform_blocks[0].size = sizeof(_sgl_uniform_t); + shd_desc.uniform_blocks[0].hlsl_register_b_n = 0; + shd_desc.uniform_blocks[0].msl_buffer_n = 0; + shd_desc.uniform_blocks[0].wgsl_group0_binding_n = 0; + shd_desc.uniform_blocks[0].spirv_set0_binding_n = 0; + shd_desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "vs_params"; + shd_desc.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.uniform_blocks[0].glsl_uniforms[0].array_count = 8; + shd_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.views[0].texture.image_type = SG_IMAGETYPE_2D; + shd_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.views[0].texture.hlsl_register_t_n = 0; + shd_desc.views[0].texture.msl_texture_n = 0; + shd_desc.views[0].texture.wgsl_group1_binding_n = 0; + shd_desc.views[0].texture.spirv_set1_binding_n = 0; + shd_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.samplers[0].hlsl_register_s_n = 0; + shd_desc.samplers[0].msl_sampler_n = 0; + shd_desc.samplers[0].wgsl_group1_binding_n = 32; + shd_desc.samplers[0].spirv_set1_binding_n = 32; + shd_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.texture_sampler_pairs[0].view_slot = 0; + shd_desc.texture_sampler_pairs[0].sampler_slot = 0; + shd_desc.texture_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.label = "sgl-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vertex_func.source = (const char*)_sgl_vs_source_glsl410; + shd_desc.fragment_func.source = (const char*)_sgl_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vertex_func.source = (const char*)_sgl_vs_source_glsl300es; + shd_desc.fragment_func.source = (const char*)_sgl_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vertex_func.entry = "main0"; + shd_desc.fragment_func.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sgl_vs_bytecode_metal_macos); + shd_desc.fragment_func.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sgl_vs_bytecode_metal_ios); + shd_desc.fragment_func.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_ios); + break; + default: + shd_desc.vertex_func.source = (const char*)_sgl_vs_source_metal_sim; + shd_desc.fragment_func.source = (const char*)_sgl_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vertex_func.bytecode = SG_RANGE(_sgl_vs_bytecode_hlsl4); + shd_desc.fragment_func.bytecode = SG_RANGE(_sgl_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vertex_func.source = (const char*)_sgl_vs_source_wgsl; + shd_desc.fragment_func.source = (const char*)_sgl_fs_source_wgsl; + #elif defined(SOKOL_VULKAN) + shd_desc.vertex_func.bytecode = SG_RANGE(_sgl_vs_bytecode_spirv_vk); + shd_desc.vertex_func.entry = "main"; + shd_desc.fragment_func.bytecode = SG_RANGE(_sgl_fs_bytecode_spirv_vk); + shd_desc.fragment_func.entry = "main"; + #else + shd_desc.vertex_func.source = _sgl_vs_source_dummy; + shd_desc.fragment_func.source = _sgl_fs_source_dummy; + #endif + _sgl.shd = sg_make_shader(&shd_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.shd.id); + sg_pop_debug_group(); +} + +// discard resources which are shared between all contexts +static void _sgl_discard_common(void) { + sg_push_debug_group("sokol-gl"); + sg_destroy_view(_sgl.def_view); + sg_destroy_image(_sgl.def_img); + sg_destroy_sampler(_sgl.def_smp); + sg_destroy_shader(_sgl.shd); + sg_pop_debug_group(); +} + +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, int layer_id) { + SOKOL_ASSERT(ctx); + if ((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_tex_id = SG_INVALID_ID; + uint32_t cur_smp_id = SG_INVALID_ID; + int cur_uniform_index = -1; + + 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); + } + + // render all successfully recorded commands (this may be less than the + // issued commands if we're in an error state) + for (int i = 0; i < ctx->commands.next; i++) { + const _sgl_command_t* cmd = &ctx->commands.ptr[i]; + if (cmd->layer_id != layer_id) { + continue; + } + switch (cmd->cmd) { + case SGL_COMMAND_VIEWPORT: + { + const _sgl_viewport_args_t* args = &cmd->args.viewport; + sg_apply_viewport(args->x, args->y, args->w, args->h, args->origin_top_left); + } + break; + case SGL_COMMAND_SCISSOR_RECT: + { + const _sgl_scissor_rect_args_t* args = &cmd->args.scissor_rect; + sg_apply_scissor_rect(args->x, args->y, args->w, args->h, args->origin_top_left); + } + break; + case SGL_COMMAND_DRAW: + { + const _sgl_draw_args_t* args = &cmd->args.draw; + if (args->pip.id != cur_pip_id) { + sg_apply_pipeline(args->pip); + cur_pip_id = args->pip.id; + // when pipeline changes, also need to re-apply uniforms and bindings + cur_tex_id = SG_INVALID_ID; + cur_smp_id = SG_INVALID_ID; + cur_uniform_index = -1; + } + if ((cur_tex_id != args->view.id) || (cur_smp_id != args->smp.id)) { + ctx->bind.views[0] = args->view; + ctx->bind.samplers[0] = args->smp; + sg_apply_bindings(&ctx->bind); + cur_tex_id = args->view.id; + cur_smp_id = args->smp.id; + } + if (cur_uniform_index != args->uniform_index) { + const sg_range ub_range = { &ctx->uniforms.ptr[args->uniform_index], sizeof(_sgl_uniform_t) }; + sg_apply_uniforms(0, &ub_range); + cur_uniform_index = args->uniform_index; + } + // FIXME: what if number of vertices doesn't match the primitive type? + if (args->num_vertices > 0) { + sg_draw(args->base_vertex, args->num_vertices, 1); + } + } + break; + } + } + sg_pop_debug_group(); + } +} + +static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) { + sgl_context_desc_t 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; + ctx_desc.depth_format = desc->depth_format; + ctx_desc.sample_count = desc->sample_count; + return ctx_desc; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { + SOKOL_ASSERT(desc); + _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); + _sgl_setup_context_pool(_sgl.desc.context_pool_size); + _sgl_setup_common(); + const sgl_context_desc_t ctx_desc = _sgl_as_context_desc(&_sgl.desc); + _sgl.def_ctx_id = sgl_make_context(&ctx_desc); + SOKOL_ASSERT(SGL_DEFAULT_CONTEXT.id == _sgl.def_ctx_id.id); + sgl_set_context(_sgl.def_ctx_id); +} + +SOKOL_API_IMPL void sgl_shutdown(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + // contexts own a pipeline, so destroy contexts before pipelines + for (int i = 0; i < _sgl.context_pool.pool.size; i++) { + _sgl_context_t* ctx = &_sgl.context_pool.contexts[i]; + _sgl_destroy_context(_sgl_make_ctx_id(ctx->slot.id)); + } + for (int i = 0; i < _sgl.pip_pool.pool.size; i++) { + _sgl_pipeline_t* pip = &_sgl.pip_pool.pips[i]; + _sgl_destroy_pipeline(_sgl_make_pip_id(pip->slot.id)); + } + _sgl_discard_context_pool(); + _sgl_discard_pipeline_pool(); + _sgl_discard_common(); + _sgl.init_cookie = 0; +} + +SOKOL_API_IMPL sgl_error_t sgl_error(void) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return ctx->error; + } else { + sgl_error_t err = _sgl_error_defaults(); + err.no_context = true; + err.any = true; + return err; + } +} + +SOKOL_API_IMPL sgl_error_t sgl_context_error(sgl_context ctx_id) { + const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + return ctx->error; + } else { + sgl_error_t err = _sgl_error_defaults(); + err.no_context = true; + err.any = true; + return err; + } +} + +SOKOL_API_IMPL float sgl_rad(float deg) { + return (deg * (float)M_PI) / 180.0f; +} + +SOKOL_API_IMPL float sgl_deg(float rad) { + return (rad * 180.0f) / (float)M_PI; +} + +SOKOL_API_IMPL sgl_context sgl_make_context(const sgl_context_desc_t* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + return _sgl_make_context(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)) { + _SGL_WARN(CANNOT_DESTROY_DEFAULT_CONTEXT); + return; + } + _sgl_destroy_context(ctx_id); + // re-validate the current context pointer (this will return a nullptr + // if we just destroyed the current context) + _sgl.cur_ctx = _sgl_lookup_context(_sgl.cur_ctx_id.id); +} + +SOKOL_API_IMPL void sgl_set_context(sgl_context ctx_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl_is_default_context(ctx_id)) { + _sgl.cur_ctx_id = _sgl.def_ctx_id; + } else { + _sgl.cur_ctx_id = ctx_id; + } + // this will return null if the handle isn't valid + _sgl.cur_ctx = _sgl_lookup_context(_sgl.cur_ctx_id.id); +} + +SOKOL_API_IMPL sgl_context sgl_get_context(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + return _sgl.cur_ctx_id; +} + +SOKOL_API_IMPL sgl_context sgl_default_context(void) { + return SGL_DEFAULT_CONTEXT; +} + +SOKOL_API_IMPL int sgl_num_vertices(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return _sgl_num_vertices(ctx); + } else { + return 0; + } +} + +SOKOL_API_IMPL int sgl_num_commands(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return _sgl_num_commands(ctx); + } else { + return 0; + } +} + +SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return _sgl_make_pipeline(desc, &ctx->desc); + } else { + return _sgl_make_pip_id(SG_INVALID_ID); + } +} + +SOKOL_API_IMPL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + return _sgl_make_pipeline(desc, &ctx->desc); + } else { + return _sgl_make_pip_id(SG_INVALID_ID); + } +} + +SOKOL_API_IMPL void sgl_destroy_pipeline(sgl_pipeline pip_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_destroy_pipeline(pip_id); +} + +SOKOL_API_IMPL void sgl_load_pipeline(sgl_pipeline pip_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->pip_tos >= 0) && (ctx->pip_tos < _SGL_MAX_STACK_DEPTH)); + ctx->pip_stack[ctx->pip_tos] = pip_id; +} + +SOKOL_API_IMPL void sgl_load_default_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->pip_tos >= 0) && (ctx->pip_tos < _SGL_MAX_STACK_DEPTH)); + ctx->pip_stack[ctx->pip_tos] = ctx->def_pip; +} + +SOKOL_API_IMPL void sgl_push_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + if (ctx->pip_tos < (_SGL_MAX_STACK_DEPTH - 1)) { + ctx->pip_tos++; + ctx->pip_stack[ctx->pip_tos] = ctx->pip_stack[ctx->pip_tos-1]; + } else { + ctx->error.stack_overflow = true; + ctx->error.any = true; + } +} + +SOKOL_API_IMPL void sgl_pop_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + if (ctx->pip_tos > 0) { + ctx->pip_tos--; + } else { + ctx->error.stack_underflow = true; + ctx->error.any = true; + } +} + +SOKOL_API_IMPL void sgl_defaults(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + 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_view = _sgl.def_view; + ctx->cur_smp = _sgl.def_smp; + sgl_load_default_pipeline(); + _sgl_identity(_sgl_matrix_texture(ctx)); + _sgl_identity(_sgl_matrix_modelview(ctx)); + _sgl_identity(_sgl_matrix_projection(ctx)); + ctx->cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW; + 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; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _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; + cmd->args.viewport.h = h; + cmd->args.viewport.origin_top_left = origin_top_left; + } +} + +SOKOL_API_IMPL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left) { + sgl_viewport((int)x, (int)y, (int)w, (int)h, origin_top_left); +} + +SOKOL_API_IMPL void sgl_scissor_rect(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; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _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; + cmd->args.scissor_rect.h = h; + cmd->args.scissor_rect.origin_top_left = origin_top_left; + } +} + +SOKOL_API_IMPL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left) { + sgl_scissor_rect((int)x, (int)y, (int)w, (int)h, origin_top_left); +} + +SOKOL_API_IMPL void sgl_enable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->texturing_enabled = true; +} + +SOKOL_API_IMPL void sgl_disable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->texturing_enabled = false; +} + +SOKOL_API_IMPL void sgl_texture(sg_view tex_view, sg_sampler smp) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + if (SG_INVALID_ID != tex_view.id) { + ctx->cur_view = tex_view; + } else { + ctx->cur_view = _sgl.def_view; + } + if (SG_INVALID_ID != smp.id) { + ctx->cur_smp = smp; + } else { + ctx->cur_smp = _sgl.def_smp; + } +} + +SOKOL_API_IMPL void sgl_begin_points(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_POINTS); +} + +SOKOL_API_IMPL void sgl_begin_lines(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_LINES); +} + +SOKOL_API_IMPL void sgl_begin_line_strip(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_LINE_STRIP); +} + +SOKOL_API_IMPL void sgl_begin_triangles(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_TRIANGLES); +} + +SOKOL_API_IMPL void sgl_begin_triangle_strip(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_TRIANGLE_STRIP); +} + +SOKOL_API_IMPL void sgl_begin_quads(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_QUADS); +} + +SOKOL_API_IMPL void sgl_end(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(ctx->in_begin); + SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex); + ctx->in_begin = false; + + bool matrix_dirty = ctx->matrix_dirty; + if (matrix_dirty) { + ctx->matrix_dirty = false; + _sgl_uniform_t* uni = _sgl_next_uniform(ctx); + if (uni) { + _sgl_matmul4(&uni->mvp, _sgl_matrix_projection(ctx), _sgl_matrix_modelview(ctx)); + uni->tm = *_sgl_matrix_texture(ctx); + } + } + + // don't record any new commands when we're in an error state + if (ctx->error.any) { + return; + } + + // check if command can be merged with current command + sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); + sg_view view = ctx->texturing_enabled ? ctx->cur_view : _sgl.def_view; + sg_sampler smp = ctx->texturing_enabled ? ctx->cur_smp : _sgl.def_smp; + _sgl_command_t* cur_cmd = _sgl_cur_command(ctx); + bool merge_cmd = false; + 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 && + (cur_cmd->args.draw.view.id == view.id) && + (cur_cmd->args.draw.smp.id == smp.id) && + (cur_cmd->args.draw.pip.id == pip.id)) + { + merge_cmd = true; + } + } + if (merge_cmd) { + // draw command can be merged with the previous command + 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->uniforms.next > 0); + cmd->cmd = SGL_COMMAND_DRAW; + cmd->layer_id = ctx->layer_id; + cmd->args.draw.view = view; + cmd->args.draw.smp = smp; + 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->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) { + ctx->u = u; + ctx->v = v; + } +} + +SOKOL_API_IMPL void sgl_c3f(float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbaf(r, g, b, 1.0f); + } +} + +SOKOL_API_IMPL void sgl_c4f(float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbaf(r, g, b, a); + } +} + +SOKOL_API_IMPL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbab(r, g, b, 255); + } +} + +SOKOL_API_IMPL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbab(r, g, b, a); + } +} + +SOKOL_API_IMPL void sgl_c1i(uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = rgba; + } +} + +SOKOL_API_IMPL void sgl_v2f(float x, float y) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f(float x, float y, float z) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f(float x, float y, float u, float v) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f(float x, float y, float z, float u, float v) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v2f_c3f(float x, float y, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c1i(float x, float y, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, rgba); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx,x, y, z, u, v, rgba); + } +} + +SOKOL_API_IMPL void sgl_matrix_mode_modelview(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW; + } +} + +SOKOL_API_IMPL void sgl_matrix_mode_projection(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->cur_matrix_mode = SGL_MATRIXMODE_PROJECTION; + } +} + +SOKOL_API_IMPL void sgl_matrix_mode_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->cur_matrix_mode = SGL_MATRIXMODE_TEXTURE; + } +} + +SOKOL_API_IMPL void sgl_load_identity(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_identity(_sgl_matrix(ctx)); +} + +SOKOL_API_IMPL void sgl_load_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + memcpy(&_sgl_matrix(ctx)->v[0][0], &m[0], 64); +} + +SOKOL_API_IMPL void sgl_load_transpose_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_transpose(_sgl_matrix(ctx), (const _sgl_matrix_t*) &m[0]); +} + +SOKOL_API_IMPL void sgl_mult_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + const _sgl_matrix_t* m0 = (const _sgl_matrix_t*) &m[0]; + _sgl_mul(_sgl_matrix(ctx), m0); +} + +SOKOL_API_IMPL void sgl_mult_transpose_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_matrix_t m0; + _sgl_transpose(&m0, (const _sgl_matrix_t*) &m[0]); + _sgl_mul(_sgl_matrix(ctx), &m0); +} + +SOKOL_API_IMPL void sgl_rotate(float angle_rad, float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_rotate(_sgl_matrix(ctx), angle_rad, x, y, z); +} + +SOKOL_API_IMPL void sgl_scale(float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_scale(_sgl_matrix(ctx), x, y, z); +} + +SOKOL_API_IMPL void sgl_translate(float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_translate(_sgl_matrix(ctx), x, y, z); +} + +SOKOL_API_IMPL void sgl_frustum(float l, float r, float b, float t, float n, float f) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_frustum(_sgl_matrix(ctx), l, r, b, t, n, f); +} + +SOKOL_API_IMPL void sgl_ortho(float l, float r, float b, float t, float n, float f) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_ortho(_sgl_matrix(ctx), l, r, b, t, n, f); +} + +SOKOL_API_IMPL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_perspective(_sgl_matrix(ctx), fov_y, aspect, z_near, z_far); +} + +SOKOL_API_IMPL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_lookat(_sgl_matrix(ctx), eye_x, eye_y, eye_z, center_x, center_y, center_z, up_x, up_y, up_z); +} + +SOKOL_GL_API_DECL void sgl_push_matrix(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->cur_matrix_mode >= 0) && (ctx->cur_matrix_mode < SGL_NUM_MATRIXMODES)); + ctx->matrix_dirty = true; + if (ctx->matrix_tos[ctx->cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) { + const _sgl_matrix_t* src = _sgl_matrix(ctx); + ctx->matrix_tos[ctx->cur_matrix_mode]++; + _sgl_matrix_t* dst = _sgl_matrix(ctx); + *dst = *src; + } else { + ctx->error.stack_overflow = true; + ctx->error.any = true; + } +} + +SOKOL_GL_API_DECL void sgl_pop_matrix(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->cur_matrix_mode >= 0) && (ctx->cur_matrix_mode < SGL_NUM_MATRIXMODES)); + ctx->matrix_dirty = true; + if (ctx->matrix_tos[ctx->cur_matrix_mode] > 0) { + ctx->matrix_tos[ctx->cur_matrix_mode]--; + } else { + ctx->error.stack_underflow = true; + ctx->error.any = true; + } +} + +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, 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); + } +} + +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, 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); + } +} + +#endif /* SOKOL_GL_IMPL */ diff --git a/thirdparty/sokol/util/sokol_imgui.h b/thirdparty/sokol/util/sokol_imgui.h new file mode 100644 index 0000000..2f3b3c3 --- /dev/null +++ b/thirdparty/sokol/util/sokol_imgui.h @@ -0,0 +1,3619 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_IMGUI_IMPL) +#define SOKOL_IMGUI_IMPL +#endif +#ifndef SOKOL_IMGUI_INCLUDED +/* + sokol_imgui.h -- drop-in Dear ImGui renderer/event-handler for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_IMGUI_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + NOTE that the implementation can be compiled either as C++ or as C. + When compiled as C++, sokol_imgui.h will directly call into the + Dear ImGui C++ API. When compiled as C, sokol_imgui.h will call + cimgui.h functions instead. + + NOTE that the formerly separate header sokol_cimgui.h has been + merged into sokol_imgui.h + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + SOKOL_VULKAN + + Optionally provide the following configuration define both before including the + the declaration and implementation: + + SOKOL_IMGUI_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details) + + Optionally provide the following macros before including the implementation + to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + 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: -) + + If sokol_imgui.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_IMGUI_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before sokol_imgui.h (both before including + the declaration and implementation): + + sokol_gfx.h + sokol_app.h (except SOKOL_IMGUI_NO_SOKOL_APP) + + Additionally, include the following headers before including the + implementation: + + If the implementation is compiled as C++: + imgui.h + + If the implementation is compiled as C: + cimgui.h + + When compiling as C, you can override the Dear ImGui C bindings prefix + via the define SOKOL_IMGUI_CPREFIX before including the sokol_imgui.h + implementation: + + #define SOKOL_IMGUI_IMPL + #define SOKOL_IMGUI_CPREFIX ImGui_ + #include "sokol_imgui.h" + + Note that the default prefix is 'ig'. + + + FEATURE OVERVIEW: + ================= + sokol_imgui.h implements the initialization, rendering and event-handling + code for Dear ImGui (https://github.com/ocornut/imgui) on top of + sokol_gfx.h and (optionally) sokol_app.h. + + The sokol_app.h dependency is optional and used for input event handling. + If you only use sokol_gfx.h but not sokol_app.h in your application, + define SOKOL_IMGUI_NO_SOKOL_APP before including the implementation + of sokol_imgui.h, this will remove any dependency to sokol_app.h, but + you must feed input events into Dear ImGui yourself. + + sokol_imgui.h is not thread-safe, all calls must be made from the + same thread where sokol_gfx.h is running. + + HOWTO: + ====== + + --- To initialize sokol-imgui, call: + + simgui_setup(const simgui_desc_t* desc) + + This will initialize Dear ImGui and create sokol-gfx resources + (two buffers for vertices and indices, a font texture and a pipeline- + state-object). + + Use the following simgui_desc_t members to configure behaviour: + + int max_vertices + The maximum number of vertices used for UI rendering, default is 65536. + sokol-imgui will use this to compute the size of the vertex- + and index-buffers allocated via sokol_gfx.h + + sg_pixel_format color_format + The color pixel format of the render pass where the UI + 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 (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 (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 + and load UI persistency data. By default this is 0, so that Dear ImGui + will not preserve state between sessions (and also won't do + any filesystem calls). Also see the ImGui functions: + - LoadIniSettingsFromMemory() + - SaveIniSettingsFromMemory() + These functions give you explicit control over loading and saving + UI state while using your own filesystem wrapper functions (in this + case keep simgui_desc.ini_filename zero) + + bool no_default_font + Set this to true if you don't want to use ImGui's default + 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. + + simgui_logger_t logger + A user-provided logging callback. Note that without logging + callback, sokol-imgui will be completely silent! + See the section about ERROR REPORTING AND LOGGING below + for more details. + + --- At the start of a frame, call: + + 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. + + '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. + + 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: + + simgui_render() + + This will first call ImGui::Render(), and then render ImGui's draw list + through sokol_gfx.h + + --- if you're using sokol_app.h, from inside the sokol_app.h event callback, + call: + + bool simgui_handle_event(const sapp_event* ev); + + The return value is the value of ImGui::GetIO().WantCaptureKeyboard, + 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() + + ON ATTACHING YOUR OWN FONTS + =========================== + Since Dear ImGui 1.92.0 using non-default fonts has been greatly simplified: + + First, call `simgui_setup()` with the `.no_default_font` so that + sokol_imgui.h skips adding the default font. + + ...then simply call `AddFontDefault()` or `AddFontFromMemoryTTF()` on + the Dear ImGui IO object, everything else is taken care of automatically. + + Specifically, do *NOT*: + - call the deprecated `GetTexDataAsRGBA32()` function + - create a sokol-gfx image object for the font atlas + - set the `Font->TexID` on the ImGui IO object + + All those things are now handled inside sokol_imgui.h via a new 'texture update' + callback which is called by Dear ImGui whenever the state of the font atlas + texture changes. + + ON USER-PROVIDED IMAGES AND SAMPLERS + ==================================== + To render your own images via ImGui::Image() you need to create a Dear ImGui + compatible texture handle (ImTextureID) from a sokol-gfx texture view handle + or optionally a texture view handle and a compatible sampler handle. + + To create a ImTextureID from a sokol-gfx image handle, call: + + sg_view tex_view = sg_make_view(&(sg_view_desc){ .texture_binding.image = img }); + ImTextureID imtex_id = simgui_imtextureid(tex_view); + + Since no sampler is provided, such a texture handle will use a default + sampler with nearest filtering and clamp-to-edge. + + If you need to render with a different sampler, do this instead: + + sg_view tex_view = ...; + sg_sampler smp = ...; + ImTextureID imtex_id = simgui_imtextureid_with_sampler(tex_img, smp); + + You don't need to 'release' the ImTextureID handle, the ImTextureID + bits is simply a combination of the sg_view and sg_sampler bits. + + Once you have constructed an ImTextureID handle via simgui_imtextureid() + or simgui_imtextureid_with_sampler(), it used in the ImGui::Image() + call like this: + + ImGui::Image(imtex_id, ...); + + To extract the sg_view and sg_sampler handle from an ImTextureID: + + sg_view tex_view = simgui_texture_view_from_imtextureid(imtex_id); + sg_sampler smp = simgui_sampler_from_imtextureid(imtex_id); + + ...use the sokol-gfx function sg_query_view_image() if you need to + extract the texture view's image object: + + sg_image img = sg_query_view_image(tex_view); + + NOTE on C bindings since Dear ImGui 1.92.0: + + Since Dear ImGui v1.92.0 the ImGui::Image function takes an + ImTextureRef object instead of ImTextureID. In C++ this doesn't + require a code change since the ImTextureRef is automatically constructed + from the ImTextureID. + + In C this doesn't work and you need to explicitly create an + ImTextureRef struct, for instance: + + igImage((ImTextureRef){ ._TexID = my_tex_id }, ...); + + Currently Dear Bindings is missing a wrapper function for this, + also see: https://github.com/dearimgui/dear_bindings/issues/99 + + + 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_fn = my_alloc, + .free_fn = 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. + + + 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" + + simgui_setup(&(simgui_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. 'simgui' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SIMGUI_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-imgui like this: + + simgui_setup(&(simgui_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. + + + IMGUI EVENT HANDLING + ==================== + You can call these functions from your platform's events to handle ImGui events + when SOKOL_IMGUI_NO_SOKOL_APP is defined. + + E.g. mouse position events can be dispatched like this: + + simgui_add_mouse_pos_event(100, 200); + + For adding key events, you're responsible to map your own key codes to ImGuiKey + values and pass those as int: + + simgui_add_key_event(imgui_key, true); + + Take note that modifiers (shift, ctrl, etc.) must be updated manually. + + If sokol_app is being used, ImGui events are handled for you. + + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 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_IMGUI_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_imgui.h" +#endif +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED) +#error "Please include sokol_app.h before sokol_imgui.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_IMGUI_API_DECL) +#define SOKOL_IMGUI_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_IMGUI_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMGUI_IMPL) +#define SOKOL_IMGUI_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_IMGUI_API_DECL __declspec(dllimport) +#else +#define SOKOL_IMGUI_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + simgui_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SIMGUI_LOG_ITEMS \ + _SIMGUI_LOGITEM_XMACRO(OK, "Ok") \ + _SIMGUI_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SIMGUI_LOGITEM_XMACRO(BUFFER_OVERFLOW, "internal vertex/index buffer overflow (increase simgui_desc_t.max_vertices)") + +#define _SIMGUI_LOGITEM_XMACRO(item,msg) SIMGUI_LOGITEM_##item, +typedef enum simgui_log_item_t { + _SIMGUI_LOG_ITEMS +} simgui_log_item_t; +#undef _SIMGUI_LOGITEM_XMACRO + +/* + 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_fn and free_fn 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_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} simgui_allocator_t; + +/* + simgui_logger + + Used in simgui_desc_t to provide a logging function. Please be aware + that without logging function, sokol-imgui 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 simgui_logger_t { + void (*func)( + const char* tag, // always "simgui" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SIMGUI_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} simgui_logger_t; + +typedef struct simgui_desc_t { + int max_vertices; // default: 65536 + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + const char* ini_filename; + bool no_default_font; + 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_logger_t logger; // optional log function override +} simgui_desc_t; + +typedef struct simgui_frame_desc_t { + int width; + int height; + double delta_time; + float dpi_scale; +} simgui_frame_desc_t; + +typedef struct simgui_font_tex_desc_t { + sg_filter min_filter; + sg_filter mag_filter; +} simgui_font_tex_desc_t; + +SOKOL_IMGUI_API_DECL void simgui_setup(const simgui_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_new_frame(const simgui_frame_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_render(void); + +SOKOL_IMGUI_API_DECL uint64_t simgui_imtextureid(sg_view tex_view); +SOKOL_IMGUI_API_DECL uint64_t simgui_imtextureid_with_sampler(sg_view tex_view, sg_sampler smp); +SOKOL_IMGUI_API_DECL sg_view simgui_texture_view_from_imtextureid(uint64_t imtex_id); +SOKOL_IMGUI_API_DECL sg_sampler simgui_sampler_from_imtextureid(uint64_t imtex_id); + +SOKOL_IMGUI_API_DECL void simgui_add_focus_event(bool focus); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_pos_event(float x, float y); +SOKOL_IMGUI_API_DECL void simgui_add_touch_pos_event(float x, float y); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_button_event(int mouse_button, bool down); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_wheel_event(float wheel_x, float wheel_y); +SOKOL_IMGUI_API_DECL void simgui_add_key_event(int imgui_key, bool down); +SOKOL_IMGUI_API_DECL void simgui_add_input_character(uint32_t c); +SOKOL_IMGUI_API_DECL void simgui_add_input_characters_utf8(const char* c); +SOKOL_IMGUI_API_DECL void simgui_add_touch_button_event(int mouse_button, bool down); + +#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); + +#ifdef __cplusplus +} // extern "C" + +// 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 ------------------------------------------------------------ +#ifdef SOKOL_IMGUI_IMPL +#define SOKOL_IMGUI_IMPL_INCLUDED (1) + +#ifndef SOKOL_IMGUI_CPREFIX +#define SOKOL_IMGUI_CPREFIX ig +#endif +#define _SIMGUI_CONCAT2(prefix, name) prefix ## name +#define _SIMGUI_CONCAT(prefix, name) _SIMGUI_CONCAT2(prefix, name) +#define _SIMGUI_CFUNC(name) _SIMGUI_CONCAT(SOKOL_IMGUI_CPREFIX, name) + +#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" + #endif +#else + #if !defined(CIMGUI_API) + #error "Please include cimgui.h before the sokol_imgui.h implementation" + #endif +#endif + +#include // memset +#include // malloc/free + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +#include +#endif + +#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 + +#define _SIMGUI_INIT_COOKIE (0xBABEBABE) + +// helper macros and constants +#define _simgui_def(val, def) (((val) == 0) ? (def) : (val)) + +// collisions with X11 headers +#if defined(Status) +#undef Status +#endif + +typedef struct { + ImVec2 disp_size; + uint8_t _pad_8[8]; +} _simgui_vs_params_t; + +typedef struct { + uint32_t init_cookie; + simgui_desc_t desc; + float cur_dpi_scale; + sg_buffer vbuf; + sg_buffer ibuf; + sg_sampler def_smp; // used as default sampler for user images + sg_shader def_shd; + sg_pipeline def_pip; + // separate shader and pipeline for unfilterable user images + sg_shader shd_unfilterable; + sg_pipeline pip_unfilterable; + sg_range vertices; + sg_range indices; + bool is_osx; +} _simgui_state_t; +static _simgui_state_t _simgui; + +/* + Embedded source code compiled with: + + sokol-shdc -i simgui.glsl -o simgui.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + layout(binding=0) uniform vs_params { + vec2 disp_size; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(((position/disp_size)-0.5)*vec2(2.0,-2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + layout(binding=0) uniform texture2D tex; + layout(binding=0) uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + @end + + @program simgui vs fs +*/ +#if defined(SOKOL_GLCORE) +/* + #version 410 + + uniform vec4 vs_params[1]; + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(((position / vs_params[0].xy) - vec2(0.5)) * vec2(2.0, -2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _simgui_vs_source_glsl410[383] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,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,0x31,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,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,0x76,0x65,0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30, + 0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,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, +}; +/* + #version 410 + + uniform sampler2D tex_smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv) * color; + } +*/ +static const uint8_t _simgui_fs_source_glsl410[219] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,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,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 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, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +/* + #version 300 es + + uniform vec4 vs_params[1]; + layout(location = 0) in vec2 position; + out vec2 uv; + layout(location = 1) in vec2 texcoord0; + out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(((position / vs_params[0].xy) - vec2(0.5)) * vec2(2.0, -2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _simgui_vs_source_glsl300es[344] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x31,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x76,0x65, + 0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20, + 0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c, + 0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,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, +}; +/* + #version 300 es + precision mediump float; + precision highp int; + + uniform highp sampler2D tex_smp; + + layout(location = 0) out highp vec4 frag_color; + in highp vec2 uv; + in highp vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv) * color; + } +*/ +static const uint8_t _simgui_fs_source_glsl300es[250] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float2 disp_size; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _22 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = float4(((in.position / _22.disp_size) - float2(0.5)) * float2(2.0, -2.0), 0.5, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _simgui_vs_bytecode_metal_macos[3116] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,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,0x6c,0xc6,0xce,0x18,0x4d,0xb5,0xf9, + 0xae,0x57,0x9c,0x34,0x76,0x94,0x96,0x17,0x9a,0x2d,0xca,0x04,0xbe,0xda,0x39,0x87, + 0xd4,0x9d,0x6e,0x8c,0x73,0xe8,0x15,0x57,0xa2,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,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,0x04,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,0x08,0x0b,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xbf,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x68,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09, + 0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14, + 0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30, + 0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01, + 0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98, + 0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19, + 0x01,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,0x39,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,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01, + 0x20,0x1d,0x4b,0x78,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe9,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4, + 0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x46,0x06, + 0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x26,0x65,0x88,0x80,0x10,0x43,0x8c, + 0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e,0x24,0x48, + 0x82,0x44,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, + 0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5, + 0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42,0x71, + 0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16, + 0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44,0xc8, + 0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd, + 0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05,0xd1, + 0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c,0x4d, + 0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58, + 0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19, + 0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34, + 0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14, + 0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x44,0x40,0x3c,0xe4,0x4b, + 0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86,0x37, + 0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4,0x43,0xbe,0x24,0x48,0x02,0x04, + 0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43, + 0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99,0x90, + 0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d, + 0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a, + 0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31, + 0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x26,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86,0x44,0x40,0x24,0x64,0x0d,0x18, + 0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0xcd,0xa5,0xe9, + 0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x0a, + 0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63,0x62,0x37,0xf7,0x05,0x17,0x26, + 0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19,0x24,0x06,0x72,0x06,0x08,0x1a, + 0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8,0x01,0xc2,0x06,0x48,0x1b,0x24, + 0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13,0x02,0x07,0x43,0x10,0x44,0x0c, + 0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01,0x20,0x1d,0x22,0x07,0x7c,0xde, + 0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40,0xc6,0xd0,0xc2,0xe4,0xf8,0x4c, + 0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80,0x50,0x09,0x05,0x05,0x0d,0x11, + 0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72,0x0c,0x31,0x90,0x3b,0x40,0xee, + 0x60,0x39,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,0x19,0x14,0xc6,0x19,0xa1, + 0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e,0x4c, + 0x09,0xe6,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4, + 0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3, + 0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87, + 0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f, + 0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5, + 0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec, + 0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21, + 0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03, + 0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07, + 0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e, + 0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08, + 0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07, + 0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94, + 0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30, + 0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73, + 0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46, + 0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a, + 0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c, + 0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv) * in.color; + return out; + } +*/ +static const uint8_t _simgui_fs_bytecode_metal_macos[3017] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,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,0xfe,0x1f,0x5f,0xc4,0x3f,0x2d,0xaa, + 0xe1,0x41,0x06,0x1f,0xeb,0x88,0x3c,0x97,0xdd,0x86,0x1d,0xfa,0x9c,0xc7,0x3f,0xac, + 0x4b,0x2b,0x8c,0xa0,0x89,0xf7,0x13,0x77,0xee,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,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,0xdc,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb4,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,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,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,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,0x49,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,0xe1,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xd2,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,0x46,0x06,0x46,0x66,0xc6, + 0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06, + 0x46,0x66,0xc6,0x65,0x66,0xa6,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,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9, + 0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d, + 0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6, + 0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2, + 0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96, + 0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86, + 0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e, + 0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58, + 0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84, + 0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c, + 0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b, + 0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c, + 0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78, + 0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0x01,0xaf,0xb0,0x34,0xb9,0x96,0x30,0xb6, + 0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2,0x39,0x94,0xb6,0xb0,0x34,0x37,0x98, + 0x94,0x21,0xc4,0xd3,0x06,0x0f,0x1b,0x10,0x0b,0x4b,0x93,0x6b,0x09,0x63,0x4b,0x0b, + 0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x6b,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x9b, + 0x1b,0x62,0x3c,0x6f,0xf0,0xb4,0xc1,0xe3,0x06,0xc4,0xc2,0xd2,0xe4,0x5a,0xc2,0xd8, + 0xd2,0xc2,0xe6,0x5a,0xe6,0xc6,0xde,0xe0,0xca,0x5a,0xe6,0xc2,0xda,0xe0,0xd8,0xca, + 0xe4,0xe6,0x86,0x18,0x4f,0x1c,0x3c,0x6d,0xf0,0xc0,0xc1,0x10,0xe2,0x79,0x83,0x27, + 0x0e,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,0x19,0x14,0xc6,0x19,0xc1,0x84, + 0x43,0x3a,0xc8,0x83,0x1b,0x98,0x83,0x3c,0x84,0xc3,0x39,0xb4,0x43,0x39,0xb8,0x03, + 0x3d,0x4c,0x09,0xd6,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8, + 0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08, + 0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20, + 0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e, + 0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2, + 0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4, + 0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8, + 0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08, + 0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10, + 0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30, + 0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78, + 0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,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,0x11,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00, + 0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00,0x00,0x23,0x06,0xca, + 0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c, + 0x02,0x62,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x06,0xe0,0x90,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float2 disp_size; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _22 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = float4(((in.position / _22.disp_size) - float2(0.5)) * float2(2.0, -2.0), 0.5, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + returnout; + } +*/ +static const uint8_t _simgui_vs_bytecode_metal_ios[3212] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x8c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x80,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,0x3f,0x6b,0x6d,0x57,0x85,0xfd,0x52, + 0x34,0x9e,0x07,0x97,0x4a,0x4b,0x88,0xe4,0x3f,0x9d,0x84,0x48,0x96,0x11,0x29,0xeb, + 0xd5,0xc4,0xa7,0x46,0x02,0x51,0xea,0x2a,0x0b,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,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,0x04,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,0x64,0x0b,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xd6,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x70,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x6c,0x02,0x01,0x2c,0x40,0x35,0x84,0x43,0x3a,0xc8,0x43,0x1b,0x88,0x43, + 0x3d,0x98,0x83,0x39,0x94,0x83,0x3c,0xb4,0x81,0x3b,0xbc,0x43,0x1b,0x84,0x03,0x3b, + 0xa4,0x43,0x38,0xcc,0x03,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09, + 0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14, + 0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30, + 0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01, + 0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98, + 0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19, + 0x01,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,0x03,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,0xca,0x12,0x18,0x01,0x28, + 0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01,0x20,0x1d,0x4b,0x90,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xfa,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25, + 0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9, + 0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05, + 0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c, + 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04, + 0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66, + 0xa6,0x26,0x65,0x88,0x80,0x10,0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46, + 0x17,0xc6,0x36,0x04,0x41,0x8e,0x24,0x48,0x82,0x44,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,0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9, + 0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02, + 0x21,0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73, + 0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e, + 0x08,0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05, + 0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a, + 0x5d,0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48, + 0x85,0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b, + 0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda, + 0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba, + 0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37, + 0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x43,0xa8,0x44,0x40,0x3c,0xe4,0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99, + 0x90,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02, + 0xc4,0x43,0xbe,0x24,0x48,0x02,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40, + 0xc0,0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67, + 0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b, + 0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34, + 0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37, + 0xb3,0x37,0x26,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48, + 0x86,0x44,0x40,0x24,0x64,0x0d,0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1, + 0xe5,0xc1,0x95,0x7d,0xcd,0xa5,0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b, + 0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b, + 0x63,0x62,0x37,0xf7,0x05,0x17,0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08, + 0x19,0x24,0x06,0x72,0x06,0x08,0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80, + 0xa8,0x01,0xc2,0x06,0x48,0x1b,0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20, + 0x13,0x02,0x07,0x43,0x10,0x44,0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6, + 0x01,0x20,0x1d,0x22,0x07,0x7c,0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8, + 0x40,0xc6,0xd0,0xc2,0xe4,0xf8,0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac, + 0x80,0x50,0x09,0x05,0x05,0x0d,0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1, + 0x72,0x0c,0x31,0x90,0x3b,0x40,0xee,0x60,0x39,0x78,0x85,0xa5,0xc9,0xb5,0x84,0xb1, + 0xa5,0x85,0xcd,0xb5,0xcc,0x8d,0xbd,0xc1,0x95,0xcd,0xa1,0xb4,0x85,0xa5,0xb9,0xc1, + 0xa4,0x0c,0x21,0x10,0x3d,0x40,0xf2,0x80,0x56,0x58,0x9a,0x5c,0x4b,0x18,0x5b,0x5a, + 0xd8,0x5c,0xcb,0xdc,0xd8,0x1b,0x5c,0x59,0x4b,0x98,0xdc,0x19,0xca,0x4c,0xca,0x10, + 0x03,0xe1,0x03,0x44,0x0f,0x90,0x3d,0x18,0x22,0x20,0x7c,0x30,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,0x42,0x30,0x8c,0x50,0xd8,0x81,0x1d,0xec, + 0xa1,0x1d,0xdc,0x20,0x1d,0xc8,0xa1,0x1c,0xdc,0x81,0x1e,0xa6,0x04,0xc5,0x88,0x25, + 0x1c,0xd2,0x41,0x1e,0xdc,0xc0,0x1e,0xca,0x41,0x1e,0xe6,0x21,0x1d,0xde,0xc1,0x1d, + 0xa6,0x04,0xc6,0x08,0x2a,0x1c,0xd2,0x41,0x1e,0xdc,0x80,0x1d,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1e,0xc2,0xe1,0x1c,0xca,0xe1,0x17,0xec,0xa1,0x1c,0xe4,0x61,0x1e,0xd2,0xe1, + 0x1d,0xdc,0x61,0x4a,0x80,0x8c,0x98,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xc6,0xe1,0x1d, + 0xda,0x01,0x1e,0xd2,0x81,0x1d,0xca,0xe1,0x17,0xde,0x01,0x1e,0xe8,0x21,0x1d,0xde, + 0xc1,0x1d,0xe6,0x61,0xca,0xa0,0x30,0xce,0x08,0x25,0x1c,0xd2,0x41,0x1e,0xdc,0xc0, + 0x1e,0xca,0x41,0x1e,0xe8,0xa1,0x1c,0xf0,0x61,0x4a,0x30,0x07,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3, + 0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c, + 0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19, + 0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5, + 0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0, + 0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81, + 0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d, + 0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39, + 0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77, + 0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef, + 0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07, + 0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73, + 0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94,0x33,0x00,0x14,0x63, + 0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30,0x96,0x00,0x82,0x20, + 0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73,0x10,0xd7,0x65,0x55, + 0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46,0x00,0x82,0x20,0x08, + 0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a,0xe8,0x63,0xc1,0x02, + 0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c,0xd1,0x6c,0xc3,0x52, + 0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x5b,0x8a,0x20,0xc0,0x83,0xa3,0x0f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv) * in.color; + return out; + } +*/ +static const uint8_t _simgui_fs_bytecode_metal_ios[3017] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,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,0x4a,0x26,0x79,0xc0,0xb7,0x63,0xb4, + 0xc2,0x1d,0xe8,0x10,0x46,0x41,0x83,0x17,0xea,0x3a,0x9a,0x24,0x37,0x8e,0xb3,0xb9, + 0x44,0xa4,0x85,0x3d,0x7c,0xd2,0xec,0x4b,0x4a,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,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,0xd4,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb2,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,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,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,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,0x98, + 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,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x41,0x02,0x00,0x00,0x79,0x18,0x00, + 0x00,0xd2,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,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,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,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e, + 0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98, + 0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d, + 0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3, + 0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6, + 0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d, + 0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb, + 0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc, + 0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c, + 0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c, + 0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72, + 0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c, + 0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0x01, + 0xaf,0xb0,0x34,0xb9,0x96,0x30,0xb6,0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2, + 0x39,0x94,0xb6,0xb0,0x34,0x37,0x98,0x94,0x21,0xc4,0xd3,0x06,0x0f,0x1b,0x10,0x0b, + 0x4b,0x93,0x6b,0x09,0x63,0x4b,0x0b,0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x6b,0xa1, + 0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x9b,0x1b,0x62,0x3c,0x6f,0xf0,0xb4,0xc1,0xe3,0x06, + 0xc4,0xc2,0xd2,0xe4,0x5a,0xc2,0xd8,0xd2,0xc2,0xe6,0x5a,0xe6,0xc6,0xde,0xe0,0xca, + 0x5a,0xe6,0xc2,0xda,0xe0,0xd8,0xca,0xe4,0xe6,0x86,0x18,0x4f,0x1c,0x3c,0x6d,0xf0, + 0xc0,0xc1,0x10,0xe2,0x79,0x83,0x27,0x0e,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,0x19,0x14,0xc6,0x19,0xc1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0x98,0x83,0x3c,0x84, + 0xc3,0x39,0xb4,0x43,0x39,0xb8,0x03,0x3d,0x4c,0x09,0xd6,0x00,0x00,0x79,0x18,0x00, + 0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43, + 0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4, + 0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6, + 0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0, + 0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1, + 0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f, + 0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc, + 0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19, + 0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3, + 0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87, + 0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77, + 0xa8,0x07,0x78,0x98,0x07,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,0x11,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00, + 0x02,0x23,0x00,0x00,0x00,0x23,0x06,0xca,0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58, + 0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c,0x02,0x62,0x00,0x00,0x00,0x02,0x00,0x00, + 0x00,0x5b,0x06,0xe0,0x90,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float2 disp_size; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _22 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = float4(((in.position / _22.disp_size) - float2(0.5)) * float2(2.0, -2.0), 0.5, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _simgui_vs_source_metal_sim[672] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63, + 0x6e,0x30,0x29,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,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,0x32,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,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, + 0x32,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,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x28,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x2d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, + 0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32, + 0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b, + 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, + +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv) * in.color; + return out; + } +*/ +static const uint8_t _simgui_fs_source_metal_sim[436] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,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,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,0x73,0x6d,0x70,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,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,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,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) +/* + cbuffer vs_params : register(b0) + { + float2 _22_disp_size : packoffset(c0); + }; + + + static float4 gl_Position; + static float2 position; + static float2 uv; + static float2 texcoord0; + static float4 color; + static float4 color0; + + struct SPIRV_Cross_Input + { + float2 position : TEXCOORD0; + float2 texcoord0 : TEXCOORD1; + float4 color0 : TEXCOORD2; + }; + + struct SPIRV_Cross_Output + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = float4(((position / _22_disp_size) - 0.5f.xx) * float2(2.0f, -2.0f), 0.5f, 1.0f); + uv = texcoord0; + color = color0; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + position = stage_input.position; + texcoord0 = stage_input.texcoord0; + color0 = stage_input.color0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.uv = uv; + stage_output.color = color; + return stage_output; + } +*/ +static const uint8_t _simgui_vs_bytecode_hlsl4[892] = { + 0x44,0x58,0x42,0x43,0x0d,0xbd,0x9e,0x9e,0x7d,0xc0,0x2b,0x54,0x88,0xf9,0xca,0x89, + 0x32,0xe4,0x0c,0x59,0x01,0x00,0x00,0x00,0x7c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x60,0x01,0x00,0x00,0xd0,0x01,0x00,0x00, + 0x00,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xc0,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,0x98,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x32,0x5f,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x00,0xab,0xab,0x01,0x00,0x03,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,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,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, + 0x03,0x03,0x00,0x00,0x50,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,0x03,0x0c,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,0x28,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x4a,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0x32,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,0x32,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,0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x08, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x0a,0x32,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08, + 0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,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,0x03,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, +}; +/* + Texture2D tex : register(t0); + SamplerState smp : register(s0); + + static float4 frag_color; + static float2 uv; + static float4 color; + + struct SPIRV_Cross_Input + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 frag_color : SV_Target0; + }; + + void frag_main() + { + frag_color = tex.Sample(smp, uv) * color; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + uv = stage_input.uv; + color = stage_input.color; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.frag_color = frag_color; + return stage_output; + } +*/ +static const uint8_t _simgui_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0x3a,0xa7,0x41,0x21,0xb4,0x2d,0xa7,0x6e,0xfe,0x31,0xb0,0xe0, + 0x14,0xe0,0xdf,0x5a,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,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,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,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,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,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +/* + diagnostic(off, derivative_uniformity); + + struct vs_params { + /_ @offset(0) _/ + disp_size : vec2f, + } + + var position_1 : vec2f; + + @binding(0) @group(0) var x_22 : vs_params; + + var uv : vec2f; + + var texcoord0 : vec2f; + + var color : vec4f; + + var color0 : vec4f; + + var gl_Position : vec4f; + + fn main_1() { + let x_33 = (((position_1 / x_22.disp_size) - vec2f(0.5f)) * vec2f(2.0f, -2.0f)); + gl_Position = vec4f(x_33.x, x_33.y, 0.5f, 1.0f); + uv = texcoord0; + color = color0; + return; + } + + struct main_out { + @builtin(position) + gl_Position : vec4f, + @location(0) + uv_1 : vec2f, + @location(1) + color_1 : vec4f, + } + + @vertex + fn main(@location(0) position_1_param : vec2f, @location(1) texcoord0_param : vec2f, @location(2) color0_param : vec4f) -> main_out { + position_1 = position_1_param; + texcoord0 = texcoord0_param; + color0 = color0_param; + main_1(); + return main_out(gl_Position, uv, color); + } +*/ +static const uint8_t _simgui_vs_source_wgsl[960] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e, + 0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x76, + 0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x32, + 0x20,0x3a,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x33, + 0x20,0x3d,0x20,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x20,0x2f,0x20,0x78,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x29, + 0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x32,0x2e,0x30,0x66,0x2c,0x20, + 0x2d,0x32,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78, + 0x5f,0x33,0x33,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x33,0x2e,0x79,0x2c,0x20,0x30, + 0x2e,0x35,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x75,0x76, + 0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72, + 0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x40,0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x76,0x65,0x72,0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d, + 0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c, + 0x20,0x75,0x76,0x2c,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, + +}; +/* + diagnostic(off, derivative_uniformity); + + var frag_color : vec4f; + + @binding(0) @group(1) var tex : texture_2d; + + @binding(32) @group(1) var smp : sampler; + + var uv : vec2f; + + var color : vec4f; + + fn main_1() { + let x_23 = uv; + let x_24 = textureSample(tex, smp, x_23); + frag_color = (x_24 * color); + return; + } + + struct main_out { + @location(0) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(0) uv_param : vec2f, @location(1) color_param : vec4f) -> main_out { + uv = uv_param; + color = color_param; + main_1(); + return main_out(frag_color); + } +*/ +static const uint8_t _simgui_fs_source_wgsl[585] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64, + 0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29, + 0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76, + 0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b, + 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66, + 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73,0x6d, + 0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x34,0x20,0x2a, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72, + 0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d, + 0x0a,0x0a,0x40,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29, + 0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74, + 0x20,0x7b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +#elif defined(SOKOL_VULKAN) +/* + #version 460 + + layout(set = 0, binding = 0, std140) uniform vs_params + { + vec2 disp_size; + } _22; + + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(((position / _22.disp_size) - vec2(0.5)) * vec2(2.0, -2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + +*/ +static const uint8_t _simgui_vs_bytecode_spirv_vk[1472] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x30,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0c,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00,0x05,0x00,0x04,0x00, + 0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0b,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0b,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,0x0b,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,0x0b,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,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x16,0x00,0x00,0x00,0x5f,0x32,0x32,0x00,0x05,0x00,0x03,0x00,0x29,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,0x2c,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2e,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00,0x47,0x00,0x03,0x00,0x0b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x29,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,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1e,0x00,0x03,0x00, + 0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x2c,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x2b,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x2c,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x20,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x27,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x29,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x2c,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +/* + #version 460 + + layout(set = 1, binding = 0) uniform texture2D tex; + layout(set = 1, binding = 32) uniform sampler smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + +*/ +static const uint8_t _simgui_fs_bytecode_spirv_vk[780] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x1d,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0a,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x74,0x65,0x78,0x00, + 0x05,0x00,0x03,0x00,0x10,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x05,0x00,0x03,0x00, + 0x16,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0a,0x00,0x00,0x00, + 0x06,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,0x20,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, + 0x0e,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,0x1b,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x56,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _simgui_vs_source_dummy = ""; +static const char* _simgui_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +static void _simgui_set_clipboard(ImGuiContext* ctx, const char* text) { + (void)ctx; + sapp_set_clipboard_string(text); +} + +static const char* _simgui_get_clipboard(ImGuiContext* ctx) { + (void)ctx; + return sapp_get_clipboard_string(); +} +#endif + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +EM_JS(int, simgui_js_is_osx, (void), { + if (navigator.userAgent.includes('Macintosh')) { + return 1; + } else { + return 0; + } +}) +#endif + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SIMGUI_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _simgui_log_messages[] = { + _SIMGUI_LOG_ITEMS +}; +#undef _SIMGUI_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SIMGUI_PANIC(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SIMGUI_ERROR(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SIMGUI_WARN(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SIMGUI_INFO(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SIMGUI_LOGMSG(code,msg) _simgui_log(SIMGUI_LOGITEM_ ##code, 3, msg, __LINE__) + +static void _simgui_log(simgui_log_item_t log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_simgui.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _simgui_log_messages[log_item]; + } + #endif + _simgui.desc.logger.func("simgui", log_level, (uint32_t)log_item, msg, line_nr, filename, _simgui.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +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_fn) { + ptr = _simgui.desc.allocator.alloc_fn(size, _simgui.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SIMGUI_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void _simgui_free(void* ptr) { + if (_simgui.desc.allocator.free_fn) { + _simgui.desc.allocator.free_fn(ptr, _simgui.desc.allocator.user_data); + } else { + free(ptr); + } +} + +static bool _simgui_is_osx(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return false; + #elif defined(__EMSCRIPTEN__) + return simgui_js_is_osx(); + #elif defined(__APPLE__) + return true; + #else + return false; + #endif +} + +static simgui_desc_t _simgui_desc_defaults(const simgui_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + simgui_desc_t res = *desc; + res.max_vertices = _simgui_def(res.max_vertices, 65536); + return res; +} + +static ImGuiPlatformIO* _simgui_imgui_get_platform_io(void) { + #if defined(__cplusplus) + return &ImGui::GetPlatformIO(); + #else + #if defined(CIMGUI_INCLUDED) + // 'original' cimgui + return _SIMGUI_CFUNC(GetPlatformIO_Nil)(); + #else + // dear bindings cimgui + return _SIMGUI_CFUNC(GetPlatformIO)(); + #endif + #endif +} + +static ImGuiIO* _simgui_imgui_get_io(void) { + #if defined(__cplusplus) + return &ImGui::GetIO(); + #else + #if defined(CIMGUI_INCLUDED) + // 'original' cimgui + return _SIMGUI_CFUNC(GetIO_Nil)(); + #else + // dear bindings cimgui + return _SIMGUI_CFUNC(GetIO)(); + #endif + #endif +} + +static void _simgui_imgui_newframe(void) { + #if defined(__cplusplus) + ImGui::NewFrame(); + #else + _SIMGUI_CFUNC(NewFrame)(); + #endif +} + +static void _simgui_imgui_create_context(void) { + #if defined(__cplusplus) + ImGui::CreateContext(); + #else + _SIMGUI_CFUNC(CreateContext)(0); + #endif +} + +static void _simgui_imgui_destroy_context(void) { + #if defined(__cplusplus) + ImGui::DestroyContext(); + #else + _SIMGUI_CFUNC(DestroyContext)(0); + #endif +} + +static void _simgui_imgui_style_colors_dark(void) { + #if defined(__cplusplus) + ImGui::StyleColorsDark(); + #else + _SIMGUI_CFUNC(StyleColorsDark)(_SIMGUI_CFUNC(GetStyle)()); + #endif +} + +static void _simgui_io_add_font_default(ImGuiIO* io) { + #if defined(__cplusplus) + io->Fonts->AddFontDefault(); + #else + ImFontAtlas_AddFontDefault(io->Fonts, 0); + #endif +} + +static void _simgui_io_add_focus_event(ImGuiIO* io, bool focus) { + #if defined(__cplusplus) + io->AddFocusEvent(focus); + #else + ImGuiIO_AddFocusEvent(io, focus); + #endif +} + +static void _simgui_io_add_mouse_source_event(ImGuiIO* io, ImGuiMouseSource source) { + #if defined(__cplusplus) + io->AddMouseSourceEvent(source); + #else + ImGuiIO_AddMouseSourceEvent(io, source); + #endif +} + +static void _simgui_io_add_mouse_pos_event(ImGuiIO* io, float x, float y) { + #if defined(__cplusplus) + io->AddMousePosEvent(x, y); + #else + ImGuiIO_AddMousePosEvent(io, x, y); + #endif +} + +static void _simgui_io_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 +} + +static void _simgui_io_add_mouse_wheel_event(ImGuiIO* io, float x, float y) { + #if defined(__cplusplus) + io->AddMouseWheelEvent(x, y); + #else + ImGuiIO_AddMouseWheelEvent(io, x, y); + #endif +} + +static void _simgui_io_add_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 +} + +static void _simgui_io_add_input_character(ImGuiIO* io, uint32_t c) { + #if defined(__cplusplus) + io->AddInputCharacter(c); + #else + ImGuiIO_AddInputCharacter(io, c); + #endif +} + +static void _simgui_io_add_input_characters_utf8(ImGuiIO* io, const char* c) { + #if defined(__cplusplus) + io->AddInputCharactersUTF8(c); + #else + ImGuiIO_AddInputCharactersUTF8(io, c); + #endif +} + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +static ImGuiMouseCursor _simgui_imgui_get_mouse_cursor(void) { + #if defined(__cplusplus) + return ImGui::GetMouseCursor(); + #else + return _SIMGUI_CFUNC(GetMouseCursor)(); + #endif +} +#endif + +static ImDrawList* _simgui_imdrawlist_at(ImDrawData* draw_data, int cl_index) { + #if defined(__cplusplus) + return draw_data->CmdLists[cl_index]; + #else + return draw_data->CmdLists.Data[cl_index]; + #endif +} + +static ImTextureID _simgui_imtexturedata_gettexid(ImTextureData* tex) { + #if defined(__cplusplus) + return tex->GetTexID(); + #else + return ImTextureData_GetTexID(tex); + #endif +} + +static void _simgui_imtexturedata_settexid(ImTextureData* tex, ImTextureID tex_id) { + #if defined(__cplusplus) + tex->SetTexID(tex_id); + #else + ImTextureData_SetTexID(tex, tex_id); + #endif +} + +static void _simgui_imtexturedata_setstatus(ImTextureData* tex, ImTextureStatus status) { + #if defined(__cplusplus) + tex->SetStatus(status); + #else + ImTextureData_SetStatus(tex, status); + #endif +} + +static void* _simgui_imtexturedata_getpixels(ImTextureData* tex) { + #if defined(__cplusplus) + return tex->GetPixels(); + #else + return ImTextureData_GetPixels(tex); + #endif +} + +static int _simgui_imtexturedata_getsizeinbytes(ImTextureData* tex) { + #if defined(__cplusplus) + return tex->GetSizeInBytes(); + #else + return ImTextureData_GetSizeInBytes(tex); + #endif +} + +static ImTextureID _simgui_imdrawcmd_gettexid(ImDrawCmd* cmd) { + #if defined(__cplusplus) + return cmd->GetTexID(); + #else + return ImDrawCmd_GetTexID(cmd); + #endif +} + +static int _simgui_imdrawlist_cmd_buffer_size(ImDrawList* cl) { + #if defined(__cplusplus) + return cl->CmdBuffer.size(); + #else + return cl->CmdBuffer.Size; + #endif +} + +static int _simgui_imdrawlist_vtx_buffer_size(ImDrawList* cl) { + #if defined(__cplusplus) + return cl->VtxBuffer.size(); + #else + return cl->VtxBuffer.Size; + #endif +} + +static int _simgui_imdrawlist_idx_buffer_size(ImDrawList* cl) { + #if defined(__cplusplus) + return cl->IdxBuffer.size(); + #else + return cl->IdxBuffer.Size; + #endif +} + +static void _simgui_imgui_render(void) { + #if defined(__cplusplus) + ImGui::Render(); + #else + _SIMGUI_CFUNC(Render)(); + #endif +} + +static ImDrawData* _simgui_imgui_get_draw_data(void) { + #if defined(__cplusplus) + return ImGui::GetDrawData(); + #else + return _SIMGUI_CFUNC(GetDrawData)(); + #endif +} + +static void _simgui_destroy_texture(ImTextureData* tex) { + SOKOL_ASSERT(tex); + const sg_view view = simgui_texture_view_from_imtextureid(_simgui_imtexturedata_gettexid(tex)); + const sg_image img = sg_query_view_image(view); + SOKOL_ASSERT(img.id != SG_INVALID_ID); + const sg_sampler smp = simgui_sampler_from_imtextureid(_simgui_imtexturedata_gettexid(tex)); + sg_destroy_view(view); + sg_destroy_image(img); + sg_destroy_sampler(smp); + _simgui_imtexturedata_settexid(tex, ImTextureID_Invalid); + _simgui_imtexturedata_setstatus(tex, ImTextureStatus_Destroyed); +} + +static void _simgui_update_texture(ImTextureData* tex) { + SOKOL_ASSERT(tex); + SOKOL_ASSERT(tex->Format == ImTextureFormat_RGBA32); + if (tex->Status == ImTextureStatus_WantCreate) { + // create new sokol-gfx image, view and sampler + SOKOL_ASSERT(tex->TexID == 0); + sg_image_desc img_desc; + _simgui_clear(&img_desc, sizeof(img_desc)); + img_desc.usage.dynamic_update = true; + img_desc.width = tex->Width; + img_desc.height = tex->Height; + img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + img_desc.label = "sokol-imgui-texture"; + sg_image img = sg_make_image(&img_desc); + + sg_view_desc view_desc; + _simgui_clear(&view_desc, sizeof(view_desc)); + view_desc.texture.image = img; + view_desc.label = "sokol-imgui-texture-view"; + sg_view view = sg_make_view(&view_desc); + + sg_sampler_desc smp_desc; + _simgui_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + smp_desc.min_filter = SG_FILTER_LINEAR; + smp_desc.mag_filter = SG_FILTER_LINEAR; + smp_desc.label = "sokol-imgui-sampler"; + sg_sampler smp = sg_make_sampler(&smp_desc); + + _simgui_imtexturedata_settexid(tex, simgui_imtextureid_with_sampler(view, smp)); + } + if ((tex->Status == ImTextureStatus_WantCreate) || (tex->Status == ImTextureStatus_WantUpdates)) { + SOKOL_ASSERT(tex->TexID != 0); + const sg_view view = simgui_texture_view_from_imtextureid(_simgui_imtexturedata_gettexid(tex)); + const sg_image img = sg_query_view_image(view); + SOKOL_ASSERT(img.id != SG_INVALID_ID); + sg_image_data img_data; + _simgui_clear(&img_data, sizeof(img_data)); + img_data.mip_levels[0].ptr = _simgui_imtexturedata_getpixels(tex); + img_data.mip_levels[0].size = (size_t)_simgui_imtexturedata_getsizeinbytes(tex); + sg_update_image(img, &img_data); + _simgui_imtexturedata_setstatus(tex, ImTextureStatus_OK); + } + if ((tex->Status == ImTextureStatus_WantDestroy) && (tex->UnusedFrames > 0)) { + SOKOL_ASSERT(tex->TexID != 0); + _simgui_destroy_texture(tex); + } +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { + SOKOL_ASSERT(desc); + _simgui_clear(&_simgui, sizeof(_simgui)); + _simgui.init_cookie = _SIMGUI_INIT_COOKIE; + _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 + // can keep color_format, depth_format and sample_count as is, + // since sokol_gfx.h will do its own default-value handling + + // 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 = _simgui_malloc(_simgui.vertices.size); + _simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx); + _simgui.indices.ptr = _simgui_malloc(_simgui.indices.size); + + // initialize Dear ImGui + _simgui_imgui_create_context(); + _simgui_imgui_style_colors_dark(); + ImGuiIO* io = _simgui_imgui_get_io(); + if (!_simgui.desc.no_default_font) { + _simgui_io_add_font_default(io); + } + io->IniFilename = _simgui.desc.ini_filename; + io->ConfigMacOSXBehaviors = _simgui_is_osx(); + io->BackendRendererName = "sokol-imgui"; + io->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + if (!_simgui.desc.disable_set_mouse_cursor) { + io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + } + ImGuiPlatformIO* pio = _simgui_imgui_get_platform_io(); + pio->Platform_SetClipboardTextFn = _simgui_set_clipboard; + pio->Platform_GetClipboardTextFn = _simgui_get_clipboard; + #endif + io->ConfigWindowsResizeFromEdges = !_simgui.desc.disable_windows_resize_from_edges; + + // create sokol-gfx resources + sg_push_debug_group("sokol-imgui"); + + // shader object for using the embedded shader source (or bytecode) + sg_shader_desc shd_desc; + _simgui_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].glsl_name = "position"; + shd_desc.attrs[1].glsl_name = "texcoord0"; + shd_desc.attrs[2].glsl_name = "color0"; + shd_desc.attrs[0].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[0].hlsl_sem_index = 0; + shd_desc.attrs[1].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[1].hlsl_sem_index = 1; + shd_desc.attrs[2].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[2].hlsl_sem_index = 2; + shd_desc.attrs[0].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[1].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[2].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX; + shd_desc.uniform_blocks[0].size = sizeof(_simgui_vs_params_t); + shd_desc.uniform_blocks[0].hlsl_register_b_n = 0; + shd_desc.uniform_blocks[0].msl_buffer_n = 0; + shd_desc.uniform_blocks[0].wgsl_group0_binding_n = 0; + shd_desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "vs_params"; + shd_desc.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.uniform_blocks[0].glsl_uniforms[0].array_count = 1; + shd_desc.uniform_blocks[0].spirv_set0_binding_n = 0; + shd_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.views[0].texture.image_type = SG_IMAGETYPE_2D; + shd_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.views[0].texture.hlsl_register_t_n = 0; + shd_desc.views[0].texture.msl_texture_n = 0; + shd_desc.views[0].texture.wgsl_group1_binding_n = 0; + shd_desc.views[0].texture.spirv_set1_binding_n = 0; + shd_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.samplers[0].hlsl_register_s_n = 0; + shd_desc.samplers[0].msl_sampler_n = 0; + shd_desc.samplers[0].wgsl_group1_binding_n = 32; + shd_desc.samplers[0].spirv_set1_binding_n = 32; + shd_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.texture_sampler_pairs[0].view_slot = 0; + shd_desc.texture_sampler_pairs[0].sampler_slot = 0; + shd_desc.texture_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.label = "sokol-imgui-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vertex_func.source = (const char*)_simgui_vs_source_glsl410; + shd_desc.fragment_func.source = (const char*)_simgui_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vertex_func.source = (const char*)_simgui_vs_source_glsl300es; + shd_desc.fragment_func.source = (const char*)_simgui_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vertex_func.entry = "main0"; + shd_desc.fragment_func.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_simgui_vs_bytecode_metal_macos); + shd_desc.fragment_func.bytecode = SG_RANGE(_simgui_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_simgui_vs_bytecode_metal_ios); + shd_desc.fragment_func.bytecode = SG_RANGE(_simgui_fs_bytecode_metal_ios); + break; + default: + shd_desc.vertex_func.source = (const char*)_simgui_vs_source_metal_sim; + shd_desc.fragment_func.source = (const char*)_simgui_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vertex_func.bytecode = SG_RANGE(_simgui_vs_bytecode_hlsl4); + shd_desc.fragment_func.bytecode = SG_RANGE(_simgui_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vertex_func.source = (const char*)_simgui_vs_source_wgsl; + shd_desc.fragment_func.source = (const char*)_simgui_fs_source_wgsl; + #elif defined(SOKOL_VULKAN) + shd_desc.vertex_func.bytecode = SG_RANGE(_simgui_vs_bytecode_spirv_vk); + shd_desc.vertex_func.entry = "main"; + shd_desc.fragment_func.bytecode = SG_RANGE(_simgui_fs_bytecode_spirv_vk); + shd_desc.fragment_func.entry = "main"; + #else + shd_desc.vertex_func.source = _simgui_vs_source_dummy; + shd_desc.fragment_func.source = _simgui_fs_source_dummy; + #endif + _simgui.def_shd = sg_make_shader(&shd_desc); + + // pipeline object for imgui rendering + sg_pipeline_desc pip_desc; + _simgui_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert); + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[0]; + attr->offset = offsetof(ImDrawVert, pos); + attr->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[1]; + attr->offset = offsetof(ImDrawVert, uv); + attr->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[2]; + attr->offset = offsetof(ImDrawVert, col); + attr->format = SG_VERTEXFORMAT_UBYTE4N; + } + pip_desc.shader = _simgui.def_shd; + pip_desc.index_type = SG_INDEXTYPE_UINT16; + 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 = _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.def_pip = sg_make_pipeline(&pip_desc); + + // create unfilterable/nonfiltering variants of the shader and pipeline + shd_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT; + shd_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_NONFILTERING; + shd_desc.label = "sokol-imgui-shader-unfilterable"; + _simgui.shd_unfilterable = sg_make_shader(&shd_desc); + pip_desc.shader = _simgui.shd_unfilterable; + pip_desc.label = "sokol-imgui-pipeline-unfilterable"; + _simgui.pip_unfilterable = sg_make_pipeline(&pip_desc); + + // NOTE: since we're in C++ mode here we can't use C99 designated init + sg_buffer_desc vb_desc; + _simgui_clear(&vb_desc, sizeof(vb_desc)); + vb_desc.usage.stream_update = true; + vb_desc.size = _simgui.vertices.size; + vb_desc.label = "sokol-imgui-vertices"; + _simgui.vbuf = sg_make_buffer(&vb_desc); + + sg_buffer_desc ib_desc; + _simgui_clear(&ib_desc, sizeof(ib_desc)); + ib_desc.usage.index_buffer = true; + ib_desc.usage.stream_update = true; + ib_desc.size = _simgui.indices.size; + ib_desc.label = "sokol-imgui-indices"; + _simgui.ibuf = sg_make_buffer(&ib_desc); + + // a default user-image sampler + sg_sampler_desc def_sampler_desc; + _simgui_clear(&def_sampler_desc, sizeof(def_sampler_desc)); + def_sampler_desc.min_filter = SG_FILTER_NEAREST; + def_sampler_desc.mag_filter = SG_FILTER_NEAREST; + def_sampler_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + def_sampler_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + def_sampler_desc.label = "sokol-imgui-default-sampler"; + _simgui.def_smp = sg_make_sampler(&def_sampler_desc); + + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void simgui_shutdown(void) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + + const ImGuiPlatformIO* pio = _simgui_imgui_get_platform_io(); + for (size_t i = 0; i < (size_t)pio->Textures.Size; i++) { + ImTextureData* tex = pio->Textures.Data[i]; + if (tex->RefCount == 1) { + _simgui_destroy_texture(tex); + } + } + _simgui_imgui_destroy_context(); + + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_destroy_pipeline(_simgui.pip_unfilterable); + sg_destroy_shader(_simgui.shd_unfilterable); + sg_destroy_pipeline(_simgui.def_pip); + sg_destroy_shader(_simgui.def_shd); + sg_destroy_sampler(_simgui.def_smp); + sg_destroy_buffer(_simgui.ibuf); + sg_destroy_buffer(_simgui.vbuf); + sg_pop_debug_group(); + sg_push_debug_group("sokol-imgui"); + SOKOL_ASSERT(_simgui.vertices.ptr); + _simgui_free((void*)_simgui.vertices.ptr); + SOKOL_ASSERT(_simgui.indices.ptr); + _simgui_free((void*)_simgui.indices.ptr); + _simgui.init_cookie = 0; +} + +SOKOL_API_IMPL uint64_t simgui_imtextureid_with_sampler(sg_view tex_view, sg_sampler smp) { + uint32_t view_id = tex_view.id; + uint32_t smp_id = smp.id; + return (((uint64_t)smp_id)<<32) | view_id; +} + +SOKOL_API_IMPL uint64_t simgui_imtextureid(sg_view tex_view) { + return simgui_imtextureid_with_sampler(tex_view, _simgui.def_smp); +} + +SOKOL_API_IMPL sg_view simgui_texture_view_from_imtextureid(uint64_t imtex_id) { + sg_view view = { (uint32_t)imtex_id }; + return view; +} + +SOKOL_API_IMPL sg_sampler simgui_sampler_from_imtextureid(uint64_t imtex_id) { + sg_sampler smp = { (uint32_t)(imtex_id >> 32) }; + return smp; +} + +SOKOL_API_IMPL void simgui_new_frame(const simgui_frame_desc_t* desc) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->width > 0); + SOKOL_ASSERT(desc->height > 0); + _simgui.cur_dpi_scale = _simgui_def(desc->dpi_scale, 1.0f); + ImGuiIO* io = _simgui_imgui_get_io(); + io->DisplaySize.x = ((float)desc->width) / _simgui.cur_dpi_scale; + io->DisplaySize.y = ((float)desc->height) / _simgui.cur_dpi_scale; + io->DisplayFramebufferScale.x = _simgui.cur_dpi_scale; + io->DisplayFramebufferScale.y = _simgui.cur_dpi_scale; + io->DeltaTime = (float)desc->delta_time; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + if (io->WantTextInput && !sapp_keyboard_shown()) { + sapp_show_keyboard(true); + } + if (!io->WantTextInput && sapp_keyboard_shown()) { + sapp_show_keyboard(false); + } + if (!_simgui.desc.disable_set_mouse_cursor) { + ImGuiMouseCursor imgui_cursor = _simgui_imgui_get_mouse_cursor(); + 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); + } + #endif + _simgui_imgui_newframe(); +} + +static sg_pipeline _simgui_bind_texture_sampler(sg_bindings* bindings, ImTextureID imtex_id) { + const sg_view tex_view = simgui_texture_view_from_imtextureid(imtex_id); + SOKOL_ASSERT(tex_view.id != SG_INVALID_ID); + const sg_image img = sg_query_view_image(tex_view); + SOKOL_ASSERT(img.id != SG_INVALID_ID); + bindings->views[0] = tex_view; + bindings->samplers[0] = simgui_sampler_from_imtextureid(imtex_id); + SOKOL_ASSERT(bindings->samplers[0].id != SG_INVALID_ID); + if (sg_query_pixelformat(sg_query_image_pixelformat(img)).filter) { + return _simgui.def_pip; + } else { + return _simgui.pip_unfilterable; + } +} + +SOKOL_API_IMPL void simgui_render(void) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_imgui_render(); + ImDrawData* draw_data = _simgui_imgui_get_draw_data(); + if (0 == draw_data) { + return; + } + + // catch up with texture updates (important: this needs to happen before + // checking the CmdListsCount, otherwise textures might get stuck in + // 'WantCreate' state) + if (draw_data->Textures) { + for (size_t i = 0; i < (size_t)draw_data->Textures->Size; i++) { + ImTextureData* tex = draw_data->Textures->Data[i]; + if (tex->Status != ImTextureStatus_OK) { + _simgui_update_texture(tex); + } + } + } + + // early-out if nothing needs to be rendered + if (draw_data->CmdListsCount == 0) { + return; + } + + // copy vertices and indices into an intermediate buffer so that + // they can be updated with a single sg_update_buffer() call each + // (sg_append_buffer() has performance problems on some GL platforms), + // also keep track of valid number of command lists in case of a + // buffer overflow + size_t all_vtx_size = 0; + size_t all_idx_size = 0; + int cmd_list_count = 0; + for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++, cmd_list_count++) { + ImDrawList* cl = _simgui_imdrawlist_at(draw_data, cl_index); + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + + // check for buffer overflow + if (((all_vtx_size + vtx_size) > _simgui.vertices.size) || + ((all_idx_size + idx_size) > _simgui.indices.size)) + { + _SIMGUI_ERROR(BUFFER_OVERFLOW); + break; + } + + // copy vertices and indices into common buffers + if (vtx_size > 0) { + const ImDrawVert* src_vtx_ptr = cl->VtxBuffer.Data; + void* dst_vtx_ptr = (void*) (((uint8_t*)_simgui.vertices.ptr) + all_vtx_size); + memcpy(dst_vtx_ptr, src_vtx_ptr, vtx_size); + } + if (idx_size > 0) { + const ImDrawIdx* src_idx_ptr = cl->IdxBuffer.Data; + void* dst_idx_ptr = (void*) (((uint8_t*)_simgui.indices.ptr) + all_idx_size); + memcpy(dst_idx_ptr, src_idx_ptr, idx_size); + } + all_vtx_size += vtx_size; + all_idx_size += idx_size; + } + if (0 == cmd_list_count) { + return; + } + + // update the sokol-gfx vertex- and index-buffer + sg_push_debug_group("sokol-imgui"); + if (all_vtx_size > 0) { + sg_range vtx_data = _simgui.vertices; + vtx_data.size = all_vtx_size; + sg_update_buffer(_simgui.vbuf, &vtx_data); + } + if (all_idx_size > 0) { + sg_range idx_data = _simgui.indices; + idx_data.size = all_idx_size; + sg_update_buffer(_simgui.ibuf, &idx_data); + } + + // render the ImGui command list + const int fb_width = (int) (io->DisplaySize.x * draw_data->FramebufferScale.x); + const int fb_height = (int) (io->DisplaySize.y * draw_data->FramebufferScale.y); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + + sg_apply_pipeline(_simgui.def_pip); + _simgui_vs_params_t 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(0, SG_RANGE_REF(vs_params)); + sg_bindings bind; + _simgui_clear((void*)&bind, sizeof(bind)); + bind.vertex_buffers[0] = _simgui.vbuf; + bind.index_buffer = _simgui.ibuf; + ImTextureID tex_id = 0; + int vb_offset = 0; + int ib_offset = 0; + for (int cl_index = 0; cl_index < cmd_list_count; cl_index++) { + ImDrawList* cl = _simgui_imdrawlist_at(draw_data, cl_index); + + bind.vertex_buffer_offsets[0] = vb_offset; + bind.index_buffer_offset = ib_offset; + if (tex_id != 0) { + sg_apply_bindings(&bind); + } + + const int num_cmds = _simgui_imdrawlist_cmd_buffer_size(cl); + uint32_t vtx_offset = 0; + for (int cmd_index = 0; cmd_index < num_cmds; cmd_index++) { + ImDrawCmd* pcmd = &cl->CmdBuffer.Data[cmd_index]; + if (pcmd->UserCallback != 0) { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback != ImDrawCallback_ResetRenderState) { + pcmd->UserCallback(cl, pcmd); + // need to re-apply all state after calling a user callback + sg_reset_state_cache(); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_pipeline(_simgui.def_pip); + sg_apply_uniforms(0, SG_RANGE_REF(vs_params)); + sg_apply_bindings(&bind); + } + } else { + ImTextureID cmd_tex_id = _simgui_imdrawcmd_gettexid(pcmd); + if ((tex_id != cmd_tex_id) || (vtx_offset != pcmd->VtxOffset)) { + tex_id = cmd_tex_id; + vtx_offset = pcmd->VtxOffset; + sg_pipeline pip = _simgui_bind_texture_sampler(&bind, tex_id); + sg_apply_pipeline(pip); + sg_apply_uniforms(0, SG_RANGE_REF(vs_params)); + bind.vertex_buffer_offsets[0] = vb_offset + (int)(pcmd->VtxOffset * sizeof(ImDrawVert)); + sg_apply_bindings(&bind); + } + const int scissor_x = (int) (pcmd->ClipRect.x * draw_data->FramebufferScale.x); + const int scissor_y = (int) (pcmd->ClipRect.y * draw_data->FramebufferScale.y); + const int scissor_w = (int) ((pcmd->ClipRect.z - pcmd->ClipRect.x) * draw_data->FramebufferScale.x); + const int scissor_h = (int) ((pcmd->ClipRect.w - pcmd->ClipRect.y) * draw_data->FramebufferScale.y); + sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true); + sg_draw((int)pcmd->IdxOffset, (int)pcmd->ElemCount, 1); + } + } + vb_offset += _simgui_imdrawlist_vtx_buffer_size(cl) * (int)sizeof(ImDrawVert); + ib_offset += _simgui_imdrawlist_idx_buffer_size(cl) * (int)sizeof(ImDrawIdx); + } + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void simgui_add_focus_event(bool focus) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_focus_event(io, focus); +} + +SOKOL_API_IMPL void simgui_add_mouse_pos_event(float x, float y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_mouse_source_event(io, ImGuiMouseSource_Mouse); + _simgui_io_add_mouse_pos_event(io, x, y); +} + +SOKOL_API_IMPL void simgui_add_touch_pos_event(float x, float y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_mouse_source_event(io, ImGuiMouseSource_TouchScreen); + _simgui_io_add_mouse_pos_event(io, x, y); +} + +SOKOL_API_IMPL void simgui_add_mouse_button_event(int mouse_button, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_mouse_source_event(io, ImGuiMouseSource_Mouse); + _simgui_io_add_mouse_button_event(io, mouse_button, down); +} + +SOKOL_API_IMPL void simgui_add_touch_button_event(int mouse_button, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_mouse_source_event(io, ImGuiMouseSource_TouchScreen); + _simgui_io_add_mouse_button_event(io, mouse_button, down); +} + +SOKOL_API_IMPL void simgui_add_mouse_wheel_event(float wheel_x, float wheel_y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_mouse_source_event(io, ImGuiMouseSource_Mouse); + _simgui_io_add_mouse_wheel_event(io, wheel_x, wheel_y); +} + +SOKOL_API_IMPL void simgui_add_key_event(int imgui_key, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_key_event(io, (ImGuiKey)imgui_key, down); +} + +SOKOL_API_IMPL void simgui_add_input_character(uint32_t c) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_input_character(io, c); +} + +SOKOL_API_IMPL void simgui_add_input_characters_utf8(const char* c) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + ImGuiIO* io = _simgui_imgui_get_io(); + _simgui_io_add_input_characters_utf8(io, c); +} + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +_SOKOL_PRIVATE bool _simgui_is_ctrl(uint32_t modifiers) { + if (_simgui.is_osx) { + return 0 != (modifiers & SAPP_MODIFIER_SUPER); + } else { + return 0 != (modifiers & SAPP_MODIFIER_CTRL); + } +} + +_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_sapp_key_event(ImGuiIO* io, sapp_keycode sapp_key, bool down) { + const ImGuiKey imgui_key = _simgui_map_keycode(sapp_key); + _simgui_io_add_key_event(io, imgui_key, down); +} + +_SOKOL_PRIVATE void _simgui_update_modifiers(ImGuiIO* io, uint32_t mods) { + _simgui_io_add_key_event(io, ImGuiMod_Ctrl, (mods & SAPP_MODIFIER_CTRL) != 0); + _simgui_io_add_key_event(io, ImGuiMod_Shift, (mods & SAPP_MODIFIER_SHIFT) != 0); + _simgui_io_add_key_event(io, ImGuiMod_Alt, (mods & SAPP_MODIFIER_ALT) != 0); + _simgui_io_add_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) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + return (int)_simgui_map_keycode(keycode); +} + +SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + const float dpi_scale = _simgui.cur_dpi_scale; + ImGuiIO* io = _simgui_imgui_get_io(); + switch (ev->type) { + case SAPP_EVENTTYPE_FOCUSED: + simgui_add_focus_event(true); + break; + case SAPP_EVENTTYPE_UNFOCUSED: + simgui_add_focus_event(false); + break; + case SAPP_EVENTTYPE_MOUSE_DOWN: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + simgui_add_mouse_button_event((int)ev->mouse_button, true); + _simgui_update_modifiers(io, ev->modifiers); + break; + case SAPP_EVENTTYPE_MOUSE_UP: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + simgui_add_mouse_button_event((int)ev->mouse_button, false); + _simgui_update_modifiers(io, ev->modifiers); + break; + case SAPP_EVENTTYPE_MOUSE_MOVE: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + break; + case SAPP_EVENTTYPE_MOUSE_ENTER: + case SAPP_EVENTTYPE_MOUSE_LEAVE: + // 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(i, false); + } + #endif + break; + case SAPP_EVENTTYPE_MOUSE_SCROLL: + simgui_add_mouse_wheel_event(ev->scroll_x, ev->scroll_y); + break; + case SAPP_EVENTTYPE_TOUCHES_BEGAN: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + simgui_add_touch_button_event(0, true); + break; + case SAPP_EVENTTYPE_TOUCHES_MOVED: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + break; + case SAPP_EVENTTYPE_TOUCHES_ENDED: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + simgui_add_touch_button_event(0, false); + break; + case SAPP_EVENTTYPE_TOUCHES_CANCELLED: + simgui_add_touch_button_event(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.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)) { + sapp_consume_event(); + } + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + // 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; + } + // 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)) { + sapp_consume_event(); + } + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + // 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 + characters, which may confuse some ImGui widgets, + 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)))) + { + simgui_add_input_character(ev->char_code); + } + break; + case SAPP_EVENTTYPE_CLIPBOARD_PASTED: + // simulate a Ctrl-V key down/up + if (!_simgui.desc.disable_paste_override) { + _simgui_io_add_key_event(io, _simgui_copypaste_modifier(), true); + _simgui_io_add_key_event(io, ImGuiKey_V, true); + _simgui_io_add_key_event(io, ImGuiKey_V, false); + _simgui_io_add_key_event(io, _simgui_copypaste_modifier(), false); + } + break; + default: + break; + } + return io->WantCaptureKeyboard || io->WantCaptureMouse; +} +#endif // SOKOL_IMGUI_NO_SOKOL_APP + +#endif // SOKOL_IMPL diff --git a/thirdparty/sokol/util/sokol_memtrack.h b/thirdparty/sokol/util/sokol_memtrack.h new file mode 100644 index 0000000..47f70fc --- /dev/null +++ b/thirdparty/sokol/util/sokol_memtrack.h @@ -0,0 +1,167 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_MEMTRACK_IMPL) +#define SOKOL_MEMTRACK_IMPL +#endif +#ifndef SOKOL_MEMTRACK_INCLUDED +/* + sokol_memtrack.h -- memory allocation wrapper to track memory usage + of sokol libraries + + Project URL: https://github.com/floooh/sokol + + Optionally provide the following defines with your own implementations: + + SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_MEMTRACK_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_memtrack.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + USAGE + ===== + Just plug the malloc/free wrapper functions into the desc.allocator + struct provided by most sokol header setup functions: + + sg_setup(&(sg_desc){ + //... + .allocator = { + .alloc_fn = smemtrack_alloc, + .free_fn = smemtrack_free, + } + }); + + Then call smemtrack_info() to get information about current number + of allocations and overall allocation size: + + const smemtrack_info_t info = smemtrack_info(); + const int num_allocs = info.num_allocs; + const int num_bytes = info.num_bytes; + + 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 + ======= + + zlib/libpng license + + Copyright (c) 2018 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_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 +#endif +#ifndef SOKOL_MEMTRACK_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_MEMTRACK_IMPL) +#define SOKOL_MEMTRACK_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_MEMTRACK_API_DECL __declspec(dllimport) +#else +#define SOKOL_MEMTRACK_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct smemtrack_info_t { + int num_allocs; + int num_bytes; +} 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" */ +#endif +#endif /* SOKOL_MEMTRACK_INCLUDED */ + +/*=== IMPLEMENTATION =========================================================*/ +#ifdef SOKOL_MEMTRACK_IMPL +#define SOKOL_MEMTRACK_IMPL_INCLUDED (1) +#include // malloc, free +#include // memset + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +// per-allocation header used to keep track of the allocation size +#define _SMEMTRACK_HEADER_SIZE (16) + +static struct { + smemtrack_info_t state; +} _smemtrack; + +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); + 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_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) { + return _smemtrack.state; +} + +#endif /* SOKOL_MEMTRACK_IMPL */ diff --git a/thirdparty/sokol/util/sokol_nuklear.h b/thirdparty/sokol/util/sokol_nuklear.h new file mode 100644 index 0000000..cc9e7c3 --- /dev/null +++ b/thirdparty/sokol/util/sokol_nuklear.h @@ -0,0 +1,3371 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_NUKLEAR_IMPL) +#define SOKOL_NUKLEAR_IMPL +#endif +#ifndef SOKOL_NUKLEAR_INCLUDED +/* + sokol_nuklear.h -- drop-in Nuklear renderer/event-handler for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_NUKLEAR_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + SOKOL_VULKAN + + Optionally provide the following configuration defines before including the + implementation: + + SOKOL_NUKLEAR_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details) + + Optionally provide the following macros before including the implementation + to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_NUKLEAR_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_NUKLEAR_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_nuklear.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_NUKLEAR_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before sokol_nuklear.h (both before including + the declaration and implementation): + + sokol_gfx.h + sokol_app.h (except SOKOL_NUKLEAR_NO_SOKOL_APP) + nuklear.h + + NOTE: Unlike most other sokol-headers, the implementation must be compiled + as C, compiling as C++ isn't supported. The interface is callable + from C++ of course. + + + FEATURE OVERVIEW: + ================= + sokol_nuklear.h implements the initialization, rendering and event-handling + code for Nuklear (https://github.com/Immediate-Mode-UI/Nuklear) on top of + sokol_gfx.h and (optionally) sokol_app.h. + + The sokol_app.h dependency is optional and used for input event handling. + If you only use sokol_gfx.h but not sokol_app.h in your application, + define SOKOL_NUKLEAR_NO_SOKOL_APP before including the implementation + of sokol_nuklear.h, this will remove any dependency to sokol_app.h, but + you must feed input events into Nuklear yourself. + + sokol_nuklear.h is not thread-safe, all calls must be made from the + same thread where sokol_gfx.h is running. + + HOWTO: + ====== + + --- To initialize sokol-nuklear, call: + + snk_setup(const snk_desc_t* desc) + + This will initialize Nuklear and create sokol-gfx resources + (two buffers for vertices and indices, a font texture and a pipeline- + state-object). + + Use the following snk_desc_t members to configure behaviour: + + int max_vertices + The maximum number of vertices used for UI rendering, default is 65536. + sokol-nuklear will use this to compute the size of the vertex- + and index-buffers allocated via sokol_gfx.h + + int image_pool_size + Number of snk_image_t objects which can be alive at the same time. + The default is 256. + + 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 + + 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. + + 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 + + bool no_default_font + Set this to true if you don't want to use Nuklear's default + font. In this case you need to initialize the font + yourself after snk_setup() is called. + + bool enable_set_mouse_cursor + If true, sokol_nuklear.h will control the mouse cursor type + by calling sapp_set_mouse_cursor(). If using this, you should + probably also call nk_style_hide_cursor() to hide Nuklear's + custom cursor. + + --- At the start of a frame, call: + + struct nk_context *snk_new_frame() + + This updates Nuklear's event handling state and then returns + a Nuklear context pointer which you use to build the UI. For + example: + + struct nk_context *ctx = snk_new_frame(); + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), ... + + + --- at the end of the frame, before the sg_end_pass() where you + want to render the UI, call: + + snk_render(int width, int height) + + where 'width' and 'height' are the dimensions of the rendering surface. + For example, if you're using sokol_app.h and render to the default + framebuffer: + + snk_render(sapp_width(), sapp_height()); + + This will convert Nuklear's command list into a vertex and index buffer, + and then render that through sokol_gfx.h + + --- if you're using sokol_app.h, from inside the sokol_app.h event callback, + call: + + bool snk_handle_event(const sapp_event* ev); + + This will feed the event into Nuklear's event handling code, and return + true if the event was handled by Nuklear, or false if the event should + be handled by the application. + + --- finally, on application shutdown, call + + snk_shutdown() + + --- Note that for touch-based systems, like iOS, there is a wrapper around + nk_edit_string(...), called snk_edit_string(...) which will show + and hide the onscreen keyboard as required. + + + ON USER-PROVIDED IMAGES AND SAMPLERS + ==================================== + To render your own images via nk_image(), first create an snk_image_t + object from a sokol-gfx texture view and sampler object. + + // create a sokol-nuklear image object which associates an sg_image with an sg_sampler + sg_image img = sg_make_image(...); + sg_view tex_view = sg_make_view(&(sg_view_desc){ + .texture = { .image = img }, + }); + sg_sampler smp = sg_make_sampler(...); + snk_image_t snk_img = snk_make_image(&(snk_image_desc_t){ + .texture_view = tex_view, + .sampler = smp, + }); + + // convert the returned image handle into an nk_handle object + struct nk_handle nk_hnd = snk_nkhandle(snk_img); + + // create a nuklear image from the generic handle (note that there a different helper functions for this) + struct nk_image nk_img = nk_image_handle(nk_hnd); + + // finally specify a Nuklear image UI object + nk_image(ctx, nk_img); + + snk_image_t objects are small and cheap (literally just the view and sampler + handle). + + You can omit the sampler handle in the snk_make_image() call, in this case a + default sampler will be used with nearest-filtering and clamp-to-edge. + + Trying to render with an invalid snk_image_t handle will render a small 8x8 + white default texture instead. + + To destroy a sokol-nuklear image object, call + + snk_destroy_image(snk_img); + + Note that this will not destroy the underlying sokol resources (sg_image, sg_view + and sg_sampler). + + Please be aware that the image object needs to be around until snk_render() is called + in a frame (if this turns out to be too much of a hassle we could introduce some sort + of garbage collection where destroyed snk_image_t objects are kept around until + the snk_render() call). + + You can call: + + snk_image_desc_t desc = snk_query_image_desc(img) + + ...to get the original desc struct, useful if you need to get the sokol-gfx image + and sampler handle of the snk_image_t object. + + You can convert an nk_handle back into an snk_image_t handle: + + snk_image_t img = snk_image_from_nkhandle(nk_hnd); + + + 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); + } + + ... + snk_setup(&(snk_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_nuklear.h + itself though, not any allocations in Nuklear. + + + 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" + + snk_setup(&(snk_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. 'snk' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SNK_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_nuklear.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-nuklear like this: + + snk_setup(&(snk_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 + + Copyright (c) 2020 Warren Merrifield + + 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_NUKLEAR_INCLUDED (1) +#include +#include +#include + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_nuklear.h" +#endif +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED) +#error "Please include sokol_app.h before sokol_nuklear.h" +#endif +#if !defined(NK_UNDEFINED) +#error "Please include nuklear.h before sokol_nuklear.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_NUKLEAR_API_DECL) +#define SOKOL_NUKLEAR_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_NUKLEAR_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_NUKLEAR_IMPL) +#define SOKOL_NUKLEAR_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_NUKLEAR_API_DECL __declspec(dllimport) +#else +#define SOKOL_NUKLEAR_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SNK_INVALID_ID = 0, +}; + +/* + snk_image_t + + A combined texture-view / sampler pair used to inject custom images and samplers into Nuklear. + + Create with snk_make_image(), and convert to an nk_handle via snk_nkhandle(). +*/ +typedef struct snk_image_t { uint32_t id; } snk_image_t; + +/* + snk_image_desc_t + + Descriptor struct for snk_make_image(). You must provide + at least an sg_view handle. Keeping the sg_sampler handle + zero-initialized will select the builtin default sampler + which uses linear filtering. +*/ +typedef struct snk_image_desc_t { + sg_view texture_view; + sg_sampler sampler; +} snk_image_desc_t; + +/* + snk_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SNK_LOG_ITEMS \ + _SNK_LOGITEM_XMACRO(OK, "Ok") \ + _SNK_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SNK_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + +#define _SNK_LOGITEM_XMACRO(item,msg) SNK_LOGITEM_##item, +typedef enum snk_log_item_t { + _SNK_LOG_ITEMS +} snk_log_item_t; +#undef _SNK_LOGITEM_XMACRO + +/* + snk_allocator_t + + Used in snk_desc_t to provide custom memory-alloc and -free functions + to sokol_nuklear.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct snk_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} snk_allocator_t; + +/* + snk_logger + + Used in snk_desc_t to provide a logging function. Please be aware + that without logging function, sokol-nuklear 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 snk_logger_t { + void (*func)( + const char* tag, // always "snk" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SNK_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} snk_logger_t; + + +typedef struct snk_desc_t { + int max_vertices; // default: 65536 + int image_pool_size; // default: 256 + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + float dpi_scale; + bool no_default_font; + bool enable_set_mouse_cursor; + snk_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + snk_logger_t logger; // optional log function override +} snk_desc_t; + +SOKOL_NUKLEAR_API_DECL void snk_setup(const snk_desc_t* desc); +SOKOL_NUKLEAR_API_DECL struct nk_context* snk_new_frame(void); +SOKOL_NUKLEAR_API_DECL void snk_render(int width, int height); +SOKOL_NUKLEAR_API_DECL snk_image_t snk_make_image(const snk_image_desc_t* desc); +SOKOL_NUKLEAR_API_DECL void snk_destroy_image(snk_image_t img); +SOKOL_NUKLEAR_API_DECL snk_image_desc_t snk_query_image_desc(snk_image_t img); +SOKOL_NUKLEAR_API_DECL nk_handle snk_nkhandle(snk_image_t img); +SOKOL_NUKLEAR_API_DECL snk_image_t snk_image_from_nkhandle(nk_handle handle); +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) +SOKOL_NUKLEAR_API_DECL bool snk_handle_event(const sapp_event* ev); +SOKOL_NUKLEAR_API_DECL nk_flags snk_edit_string(struct nk_context *ctx, nk_flags flags, char *memory, int *len, int max, nk_plugin_filter filter); +#endif +SOKOL_NUKLEAR_API_DECL void snk_shutdown(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void snk_setup(const snk_desc_t& desc) { return snk_setup(&desc); } +inline snk_image_t snk_make_image(const snk_image_desc_t& desc) { return snk_make_image(&desc); } + +#endif +#endif /* SOKOL_NUKLEAR_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_NUKLEAR_IMPL +#define SOKOL_NUKLEAR_IMPL_INCLUDED (1) + +#if !defined(NK_INCLUDE_VERTEX_BUFFER_OUTPUT) +#error "Please ensure that NK_INCLUDE_VERTEX_BUFFER_OUTPUT is #defined before including nuklear.h" +#endif + +#ifdef __cplusplus +#error "The sokol_nuklear.h implementation must be compiled as C." +#endif + +#include +#include // memset + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) && !defined(SOKOL_DUMMY_BACKEND) +#include +#endif + +#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 + +#define _SNK_INIT_COOKIE (0xBABEBABE) +#define _SNK_INVALID_SLOT_INDEX (0) +#define _SNK_SLOT_SHIFT (16) +#define _SNK_MAX_POOL_SIZE (1<<_SNK_SLOT_SHIFT) +#define _SNK_SLOT_MASK (_SNK_MAX_POOL_SIZE-1) + +// helper macros +#define _snk_def(val, def) (((val) == 0) ? (def) : (val)) + +typedef struct _snk_vertex_t { + float pos[2]; + float uv[2]; + uint8_t col[4]; +} _snk_vertex_t; + +typedef struct _snk_vs_params_t { + float disp_size[2]; + uint8_t _pad_8[8]; +} _snk_vs_params_t; + +typedef enum { + _SNK_RESOURCESTATE_INITIAL, + _SNK_RESOURCESTATE_ALLOC, + _SNK_RESOURCESTATE_VALID, + _SNK_RESOURCESTATE_FAILED, + _SNK_RESOURCESTATE_INVALID, + _SNK_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} _snk_resource_state; + +typedef struct { + uint32_t id; + _snk_resource_state state; +} _snk_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _snk_pool_t; + +typedef struct { + _snk_slot_t slot; + sg_view tex_view; + sg_sampler sampler; +} _snk_image_t; + +typedef struct { + _snk_pool_t pool; + _snk_image_t* items; +} _snk_image_pool_t; + +typedef struct { + uint32_t init_cookie; + snk_desc_t desc; + struct nk_context ctx; + struct nk_font_atlas atlas; + _snk_vs_params_t vs_params; + size_t vertex_buffer_size; + size_t index_buffer_size; + sg_buffer vbuf; + sg_buffer ibuf; + sg_image font_img; + sg_view font_tex_view; + sg_sampler font_smp; + snk_image_t default_font; + sg_image def_img; + sg_view def_tex_view; + sg_sampler def_smp; + sg_shader shd; + sg_pipeline pip; + bool is_osx; // true if running on OSX (or HTML5 OSX), needed for copy/paste + _snk_image_pool_t image_pool; + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + int mouse_pos[2]; + float mouse_scroll[2]; + bool mouse_did_move; + bool mouse_did_scroll; + bool btn_down[NK_BUTTON_MAX]; + bool btn_up[NK_BUTTON_MAX]; + char char_buffer[NK_INPUT_MAX]; + bool keys_down[NK_KEY_MAX]; + bool keys_up[NK_KEY_MAX]; + #endif +} _snk_state_t; +static _snk_state_t _snuklear; + +/* + Embedded source code compiled with: + + sokol-shdc -i snuk.glsl -o snuk.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl:spirv_vk -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + layout(binding=0) uniform vs_params { + vec2 disp_size; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(((position/disp_size)-0.5)*vec2(2.0,-2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + layout(binding=0) uniform texture2D tex; + layout(binding=0) uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + @end + + @program snuk vs fs +*/ +#if defined(SOKOL_GLCORE) +/* + #version 410 + + uniform vec4 vs_params[1]; + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(((position / vs_params[0].xy) - vec2(0.5)) * vec2(2.0, -2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _snk_vs_source_glsl410[383] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,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,0x31,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,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,0x76,0x65,0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30, + 0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,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, +}; +/* + #version 410 + + uniform sampler2D tex_smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv) * color; + } +*/ +static const uint8_t _snk_fs_source_glsl410[219] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,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,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 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, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +/* + #version 300 es + + uniform vec4 vs_params[1]; + layout(location = 0) in vec2 position; + out vec2 uv; + layout(location = 1) in vec2 texcoord0; + out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(((position / vs_params[0].xy) - vec2(0.5)) * vec2(2.0, -2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _snk_vs_source_glsl300es[344] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x31,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x76,0x65, + 0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20, + 0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c, + 0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,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, +}; +/* + #version 300 es + precision mediump float; + precision highp int; + + uniform highp sampler2D tex_smp; + + layout(location = 0) out highp vec4 frag_color; + in highp vec2 uv; + in highp vec4 color; + + void main() + { + frag_color = texture(tex_smp, uv) * color; + } +*/ +static const uint8_t _snk_fs_source_glsl300es[250] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float2 disp_size; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _22 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = float4(((in.position / _22.disp_size) - float2(0.5)) * float2(2.0, -2.0), 0.5, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _snk_vs_bytecode_metal_macos[3116] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,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,0x6c,0xc6,0xce,0x18,0x4d,0xb5,0xf9, + 0xae,0x57,0x9c,0x34,0x76,0x94,0x96,0x17,0x9a,0x2d,0xca,0x04,0xbe,0xda,0x39,0x87, + 0xd4,0x9d,0x6e,0x8c,0x73,0xe8,0x15,0x57,0xa2,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,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,0x04,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,0x08,0x0b,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xbf,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x68,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09, + 0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14, + 0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30, + 0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01, + 0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98, + 0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19, + 0x01,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,0x39,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,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01, + 0x20,0x1d,0x4b,0x78,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe9,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4, + 0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x46,0x06, + 0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x26,0x65,0x88,0x80,0x10,0x43,0x8c, + 0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e,0x24,0x48, + 0x82,0x44,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, + 0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5, + 0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42,0x71, + 0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16, + 0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44,0xc8, + 0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd, + 0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05,0xd1, + 0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c,0x4d, + 0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58, + 0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19, + 0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34, + 0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14, + 0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x44,0x40,0x3c,0xe4,0x4b, + 0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86,0x37, + 0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4,0x43,0xbe,0x24,0x48,0x02,0x04, + 0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43, + 0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99,0x90, + 0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d, + 0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a, + 0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31, + 0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x26,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86,0x44,0x40,0x24,0x64,0x0d,0x18, + 0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0xcd,0xa5,0xe9, + 0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x0a, + 0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63,0x62,0x37,0xf7,0x05,0x17,0x26, + 0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19,0x24,0x06,0x72,0x06,0x08,0x1a, + 0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8,0x01,0xc2,0x06,0x48,0x1b,0x24, + 0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13,0x02,0x07,0x43,0x10,0x44,0x0c, + 0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01,0x20,0x1d,0x22,0x07,0x7c,0xde, + 0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40,0xc6,0xd0,0xc2,0xe4,0xf8,0x4c, + 0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80,0x50,0x09,0x05,0x05,0x0d,0x11, + 0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72,0x0c,0x31,0x90,0x3b,0x40,0xee, + 0x60,0x39,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,0x19,0x14,0xc6,0x19,0xa1, + 0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e,0x4c, + 0x09,0xe6,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4, + 0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3, + 0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87, + 0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f, + 0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5, + 0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec, + 0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21, + 0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03, + 0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07, + 0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e, + 0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08, + 0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07, + 0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94, + 0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30, + 0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73, + 0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46, + 0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a, + 0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c, + 0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv) * in.color; + return out; + } +*/ +static const uint8_t _snk_fs_bytecode_metal_macos[3017] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,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,0xfe,0x1f,0x5f,0xc4,0x3f,0x2d,0xaa, + 0xe1,0x41,0x06,0x1f,0xeb,0x88,0x3c,0x97,0xdd,0x86,0x1d,0xfa,0x9c,0xc7,0x3f,0xac, + 0x4b,0x2b,0x8c,0xa0,0x89,0xf7,0x13,0x77,0xee,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,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,0xdc,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb4,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,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,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,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,0x49,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,0xe1,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xd2,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,0x46,0x06,0x46,0x66,0xc6, + 0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06, + 0x46,0x66,0xc6,0x65,0x66,0xa6,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,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9, + 0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d, + 0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6, + 0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2, + 0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96, + 0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86, + 0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e, + 0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58, + 0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84, + 0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c, + 0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b, + 0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c, + 0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78, + 0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0x01,0xaf,0xb0,0x34,0xb9,0x96,0x30,0xb6, + 0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2,0x39,0x94,0xb6,0xb0,0x34,0x37,0x98, + 0x94,0x21,0xc4,0xd3,0x06,0x0f,0x1b,0x10,0x0b,0x4b,0x93,0x6b,0x09,0x63,0x4b,0x0b, + 0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x6b,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x9b, + 0x1b,0x62,0x3c,0x6f,0xf0,0xb4,0xc1,0xe3,0x06,0xc4,0xc2,0xd2,0xe4,0x5a,0xc2,0xd8, + 0xd2,0xc2,0xe6,0x5a,0xe6,0xc6,0xde,0xe0,0xca,0x5a,0xe6,0xc2,0xda,0xe0,0xd8,0xca, + 0xe4,0xe6,0x86,0x18,0x4f,0x1c,0x3c,0x6d,0xf0,0xc0,0xc1,0x10,0xe2,0x79,0x83,0x27, + 0x0e,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,0x19,0x14,0xc6,0x19,0xc1,0x84, + 0x43,0x3a,0xc8,0x83,0x1b,0x98,0x83,0x3c,0x84,0xc3,0x39,0xb4,0x43,0x39,0xb8,0x03, + 0x3d,0x4c,0x09,0xd6,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8, + 0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08, + 0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20, + 0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e, + 0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2, + 0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4, + 0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8, + 0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08, + 0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10, + 0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30, + 0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78, + 0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,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,0x11,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00, + 0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00,0x00,0x23,0x06,0xca, + 0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c, + 0x02,0x62,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x06,0xe0,0x90,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float2 disp_size; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _22 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = float4(((in.position / _22.disp_size) - float2(0.5)) * float2(2.0, -2.0), 0.5, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _snk_vs_bytecode_metal_ios[3212] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x8c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x80,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,0x3f,0x6b,0x6d,0x57,0x85,0xfd,0x52, + 0x34,0x9e,0x07,0x97,0x4a,0x4b,0x88,0xe4,0x3f,0x9d,0x84,0x48,0x96,0x11,0x29,0xeb, + 0xd5,0xc4,0xa7,0x46,0x02,0x51,0xea,0x2a,0x0b,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,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,0x04,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,0x64,0x0b,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xd6,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x70,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x6c,0x02,0x01,0x2c,0x40,0x35,0x84,0x43,0x3a,0xc8,0x43,0x1b,0x88,0x43, + 0x3d,0x98,0x83,0x39,0x94,0x83,0x3c,0xb4,0x81,0x3b,0xbc,0x43,0x1b,0x84,0x03,0x3b, + 0xa4,0x43,0x38,0xcc,0x03,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09, + 0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14, + 0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30, + 0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01, + 0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98, + 0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19, + 0x01,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,0x03,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,0xca,0x12,0x18,0x01,0x28, + 0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01,0x20,0x1d,0x4b,0x90,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xfa,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25, + 0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9, + 0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05, + 0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c, + 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04, + 0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66, + 0xa6,0x26,0x65,0x88,0x80,0x10,0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46, + 0x17,0xc6,0x36,0x04,0x41,0x8e,0x24,0x48,0x82,0x44,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,0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9, + 0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02, + 0x21,0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73, + 0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e, + 0x08,0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05, + 0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a, + 0x5d,0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48, + 0x85,0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b, + 0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda, + 0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba, + 0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37, + 0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x43,0xa8,0x44,0x40,0x3c,0xe4,0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99, + 0x90,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02, + 0xc4,0x43,0xbe,0x24,0x48,0x02,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40, + 0xc0,0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67, + 0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b, + 0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34, + 0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37, + 0xb3,0x37,0x26,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48, + 0x86,0x44,0x40,0x24,0x64,0x0d,0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1, + 0xe5,0xc1,0x95,0x7d,0xcd,0xa5,0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b, + 0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b, + 0x63,0x62,0x37,0xf7,0x05,0x17,0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08, + 0x19,0x24,0x06,0x72,0x06,0x08,0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80, + 0xa8,0x01,0xc2,0x06,0x48,0x1b,0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20, + 0x13,0x02,0x07,0x43,0x10,0x44,0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6, + 0x01,0x20,0x1d,0x22,0x07,0x7c,0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8, + 0x40,0xc6,0xd0,0xc2,0xe4,0xf8,0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac, + 0x80,0x50,0x09,0x05,0x05,0x0d,0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1, + 0x72,0x0c,0x31,0x90,0x3b,0x40,0xee,0x60,0x39,0x78,0x85,0xa5,0xc9,0xb5,0x84,0xb1, + 0xa5,0x85,0xcd,0xb5,0xcc,0x8d,0xbd,0xc1,0x95,0xcd,0xa1,0xb4,0x85,0xa5,0xb9,0xc1, + 0xa4,0x0c,0x21,0x10,0x3d,0x40,0xf2,0x80,0x56,0x58,0x9a,0x5c,0x4b,0x18,0x5b,0x5a, + 0xd8,0x5c,0xcb,0xdc,0xd8,0x1b,0x5c,0x59,0x4b,0x98,0xdc,0x19,0xca,0x4c,0xca,0x10, + 0x03,0xe1,0x03,0x44,0x0f,0x90,0x3d,0x18,0x22,0x20,0x7c,0x30,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,0x42,0x30,0x8c,0x50,0xd8,0x81,0x1d,0xec, + 0xa1,0x1d,0xdc,0x20,0x1d,0xc8,0xa1,0x1c,0xdc,0x81,0x1e,0xa6,0x04,0xc5,0x88,0x25, + 0x1c,0xd2,0x41,0x1e,0xdc,0xc0,0x1e,0xca,0x41,0x1e,0xe6,0x21,0x1d,0xde,0xc1,0x1d, + 0xa6,0x04,0xc6,0x08,0x2a,0x1c,0xd2,0x41,0x1e,0xdc,0x80,0x1d,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1e,0xc2,0xe1,0x1c,0xca,0xe1,0x17,0xec,0xa1,0x1c,0xe4,0x61,0x1e,0xd2,0xe1, + 0x1d,0xdc,0x61,0x4a,0x80,0x8c,0x98,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xc6,0xe1,0x1d, + 0xda,0x01,0x1e,0xd2,0x81,0x1d,0xca,0xe1,0x17,0xde,0x01,0x1e,0xe8,0x21,0x1d,0xde, + 0xc1,0x1d,0xe6,0x61,0xca,0xa0,0x30,0xce,0x08,0x25,0x1c,0xd2,0x41,0x1e,0xdc,0xc0, + 0x1e,0xca,0x41,0x1e,0xe8,0xa1,0x1c,0xf0,0x61,0x4a,0x30,0x07,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3, + 0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c, + 0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19, + 0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5, + 0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0, + 0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81, + 0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d, + 0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39, + 0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77, + 0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef, + 0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07, + 0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73, + 0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94,0x33,0x00,0x14,0x63, + 0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30,0x96,0x00,0x82,0x20, + 0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73,0x10,0xd7,0x65,0x55, + 0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46,0x00,0x82,0x20,0x08, + 0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a,0xe8,0x63,0xc1,0x02, + 0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c,0xd1,0x6c,0xc3,0x52, + 0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x5b,0x8a,0x20,0xc0,0x83,0xa3,0x0f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv) * in.color; + return out; + } +*/ +static const uint8_t _snk_fs_bytecode_metal_ios[3017] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc9,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,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,0x4a,0x26,0x79,0xc0,0xb7,0x63,0xb4, + 0xc2,0x1d,0xe8,0x10,0x46,0x41,0x83,0x17,0xea,0x3a,0x9a,0x24,0x37,0x8e,0xb3,0xb9, + 0x44,0xa4,0x85,0x3d,0x7c,0xd2,0xec,0x4b,0x4a,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,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,0xd4,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xb2,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,0x74,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,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,0xb5,0x01,0x39,0xfe,0xff,0xff,0xff,0x7f,0x00,0x18,0x40, + 0x02,0x2a,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x86,0x40, + 0x18,0x26,0x0c,0x44,0x61,0x4c,0x18,0x8e,0xc2,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,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,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,0x98, + 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,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x41,0x02,0x00,0x00,0x79,0x18,0x00, + 0x00,0xd2,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,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,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,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,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e, + 0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98, + 0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d, + 0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3, + 0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6, + 0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d, + 0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb, + 0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc, + 0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c, + 0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c, + 0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72, + 0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c, + 0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0x01, + 0xaf,0xb0,0x34,0xb9,0x96,0x30,0xb6,0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2, + 0x39,0x94,0xb6,0xb0,0x34,0x37,0x98,0x94,0x21,0xc4,0xd3,0x06,0x0f,0x1b,0x10,0x0b, + 0x4b,0x93,0x6b,0x09,0x63,0x4b,0x0b,0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x6b,0xa1, + 0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x9b,0x1b,0x62,0x3c,0x6f,0xf0,0xb4,0xc1,0xe3,0x06, + 0xc4,0xc2,0xd2,0xe4,0x5a,0xc2,0xd8,0xd2,0xc2,0xe6,0x5a,0xe6,0xc6,0xde,0xe0,0xca, + 0x5a,0xe6,0xc2,0xda,0xe0,0xd8,0xca,0xe4,0xe6,0x86,0x18,0x4f,0x1c,0x3c,0x6d,0xf0, + 0xc0,0xc1,0x10,0xe2,0x79,0x83,0x27,0x0e,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,0x19,0x14,0xc6,0x19,0xc1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0x98,0x83,0x3c,0x84, + 0xc3,0x39,0xb4,0x43,0x39,0xb8,0x03,0x3d,0x4c,0x09,0xd6,0x00,0x00,0x79,0x18,0x00, + 0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43, + 0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4, + 0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6, + 0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0, + 0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1, + 0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f, + 0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc, + 0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19, + 0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3, + 0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87, + 0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77, + 0xa8,0x07,0x78,0x98,0x07,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,0x11,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00, + 0x02,0x23,0x00,0x00,0x00,0x23,0x06,0xca,0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58, + 0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c,0x02,0x62,0x00,0x00,0x00,0x02,0x00,0x00, + 0x00,0x5b,0x06,0xe0,0x90,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float2 disp_size; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _22 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = float4(((in.position / _22.disp_size) - float2(0.5)) * float2(2.0, -2.0), 0.5, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _snk_vs_source_metal_sim[672] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63, + 0x6e,0x30,0x29,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,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,0x32,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,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, + 0x32,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,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x28,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x2d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, + 0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32, + 0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b, + 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, +}; +/* + #include + #include + + using namespace metal; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + out.frag_color = tex.sample(smp, in.uv) * in.color; + return out; + } +*/ +static const uint8_t _snk_fs_source_metal_sim[436] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,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,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 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,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,0x73,0x6d,0x70,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,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,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,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) +/* + cbuffer vs_params : register(b0) + { + float2 _22_disp_size : packoffset(c0); + }; + + + static float4 gl_Position; + static float2 position; + static float2 uv; + static float2 texcoord0; + static float4 color; + static float4 color0; + + struct SPIRV_Cross_Input + { + float2 position : TEXCOORD0; + float2 texcoord0 : TEXCOORD1; + float4 color0 : TEXCOORD2; + }; + + struct SPIRV_Cross_Output + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = float4(((position / _22_disp_size) - 0.5f.xx) * float2(2.0f, -2.0f), 0.5f, 1.0f); + uv = texcoord0; + color = color0; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + position = stage_input.position; + texcoord0 = stage_input.texcoord0; + color0 = stage_input.color0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.uv = uv; + stage_output.color = color; + return stage_output; + } +*/ +static const uint8_t _snk_vs_bytecode_hlsl4[892] = { + 0x44,0x58,0x42,0x43,0x0d,0xbd,0x9e,0x9e,0x7d,0xc0,0x2b,0x54,0x88,0xf9,0xca,0x89, + 0x32,0xe4,0x0c,0x59,0x01,0x00,0x00,0x00,0x7c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x60,0x01,0x00,0x00,0xd0,0x01,0x00,0x00, + 0x00,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xc0,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,0x98,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x32,0x5f,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x00,0xab,0xab,0x01,0x00,0x03,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,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,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, + 0x03,0x03,0x00,0x00,0x50,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,0x03,0x0c,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,0x28,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x4a,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0x32,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,0x32,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,0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x08, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x0a,0x32,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08, + 0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,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,0x03,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, +}; +/* + Texture2D tex : register(t0); + SamplerState smp : register(s0); + + static float4 frag_color; + static float2 uv; + static float4 color; + + struct SPIRV_Cross_Input + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 frag_color : SV_Target0; + }; + + void frag_main() + { + frag_color = tex.Sample(smp, uv) * color; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + uv = stage_input.uv; + color = stage_input.color; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.frag_color = frag_color; + return stage_output; + } +*/ +static const uint8_t _snk_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0x3a,0xa7,0x41,0x21,0xb4,0x2d,0xa7,0x6e,0xfe,0x31,0xb0,0xe0, + 0x14,0xe0,0xdf,0x5a,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,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,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,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,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,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +/* + diagnostic(off, derivative_uniformity); + + struct vs_params { + /_ @offset(0) _/ + disp_size : vec2f, + } + + var position_1 : vec2f; + + @binding(0) @group(0) var x_22 : vs_params; + + var uv : vec2f; + + var texcoord0 : vec2f; + + var color : vec4f; + + var color0 : vec4f; + + var gl_Position : vec4f; + + fn main_1() { + let x_33 = (((position_1 / x_22.disp_size) - vec2f(0.5f)) * vec2f(2.0f, -2.0f)); + gl_Position = vec4f(x_33.x, x_33.y, 0.5f, 1.0f); + uv = texcoord0; + color = color0; + return; + } + + struct main_out { + @builtin(position) + gl_Position : vec4f, + @location(0) + uv_1 : vec2f, + @location(1) + color_1 : vec4f, + } + + @vertex + fn main(@location(0) position_1_param : vec2f, @location(1) texcoord0_param : vec2f, @location(2) color0_param : vec4f) -> main_out { + position_1 = position_1_param; + texcoord0 = texcoord0_param; + color0 = color0_param; + main_1(); + return main_out(gl_Position, uv, color); + } +*/ +static const uint8_t _snk_vs_source_wgsl[960] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e, + 0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x76, + 0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x32, + 0x20,0x3a,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x33, + 0x20,0x3d,0x20,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x20,0x2f,0x20,0x78,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x29, + 0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x32,0x2e,0x30,0x66,0x2c,0x20, + 0x2d,0x32,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78, + 0x5f,0x33,0x33,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x33,0x2e,0x79,0x2c,0x20,0x30, + 0x2e,0x35,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x75,0x76, + 0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72, + 0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x40,0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x76,0x65,0x72,0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d, + 0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c, + 0x20,0x75,0x76,0x2c,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +/* + diagnostic(off, derivative_uniformity); + + var frag_color : vec4f; + + @binding(0) @group(1) var tex : texture_2d; + + @binding(32) @group(1) var smp : sampler; + + var uv : vec2f; + + var color : vec4f; + + fn main_1() { + let x_23 = uv; + let x_24 = textureSample(tex, smp, x_23); + frag_color = (x_24 * color); + return; + } + + struct main_out { + @location(0) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(0) uv_param : vec2f, @location(1) color_param : vec4f) -> main_out { + uv = uv_param; + color = color_param; + main_1(); + return main_out(frag_color); + } +*/ +static const uint8_t _snk_fs_source_wgsl[585] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64, + 0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29, + 0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32,0x29,0x20,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76, + 0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b, + 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66, + 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73,0x6d, + 0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x34,0x20,0x2a, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72, + 0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d, + 0x0a,0x0a,0x40,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29, + 0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74, + 0x20,0x7b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x00, +}; +#elif defined(SOKOL_VULKAN) +/* + #version 460 + + layout(set = 0, binding = 0, std140) uniform vs_params + { + vec2 disp_size; + } _22; + + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = vec4(((position / _22.disp_size) - vec2(0.5)) * vec2(2.0, -2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + +*/ +static const uint8_t _snk_vs_bytecode_spirv_vk[1472] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x30,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0c,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00,0x05,0x00,0x04,0x00, + 0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0b,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0b,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,0x0b,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,0x0b,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,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x16,0x00,0x00,0x00,0x5f,0x32,0x32,0x00,0x05,0x00,0x03,0x00,0x29,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,0x2c,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2e,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00,0x47,0x00,0x03,0x00,0x0b,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x29,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,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1e,0x00,0x03,0x00, + 0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x2c,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x2b,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x2c,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x20,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x27,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x29,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x2c,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +/* + #version 460 + + layout(set = 1, binding = 0) uniform texture2D tex; + layout(set = 1, binding = 32) uniform sampler smp; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + +*/ +static const uint8_t _snk_fs_bytecode_spirv_vk[780] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x1d,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0a,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, + 0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x74,0x65,0x78,0x00, + 0x05,0x00,0x03,0x00,0x10,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x05,0x00,0x03,0x00, + 0x16,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0a,0x00,0x00,0x00, + 0x06,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,0x20,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0b,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, + 0x0e,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,0x1b,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0a,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x56,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _snk_vs_source_dummy = ""; +static const char* _snk_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) +static void _snk_clipboard_copy(nk_handle usr, const char *text, int len) { + (void)usr; + if (len == 0) { + return; + } + sapp_set_clipboard_string(text); +} + +static void _snk_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) { + const char *text = sapp_get_clipboard_string(); + if (text) { + nk_textedit_paste(edit, text, nk_strlen(text)); + } + (void)usr; +} + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +EM_JS(int, snk_js_is_osx, (void), { + if (navigator.userAgent.includes('Macintosh')) { + return 1; + } else { + return 0; + } +}) +#endif + +static bool _snk_is_osx(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return false; + #elif defined(__EMSCRIPTEN__) + return snk_js_is_osx(); + #elif defined(__APPLE__) + return true; + #else + return false; + #endif +} +#endif // !SOKOL_NUKLEAR_NO_SOKOL_APP + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SNK_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _snk_log_messages[] = { + _SNK_LOG_ITEMS +}; +#undef _SNK_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SNK_PANIC(code) _snk_log(SNK_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SNK_ERROR(code) _snk_log(SNK_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SNK_WARN(code) _snk_log(SNK_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SNK_INFO(code) _snk_log(SNK_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SNK_LOGMSG(code,msg) _snk_log(SNK_LOGITEM_ ##code, 3, msg, __LINE__) + +static void _snk_log(snk_log_item_t log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_snuklear.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _snk_log_messages[log_item]; + } + #endif + _snuklear.desc.logger.func("snk", log_level, (uint32_t)log_item, msg, line_nr, filename, _snuklear.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _snk_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _snk_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_snuklear.desc.allocator.alloc_fn) { + ptr = _snuklear.desc.allocator.alloc_fn(size, _snuklear.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SNK_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _snk_malloc_clear(size_t size) { + void* ptr = _snk_malloc(size); + _snk_clear(ptr, size); + return ptr; +} + +static void _snk_free(void* ptr) { + if (_snuklear.desc.allocator.free_fn) { + _snuklear.desc.allocator.free_fn(ptr, _snuklear.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _snk_init_pool(_snk_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 + pool->size = num + 1; + 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*) _snk_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _snk_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; + } +} + +static void _snk_discard_pool(_snk_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _snk_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _snk_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _snk_pool_alloc_index(_snk_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SNK_INVALID_SLOT_INDEX; + } +} + +static void _snk_pool_free_index(_snk_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SNK_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* initialize a pool slot: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the handle id +*/ +static uint32_t _snk_slot_init(_snk_pool_t* pool, _snk_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SNK_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == _SNK_RESOURCESTATE_INITIAL) && (slot->id == SNK_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SNK_SLOT_SHIFT)|(slot_index & _SNK_SLOT_MASK); + slot->state = _SNK_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _snk_slot_index(uint32_t id) { + int slot_index = (int) (id & _SNK_SLOT_MASK); + SOKOL_ASSERT(_SNK_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +static void _snk_init_item_pool(_snk_pool_t* pool, int pool_size, void** items_ptr, size_t item_size_bytes) { + // NOTE: the pools will have an additional item, since slot 0 is reserved + SOKOL_ASSERT(pool && (pool->size == 0)); + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SNK_MAX_POOL_SIZE)); + SOKOL_ASSERT(items_ptr && (*items_ptr == 0)); + SOKOL_ASSERT(item_size_bytes > 0); + _snk_init_pool(pool, pool_size); + const size_t pool_size_bytes = item_size_bytes * (size_t)pool->size; + *items_ptr = _snk_malloc_clear(pool_size_bytes); +} + +static void _snk_discard_item_pool(_snk_pool_t* pool, void** items_ptr) { + SOKOL_ASSERT(pool && (pool->size != 0)); + SOKOL_ASSERT(items_ptr && (*items_ptr != 0)); + _snk_free(*items_ptr); *items_ptr = 0; + _snk_discard_pool(pool); +} + +static void _snk_setup_image_pool(int pool_size) { + _snk_image_pool_t* p = &_snuklear.image_pool; + _snk_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_snk_image_t)); +} + +static void _snk_discard_image_pool(void) { + _snk_image_pool_t* p = &_snuklear.image_pool; + _snk_discard_item_pool(&p->pool, (void**)&p->items); +} + +static snk_image_t _snk_make_image_handle(uint32_t id) { + snk_image_t handle = { id }; + return handle; +} + +static _snk_image_t* _snk_image_at(uint32_t id) { + SOKOL_ASSERT(SNK_INVALID_ID != id); + const _snk_image_pool_t* p = &_snuklear.image_pool; + int slot_index = _snk_slot_index(id); + SOKOL_ASSERT((slot_index > _SNK_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _snk_image_t* _snk_lookup_image(uint32_t id) { + if (SNK_INVALID_ID != id) { + _snk_image_t* img = _snk_image_at(id); + if (img->slot.id == id) { + return img; + } + } + return 0; +} + +static snk_image_t _snk_alloc_image(void) { + _snk_image_pool_t* p = &_snuklear.image_pool; + int slot_index = _snk_pool_alloc_index(&p->pool); + if (_SNK_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _snk_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _snk_make_image_handle(id); + } else { + // pool exhausted + return _snk_make_image_handle(SNK_INVALID_ID); + } +} + +static _snk_resource_state _snk_init_image(_snk_image_t* img, const snk_image_desc_t* desc) { + SOKOL_ASSERT(img && (img->slot.state == _SNK_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + img->tex_view = desc->texture_view; + img->sampler = desc->sampler; + return _SNK_RESOURCESTATE_VALID; +} + +static void _snk_deinit_image(_snk_image_t* img) { + SOKOL_ASSERT(img); + img->tex_view.id = SNK_INVALID_ID; + img->sampler.id = SNK_INVALID_ID; +} + +static void _snk_destroy_image(snk_image_t img_id) { + _snk_image_t* img = _snk_lookup_image(img_id.id); + if (img) { + _snk_deinit_image(img); + _snk_image_pool_t* p = &_snuklear.image_pool; + _snk_clear(img, sizeof(_snk_image_t)); + _snk_pool_free_index(&p->pool, _snk_slot_index(img_id.id)); + } +} + +static void _snk_destroy_all_images(void) { + _snk_image_pool_t* p = &_snuklear.image_pool; + for (int i = 0; i < p->pool.size; i++) { + _snk_image_t* img = &p->items[i]; + _snk_destroy_image(_snk_make_image_handle(img->slot.id)); + } +} + +static snk_image_desc_t _snk_image_desc_defaults(const snk_image_desc_t* desc) { + SOKOL_ASSERT(desc); + snk_image_desc_t res = *desc; + res.texture_view.id = _snk_def(res.texture_view.id, _snuklear.def_tex_view.id); + res.sampler.id = _snk_def(res.sampler.id, _snuklear.def_smp.id); + return res; +} + +static snk_desc_t _snk_desc_defaults(const snk_desc_t* desc) { + SOKOL_ASSERT(desc); + snk_desc_t res = *desc; + res.max_vertices = _snk_def(res.max_vertices, 65536); + res.dpi_scale = _snk_def(res.dpi_scale, 1.0f); + res.image_pool_size = _snk_def(res.image_pool_size, 256); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void snk_setup(const snk_desc_t* desc) { + SOKOL_ASSERT(desc); + _snk_clear(&_snuklear, sizeof(_snuklear)); + _snuklear.init_cookie = _SNK_INIT_COOKIE; + _snuklear.desc = _snk_desc_defaults(desc); + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + _snuklear.is_osx = _snk_is_osx(); + #endif + // can keep color_format, depth_format and sample_count as is, + // since sokol_gfx.h will do its own default-value handling + + _snk_setup_image_pool(_snuklear.desc.image_pool_size); + + // initialize Nuklear + nk_bool init_res = nk_init_default(&_snuklear.ctx, 0); + SOKOL_ASSERT(1 == init_res); (void)init_res; // silence unused warning in release mode +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + _snuklear.ctx.clip.copy = _snk_clipboard_copy; + _snuklear.ctx.clip.paste = _snk_clipboard_paste; +#endif + + // create sokol-gfx resources + sg_push_debug_group("sokol-nuklear"); + + // vertex buffer + _snuklear.vertex_buffer_size = (size_t)_snuklear.desc.max_vertices * sizeof(_snk_vertex_t); + _snuklear.vbuf = sg_make_buffer(&(sg_buffer_desc){ + .usage = { + .vertex_buffer = true, + .stream_update = true, + }, + .size = _snuklear.vertex_buffer_size, + .label = "sokol-nuklear-vertices" + }); + + // index buffer + _snuklear.index_buffer_size = (size_t)_snuklear.desc.max_vertices * 3 * sizeof(uint16_t); + _snuklear.ibuf = sg_make_buffer(&(sg_buffer_desc){ + .usage = { + .index_buffer = true, + .stream_update = true, + }, + .size = _snuklear.index_buffer_size, + .label = "sokol-nuklear-indices" + }); + + // default font sampler + _snuklear.font_smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .label = "sokol-nuklear-font-sampler", + }); + + // default user-image sampler + _snuklear.def_smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .label = "sokol-nuklear-default-sampler", + }); + + // default font texture + if (!_snuklear.desc.no_default_font) { + nk_font_atlas_init_default(&_snuklear.atlas); + nk_font_atlas_begin(&_snuklear.atlas); + int font_width = 0, font_height = 0; + const void* pixels = nk_font_atlas_bake(&_snuklear.atlas, &font_width, &font_height, NK_FONT_ATLAS_RGBA32); + SOKOL_ASSERT((font_width > 0) && (font_height > 0)); + _snuklear.font_img = sg_make_image(&(sg_image_desc){ + .width = font_width, + .height = font_height, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .data.mip_levels[0] = { + .ptr = pixels, + .size = (size_t)(font_width * font_height) * sizeof(uint32_t) + }, + .label = "sokol-nuklear-font-image", + }); + _snuklear.font_tex_view = sg_make_view(&(sg_view_desc){ + .texture = { .image = _snuklear.font_img }, + .label = "sokol-nuklear-font-texview", + }); + _snuklear.default_font = snk_make_image(&(snk_image_desc_t){ + .texture_view = _snuklear.font_tex_view, + .sampler = _snuklear.font_smp, + }); + nk_font_atlas_end(&_snuklear.atlas, snk_nkhandle(_snuklear.default_font), 0); + nk_font_atlas_cleanup(&_snuklear.atlas); + if (_snuklear.atlas.default_font) { + nk_style_set_font(&_snuklear.ctx, &_snuklear.atlas.default_font->handle); + // This adds the default cursors into the nuklear overlay for use. + nk_style_load_all_cursors(&_snuklear.ctx, &_snuklear.atlas.cursors[0]); + } + } + + // default user image and texture view + static uint32_t def_pixels[64]; + memset(def_pixels, 0xFF, sizeof(def_pixels)); + _snuklear.def_img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .data.mip_levels[0] = SG_RANGE(def_pixels), + .label = "sokol-nuklear-default-image", + }); + _snuklear.def_tex_view = sg_make_view(&(sg_view_desc){ + .texture = { .image = _snuklear.def_img }, + .label = "sokol-nuklear-default-texview", + }); + + // shader + #if defined SOKOL_METAL + const char* vs_entry = "main0"; + const char* fs_entry = "main0"; + #else + const char* vs_entry = "main"; + const char* fs_entry = "main"; + #endif + sg_range vs_bytecode = { .ptr = 0, .size = 0 }; + sg_range fs_bytecode = { .ptr = 0, .size = 0 }; + const char* vs_source = 0; + const char* fs_source = 0; + #if defined(SOKOL_GLCORE) + vs_source = (const char*)_snk_vs_source_glsl410; + fs_source = (const char*)_snk_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + vs_source = (const char*)_snk_vs_source_glsl300es; + fs_source = (const char*)_snk_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + vs_bytecode = SG_RANGE(_snk_vs_bytecode_metal_macos); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + vs_bytecode = SG_RANGE(_snk_vs_bytecode_metal_ios); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_metal_ios); + break; + default: + vs_source = (const char*)_snk_vs_source_metal_sim; + fs_source = (const char*)_snk_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + vs_bytecode = SG_RANGE(_snk_vs_bytecode_hlsl4); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + vs_source = (const char*)_snk_vs_source_wgsl; + fs_source = (const char*)_snk_fs_source_wgsl; + #elif defined(SOKOL_VULKAN) + vs_bytecode = SG_RANGE(_snk_vs_bytecode_spirv_vk); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_spirv_vk); + #else + vs_source = _snk_vs_source_dummy; + fs_source = _snk_fs_source_dummy; + #endif + _snuklear.shd = sg_make_shader(&(sg_shader_desc){ + .attrs = { + [0] = { .glsl_name = "position", .hlsl_sem_name = "TEXCOORD", .hlsl_sem_index = 0, .base_type = SG_SHADERATTRBASETYPE_FLOAT }, + [1] = { .glsl_name = "texcoord0", .hlsl_sem_name = "TEXCOORD", .hlsl_sem_index = 1, .base_type = SG_SHADERATTRBASETYPE_FLOAT }, + [2] = { .glsl_name = "color0", .hlsl_sem_name = "TEXCOORD", .hlsl_sem_index = 2, .base_type = SG_SHADERATTRBASETYPE_FLOAT }, + }, + .vertex_func = { + .source = vs_source, + .bytecode = vs_bytecode, + .entry = vs_entry, + .d3d11_target = "vs_4_0", + }, + .fragment_func = { + .source = fs_source, + .bytecode = fs_bytecode, + .entry = fs_entry, + .d3d11_target = "ps_4_0", + }, + .uniform_blocks[0] = { + .stage = SG_SHADERSTAGE_VERTEX, + .size = sizeof(_snk_vs_params_t), + .hlsl_register_b_n = 0, + .msl_buffer_n = 0, + .wgsl_group0_binding_n = 0, + .spirv_set0_binding_n = 0, + .glsl_uniforms[0] = { + .glsl_name = "vs_params", + .type = SG_UNIFORMTYPE_FLOAT4, + .array_count = 1, + } + }, + .views[0] = { + .texture = { + .stage = SG_SHADERSTAGE_FRAGMENT, + .image_type = SG_IMAGETYPE_2D, + .sample_type = SG_IMAGESAMPLETYPE_FLOAT, + .hlsl_register_t_n = 0, + .msl_texture_n = 0, + .wgsl_group1_binding_n = 0, + .spirv_set1_binding_n = 0, + }, + }, + .samplers[0] = { + .stage = SG_SHADERSTAGE_FRAGMENT, + .sampler_type = SG_SAMPLERTYPE_FILTERING, + .hlsl_register_s_n = 0, + .msl_sampler_n = 0, + .wgsl_group1_binding_n = 32, + .spirv_set1_binding_n = 32, + }, + .texture_sampler_pairs[0] = { + .stage = SG_SHADERSTAGE_FRAGMENT, + .glsl_name = "tex_smp", + .view_slot = 0, + .sampler_slot = 0 + }, + .label = "sokol-nuklear-shader" + }); + + // pipeline object + _snuklear.pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .attrs = { + [0] = { .offset = offsetof(_snk_vertex_t, pos), .format=SG_VERTEXFORMAT_FLOAT2 }, + [1] = { .offset = offsetof(_snk_vertex_t, uv), .format=SG_VERTEXFORMAT_FLOAT2 }, + [2] = { .offset = offsetof(_snk_vertex_t, col), .format=SG_VERTEXFORMAT_UBYTE4N } + } + }, + .shader = _snuklear.shd, + .index_type = SG_INDEXTYPE_UINT16, + .sample_count = _snuklear.desc.sample_count, + .depth.pixel_format = _snuklear.desc.depth_format, + .colors[0] = { + .pixel_format = _snuklear.desc.color_format, + .write_mask = SG_COLORMASK_RGB, + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA, + .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + } + }, + .label = "sokol-nuklear-pipeline" + }); + + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void snk_shutdown(void) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + nk_free(&_snuklear.ctx); + nk_font_atlas_clear(&_snuklear.atlas); + + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_push_debug_group("sokol-nuklear"); + sg_destroy_pipeline(_snuklear.pip); + sg_destroy_shader(_snuklear.shd); + sg_destroy_sampler(_snuklear.font_smp); + sg_destroy_view(_snuklear.font_tex_view); + sg_destroy_image(_snuklear.font_img); + sg_destroy_sampler(_snuklear.def_smp); + sg_destroy_view(_snuklear.def_tex_view); + sg_destroy_image(_snuklear.def_img); + sg_destroy_buffer(_snuklear.ibuf); + sg_destroy_buffer(_snuklear.vbuf); + sg_pop_debug_group(); + _snk_destroy_all_images(); + _snk_discard_image_pool(); + _snuklear.init_cookie = 0; +} + +SOKOL_API_IMPL struct nk_context* snk_new_frame(void) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + nk_input_begin(&_snuklear.ctx); + if (_snuklear.mouse_did_move) { + nk_input_motion(&_snuklear.ctx, _snuklear.mouse_pos[0], _snuklear.mouse_pos[1]); + _snuklear.mouse_did_move = false; + } + if (_snuklear.mouse_did_scroll) { + nk_input_scroll(&_snuklear.ctx, nk_vec2(_snuklear.mouse_scroll[0], _snuklear.mouse_scroll[1])); + _snuklear.mouse_did_scroll = false; + } + for (int i = 0; i < NK_BUTTON_MAX; i++) { + if (_snuklear.btn_down[i]) { + _snuklear.btn_down[i] = false; + nk_input_button(&_snuklear.ctx, (enum nk_buttons)i, _snuklear.mouse_pos[0], _snuklear.mouse_pos[1], 1); + } else if (_snuklear.btn_up[i]) { + _snuklear.btn_up[i] = false; + nk_input_button(&_snuklear.ctx, (enum nk_buttons)i, _snuklear.mouse_pos[0], _snuklear.mouse_pos[1], 0); + } + } + const size_t char_buffer_len = strlen(_snuklear.char_buffer); + if (char_buffer_len > 0) { + for (size_t i = 0; i < char_buffer_len; i++) { + nk_input_char(&_snuklear.ctx, _snuklear.char_buffer[i]); + } + _snk_clear(_snuklear.char_buffer, NK_INPUT_MAX); + } + for (int i = 0; i < NK_KEY_MAX; i++) { + if (_snuklear.keys_down[i]) { + nk_input_key(&_snuklear.ctx, (enum nk_keys)i, true); + _snuklear.keys_down[i] = 0; + } + if (_snuklear.keys_up[i]) { + nk_input_key(&_snuklear.ctx, (enum nk_keys)i, false); + _snuklear.keys_up[i] = 0; + } + } + nk_input_end(&_snuklear.ctx); + #endif + + nk_clear(&_snuklear.ctx); + return &_snuklear.ctx; +} + +SOKOL_API_IMPL snk_image_t snk_make_image(const snk_image_desc_t* desc) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + SOKOL_ASSERT(desc); + const snk_image_desc_t desc_def = _snk_image_desc_defaults(desc); + snk_image_t img_id = _snk_alloc_image(); + _snk_image_t* img = _snk_lookup_image(img_id.id); + if (img) { + img->slot.state = _snk_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == _SNK_RESOURCESTATE_VALID) || (img->slot.state == _SNK_RESOURCESTATE_FAILED)); + } else { + _SNK_ERROR(IMAGE_POOL_EXHAUSTED); + } + return img_id; +} + +SOKOL_API_IMPL void snk_destroy_image(snk_image_t img_id) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + _snk_destroy_image(img_id); +} + +SOKOL_API_IMPL snk_image_desc_t snk_query_image_desc(snk_image_t img_id) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + _snk_image_t* img = _snk_lookup_image(img_id.id); + if (img) { + return (snk_image_desc_t){ + .texture_view = img->tex_view, + .sampler = img->sampler, + }; + } else { + return (snk_image_desc_t){0}; + } +} + +SOKOL_API_IMPL nk_handle snk_nkhandle(snk_image_t img) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + return (nk_handle) { .id = (int)img.id }; +} + +SOKOL_API_IMPL snk_image_t snk_image_from_nkhandle(nk_handle h) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + return (snk_image_t){ .id = (uint32_t) h.id }; +} + +static void _snk_bind_image_sampler(sg_bindings* bindings, nk_handle h) { + _snk_image_t* img = _snk_lookup_image((uint32_t)h.id); + if (img) { + bindings->views[0] = img->tex_view; + bindings->samplers[0] = img->sampler; + } else { + bindings->views[0] = _snuklear.def_tex_view; + bindings->samplers[0] = _snuklear.def_smp; + } +} + +SOKOL_API_IMPL void snk_render(int width, int height) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + if (_snuklear.desc.enable_set_mouse_cursor) { + for (enum nk_style_cursor c = 0; c < NK_CURSOR_COUNT; ++c) { + if (_snuklear.ctx.style.cursor_active == _snuklear.ctx.style.cursors[c]) { + sapp_mouse_cursor sapp_cur = SAPP_MOUSECURSOR_ARROW; + switch (c) { + case NK_CURSOR_TEXT: sapp_cur = SAPP_MOUSECURSOR_IBEAM; break; + case NK_CURSOR_MOVE: sapp_cur = SAPP_MOUSECURSOR_RESIZE_ALL; break; + case NK_CURSOR_RESIZE_VERTICAL: sapp_cur = SAPP_MOUSECURSOR_RESIZE_NS; break; + case NK_CURSOR_RESIZE_HORIZONTAL: sapp_cur = SAPP_MOUSECURSOR_RESIZE_EW; break; + case NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT: sapp_cur = SAPP_MOUSECURSOR_RESIZE_NESW; break; + case NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT: sapp_cur = SAPP_MOUSECURSOR_RESIZE_NWSE; break; + default: break; + } + sapp_set_mouse_cursor(sapp_cur); + break; + } + } + } + #endif + + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct _snk_vertex_t, pos)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct _snk_vertex_t, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct _snk_vertex_t, col)}, + {NK_VERTEX_LAYOUT_END} + }; + struct nk_convert_config cfg = { + .shape_AA = NK_ANTI_ALIASING_ON, + .line_AA = NK_ANTI_ALIASING_ON, + .vertex_layout = vertex_layout, + .vertex_size = sizeof(_snk_vertex_t), + .vertex_alignment = 4, + .circle_segment_count = 22, + .curve_segment_count = 22, + .arc_segment_count = 22, + .global_alpha = 1.0f + }; + + _snuklear.vs_params.disp_size[0] = (float)width; + _snuklear.vs_params.disp_size[1] = (float)height; + + // Setup vert/index buffers and convert + struct nk_buffer cmds, verts, idx; + nk_buffer_init_default(&cmds); + nk_buffer_init_default(&verts); + nk_buffer_init_default(&idx); + nk_convert(&_snuklear.ctx, &cmds, &verts, &idx, &cfg); + + // Check for vertex- and index-buffer overflow, assert in debug-mode, + // otherwise silently skip rendering + const bool vertex_buffer_overflow = nk_buffer_total(&verts) > _snuklear.vertex_buffer_size; + const bool index_buffer_overflow = nk_buffer_total(&idx) > _snuklear.index_buffer_size; + SOKOL_ASSERT(!vertex_buffer_overflow && !index_buffer_overflow); + if (!vertex_buffer_overflow && !index_buffer_overflow) { + + // Setup rendering + sg_update_buffer(_snuklear.vbuf, &(sg_range){ nk_buffer_memory_const(&verts), nk_buffer_total(&verts) }); + sg_update_buffer(_snuklear.ibuf, &(sg_range){ nk_buffer_memory_const(&idx), nk_buffer_total(&idx) }); + const float dpi_scale = _snuklear.desc.dpi_scale; + const int fb_width = (int)(_snuklear.vs_params.disp_size[0] * dpi_scale); + const int fb_height = (int)(_snuklear.vs_params.disp_size[1] * dpi_scale); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + sg_apply_pipeline(_snuklear.pip); + sg_apply_uniforms(0, &SG_RANGE(_snuklear.vs_params)); + + // Iterate through the command list, rendering each one + const struct nk_draw_command* cmd = NULL; + int idx_offset = 0; + sg_bindings bindings = { + .vertex_buffers[0] = _snuklear.vbuf, + .index_buffer = _snuklear.ibuf, + .index_buffer_offset = idx_offset + }; + nk_draw_foreach(cmd, &_snuklear.ctx, &cmds) { + if (cmd->elem_count > 0) { + _snk_bind_image_sampler(&bindings, cmd->texture); + sg_apply_bindings(&bindings); + sg_apply_scissor_rectf(cmd->clip_rect.x * dpi_scale, + cmd->clip_rect.y * dpi_scale, + cmd->clip_rect.w * dpi_scale, + cmd->clip_rect.h * dpi_scale, + true); + sg_draw(0, (int)cmd->elem_count, 1); + bindings.index_buffer_offset += (int)cmd->elem_count * (int)sizeof(uint16_t); + } + } + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + } + + // Cleanup + nk_buffer_free(&cmds); + nk_buffer_free(&verts); + nk_buffer_free(&idx); +} + +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) +_SOKOL_PRIVATE bool _snk_is_ctrl(uint32_t modifiers) { + if (_snuklear.is_osx) { + return 0 != (modifiers & SAPP_MODIFIER_SUPER); + } else { + return 0 != (modifiers & SAPP_MODIFIER_CTRL); + } +} + +_SOKOL_PRIVATE void _snk_append_char(uint32_t char_code) { + size_t idx = strlen(_snuklear.char_buffer); + if (idxkey_code) { + case SAPP_KEYCODE_C: + if (_snk_is_ctrl(ev->modifiers)) { + return NK_KEY_COPY; + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_X: + if (_snk_is_ctrl(ev->modifiers)) { + return NK_KEY_CUT; + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_A: + if (_snk_is_ctrl(ev->modifiers)) { + return NK_KEY_TEXT_SELECT_ALL; + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_Z: + if (_snk_is_ctrl(ev->modifiers)) { + if (ev->modifiers & SAPP_MODIFIER_SHIFT) { + return NK_KEY_TEXT_REDO; + } else { + return NK_KEY_TEXT_UNDO; + } + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_DELETE: return NK_KEY_DEL; + case SAPP_KEYCODE_ENTER: return NK_KEY_ENTER; + case SAPP_KEYCODE_TAB: return NK_KEY_TAB; + case SAPP_KEYCODE_BACKSPACE: return NK_KEY_BACKSPACE; + case SAPP_KEYCODE_UP: return NK_KEY_UP; + case SAPP_KEYCODE_DOWN: return NK_KEY_DOWN; + case SAPP_KEYCODE_LEFT: return NK_KEY_LEFT; + case SAPP_KEYCODE_RIGHT: return NK_KEY_RIGHT; + case SAPP_KEYCODE_LEFT_SHIFT: return NK_KEY_SHIFT; + case SAPP_KEYCODE_RIGHT_SHIFT: return NK_KEY_SHIFT; + case SAPP_KEYCODE_LEFT_CONTROL: return NK_KEY_CTRL; + case SAPP_KEYCODE_RIGHT_CONTROL: return NK_KEY_CTRL; + default: + return NK_KEY_NONE; + } +} + +SOKOL_API_IMPL bool snk_handle_event(const sapp_event* ev) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + const float dpi_scale = _snuklear.desc.dpi_scale; + switch (ev->type) { + case SAPP_EVENTTYPE_MOUSE_DOWN: + _snuklear.mouse_pos[0] = (int) (ev->mouse_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->mouse_y / dpi_scale); + switch (ev->mouse_button) { + case SAPP_MOUSEBUTTON_LEFT: + _snuklear.btn_down[NK_BUTTON_LEFT] = true; + break; + case SAPP_MOUSEBUTTON_RIGHT: + _snuklear.btn_down[NK_BUTTON_RIGHT] = true; + break; + case SAPP_MOUSEBUTTON_MIDDLE: + _snuklear.btn_down[NK_BUTTON_MIDDLE] = true; + break; + default: + break; + } + break; + case SAPP_EVENTTYPE_MOUSE_UP: + _snuklear.mouse_pos[0] = (int) (ev->mouse_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->mouse_y / dpi_scale); + switch (ev->mouse_button) { + case SAPP_MOUSEBUTTON_LEFT: + _snuklear.btn_up[NK_BUTTON_LEFT] = true; + break; + case SAPP_MOUSEBUTTON_RIGHT: + _snuklear.btn_up[NK_BUTTON_RIGHT] = true; + break; + case SAPP_MOUSEBUTTON_MIDDLE: + _snuklear.btn_up[NK_BUTTON_MIDDLE] = true; + break; + default: + break; + } + break; + case SAPP_EVENTTYPE_MOUSE_MOVE: + _snuklear.mouse_pos[0] = (int) (ev->mouse_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->mouse_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_MOUSE_ENTER: + case SAPP_EVENTTYPE_MOUSE_LEAVE: + for (int i = 0; i < NK_BUTTON_MAX; i++) { + _snuklear.btn_down[i] = false; + _snuklear.btn_up[i] = false; + } + break; + case SAPP_EVENTTYPE_MOUSE_SCROLL: + _snuklear.mouse_scroll[0] = ev->scroll_x; + _snuklear.mouse_scroll[1] = ev->scroll_y; + _snuklear.mouse_did_scroll = true; + break; + case SAPP_EVENTTYPE_TOUCHES_BEGAN: + _snuklear.btn_down[NK_BUTTON_LEFT] = true; + _snuklear.mouse_pos[0] = (int) (ev->touches[0].pos_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->touches[0].pos_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_TOUCHES_MOVED: + _snuklear.mouse_pos[0] = (int) (ev->touches[0].pos_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->touches[0].pos_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_TOUCHES_ENDED: + _snuklear.btn_up[NK_BUTTON_LEFT] = true; + _snuklear.mouse_pos[0] = (int) (ev->touches[0].pos_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->touches[0].pos_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_TOUCHES_CANCELLED: + _snuklear.btn_up[NK_BUTTON_LEFT] = false; + _snuklear.btn_down[NK_BUTTON_LEFT] = false; + break; + case SAPP_EVENTTYPE_KEY_DOWN: + /* intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED */ + if (_snk_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 (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + _snuklear.keys_down[_snk_event_to_nuklearkey(ev)] = true; + break; + case SAPP_EVENTTYPE_KEY_UP: + /* intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED */ + if (_snk_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 (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + _snuklear.keys_up[_snk_event_to_nuklearkey(ev)] = true; + break; + case SAPP_EVENTTYPE_CHAR: + if ((ev->char_code >= 32) && + (ev->char_code != 127) && + (0 == (ev->modifiers & (SAPP_MODIFIER_ALT|SAPP_MODIFIER_CTRL|SAPP_MODIFIER_SUPER)))) + { + _snk_append_char(ev->char_code); + } + break; + case SAPP_EVENTTYPE_CLIPBOARD_PASTED: + _snuklear.keys_down[NK_KEY_PASTE] = _snuklear.keys_up[NK_KEY_PASTE] = true; + break; + default: + break; + } + return nk_item_is_any_active(&_snuklear.ctx); +} + +SOKOL_API_IMPL nk_flags snk_edit_string(struct nk_context *ctx, nk_flags flags, char *memory, int *len, int max, nk_plugin_filter filter) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + nk_flags event = nk_edit_string(ctx, flags, memory, len, max, filter); + if ((event & NK_EDIT_ACTIVATED) && !sapp_keyboard_shown()) { + sapp_show_keyboard(true); + } + if ((event & NK_EDIT_DEACTIVATED) && sapp_keyboard_shown()) { + sapp_show_keyboard(false); + } + return event; +} +#endif // SOKOL_NUKLEAR_NO_SOKOL_APP + +#endif // SOKOL_IMPL diff --git a/thirdparty/sokol/util/sokol_shape.h b/thirdparty/sokol/util/sokol_shape.h new file mode 100644 index 0000000..67221dc --- /dev/null +++ b/thirdparty/sokol/util/sokol_shape.h @@ -0,0 +1,1431 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_SHAPE_IMPL) +#define SOKOL_SHAPE_IMPL +#endif +#ifndef SOKOL_SHAPE_INCLUDED +/* + sokol_shape.h -- create simple primitive shapes for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_SHAPE_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Include the following headers before including sokol_shape.h: + + sokol_gfx.h + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_SHAPE_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_SHAPE_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_shape.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_SHAPE_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + FEATURE OVERVIEW + ================ + sokol_shape.h creates vertices and indices for simple shapes and + builds structs which can be plugged into sokol-gfx resource + creation functions: + + The following shape types are supported: + + - plane + - cube + - sphere (with poles, not geodesic) + - cylinder + - torus (donut) + + Generated vertices look like this: + + typedef struct sshape_vertex_t { + float x, y, z; + uint32_t normal; // packed normal as BYTE4N + uint16_t u, v; // packed uv coords as USHORT2N + uint32_t color; // packed color as UBYTE4N (r,g,b,a); + } sshape_vertex_t; + + Indices are generally 16-bits wide (SG_INDEXTYPE_UINT16) and the indices + are written as triangle-lists (SG_PRIMITIVETYPE_TRIANGLES). + + EXAMPLES: + ========= + + Create multiple shapes into the same vertex- and index-buffer and + render with separate draw calls: + + https://github.com/floooh/sokol-samples/blob/master/sapp/shapes-sapp.c + + Same as the above, but pre-transform shapes and merge them into a single + shape that's rendered with a single draw call. + + https://github.com/floooh/sokol-samples/blob/master/sapp/shapes-transform-sapp.c + + STEP-BY-STEP: + ============= + + Setup an sshape_buffer_t struct with pointers to memory buffers where + generated vertices and indices will be written to: + + ```c + sshape_vertex_t vertices[512]; + uint16_t indices[4096]; + + sshape_buffer_t buf = { + .vertices = { + .buffer = SSHAPE_RANGE(vertices), + }, + .indices = { + .buffer = SSHAPE_RANGE(indices), + } + }; + ``` + + To find out how big those memory buffers must be (in case you want + to allocate dynamically) call the following functions: + + ```c + sshape_sizes_t sshape_plane_sizes(uint32_t tiles); + sshape_sizes_t sshape_box_sizes(uint32_t tiles); + sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks); + sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks); + sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings); + ``` + + The returned sshape_sizes_t struct contains vertex- and index-counts + as well as the equivalent buffer sizes in bytes. For instance: + + ```c + sshape_sizes_t sizes = sshape_sphere_sizes(36, 12); + uint32_t num_vertices = sizes.vertices.num; + uint32_t num_indices = sizes.indices.num; + uint32_t vertex_buffer_size = sizes.vertices.size; + uint32_t index_buffer_size = sizes.indices.size; + ``` + + With the sshape_buffer_t struct that was setup earlier, call any + of the shape-builder functions: + + ```c + sshape_buffer_t sshape_build_plane(const sshape_buffer_t* buf, const sshape_plane_t* params); + sshape_buffer_t sshape_build_box(const sshape_buffer_t* buf, const sshape_box_t* params); + sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* buf, const sshape_sphere_t* params); + sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* buf, const sshape_cylinder_t* params); + sshape_buffer_t sshape_build_torus(const sshape_buffer_t* buf, const sshape_torus_t* params); + ``` + + Note how the sshape_buffer_t struct is both an input value and the + return value. This can be used to append multiple shapes into the + same vertex- and index-buffers (more on this later). + + The second argument is a struct which holds creation parameters. + + For instance to build a sphere with radius 2, 36 "cake slices" and 12 stacks: + + ```c + sshape_buffer_t buf = ...; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t){ + .radius = 2.0f, + .slices = 36, + .stacks = 12, + }); + ``` + + If the provided buffers are big enough to hold all generated vertices and + indices, the "valid" field in the result will be true: + + ```c + assert(buf.valid); + ``` + + The shape creation parameters have "useful defaults", refer to the + actual C struct declarations below to look up those defaults. + + You can also provide additional creation parameters, like a common vertex + color, a debug-helper to randomize colors, tell the shape builder function + to merge the new shape with the previous shape into the same draw-element-range, + or a 4x4 transform matrix to move, rotate and scale the generated vertices: + + ```c + sshape_buffer_t buf = ...; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t){ + .radius = 2.0f, + .slices = 36, + .stacks = 12, + // merge with previous shape into a single element-range + .merge = true, + // set vertex color to red+opaque + .color = sshape_color_4f(1.0f, 0.0f, 0.0f, 1.0f), + // set position to y = 2.0 + .transform = { + .m = { + { 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, 2.0f, 0.0f, 1.0f }, + } + } + }); + assert(buf.valid); + ``` + + The following helper functions can be used to build a packed + color value or to convert from external matrix types: + + ```c + uint32_t sshape_color_4f(float r, float g, float b, float a); + uint32_t sshape_color_3f(float r, float g, float b); + uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b); + sshape_mat4_t sshape_mat4(const float m[16]); + sshape_mat4_t sshape_mat4_transpose(const float m[16]); + ``` + + After the shape builder function has been called, the following functions + are used to extract the build result for plugging into sokol_gfx.h: + + ```c + sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf); + sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf); + sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf); + sg_vertex_buffer_layout_state sshape_vertex_buffer_layout_state(void); + sg_vertex_attr_state sshape_position_vertex_attr_state(void); + sg_vertex_attr_state sshape_normal_vertex_attr_state(void); + sg_vertex_attr_state sshape_texcoord_vertex_attr_state(void); + sg_vertex_attr_state sshape_color_vertex_attr_state(void); + ``` + + The sshape_element_range_t struct contains the base-index and number of + indices which can be plugged into the sg_draw() call: + + ```c + sshape_element_range_t elms = sshape_element_range(&buf); + ... + sg_draw(elms.base_element, elms.num_elements, 1); + ``` + + To create sokol-gfx vertex- and index-buffers from the generated + shape data: + + ```c + // create sokol-gfx vertex buffer + sg_buffer_desc vbuf_desc = sshape_vertex_buffer_desc(&buf); + sg_buffer vbuf = sg_make_buffer(&vbuf_desc); + + // create sokol-gfx index buffer + sg_buffer_desc ibuf_desc = sshape_index_buffer_desc(&buf); + sg_buffer ibuf = sg_make_buffer(&ibuf_desc); + ``` + + The remaining functions are used to populate the vertex-layout item + in sg_pipeline_desc, note that these functions don't depend on the + created geometry, they always return the same result: + + ```c + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .buffers[0] = sshape_vertex_buffer_layout_state(), + .attrs = { + [0] = sshape_position_vertex_attr_state(), + [1] = ssape_normal_vertex_attr_state(), + [2] = sshape_texcoord_vertex_attr_state(), + [3] = sshape_color_vertex_attr_state() + } + }, + ... + }); + ``` + + Note that you don't have to use all generated vertex attributes in the + pipeline's vertex layout, the sg_vertex_buffer_layout_state struct returned + by sshape_vertex_buffer_layout_state() contains the correct vertex stride + to skip vertex components. + + WRITING MULTIPLE SHAPES INTO THE SAME BUFFER + ============================================ + You can merge multiple shapes into the same vertex- and + index-buffers and either render them as a single shape, or + in separate draw calls. + + To build a single shape made of two cubes which can be rendered + in a single draw-call: + + ``` + sshape_vertex_t vertices[128]; + uint16_t indices[16]; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vertices), + .indices.buffer = SSHAPE_RANGE(indices) + }; + + // first cube at pos x=-2.0 (with default size of 1x1x1) + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + {-2.0f, 0.0f, 0.0f, 1.0f }, + } + } + }); + // ...and append another cube at pos pos=+1.0 + // NOTE the .merge = true, this tells the shape builder + // function to not advance the current shape start offset + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .merge = true, + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + {-2.0f, 0.0f, 0.0f, 1.0f }, + } + } + }); + assert(buf.valid); + + // skipping buffer- and pipeline-creation... + + sshape_element_range_t elms = sshape_element_range(&buf); + sg_draw(elms.base_element, elms.num_elements, 1); + ``` + + To render the two cubes in separate draw-calls, the element-ranges used + in the sg_draw() calls must be captured right after calling the + builder-functions: + + ```c + sshape_vertex_t vertices[128]; + uint16_t indices[16]; + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vertices), + .indices.buffer = SSHAPE_RANGE(indices) + }; + + // build a red cube... + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .color = sshape_color_3b(255, 0, 0) + }); + sshape_element_range_t red_cube = sshape_element_range(&buf); + + // append a green cube to the same vertex-/index-buffer: + buf = sshape_build_cube(&bud, &sshape_box_t){ + .color = sshape_color_3b(0, 255, 0); + }); + sshape_element_range_t green_cube = sshape_element_range(&buf); + + // skipping buffer- and pipeline-creation... + + sg_draw(red_cube.base_element, red_cube.num_elements, 1); + sg_draw(green_cube.base_element, green_cube.num_elements, 1); + ``` + + ...that's about all :) + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2020 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_SHAPE_INCLUDED +#include // size_t, offsetof +#include +#include + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_shape.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_SHAPE_API_DECL) +#define SOKOL_SHAPE_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_SHAPE_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_SHAPE_IMPL) +#define SOKOL_SHAPE_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_SHAPE_API_DECL __declspec(dllimport) +#else +#define SOKOL_SHAPE_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sshape_range is a pointer-size-pair struct used to pass memory + blobs into sokol-shape. When initialized from a value type + (array or struct), use the SSHAPE_RANGE() macro to build + an sshape_range struct. +*/ +typedef struct sshape_range { + const void* ptr; + size_t size; +} sshape_range; + +// disabling this for every includer isn't great, but the warning is 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' */ +#endif +#if defined(__cplusplus) +#define SSHAPE_RANGE(x) sshape_range{ &x, sizeof(x) } +#else +#define SSHAPE_RANGE(x) (sshape_range){ &x, sizeof(x) } +#endif + +/* a 4x4 matrix wrapper struct */ +typedef struct sshape_mat4_t { float m[4][4]; } sshape_mat4_t; + +/* vertex layout of the generated geometry */ +typedef struct sshape_vertex_t { + float x, y, z; + uint32_t normal; // packed normal as BYTE4N + uint16_t u, v; // packed uv coords as USHORT2N + uint32_t color; // packed color as UBYTE4N (r,g,b,a); +} sshape_vertex_t; + +/* a range of draw-elements (sg_draw(int base_element, int num_element, ...)) */ +typedef struct sshape_element_range_t { + int base_element; + int num_elements; +} sshape_element_range_t; + +/* number of elements and byte size of build actions */ +typedef struct sshape_sizes_item_t { + uint32_t num; // number of elements + uint32_t size; // the same as size in bytes +} sshape_sizes_item_t; + +typedef struct sshape_sizes_t { + sshape_sizes_item_t vertices; + sshape_sizes_item_t indices; +} sshape_sizes_t; + +/* in/out struct to keep track of mesh-build state */ +typedef struct sshape_buffer_item_t { + sshape_range buffer; // pointer/size pair of output buffer + size_t data_size; // size in bytes of valid data in buffer + size_t shape_offset; // data offset of the most recent shape +} sshape_buffer_item_t; + +typedef struct sshape_buffer_t { + bool valid; + sshape_buffer_item_t vertices; + sshape_buffer_item_t indices; +} sshape_buffer_t; + +/* creation parameters for the different shape types */ +typedef struct sshape_plane_t { + float width, depth; // default: 1.0 + uint16_t tiles; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_plane_t; + +typedef struct sshape_box_t { + float width, height, depth; // default: 1.0 + uint16_t tiles; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_box_t; + +typedef struct sshape_sphere_t { + float radius; // default: 0.5 + uint16_t slices; // default: 5 + uint16_t stacks; // default: 4 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_sphere_t; + +typedef struct sshape_cylinder_t { + float radius; // default: 0.5 + float height; // default: 1.0 + uint16_t slices; // default: 5 + uint16_t stacks; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_cylinder_t; + +typedef struct sshape_torus_t { + float radius; // default: 0.5f + float ring_radius; // default: 0.2f + uint16_t sides; // default: 5 + uint16_t rings; // default: 5 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_torus_t; + +/* shape builder functions */ +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_plane(const sshape_buffer_t* buf, const sshape_plane_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_box(const sshape_buffer_t* buf, const sshape_box_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* buf, const sshape_sphere_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* buf, const sshape_cylinder_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_torus(const sshape_buffer_t* buf, const sshape_torus_t* params); + +/* query required vertex- and index-buffer sizes in bytes */ +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_plane_sizes(uint32_t tiles); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_box_sizes(uint32_t tiles); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings); + +/* extract sokol-gfx desc structs and primitive ranges from build state */ +SOKOL_SHAPE_API_DECL sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf); +SOKOL_SHAPE_API_DECL sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf); +SOKOL_SHAPE_API_DECL sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf); +SOKOL_SHAPE_API_DECL sg_vertex_buffer_layout_state sshape_vertex_buffer_layout_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_position_vertex_attr_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_normal_vertex_attr_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_texcoord_vertex_attr_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_color_vertex_attr_state(void); + +/* helper functions to build packed color value from floats or bytes */ +SOKOL_SHAPE_API_DECL uint32_t sshape_color_4f(float r, float g, float b, float a); +SOKOL_SHAPE_API_DECL uint32_t sshape_color_3f(float r, float g, float b); +SOKOL_SHAPE_API_DECL uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_SHAPE_API_DECL uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b); + +/* adapter function for filling matrix struct from generic float[16] array */ +SOKOL_SHAPE_API_DECL sshape_mat4_t sshape_mat4(const float m[16]); +SOKOL_SHAPE_API_DECL sshape_mat4_t sshape_mat4_transpose(const float m[16]); + +#ifdef __cplusplus +} // extern "C" + +// FIXME: C++ helper functions + +#endif +#endif // SOKOL_SHAPE_INCLUDED + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_SHAPE_IMPL +#define SOKOL_SHAPE_IMPL_INCLUDED (1) + +#include // memcpy +#include // sinf, cosf + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#define _sshape_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sshape_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sshape_white (0xFFFFFFFF) + +typedef struct { float x, y, z, w; } _sshape_vec4_t; +typedef struct { float x, y; } _sshape_vec2_t; + +static inline float _sshape_clamp(float v) { + if (v < 0.0f) return 0.0f; + else if (v > 1.0f) return 1.0f; + else return v; +} + +static inline uint32_t _sshape_pack_ub4_ubyte4n(uint8_t x, uint8_t y, uint8_t z, uint8_t w) { + return (uint32_t)(((uint32_t)w<<24)|((uint32_t)z<<16)|((uint32_t)y<<8)|x); +} + +static inline uint32_t _sshape_pack_f4_ubyte4n(float x, float y, float z, float w) { + uint8_t x8 = (uint8_t) (x * 255.0f); + uint8_t y8 = (uint8_t) (y * 255.0f); + uint8_t z8 = (uint8_t) (z * 255.0f); + uint8_t w8 = (uint8_t) (w * 255.0f); + return _sshape_pack_ub4_ubyte4n(x8, y8, z8, w8); +} + +static inline uint32_t _sshape_pack_f4_byte4n(float x, float y, float z, float w) { + int8_t x8 = (int8_t) (x * 127.0f); + int8_t y8 = (int8_t) (y * 127.0f); + int8_t z8 = (int8_t) (z * 127.0f); + int8_t w8 = (int8_t) (w * 127.0f); + return _sshape_pack_ub4_ubyte4n((uint8_t)x8, (uint8_t)y8, (uint8_t)z8, (uint8_t)w8); +} + +static inline uint16_t _sshape_pack_f_ushortn(float x) { + return (uint16_t) (x * 65535.0f); +} + +static inline _sshape_vec4_t _sshape_vec4(float x, float y, float z, float w) { + _sshape_vec4_t v = { x, y, z, w }; + return v; +} + +static inline _sshape_vec4_t _sshape_vec4_norm(_sshape_vec4_t v) { + float l = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); + if (l != 0.0f) { + return _sshape_vec4(v.x/l, v.y/l, v.z/l, v.w/l); + } + else { + return _sshape_vec4(0.0f, 1.0f, 0.0f, 0.0f); + } +} + +static inline _sshape_vec2_t _sshape_vec2(float x, float y) { + _sshape_vec2_t v = { x, y }; + return v; +} + +static bool _sshape_mat4_isnull(const sshape_mat4_t* m) { + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + if (0.0f != m->m[y][x]) { + return false; + } + } + } + return true; +} + +static sshape_mat4_t _sshape_mat4_identity(void) { + sshape_mat4_t m = { + { + { 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 } + } + }; + return m; +} + +static _sshape_vec4_t _sshape_mat4_mul(const sshape_mat4_t* m, _sshape_vec4_t v) { + _sshape_vec4_t res = { + m->m[0][0]*v.x + m->m[1][0]*v.y + m->m[2][0]*v.z + m->m[3][0]*v.w, + m->m[0][1]*v.x + m->m[1][1]*v.y + m->m[2][1]*v.z + m->m[3][1]*v.w, + m->m[0][2]*v.x + m->m[1][2]*v.y + m->m[2][2]*v.z + m->m[3][2]*v.w, + m->m[0][3]*v.x + m->m[1][3]*v.y + m->m[2][3]*v.z + m->m[3][3]*v.w + }; + return res; +} + +static uint32_t _sshape_plane_num_vertices(uint32_t tiles) { + return (tiles + 1) * (tiles + 1); +} + +static uint32_t _sshape_plane_num_indices(uint32_t tiles) { + return tiles * tiles * 2 * 3; +} + +static uint32_t _sshape_box_num_vertices(uint32_t tiles) { + return (tiles + 1) * (tiles + 1) * 6; +} + +static uint32_t _sshape_box_num_indices(uint32_t tiles) { + return tiles * tiles * 2 * 6 * 3; +} + +static uint32_t _sshape_sphere_num_vertices(uint32_t slices, uint32_t stacks) { + return (slices + 1) * (stacks + 1); +} + +static uint32_t _sshape_sphere_num_indices(uint32_t slices, uint32_t stacks) { + return ((2 * slices * stacks) - (2 * slices)) * 3; +} + +static uint32_t _sshape_cylinder_num_vertices(uint32_t slices, uint32_t stacks) { + return (slices + 1) * (stacks + 5); +} + +static uint32_t _sshape_cylinder_num_indices(uint32_t slices, uint32_t stacks) { + return ((2 * slices * stacks) + (2 * slices)) * 3; +} + +static uint32_t _sshape_torus_num_vertices(uint32_t sides, uint32_t rings) { + return (sides + 1) * (rings + 1); +} + +static uint32_t _sshape_torus_num_indices(uint32_t sides, uint32_t rings) { + return sides * rings * 2 * 3; +} + +static bool _sshape_validate_buffer_item(const sshape_buffer_item_t* item, uint32_t build_size) { + if (0 == item->buffer.ptr) { + return false; + } + if (0 == item->buffer.size) { + return false; + } + if ((item->data_size + build_size) > item->buffer.size) { + return false; + } + if (item->shape_offset > item->data_size) { + return false; + } + return true; +} + +static bool _sshape_validate_buffer(const sshape_buffer_t* buf, uint32_t num_vertices, uint32_t num_indices) { + if (!_sshape_validate_buffer_item(&buf->vertices, num_vertices * sizeof(sshape_vertex_t))) { + return false; + } + if (!_sshape_validate_buffer_item(&buf->indices, num_indices * sizeof(uint16_t))) { + return false; + } + return true; +} + +static void _sshape_advance_offset(sshape_buffer_item_t* item) { + item->shape_offset = item->data_size; +} + +static uint16_t _sshape_base_index(const sshape_buffer_t* buf) { + return (uint16_t) (buf->vertices.data_size / sizeof(sshape_vertex_t)); +} + +static sshape_plane_t _sshape_plane_defaults(const sshape_plane_t* params) { + sshape_plane_t res = *params; + res.width = _sshape_def_flt(res.width, 1.0f); + res.depth = _sshape_def_flt(res.depth, 1.0f); + res.tiles = _sshape_def(res.tiles, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_box_t _sshape_box_defaults(const sshape_box_t* params) { + sshape_box_t res = *params; + res.width = _sshape_def_flt(res.width, 1.0f); + res.height = _sshape_def_flt(res.height, 1.0f); + res.depth = _sshape_def_flt(res.depth, 1.0f); + res.tiles = _sshape_def(res.tiles, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_sphere_t _sshape_sphere_defaults(const sshape_sphere_t* params) { + sshape_sphere_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.slices = _sshape_def(res.slices, 5); + res.stacks = _sshape_def(res.stacks, 4); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_cylinder_t _sshape_cylinder_defaults(const sshape_cylinder_t* params) { + sshape_cylinder_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.height = _sshape_def_flt(res.height, 1.0f); + res.slices = _sshape_def(res.slices, 5); + res.stacks = _sshape_def(res.stacks, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_torus_t _sshape_torus_defaults(const sshape_torus_t* params) { + sshape_torus_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.ring_radius = _sshape_def_flt(res.ring_radius, 0.2f); + res.sides = _sshape_def_flt(res.sides, 5); + res.rings = _sshape_def_flt(res.rings, 5); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static void _sshape_add_vertex(sshape_buffer_t* buf, _sshape_vec4_t pos, _sshape_vec4_t norm, _sshape_vec2_t uv, uint32_t color) { + size_t offset = buf->vertices.data_size; + SOKOL_ASSERT((offset + sizeof(sshape_vertex_t)) <= buf->vertices.buffer.size); + buf->vertices.data_size += sizeof(sshape_vertex_t); + sshape_vertex_t* v_ptr = (sshape_vertex_t*) ((uint8_t*)buf->vertices.buffer.ptr + offset); + v_ptr->x = pos.x; + v_ptr->y = pos.y; + v_ptr->z = pos.z; + v_ptr->normal = _sshape_pack_f4_byte4n(norm.x, norm.y, norm.z, norm.w); + v_ptr->u = _sshape_pack_f_ushortn(uv.x); + v_ptr->v = _sshape_pack_f_ushortn(uv.y); + v_ptr->color = color; +} + +static void _sshape_add_triangle(sshape_buffer_t* buf, uint16_t i0, uint16_t i1, uint16_t i2) { + size_t offset = buf->indices.data_size; + SOKOL_ASSERT((offset + 3*sizeof(uint16_t)) <= buf->indices.buffer.size); + buf->indices.data_size += 3*sizeof(uint16_t); + uint16_t* i_ptr = (uint16_t*) ((uint8_t*)buf->indices.buffer.ptr + offset); + i_ptr[0] = i0; + i_ptr[1] = i1; + i_ptr[2] = i2; +} + +static uint32_t _sshape_rand_color(uint32_t* xorshift_state) { + // xorshift32 + uint32_t x = *xorshift_state; + x ^= x<<13; + x ^= x>>17; + x ^= x<<5; + *xorshift_state = x; + + // rand => bright color with alpha 1.0 + x |= 0xFF000000; + return x; + +} + +/*=== PUBLIC API FUNCTIONS ===================================================*/ +SOKOL_API_IMPL uint32_t sshape_color_4f(float r, float g, float b, float a) { + return _sshape_pack_f4_ubyte4n(_sshape_clamp(r), _sshape_clamp(g), _sshape_clamp(b), _sshape_clamp(a)); +} + +SOKOL_API_IMPL uint32_t sshape_color_3f(float r, float g, float b) { + return _sshape_pack_f4_ubyte4n(_sshape_clamp(r), _sshape_clamp(g), _sshape_clamp(b), 1.0f); +} + +SOKOL_API_IMPL uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return _sshape_pack_ub4_ubyte4n(r, g, b, a); +} + +SOKOL_API_IMPL uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b) { + return _sshape_pack_ub4_ubyte4n(r, g, b, 255); +} + +SOKOL_API_IMPL sshape_mat4_t sshape_mat4(const float m[16]) { + sshape_mat4_t res; + memcpy(&res.m[0][0], &m[0], 64); + return res; +} + +SOKOL_API_IMPL sshape_mat4_t sshape_mat4_transpose(const float m[16]) { + sshape_mat4_t res; + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + res.m[r][c] = m[c*4 + r]; + } + } + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_plane_sizes(uint32_t tiles) { + SOKOL_ASSERT(tiles >= 1); + 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); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_box_sizes(uint32_t tiles) { + SOKOL_ASSERT(tiles >= 1); + 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); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +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} }; + 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); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +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} }; + 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); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +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} }; + 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); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +/* + Geometry layout for plane (4 tiles): + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ 25 vertices (tiles + 1) * (tiles + 1) + |\ |\ |\ |\ | 32 triangles (tiles + 1) * (tiles + 1) * 2 + | \| \| \| \| + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_plane(const sshape_buffer_t* in_buf, const sshape_plane_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_plane_t params = _sshape_plane_defaults(in_params); + const uint32_t num_vertices = _sshape_plane_num_vertices(params.tiles); + const uint32_t num_indices = _sshape_plane_num_indices(params.tiles); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + // write vertices + uint32_t rand_seed = 0x12345678; + const float x0 = -params.width * 0.5f; + const float z0 = params.depth * 0.5f; + const float dx = params.width / params.tiles; + const float dz = -params.depth / params.tiles; + const float duv = 1.0f / params.tiles; + _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, _sshape_vec4(0.0f, 1.0f, 0.0f, 0.0f))); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + const _sshape_vec4_t pos = _sshape_vec4(x0 + dx*ix, 0.0f, z0 + dz*iz, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(duv*ix, duv*iz); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // write indices + for (uint16_t j = 0; j < params.tiles; j++) { + for (uint16_t i = 0; i < params.tiles; i++) { + const uint16_t i0 = start_index + (j * (params.tiles + 1)) + i; + const uint16_t i1 = i0 + 1; + const uint16_t i2 = i0 + params.tiles + 1; + const uint16_t i3 = i2 + 1; + _sshape_add_triangle(&buf, i0, i1, i3); + _sshape_add_triangle(&buf, i0, i3, i2); + } + } + return buf; +} + +SOKOL_API_IMPL sshape_buffer_t sshape_build_box(const sshape_buffer_t* in_buf, const sshape_box_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_box_t params = _sshape_box_defaults(in_params); + const uint32_t num_vertices = _sshape_box_num_vertices(params.tiles); + const uint32_t num_indices = _sshape_box_num_indices(params.tiles); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + // build vertices + uint32_t rand_seed = 0x12345678; + const float x0 = -params.width * 0.5f; + const float x1 = params.width * 0.5f; + const float y0 = -params.height * 0.5f; + const float y1 = params.height * 0.5f; + const float z0 = -params.depth * 0.5f; + const float z1 = params.depth * 0.5f; + const float dx = params.width / params.tiles; + const float dy = params.height / params.tiles; + const float dz = params.depth / params.tiles; + const float duv = 1.0f / params.tiles; + + // bottom/top vertices + for (uint32_t top_bottom = 0; top_bottom < 2; top_bottom++) { + _sshape_vec4_t pos = _sshape_vec4(0.0f, (0==top_bottom) ? y0:y1, 0.0f, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(0.0f, (0==top_bottom) ? -1.0f:1.0f, 0.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + pos.x = (0==top_bottom) ? (x0 + dx * ix) : (x1 - dx * ix); + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + pos.z = z0 + dz * iz; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(ix * duv, iz * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // left/right vertices + for (uint32_t left_right = 0; left_right < 2; left_right++) { + _sshape_vec4_t pos = _sshape_vec4((0==left_right) ? x0:x1, 0.0f, 0.0f, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4((0==left_right) ? -1.0f:1.0f, 0.0f, 0.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t iy = 0; iy <= params.tiles; iy++) { + pos.y = (0==left_right) ? (y1 - dy * iy) : (y0 + dy * iy); + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + pos.z = z0 + dz * iz; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(iy * duv, iz * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // front/back vertices + for (uint32_t front_back = 0; front_back < 2; front_back++) { + _sshape_vec4_t pos = _sshape_vec4(0.0f, 0.0f, (0==front_back) ? z0:z1, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(0.0f, 0.0f, (0==front_back) ? -1.0f:1.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + pos.x = (0==front_back) ? (x1 - dx * ix) : (x0 + dx * ix); + for (uint32_t iy = 0; iy <= params.tiles; iy++) { + pos.y = y0 + dy * iy; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(ix * duv, iy * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // build indices + const uint16_t verts_per_face = (params.tiles + 1) * (params.tiles + 1); + for (uint16_t face = 0; face < 6; face++) { + uint16_t face_start_index = start_index + face * verts_per_face; + for (uint16_t j = 0; j < params.tiles; j++) { + for (uint16_t i = 0; i < params.tiles; i++) { + const uint16_t i0 = face_start_index + (j * (params.tiles + 1)) + i; + const uint16_t i1 = i0 + 1; + const uint16_t i2 = i0 + params.tiles + 1; + const uint16_t i3 = i2 + 1; + _sshape_add_triangle(&buf, i0, i1, i3); + _sshape_add_triangle(&buf, i0, i3, i2); + } + } + } + return buf; +} + +/* + Geometry layout for spheres is as follows (for 5 slices, 4 stacks): + + + + + + + + north pole + |\ |\ |\ |\ |\ + | \| \| \| \| \ + +--+--+--+--+--+ 30 vertices (slices + 1) * (stacks + 1) + |\ |\ |\ |\ |\ | 30 triangles (2 * slices * stacks) - (2 * slices) + | \| \| \| \| \| 2 orphaned vertices + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + \ |\ |\ |\ |\ | + \| \| \| \| \| + + + + + + + south pole +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* in_buf, const sshape_sphere_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_sphere_t params = _sshape_sphere_defaults(in_params); + const uint32_t num_vertices = _sshape_sphere_num_vertices(params.slices, params.stacks); + const uint32_t num_indices = _sshape_sphere_num_indices(params.slices, params.stacks); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float pi = 3.14159265358979323846f; + const float two_pi = 2.0f * pi; + const float du = 1.0f / params.slices; + const float dv = 1.0f / params.stacks; + + // generate vertices + for (uint32_t stack = 0; stack <= params.stacks; stack++) { + const float stack_angle = (pi * stack) / params.stacks; + const float sin_stack = sinf(stack_angle); + const float cos_stack = cosf(stack_angle); + for (uint32_t slice = 0; slice <= params.slices; slice++) { + const float slice_angle = (two_pi * slice) / params.slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t norm = _sshape_vec4(-sin_slice * sin_stack, cos_stack, cos_slice * sin_stack, 0.0f); + const _sshape_vec4_t pos = _sshape_vec4(norm.x * params.radius, norm.y * params.radius, norm.z * params.radius, 1.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(1.0f - slice * du, 1.0f - stack * dv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // generate indices + { + // north-pole triangles + const uint16_t row_a = start_index; + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice, row_b + slice + 1); + } + } + // stack triangles + for (uint16_t stack = 1; stack < params.stacks - 1; stack++) { + const uint16_t row_a = start_index + stack * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_a + slice + 1); + _sshape_add_triangle(&buf, row_a + slice, row_b + slice, row_b + slice + 1); + } + } + { + // south-pole triangles + const uint16_t row_a = start_index + (params.stacks - 1) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_a + slice + 1); + } + } + return buf; +} + +/* + Geometry for cylinders is as follows (2 stacks, 5 slices): + + + + + + + + + |\ |\ |\ |\ |\ + | \| \| \| \| \ + +--+--+--+--+--+ + +--+--+--+--+--+ 42 vertices (2 wasted) (slices + 1) * (stacks + 5) + |\ |\ |\ |\ |\ | 30 triangles (2 * slices * stacks) + (2 * slices) + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + +--+--+--+--+--+ + \ |\ |\ |\ |\ | + \| \| \| \| \| + + + + + + + +*/ +static void _sshape_build_cylinder_cap_pole(sshape_buffer_t* buf, const sshape_cylinder_t* params, float pos_y, float norm_y, float du, float v, uint32_t* rand_seed) { + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f))); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, pos_y, 0.0f, 1.0f)); + for (uint32_t slice = 0; slice <= params->slices; slice++) { + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params->random_colors ? _sshape_rand_color(rand_seed) : params->color; + _sshape_add_vertex(buf, tpos, tnorm, uv, color); + } +} + +static void _sshape_build_cylinder_cap_ring(sshape_buffer_t* buf, const sshape_cylinder_t* params, float pos_y, float norm_y, float du, float v, uint32_t* rand_seed) { + const float two_pi = 2.0f * 3.14159265358979323846f; + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f))); + for (uint32_t slice = 0; slice <= params->slices; slice++) { + const float slice_angle = (two_pi * slice) / params->slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t pos = _sshape_vec4(sin_slice * params->radius, pos_y, cos_slice * params->radius, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms->transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params->random_colors ? _sshape_rand_color(rand_seed) : params->color; + _sshape_add_vertex(buf, tpos, tnorm, uv, color); + } +} + +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* in_buf, const sshape_cylinder_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_cylinder_t params = _sshape_cylinder_defaults(in_params); + const uint32_t num_vertices = _sshape_cylinder_num_vertices(params.slices, params.stacks); + const uint32_t num_indices = _sshape_cylinder_num_indices(params.slices, params.stacks); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float two_pi = 2.0f * 3.14159265358979323846f; + const float du = 1.0f / params.slices; + const float dv = 1.0f / (params.stacks + 2); + const float y0 = params.height * 0.5f; + const float y1 = -params.height * 0.5f; + const float dy = params.height / params.stacks; + + // generate vertices + _sshape_build_cylinder_cap_pole(&buf, ¶ms, y0, 1.0f, du, 0.0f, &rand_seed); + _sshape_build_cylinder_cap_ring(&buf, ¶ms, y0, 1.0f, du, dv, &rand_seed); + for (uint32_t stack = 0; stack <= params.stacks; stack++) { + const float y = y0 - dy * stack; + const float v = dv * stack + dv; + for (uint32_t slice = 0; slice <= params.slices; slice++) { + const float slice_angle = (two_pi * slice) / params.slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t pos = _sshape_vec4(sin_slice * params.radius, y, cos_slice * params.radius, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec4_t norm = _sshape_vec4(sin_slice, 0.0f, cos_slice, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + _sshape_build_cylinder_cap_ring(&buf, ¶ms, y1, -1.0f, du, 1.0f - dv, &rand_seed); + _sshape_build_cylinder_cap_pole(&buf, ¶ms, y1, -1.0f, du, 1.0f, &rand_seed); + + // generate indices + { + // top-cap indices + const uint16_t row_a = start_index; + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_b + slice); + } + } + // shaft triangles + for (uint16_t stack = 0; stack < params.stacks; stack++) { + const uint16_t row_a = start_index + (stack + 2) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_a + slice + 1, row_b + slice + 1); + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_b + slice); + } + } + { + // bottom-cap indices + const uint16_t row_a = start_index + (params.stacks + 3) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_a + slice + 1, row_b + slice + 1); + } + } + return buf; +} + +/* + Geometry layout for torus (sides = 4, rings = 5): + + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ 30 vertices (sides + 1) * (rings + 1) + |\ |\ |\ |\ |\ | 40 triangles (2 * sides * rings) + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_torus(const sshape_buffer_t* in_buf, const sshape_torus_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_torus_t params = _sshape_torus_defaults(in_params); + const uint32_t num_vertices = _sshape_torus_num_vertices(params.sides, params.rings); + const uint32_t num_indices = _sshape_torus_num_indices(params.sides, params.rings); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float two_pi = 2.0f * 3.14159265358979323846f; + const float dv = 1.0f / params.sides; + const float du = 1.0f / params.rings; + + // generate vertices + for (uint32_t side = 0; side <= params.sides; side++) { + const float phi = (side * two_pi) / params.sides; + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + for (uint32_t ring = 0; ring <= params.rings; ring++) { + const float theta = (ring * two_pi) / params.rings; + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + // torus surface position + const float spx = sin_theta * (params.radius - (params.ring_radius * cos_phi)); + const float spy = sin_phi * params.ring_radius; + const float spz = cos_theta * (params.radius - (params.ring_radius * cos_phi)); + + // torus position with ring-radius zero (for normal computation) + const float ipx = sin_theta * params.radius; + const float ipy = 0.0f; + const float ipz = cos_theta * params.radius; + + const _sshape_vec4_t pos = _sshape_vec4(spx, spy, spz, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(spx - ipx, spy - ipy, spz - ipz, 0.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec2_t uv = _sshape_vec2(ring * du, 1.0f - side * dv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // generate indices + for (uint16_t side = 0; side < params.sides; side++) { + const uint16_t row_a = start_index + side * (params.rings + 1); + const uint16_t row_b = row_a + params.rings + 1; + for (uint16_t ring = 0; ring < params.rings; ring++) { + _sshape_add_triangle(&buf, row_a + ring, row_a + ring + 1, row_b + ring + 1); + _sshape_add_triangle(&buf, row_a + ring, row_b + ring + 1, row_b + ring); + } + } + return buf; +} + +SOKOL_API_IMPL sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + sg_buffer_desc desc = { 0 }; + if (buf->valid) { + desc.usage.vertex_buffer = true; + desc.usage.immutable = true; + desc.data.ptr = buf->vertices.buffer.ptr; + desc.data.size = buf->vertices.data_size; + } + return desc; +} + +SOKOL_API_IMPL sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + sg_buffer_desc desc = { 0 }; + if (buf->valid) { + desc.usage.index_buffer = true; + desc.usage.immutable = true; + desc.data.ptr = buf->indices.buffer.ptr; + desc.data.size = buf->indices.data_size; + } + return desc; +} + +SOKOL_SHAPE_API_DECL sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + SOKOL_ASSERT(buf->indices.shape_offset < buf->indices.data_size); + SOKOL_ASSERT(0 == (buf->indices.shape_offset & (sizeof(uint16_t) - 1))); + SOKOL_ASSERT(0 == (buf->indices.data_size & (sizeof(uint16_t) - 1))); + sshape_element_range_t range = { 0 }; + range.base_element = (int) (buf->indices.shape_offset / sizeof(uint16_t)); + if (buf->valid) { + range.num_elements = (int) ((buf->indices.data_size - buf->indices.shape_offset) / sizeof(uint16_t)); + } + else { + range.num_elements = 0; + } + return range; +} + +SOKOL_API_IMPL sg_vertex_buffer_layout_state sshape_vertex_buffer_layout_state(void) { + sg_vertex_buffer_layout_state state = { 0 }; + state.stride = sizeof(sshape_vertex_t); + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_position_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, x); + state.format = SG_VERTEXFORMAT_FLOAT3; + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_normal_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, normal); + state.format = SG_VERTEXFORMAT_BYTE4N; + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_texcoord_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, u); + state.format = SG_VERTEXFORMAT_USHORT2N; + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_color_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, color); + state.format = SG_VERTEXFORMAT_UBYTE4N; + return state; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif // SOKOL_SHAPE_IMPL diff --git a/thirdparty/sokol/util/sokol_spine.h b/thirdparty/sokol/util/sokol_spine.h new file mode 100644 index 0000000..a2b3282 --- /dev/null +++ b/thirdparty/sokol/util/sokol_spine.h @@ -0,0 +1,6701 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_SPINE_IMPL) +#define SOKOL_SPINE_IMPL +#endif +#ifndef SOKOL_SPINE_INCLUDED +/* + sokol_spine.h -- a sokol-gfx renderer for the spine-c runtime + (see https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/spine-c) + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_SPINE_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + SOKOL_VULKAN + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_SPINE_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_SPINE_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_spine.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_SPINE_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_spine.h: + + sokol_gfx.h + + Include the following headers before include the sokol_spine.h *IMPLEMENTATION*: + + spine/spine.h + + You'll also need to compile and link with the spine-c runtime: + + https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/spine-c/spine-c + + + FEATURE OVERVIEW + ================ + sokol_spine.h is a sokol-gfx renderer and 'handle wrapper' for Spine + (http://en.esotericsoftware.com/spine-in-depth) on top of the + spine-c runtime: http://en.esotericsoftware.com/spine-c (source code: + https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/spine-c/spine-c). + + The sokol-gfx renderer allows to manage multiple contexts for rendering + Spine scenes into different sokol-gfx render passes (similar to sokol-gl and + sokol-debugtext), allows to split rendering into layers to mix Spine + rendering with other rendering operations, and it automatically batches + adjacent draw calls for Spine objects that use the same texture and in the + same layer. + + Sokol-spine wraps 'raw' spine-c objects with tagged index handles. This + eliminates the risk of memory corruption via dangling pointers. Any + API calls involving invalid objects either result in a no-op, or + in a proper error. + + The sokol-spine API exposes four 'base object types', and a number of + 'subobject types' which are owned by base objects. + + Base object types are: + + - sspine_atlas: A wrapper around a spine-c spAtlas object, each spAtlas + object owns at least one spAtlasPage object, and each spAtlasPage object + owns one sokol-gfx image, view and sampler object. + + - sspine_skeleton: A skeleton object requires an atlas object for creation, + and is a wrapper around one spine-c spSkeletonData and one + spAnimationStateData object. both contain the shared static data for + individual spine instances + + - sspine_instance: Instance objects are created from skeleton objects. + Instances are the objects that are actually getting rendered. Each instance + tracks its own transformation and animation state, but otherwise just + references shared data of the skeleton object it was created from. An + sspine_instance object is a wrapper around one spine-c spSkeleton, + spAnimationState and spSkeletonClipping object each. + + - sspine_skinset: Skin-set objects are collections of skins which define + the look of an instance. Some Spine scenes consist of combinable skins + (for instance a human character could offer different skins for different + types of clothing, hats, scarfs, shirts, pants, and so on..., and a skin + set would represent a specific outfit). + + Subobject types allow to inspect and manipulate Spine objects in more detail: + + - sspine_anim: Each skeleton object usually offers animations which can + then be scheduled and mixed on an instance. + + - sspine_bone: Bone objects are the hierarchical transform nodes of + a skeleton. The sokol-spine API allows both to inspect the shared + static bone attributes of an sspine_skeleton object, as well as + inspecting and manipulating the per-instance bone attributes + on an sspine_instance object. + + - sspine_event: A running Spine animation may fire 'events' at certain + positions in time (for instance a 'footstep' event whenever a foot + hits the ground). Events can be used to play sound effects (or visual + effects) at the right time. + + - sspine_iktarget: Allows to set the target position for a group of + bones controlled by inverse kinematics. + + There's a couple of other subobject types which are mostly useful to + inspect the interior structure of skeletons. Those will be explained + in detail further down. + + MINIMAL API USAGE OVERVIEW + ========================== + During initialization: + + - call sspine_setup() after initializing sokol-gfx + - create an atlas object from a Spine atlas file with sspine_make_atlas() + - load and initialize the sokol-gfx image, view and sampler objects for the texture + data referenced by the atlas + - create a skeleton object from a Spine skeleton file with sspine_make_skeleton() + - create at least one instance object with sspine_make_instance() + + In the frame loop, outside of sokol-gfx render passes: + + - if needed, move instances around with sspine_set_position() + - if needed, schedule new animations with sspine_set_animation() and sspine_add_animation() + - each frame, advance the current instance animation state with sspine_update_instance() + - each frame, render instances with sspine_draw_instance_in_layer(), this just records + vertices, indices and draw commands into internal buffers, but does no actual + sokol-gfx rendering + + In the frame loop, inside a sokol-gfx render pass: + + - call sspine_draw_layer() to draw all previously recorded instances in a specific layer + + On shutdown: + + - call sspine_shutdown(), ideally before shutting down sokol-gfx + + QUICKSTART STEP BY STEP + ======================= + For a simple demo program using sokol_app.h, sokol_gfx.h and sokol_fetch.h, + see here: [TODO: add link to spine-simple-sapp wasm demo]. + + - sokol_spine.h must be included after sokol_gfx.h (this is true both + for the declaration and implementation): + + #include "sokol_gfx.h" + #include "sokol_spine.h" + + - ...and sokol_gfx.h must be initialized before sokol_spine.h: + + sg_setup(&(sg_desc){ ... }); + sspine_setup(&(sspine_desc){ ... }); + + - You should always provide a logging callback to sokol-spine, otherwise + no warning or errors will be logged. The easiest way is to use sokol_log.h + for this: + + #include "sokol_log.h" + + sspine_setup(&(sspine_desc){ + .logger = { + .func = slog_func + } + }); + + - You can tweak the memory usage of sokol-spine by limiting or expanding the + maximum number of vertices, draw commands and pool sizes: + + sspine_setup(&(sspine_desc){ + .max_vertices = 1024, // default: (1<<16) = 65536 + .max_commands = 128, // default: (1<<14) = 16384 + .context_pool_size = 1, // default: 4 + .atlas_pool_size = 1, // default: 64 + .skeleton_pool_size = 1, // default: 64 + .skinset_pool_size = 1, // default: 64 + .instance_pool_size = 16, // default: 1024 + .logger = { + .func = slog_func, + } + }); + + Sokol-spine uses 32-bit vertex-indices for rendering + (SG_INDEXTYPE_UINT32), so that the maximum number of Spine vertices + in a frame isn't limited to (1<<16). + + - You can override memory allocation and logging with your own + functions, this is explained in detail further down: + + sspine_setup(&(sspine_desc){ + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + }, + .logger = { + .log_func = my_log_func, + .user_data = ..., + } + }); + + - After initialization, the first thing you need is an sspine_atlas object. + Sokol-spine doesn't concern itself with file IO, it expects all external + data to be provided as pointer/size pairs: + + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = { + .ptr = ..., // pointer to Spine atlas file data in memory + .size = ..., // atlas file data size in bytes + } + }); + assert(sspine_atlas_valid(atlas)); + + If you load the atlas data asynchronously, you can still run your + per-frame rendering code without waiting for the atlas data to be loaded + and the atlas to be created. This works because calling sokol-spine + functions with 'invalid' object handles is a valid no-op. + + - Optionally you can override some or all of the atlas texture creation parameters: + + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = { ... }, + .overrides = { + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .mipmap_filter = SG_FILTER_NEAREST, + .wrap_u = SG_WRAP_MIRROR, + .wrap_v = SG_WRAP_MIRROR, + .premul_alpha_enabled = ..., + .premul_alpha_disabled = ..., + } + }); + + - The atlas file itself doesn't contain any texture data, it only contains + filenames of the required textures. Sokol-spine has already allocated + a sokol-gfx sg_image, sg_view and sg_sampler handle for each required texture, but the + actual loading and initialization must be performed by user code: + + // iterate over atlas textures and initialize sokol-gfx image objects + // with existing handles + const int num = sspine_num_images(atlas); + for (int i = 0; i < num; i++) { + const sspine_image img = sspine_image_by_index(atlas, i); + const sspine_image_info img_info = sspine_get_image_info(img); + assert(img_info.valid); + assert(!img_info.filename.truncated); + + // the filename is now in img_info.filename.cstr, 'somehow' + // load and decode the image data into memory, and then + // initialize the sokol-gfx image, view and sampler from the existing + // handles in img_info.sgimage: + sg_init_image(img_info.sgimage, &(sg_image_desc){ + .width = ..., + .height = ..., + .pixel_format = ..., + .data.subimage[0][0] = { + .ptr = ..., // pointer to decoded image pixel data + .size = ..., // size of decoded image pixel data in bytes + } + }); + sg_init_view(img_info.sgview, &(sg_view_desc){ + .texture = { .image = img_info.sgimage }, + }); + sg_init_sampler(img_info.sgsampler, &(sg_image_desc){ + .min_filter = img_info.min_filter, + .mag_filter = img_info.mag_filter, + .mipmap_filter = img_info.mipmap_filter, + .wrap_u = img_info.wrap_u, + .wrap_v = img_info.wrap_v, + }); + } + + If you load the image data asynchronously, you can still simply start rendering + before the image data is loaded. This works because sokol-gfx will silently drop + any rendering operations that involve 'incomplete' objects. + + - Once an atlas object has been created (independently from loading any image data), + an sspine_skeleton object is needed next. This requires a valid atlas object + handle as input, and a pointer to the Spine skeleton file data loaded into memory. + + Spine skeleton files come in two flavours: binary or json, for binary data, + a ptr/size pair must be provided: + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, // atlas must be a valid sspine_atlas handle + .binary_data = { + .ptr = ..., // pointer to binary skeleton data in memory + .size = ..., // size of binary skeleton data in bytes + } + }); + assert(sspine_skeleton_valid(skeleton)); + + For JSON skeleton file data, the data must be provided as a zero-terminated C string: + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .json_data = ..., // JSON skeleton data as zero-terminated(!) C-string + }); + + Like with all sokol-spine objects, if you load the skeleton data asynchronously + and only then create a skeleton object, you can already start rendering before + the data is loaded and the Spine objects have been created. Any operations + involving 'incomplete' handles will be dropped. + + - You can pre-scale the Spine scene size, and you can provide a default cross-fade + duration for animation mixing: + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .binary_data = { ... }, + .prescale = 0.5f, // scale to half-size + .anim_default_mix = 0.2f, // default anim mixing cross-fade duration 0.2 seconds + }); + + - Once the skeleton object has been created, it's finally time to create one or many instance objects. + If you want to independently render and animate the 'same' Spine object many times in a frame, + you should only create one sspine_skeleton object, and then as many sspine_instance object + as needed from the shared skeleton object: + + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = skeleton, // must be a valid skeleton handle + }); + assert(sspine_instance_valid(instance)); + + After creation, the sspine_instance will have a 'default skin' set as its appearance. + + - To set the position of an instance: + + sspine_set_position(inst, (sspine_vec2){ .x=..., .y=... }); + + Sokol-spine doesn't define a specific unit (like pixels or meters), instead the + rendering coordinate system is defined later at 'render time'. + + - To schedule an initial looping animation by its name: + + // first lookup up the animation by name on the skeleton: + sspine_anim anim = sspine_anim_by_name(skeleton, "walk"); + assert(sspine_anim_valid(anim)); + + // then schedule the animation on the instance, on mixer track 0, as looping: + sspine_set_animation(instance, anim, 0, true); + + Scheduling and mixing animations will be explained in more detail further down. + + - To advance and mix instance animations: + + sspine_update_instance(instance, delta_time_in_seconds); + + Usually you'd call this each frame for each active instance with the + frame duration in seconds. + + - Now it's finally time to 'render' the instance at its current position and + animation state: + + sspine_draw_instance_in_layer(instance, 0); + + Instances are generally rendered into numbered virtual 'render layers' (in this + case, layer 0). Layers are useful for interleaving sokol-spine rendering + with other rendering commands (like background and foreground tile maps, + sprites or text). + + - It's important to note that no actual sokol-gfx rendering happens in + sspine_draw_instance_in_layer(), instead only vertices, indices and + draw commands are recorded into internal memory buffers. + + - The only sokol-spine function which *must* (and should) be called inside + a sokol-gfx rendering pass is sspine_draw_layer(). + + This renders all draw commands that have been recorded previously in a + specific layer via sspine_draw_instance_in_layer(). + + const sspine_layer_transform tform = { ... }; + + sg_begin_pass(...); + sspine_draw_layer(0, tform); + sg_end_pass(); + sg_commit(); + + IMPORTANT: DO *NOT* MIX any calls to sspine_draw_instance_in_layer() + with sspine_draw_layer(), as this will confuse the internal draw command + recording. Ideally, move all sokol-gfx pass rendering (including all + sspine_draw_layer() calls) towards the end of the frame, separate from + any other sokol-spine calls. + + The sspine_layer_transform struct defines the layer's screen space coordinate + system. For instance to map Spine coordinates to framebuffer pixels, with the + origin in the screen center, you'd setup the layer transform like this: + + const float width = sapp_widthf(); + const float height = sapp_heightf(); + const sspine_layer_transform tform = { + .size = { .x = width, .y = height }, + .origin = { .x = width * 0.5f, .y = height * 0.5f }, + }; + + With this pixel mapping, the Spine scene would *not* scale with window size, + which often is not very useful. Instead it might make more sense to render + to a fixed 'virtual' resolution, for instance 1024 * 768: + + const sspine_layer_transform tform = { + .size = { .x = 1024.0f, .y = 768.0f }, + .origin = { .x = 512.0f, .y = 384.0f }, + }; + + How to configure a virtual resolution with a fixed aspect ratio is + left as an exercise to the reader ;) + + - That's it for basic sokol-spine setup and rendering. Any existing objects + will automatically be cleaned up when calling sspine_shutdown(), this + should be called before shutting down sokol-gfx, but this is not required: + + sspine_shutdown(); + sg_shutdown(); + + - You can explicitly destroy the base object types if you don't need them + any longer. This will cause the underlying spine-c objects to be + freed and the memory to be returned to the operating system: + + sspine_destroy_instance(instance); + sspine_destroy_skinset(skinset); + sspine_destroy_skeleton(skeleton); + sspine_destroy_atlas(atlas); + + You can destroy these objects in any order without causing memory corruption + issues. Instead any dependent object handles will simply become invalid (e.g. + if you destroy an atlas object, all skeletons and instances created from + this atlas will 'technically' still exist, but their handles will resolve to + 'invalid' and all sokol-spine calls involving these handles will silently fail). + + For instance: + + // create an atlas, skeleton and instance + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ ... }); + assert(sspine_atlas_valid(atlas)); + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + ... + }); + assert(sspine_skeleton_valid(skeleton)); + + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = skeleton, + }); + assert(sspine_instance_valid(instance)); + + // destroy the atlas object: + sspine_destroy_atlas(atlas); + + // the skeleton and instance handle should now be invalid, but + // otherwise, nothing bad will happen: + if (!sspine_skeleton_valid(skeleton)) { + ... + } + if (!sspine_instance_valid(instance)) { + ... + } + + RENDERER DETAILS + ================ + Any rendering related work happens in the functions sspine_draw_instance_in_layer() and + sspine_draw_layer(). + + sspine_draw_instance_in_layer() will result in vertices, indices and internal + draw commands which will be recorded into internal memory buffers (e.g. + no sokol-gfx functions will be called here). + + If possible, batching will be performed by merging a new draw command with + the previously recorded draw command. For two draw commands to be merged, + the following conditions must be true: + + - rendering needs to go into the same layer + - the same atlas texture must be used + - the blend mode must be compatible (the Spine blending modes + 'normal' and 'additive' can be merged, but not 'multiply') + - the same premultiplied alpha mode must be used + + To make the most out of batching: + + - use Spine objects which only have a single atlas texture + and blend mode across all slots + - group sspine_draw_instance_in_layer() calls by layer + + After all instances have been 'rendered' (or rather: recorded) into layers, + the actually rendering happens inside a sokol-gfx pass by calling the + function sspine_draw_layer() for each layer in 'z order' (e.g. the layer + index doesn't matter for z-ordering, only the order how sspine_draw_layer() is + called). + + Only the first call to sspine_draw_layer() in a frame will copy the recorded + vertices and indices into sokol-gfx buffers. + + Each call to sspine_draw_layer() will iterate over all recorded (and + hopefully well-batched) draw commands, skip any draw commands with a + non-matching layer index, and draw only those with a matching layer by + calling: + + - if the pipeline object has changed: + - sg_apply_pipeline() + - sg_apply_uniforms() for the vertex stage + - if the atlas texture has changed: + - sg_apply_bindings() + - if the premultiplied-alpha mode has changed: + - sg_apply_uniforms() for the fragment stage + - and finally sg_draw() + + The main purpose of render layers is to mix Spine rendering with other + render operations. In the not too distant future, the same render layer idea + will also be implemented at least for sokol-gl and sokol-debugtext. + + FIXME: does this section need more details about layer transforms? + + RENDERING WITH CONTEXTS + ======================= + At first glance, render contexts may look like more heavy-weight + render layers, but they serve a different purpose: they are useful + if Spine rendering needs to happen in different sokol-gfx render passes + with different pixel formats and MSAA sample counts. + + All Spine rendering happens within a context, even you don't call any + of the context API functions, in this case, an internal 'default context' + will be used. + + Each context has its own internal vertex-, index- and command buffer and + all context state is completely independent from any other contexts. + + To create a new context object, call: + + sspine_context ctx = sspine_make_context(&(sspine_context_desc){ + .max_vertices = ..., + .max_commands = ..., + .color_format = SG_PIXELFORMAT_..., + .depth_format = SG_PIXELFORMAT_..., + .sample_count = ..., + .color_write_mask = SG_COLORMASK_..., + }); + + The color_format, depth_format and sample_count items must be compatible + with the sokol-gfx render pass you're going to render into. + + If you omit the color_format, depth_format and sample_count designators, + the new context will be compatible with the sokol-gfx default pass + (which is most likely not what you want, unless your offscreen render passes + exactly match the default pass attributes). + + Once a context has been created, it can be made active with: + + sspine_set_context(ctx); + + To set the default context again: + + sspine_set_contxt(sspine_default_context()); + + ...and to get the currently active context: + + sspine_context cur_ctx = sspine_get_context(); + + The currently active context only matter for two functions: + + - sspine_draw_instance_in_layer() + - sspine_draw_layer() + + Alternatively you can bypass the currently set context with these + alternative functions: + + - sspine_context_draw_layer_in_instance(ctx, ...) + - sspine_context_draw_layer(ctx, ...) + + These explicitly take a context argument, completely ignore + and don't change the active context. + + You can query some information about a context with the function: + + sspine_context_info info = ssgpine_get_context_info(ctx); + + This returns the current number of recorded vertices, indices + and draw commands. + + RESOURCE STATES: + ================ + Similar to sokol-gfx, you can query the current 'resource state' of Spine + objects: + + sspine_resource_state sspine_get_atlas_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_skeleton_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_instance_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_skinset_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_context_resource_state(sspine_atlas atlas); + + This returns one of + + - SSPINE_RESOURCE_VALID: the object is valid and ready to use + - SSPINE_RESOURCE_FAILED: the object creation has failed + - SSPINE_RESOURCE_INVALID: the object or one of its dependencies is + invalid, it either no longer exists, or the handle hasn't been + initialized with a call to one of the object creation functions + + MISC HELPER FUNCTIONS: + ====================== + There's a couple of helper functions which don't fit into a big enough category + of their own: + + You can ask a skeleton for the atlas it has been created from: + + sspine_atlas atlas = sspine_get_skeleton_atlas(skeleton); + + ...and likewise, ask an instance for the skeleton it has been created from: + + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + + ...and finally you can convert a layer transform struct into a 4x4 projection + matrix that's memory-layout compatible with sokol-gl: + + const sspine_layer_transform tform = { ... }; + const sspine_mat4 proj = sspine_layer_transform_to_mat4(&tform); + sgl_matrix_mode_projection(); + sgl_load_matrix(proj.m); + + ANIMATIONS + ========== + Animations have their own handle type sspine_anim. A valid sspine_anim + handle is either obtained by looking up an animation by name from a skeleton: + + sspine_anim anim = sspine_anim_by_name(skeleton, "walk"); + + ...or by index: + + sspine_anim anim = sspine_anim_by_index(skeleton, 0); + + The returned anim handle will be invalid if an animation of that name doesn't + exist, or the provided index is out-of-range: + + if (!sspine_anim_is_valid(anim)) { + // animation handle is not valid + } + + An animation handle will also become invalid when the skeleton object it was + created is destroyed, or otherwise becomes invalid. + + You can iterate over all animations in a skeleton: + + const int num_anims = sspine_num_anims(skeleton); + for (int anim_index = 0; anim_index < num_anims; anim_index++) { + sspine_anim anim = sspine_anim_by_index(skeleton, anim_index); + ... + } + + Since sspine_anim is a 'fat handle' (it houses a skeleton handle and an index), + there's a helper function which checks if two anim handles are equal: + + if (sspine_anim_equal(anim0, anim1)) { + ... + } + + To query information about an animation: + + const sspine_anim_info info = sspine_get_anim_info(anim); + if (info.valid) { + printf("index: %d, duration: %f, name: %s", info.index, info.duration, info.name.cstr); + } + + Scheduling and mixing animations is controlled through the following functions: + + void sspine_clear_animation_tracks(sspine_instance instance); + void sspine_clear_animation_track(sspine_instance instance, int track_index); + void sspine_set_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop); + void sspine_add_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop, float delay); + void sspine_set_empty_animation(sspine_instance instance, int track_index, float mix_duration); + void sspine_add_empty_animation(sspine_instance instance, int track_index, float mix_duration, float delay); + + Please refer to the spine-c documentation to get an idea what these functions do: + + http://en.esotericsoftware.com/spine-c#Applying-animations + + EVENTS + ====== + For a general idea of Spine events, see here: http://esotericsoftware.com/spine-events + + After calling sspine_update_instance() to advance the currently configured animations, + you can poll for triggered events like this: + + const int num_triggered_events = sspine_num_triggered_events(instance); + for (int i = 0; i < num_triggered_events; i++) { + const sspine_triggered_event_info info = sspine_get_triggered_event_info(instance, i); + if (info.valid) { + ... + } + } + + The returned sspine_triggered_event_info struct gives you the current runtime properties + of the event (in case the event has keyed properties). For the actual list of event + properties please see the actual sspine_triggered_event_info struct declaration. + + It's also possible to inspect the static event definition on a skeleton, this works + the same as iterating through animations. You can lookup an event by name, + get the number of events, lookup an event by its index, and get detailed + information about an event: + + int sspine_num_events(sspine_skeleton skeleton); + sspine_event sspine_event_by_name(sspine_skeleton skeleton, const char* name); + sspine_event sspine_event_by_index(sspine_skeleton skeleton, int index); + bool sspine_event_valid(sspine_event event); + bool sspine_event_equal(sspine_event first, sspine_event second); + sspine_event_info sspine_get_event_info(sspine_event event); + + (FIXME: shouldn't the event info struct contains an sspine_anim handle?) + + IK TARGETS + ========== + The IK target function group allows to iterate over the IK targets that have been + defined on a skeleton, find an IK target by name, get detailed information about + an IK target, and most importantly, set the world space position of an IK target + which updates the position of all bones influenced by the IK target: + + int sspine_num_iktargets(sspine_skeleton skeleton); + sspine_iktarget sspine_iktarget_by_name(sspine_skeleton skeleton, const char* name); + sspine_iktarget sspine_iktarget_by_index(sspine_skeleton skeleton, int index); + bool sspine_iktarget_valid(sspine_iktarget iktarget); + bool sspine_iktarget_equal(sspine_iktarget first, sspine_iktarget second); + sspine_iktarget_info sspine_get_iktarget_info(sspine_iktarget iktarget); + void sspine_set_iktarget_world_pos(sspine_instance instance, sspine_iktarget iktarget, sspine_vec2 world_pos); + + BONES + ===== + Skeleton bones are wrapped with an sspine_bone handle which can be created from + a skeleton handle, and either a bone name: + + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + assert(sspine_bone_valid(bone)); + + ...or a bone index: + + sspine_bone bone = sspine_bone_by_index(skeleton, 0); + assert(sspine_bone_valid(bone)); + + ...to iterate over all bones of a skeleton and query information about each + bone: + + const int num_bones = sspine_num_bones(skeleton); + for (int bone_index = 0; bone_index < num_bones; bone_index++) { + sspine_bone bone = sspine_bone_by_index(skeleton, bone_index); + const sspine_bone_info info = sspine_get_bone_info(skeleton, bone); + if (info.valid) { + ... + } + } + + The sspine_bone_info struct provides the shared, static bone state in the skeleton (like + the name, a parent bone handle, bone length, pose transform and a color attribute), + but doesn't contain any dynamic information of per-instance bones. + + To manipulate the per-instance bone attributes use the following setter functions: + + void sspine_set_bone_transform(sspine_instance instance, sspine_bone bone, const sspine_bone_transform* transform); + void sspine_set_bone_position(sspine_instance instance, sspine_bone bone, sspine_vec2 position); + void sspine_set_bone_rotation(sspine_instance instance, sspine_bone bone, float rotation); + void sspine_set_bone_scale(sspine_instance instance, sspine_bone bone, sspine_vec2 scale); + void sspine_set_bone_shear(sspine_instance instance, sspine_bone bone, sspine_vec2 shear); + + ...and to query the per-instance bone attributes, the following getters: + + sspine_bone_transform sspine_get_bone_transform(sspine_instance instance, sspine_bone bone); + sspine_vec2 sspine_get_bone_position(sspine_instance instance, sspine_bone bone); + float sspine_get_bone_rotation(sspine_instance instance, sspine_bone bone); + sspine_vec2 sspine_get_bone_scale(sspine_instance instance, sspine_bone bone); + sspine_vec2 sspine_get_bone_shear(sspine_instance instance, sspine_bone bone); + + These functions all work in the local bone coordinate system (relative to a bone's parent bone). + + To transform positions between bone-local and global space use the following helper functions: + + sspine_vec2 sspine_bone_local_to_world(sspine_instance instance, sspine_bone bone, sspine_vec2 local_pos); + sspine_vec2 sspine_bone_world_to_local(sspine_instance instance, sspine_bone bone, sspine_vec2 world_pos); + + ...and as a convenience, there's a helper function which obtains the bone position in global space + directly: + + sspine_vec2 sspine_get_bone_world_position(sspine_instance instance, sspine_bone bone); + + SKINS AND SKINSETS + ================== + Skins are named pieces of geometry which can be turned on and off, what makes Spine skins a bit + confusing is that they are hierarchical. A skin can itself be a collection of other skins. Setting + the 'root skin' will also make all 'child skins' visible. In sokol-spine collections of skins are + managed through dedicated 'skin set' objects. Under the hood they create a 'root skin' where the + skins of the skin set are attached to, but from the outside it just looks like a 'flat' collection + of skins without the tricky hierarchical management. + + Like other 'subobjects', skin handles can be obtained by the skin name from a skeleton handle: + + sspine_skin skin = sspine_skin_by_name(skeleton, "jacket"); + assert(sspine_skin_valid(skin)); + + ...or by a skin index: + + sspine_skin skin = sspine_skin_by_index(skeleton, 0); + assert(sspine_skin_valid(skin)); + + ...you can iterate over all skins of a skeleton and query some information about the skin: + + const int num_skins = sspine_num_skins(skeleton); + for (int skin_index = 0; skin_index < num_skins; skin_index++) { + sspine_skin skin = sspine_skin_by_index(skin_index); + sspine_skin_info info = sspine_get_skin_info(skin); + if (info.valid) { + ... + } + } + + Currently, the only useful query item is the skin name though. + + To make a skin visible on an instance, just call: + + sspine_set_skin(instance, skin); + + ...this will first deactivate the previous skin before setting a new skin. + + A more powerful way to configure the skin visibility is through 'skin sets'. Skin + sets are simply flat collections of skins which should be made visible at once. + A new skin set is created like this: + + sspine_skinset skinset = sspine_make_skinset(&(sspine_skinset_desc){ + .skeleton = skeleton, + .skins = { + sspine_skin_by_name(skeleton, "blue-jacket"), + sspine_skin_by_name(skeleton, "green-pants"), + sspine_skin_by_name(skeleton, "blonde-hair"), + ... + } + }); + assert(sspine_skinset_valid(skinset)) + + ...then simply set the skinset on an instance to reconfigure the appearance + of the instance: + + sspine_set_skinset(instance, skinset); + + The functions sspine_set_skinset() and sspine_set_skin() will cancel each other. + Calling sspine_set_skinset() deactivates the effect of sspine_set_skin() and + vice versa. + + + 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" + + sspine_setup(&(sspine_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. 'sspine' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SSPINE_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_spine.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-spine like this: + + sspine_setup(&(sspine_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. + + + 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); + } + + ... + sspine_setup(&(sspine_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = 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. + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2022 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_SPINE_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_spine.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_SPINE_API_DECL) +#define SOKOL_SPINE_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_SPINE_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_SPINE_IMPL) +#define SOKOL_SPINE_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_SPINE_API_DECL __declspec(dllimport) +#else +#define SOKOL_SPINE_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SSPINE_INVALID_ID = 0, + SSPINE_MAX_SKINSET_SKINS = 32, + SSPINE_MAX_STRING_SIZE = 61, // see sspine_string struct +}; + +typedef struct sspine_context { uint32_t id; } sspine_context; +typedef struct sspine_atlas { uint32_t id; } sspine_atlas; +typedef struct sspine_skeleton { uint32_t id; } sspine_skeleton; +typedef struct sspine_instance { uint32_t id; } sspine_instance; +typedef struct sspine_skinset { uint32_t id; } sspine_skinset; + +typedef struct sspine_image { uint32_t atlas_id; int index; } sspine_image; +typedef struct sspine_atlas_page { uint32_t atlas_id; int index; } sspine_atlas_page; +typedef struct sspine_anim { uint32_t skeleton_id; int index; } sspine_anim; +typedef struct sspine_bone { uint32_t skeleton_id; int index; } sspine_bone; +typedef struct sspine_slot { uint32_t skeleton_id; int index; } sspine_slot; +typedef struct sspine_event { uint32_t skeleton_id; int index; } sspine_event; +typedef struct sspine_iktarget { uint32_t skeleton_id; int index; } sspine_iktarget; +typedef struct sspine_skin { uint32_t skeleton_id; int index; } sspine_skin; + +typedef struct sspine_range { const void* ptr; size_t size; } sspine_range; +typedef struct sspine_vec2 { float x, y; } sspine_vec2; +typedef struct sspine_mat4 { float m[16]; } sspine_mat4; +typedef sg_color sspine_color; + +typedef struct sspine_string { + bool valid; + bool truncated; + uint8_t len; + char cstr[SSPINE_MAX_STRING_SIZE]; +} sspine_string; + +typedef enum sspine_resource_state { + SSPINE_RESOURCESTATE_INITIAL, + SSPINE_RESOURCESTATE_ALLOC, + SSPINE_RESOURCESTATE_VALID, + SSPINE_RESOURCESTATE_FAILED, + SSPINE_RESOURCESTATE_INVALID, + _SSPINE_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sspine_resource_state; + +// log item codes via x-macro magic +#define _SSPINE_LOG_ITEMS \ + _SSPINE_LOGITEM_XMACRO(OK, "Ok")\ + _SSPINE_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed")\ + _SSPINE_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (adjust via sspine_desc.context_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(ATLAS_POOL_EXHAUSTED, "atlas pool exhausted (adjust via sspine_desc.atlas_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_POOL_EXHAUSTED, "skeleton pool exhausted (adjust via sspine_desc.skeleton_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_POOL_EXHAUSTED, "skinset pool exhausted (adjust via sspine_desc.skinset_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_POOL_EXHAUSTED, "instance pool exhausted (adjust via sspine_desc.instance_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context")\ + _SSPINE_LOGITEM_XMACRO(ATLAS_DESC_NO_DATA, "no data provided in sspine_atlas_desc.data")\ + _SSPINE_LOGITEM_XMACRO(SPINE_ATLAS_CREATION_FAILED, "spAtlas_create() failed")\ + _SSPINE_LOGITEM_XMACRO(SG_ALLOC_IMAGE_FAILED, "sg_alloc_image() failed")\ + _SSPINE_LOGITEM_XMACRO(SG_ALLOC_VIEW_FAILED, "sg_alloc_view() failed")\ + _SSPINE_LOGITEM_XMACRO(SG_ALLOC_SAMPLER_FAILED, "sg_alloc_sampler() failed")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_DESC_NO_DATA, "no data provided in sspine_skeleton_desc.json_data or .binary_data")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_DESC_NO_ATLAS, "no atlas object provided in sspine_skeleton_desc.atlas")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_ATLAS_NOT_VALID, "sspine_skeleton_desc.atlas is not in valid state")\ + _SSPINE_LOGITEM_XMACRO(CREATE_SKELETON_DATA_FROM_JSON_FAILED, "spSkeletonJson_readSkeletonData() failed")\ + _SSPINE_LOGITEM_XMACRO(CREATE_SKELETON_DATA_FROM_BINARY_FAILED, "spSkeletonBinary_readSkeletonData() failed")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_DESC_NO_SKELETON, "no skeleton object provided in sspine_skinset_desc.skeleton")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_SKELETON_NOT_VALID, "sspine_skinset_desc.skeleton is not in valid state")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_INVALID_SKIN_HANDLE, "invalid skin handle in sspine_skinset_desc.skins[]")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_DESC_NO_SKELETON, "no skeleton object provided in sspine_instance_desc.skeleton")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_SKELETON_NOT_VALID, "sspine_instance_desc.skeleton is not in valid state")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_ATLAS_NOT_VALID, "skeleton's atlas object no longer valid via sspine_instance_desc.skeleton")\ + _SSPINE_LOGITEM_XMACRO(SPINE_SKELETON_CREATION_FAILED, "spSkeleton_create() failed")\ + _SSPINE_LOGITEM_XMACRO(SPINE_ANIMATIONSTATE_CREATION_FAILED, "spAnimationState_create() failed")\ + _SSPINE_LOGITEM_XMACRO(SPINE_SKELETONCLIPPING_CREATION_FAILED, "spSkeletonClipping_create() failed")\ + _SSPINE_LOGITEM_XMACRO(COMMAND_BUFFER_FULL, "command buffer full (adjust via sspine_desc.max_commands)")\ + _SSPINE_LOGITEM_XMACRO(VERTEX_BUFFER_FULL, "vertex buffer (adjust via sspine_desc.max_vertices)")\ + _SSPINE_LOGITEM_XMACRO(INDEX_BUFFER_FULL, "index buffer full (adjust via sspine_desc.max_vertices)")\ + _SSPINE_LOGITEM_XMACRO(STRING_TRUNCATED, "a string has been truncated")\ + _SSPINE_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed")\ + +#define _SSPINE_LOGITEM_XMACRO(item,msg) SSPINE_LOGITEM_##item, +typedef enum sspine_log_item { + _SSPINE_LOG_ITEMS +} sspine_log_item; +#undef _SSPINE_LOGITEM_XMACRO + +typedef struct sspine_layer_transform { + sspine_vec2 size; + sspine_vec2 origin; +} sspine_layer_transform; + +typedef struct sspine_bone_transform { + sspine_vec2 position; + float rotation; // in degrees + sspine_vec2 scale; + sspine_vec2 shear; // in degrees +} sspine_bone_transform; + +typedef struct sspine_context_desc { + int max_vertices; + int max_commands; + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_color_mask color_write_mask; +} sspine_context_desc; + +typedef struct sspine_context_info { + int num_vertices; // current number of vertices + int num_indices; // current number of indices + int num_commands; // current number of commands +} sspine_context_info; + +typedef struct sspine_image_info { + bool valid; + sg_image sgimage; + sg_view sgview; + sg_sampler sgsampler; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + int width; + int height; + bool premul_alpha; + sspine_string filename; +} sspine_image_info; + +typedef struct sspine_atlas_overrides { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + bool premul_alpha_enabled; + bool premul_alpha_disabled; +} sspine_atlas_overrides; + +typedef struct sspine_atlas_desc { + sspine_range data; + sspine_atlas_overrides override; +} sspine_atlas_desc; + +typedef struct sspine_atlas_page_info { + bool valid; + sspine_atlas atlas; + sspine_image_info image; + sspine_atlas_overrides overrides; +} sspine_atlas_page_info; + +typedef struct sspine_skeleton_desc { + sspine_atlas atlas; + float prescale; + float anim_default_mix; + const char* json_data; + sspine_range binary_data; +} sspine_skeleton_desc; + +typedef struct sspine_skinset_desc { + sspine_skeleton skeleton; + sspine_skin skins[SSPINE_MAX_SKINSET_SKINS]; +} sspine_skinset_desc; + +typedef struct sspine_anim_info { + bool valid; + int index; + float duration; + sspine_string name; +} sspine_anim_info; + +typedef struct sspine_bone_info { + bool valid; + int index; + sspine_bone parent_bone; + float length; + sspine_bone_transform pose; + sspine_color color; + sspine_string name; +} sspine_bone_info; + +typedef struct sspine_slot_info { + bool valid; + int index; + sspine_bone bone; + sspine_color color; + sspine_string attachment_name; + sspine_string name; +} sspine_slot_info; + +typedef struct sspine_iktarget_info { + bool valid; + int index; + sspine_bone target_bone; + sspine_string name; +} sspine_iktarget_info; + +typedef struct sspine_skin_info { + bool valid; + int index; + sspine_string name; +} sspine_skin_info; + +typedef struct sspine_event_info { + bool valid; + int index; + int int_value; + float float_value; + float volume; + float balance; + sspine_string name; + sspine_string string_value; + sspine_string audio_path; +} sspine_event_info; + +typedef struct sspine_triggered_event_info { + bool valid; + sspine_event event; + float time; + int int_value; + float float_value; + float volume; + float balance; + sspine_string string_value; +} sspine_triggered_event_info; + +typedef struct sspine_instance_desc { + sspine_skeleton skeleton; +} sspine_instance_desc; + +typedef struct sspine_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sspine_allocator; + +typedef struct sspine_logger { + void (*func)( + const char* tag, // always "sspine" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SSPINE_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_spine.h + const char* filename_or_null, // the source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sspine_logger; + +typedef struct sspine_desc { + int max_vertices; + int max_commands; + int context_pool_size; + int atlas_pool_size; + int skeleton_pool_size; + int skinset_pool_size; + int instance_pool_size; + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_color_mask color_write_mask; + sspine_allocator allocator; // optional allocation override functions (default: malloc/free) + sspine_logger logger; // optional logging function (default: NO LOGGING!) +} sspine_desc; + +// setup/shutdown +SOKOL_SPINE_API_DECL void sspine_setup(const sspine_desc* desc); +SOKOL_SPINE_API_DECL void sspine_shutdown(void); + +// context functions +SOKOL_SPINE_API_DECL sspine_context sspine_make_context(const sspine_context_desc* desc); +SOKOL_SPINE_API_DECL void sspine_destroy_context(sspine_context ctx); +SOKOL_SPINE_API_DECL void sspine_set_context(sspine_context ctx); +SOKOL_SPINE_API_DECL sspine_context sspine_get_context(void); +SOKOL_SPINE_API_DECL sspine_context sspine_default_context(void); +SOKOL_SPINE_API_DECL sspine_context_info sspine_get_context_info(sspine_context ctx); + +// create and destroy spine objects +SOKOL_SPINE_API_DECL sspine_atlas sspine_make_atlas(const sspine_atlas_desc* desc); +SOKOL_SPINE_API_DECL sspine_skeleton sspine_make_skeleton(const sspine_skeleton_desc* desc); +SOKOL_SPINE_API_DECL sspine_skinset sspine_make_skinset(const sspine_skinset_desc* desc); +SOKOL_SPINE_API_DECL sspine_instance sspine_make_instance(const sspine_instance_desc* desc); +SOKOL_SPINE_API_DECL void sspine_destroy_atlas(sspine_atlas atlas); +SOKOL_SPINE_API_DECL void sspine_destroy_skeleton(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL void sspine_destroy_skinset(sspine_skinset skinset); +SOKOL_SPINE_API_DECL void sspine_destroy_instance(sspine_instance instance); + +// configure instance appearance via skinsets +SOKOL_SPINE_API_DECL void sspine_set_skinset(sspine_instance instance, sspine_skinset skinset); + +// update instance animations before drawing +SOKOL_SPINE_API_DECL void sspine_update_instance(sspine_instance instance, float delta_time); + +// iterate over triggered events after updating an instance +SOKOL_SPINE_API_DECL int sspine_num_triggered_events(sspine_instance instance); +SOKOL_SPINE_API_DECL sspine_triggered_event_info sspine_get_triggered_event_info(sspine_instance instance, int triggered_event_index); + +// draw instance into current or explicit context +SOKOL_SPINE_API_DECL void sspine_draw_instance_in_layer(sspine_instance instance, int layer); +SOKOL_SPINE_API_DECL void sspine_context_draw_instance_in_layer(sspine_context ctx, sspine_instance instance, int layer); + +// helper function to convert sspine_layer_transform into projection matrix +SOKOL_SPINE_API_DECL sspine_mat4 sspine_layer_transform_to_mat4(const sspine_layer_transform* tform); + +// draw a layer in current context or explicit context (call once per context and frame in sokol-gfx pass) +SOKOL_SPINE_API_DECL void sspine_draw_layer(int layer, const sspine_layer_transform* tform); +SOKOL_SPINE_API_DECL void sspine_context_draw_layer(sspine_context ctx, int layer, const sspine_layer_transform* tform); + +// get current resource state (INITIAL, ALLOC, VALID, FAILED, INVALID) +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_context_resource_state(sspine_context context); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_atlas_resource_state(sspine_atlas atlas); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_skeleton_resource_state(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_skinset_resource_state(sspine_skinset skinset); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_instance_resource_state(sspine_instance instance); + +// shortcut for sspine_get_*_state() == SSPINE_RESOURCESTATE_VALID +SOKOL_SPINE_API_DECL bool sspine_context_valid(sspine_context context); +SOKOL_SPINE_API_DECL bool sspine_atlas_valid(sspine_atlas atlas); +SOKOL_SPINE_API_DECL bool sspine_skeleton_valid(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL bool sspine_instance_valid(sspine_instance instance); +SOKOL_SPINE_API_DECL bool sspine_skinset_valid(sspine_skinset skinset); + +// get dependency objects +SOKOL_SPINE_API_DECL sspine_atlas sspine_get_skeleton_atlas(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_skeleton sspine_get_instance_skeleton(sspine_instance instance); + +// atlas images +SOKOL_SPINE_API_DECL int sspine_num_images(sspine_atlas atlas); +SOKOL_SPINE_API_DECL sspine_image sspine_image_by_index(sspine_atlas atlas, int index); +SOKOL_SPINE_API_DECL bool sspine_image_valid(sspine_image image); +SOKOL_SPINE_API_DECL bool sspine_image_equal(sspine_image first, sspine_image second); +SOKOL_SPINE_API_DECL sspine_image_info sspine_get_image_info(sspine_image image); + +// atlas page functions +SOKOL_SPINE_API_DECL int sspine_num_atlas_pages(sspine_atlas atlas); +SOKOL_SPINE_API_DECL sspine_atlas_page sspine_atlas_page_by_index(sspine_atlas atlas, int index); +SOKOL_SPINE_API_DECL bool sspine_atlas_page_valid(sspine_atlas_page page); +SOKOL_SPINE_API_DECL bool sspine_atlas_page_equal(sspine_atlas_page first, sspine_atlas_page second); +SOKOL_SPINE_API_DECL sspine_atlas_page_info sspine_get_atlas_page_info(sspine_atlas_page page); + +// instance transform functions +SOKOL_SPINE_API_DECL void sspine_set_position(sspine_instance instance, sspine_vec2 position); +SOKOL_SPINE_API_DECL void sspine_set_scale(sspine_instance instance, sspine_vec2 scale); +SOKOL_SPINE_API_DECL void sspine_set_color(sspine_instance instance, sspine_color color); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_position(sspine_instance instance); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_scale(sspine_instance instance); +SOKOL_SPINE_API_DECL sspine_color sspine_get_color(sspine_instance instance); + +// instance animation functions +SOKOL_SPINE_API_DECL int sspine_num_anims(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_anim sspine_anim_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_anim sspine_anim_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_anim_valid(sspine_anim anim); +SOKOL_SPINE_API_DECL bool sspine_anim_equal(sspine_anim first, sspine_anim second); +SOKOL_SPINE_API_DECL sspine_anim_info sspine_get_anim_info(sspine_anim anim); +SOKOL_SPINE_API_DECL void sspine_clear_animation_tracks(sspine_instance instance); +SOKOL_SPINE_API_DECL void sspine_clear_animation_track(sspine_instance instance, int track_index); +SOKOL_SPINE_API_DECL void sspine_set_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop); +SOKOL_SPINE_API_DECL void sspine_add_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop, float delay); +SOKOL_SPINE_API_DECL void sspine_set_empty_animation(sspine_instance instance, int track_index, float mix_duration); +SOKOL_SPINE_API_DECL void sspine_add_empty_animation(sspine_instance instance, int track_index, float mix_duration, float delay); + +// bone functions +SOKOL_SPINE_API_DECL int sspine_num_bones(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_bone sspine_bone_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_bone sspine_bone_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_bone_valid(sspine_bone bone); +SOKOL_SPINE_API_DECL bool sspine_bone_equal(sspine_bone first, sspine_bone second); +SOKOL_SPINE_API_DECL sspine_bone_info sspine_get_bone_info(sspine_bone bone); +SOKOL_SPINE_API_DECL void sspine_set_bone_transform(sspine_instance instance, sspine_bone bone, const sspine_bone_transform* transform); +SOKOL_SPINE_API_DECL void sspine_set_bone_position(sspine_instance instance, sspine_bone bone, sspine_vec2 position); +SOKOL_SPINE_API_DECL void sspine_set_bone_rotation(sspine_instance instance, sspine_bone bone, float rotation); +SOKOL_SPINE_API_DECL void sspine_set_bone_scale(sspine_instance instance, sspine_bone bone, sspine_vec2 scale); +SOKOL_SPINE_API_DECL void sspine_set_bone_shear(sspine_instance instance, sspine_bone bone, sspine_vec2 shear); +SOKOL_SPINE_API_DECL sspine_bone_transform sspine_get_bone_transform(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_position(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL float sspine_get_bone_rotation(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_scale(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_shear(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_world_position(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_bone_local_to_world(sspine_instance instance, sspine_bone bone, sspine_vec2 local_pos); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_bone_world_to_local(sspine_instance instance, sspine_bone bone, sspine_vec2 world_pos); + +// slot functions +SOKOL_SPINE_API_DECL int sspine_num_slots(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_slot sspine_slot_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_slot sspine_slot_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_slot_valid(sspine_slot slot); +SOKOL_SPINE_API_DECL bool sspine_slot_equal(sspine_slot first, sspine_slot second); +SOKOL_SPINE_API_DECL sspine_slot_info sspine_get_slot_info(sspine_slot slot); +SOKOL_SPINE_API_DECL void sspine_set_slot_color(sspine_instance instance, sspine_slot slot, sspine_color color); +SOKOL_SPINE_API_DECL sspine_color sspine_get_slot_color(sspine_instance instance, sspine_slot slot); + +// event functions +SOKOL_SPINE_API_DECL int sspine_num_events(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_event sspine_event_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_event sspine_event_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_event_valid(sspine_event event); +SOKOL_SPINE_API_DECL bool sspine_event_equal(sspine_event first, sspine_event second); +SOKOL_SPINE_API_DECL sspine_event_info sspine_get_event_info(sspine_event event); + +// ik target functions +SOKOL_SPINE_API_DECL int sspine_num_iktargets(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_iktarget sspine_iktarget_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_iktarget sspine_iktarget_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_iktarget_valid(sspine_iktarget iktarget); +SOKOL_SPINE_API_DECL bool sspine_iktarget_equal(sspine_iktarget first, sspine_iktarget second); +SOKOL_SPINE_API_DECL sspine_iktarget_info sspine_get_iktarget_info(sspine_iktarget iktarget); +SOKOL_SPINE_API_DECL void sspine_set_iktarget_world_pos(sspine_instance instance, sspine_iktarget iktarget, sspine_vec2 world_pos); + +// skin functions +SOKOL_SPINE_API_DECL int sspine_num_skins(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_skin sspine_skin_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_skin sspine_skin_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_skin_valid(sspine_skin skin); +SOKOL_SPINE_API_DECL bool sspine_skin_equal(sspine_skin first, sspine_skin second); +SOKOL_SPINE_API_DECL sspine_skin_info sspine_get_skin_info(sspine_skin skin); +SOKOL_SPINE_API_DECL void sspine_set_skin(sspine_instance instance, sspine_skin skin); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_SPINE_IMPL +#define SOKOL_SPINE_IMPL_INCLUDED (1) + +#if !defined(SPINE_SPINE_H_) +#error "Please include spine/spine.h before the sokol_spine.h implementation" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef SOKOL_UNUSED + #define SOKOL_UNUSED(x) (void)(x) +#endif + +#include // malloc/free +#include // memset, strcmp + +// ███████╗██╗ ██╗ █████╗ ██████╗ ███████╗██████╗ ███████╗ +// ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔════╝██╔══██╗██╔════╝ +// ███████╗███████║███████║██║ ██║█████╗ ██████╔╝███████╗ +// ╚════██║██╔══██║██╔══██║██║ ██║██╔══╝ ██╔══██╗╚════██║ +// ███████║██║ ██║██║ ██║██████╔╝███████╗██║ ██║███████║ +// ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝ +// +// >>shaders +/* + Embedded source compiled with: + + sokol-shdc -i sspine.glsl -o sspine.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl:spirv_vk -b + + @vs vs + layout(binding=0) uniform vs_params { + mat4 mvp; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = mvp * vec4(position, 0.0, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + layout(binding=0) uniform texture2D tex; + layout(binding=0) uniform sampler smp; + layout(binding=1) uniform fs_params { + float pma; + }; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + vec4 c0 = texture(sampler2D(tex, smp), uv) * color; + vec4 c1 = vec4(c0.rgb * c0.a, c0.a) * color; + frag_color = mix(c0, c1, pma); + } + @end + + @program sspine vs fs +*/ +#if defined(SOKOL_GLCORE) +/* + #version 410 + + uniform vec4 vs_params[4]; + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = mat4(vs_params[0], vs_params[1], vs_params[2], vs_params[3]) * vec4(position, 0.0, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _sspine_vs_source_glsl410[394] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,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,0x34,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,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,0x76,0x65,0x63,0x34,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 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, +}; +/* + #version 410 + + uniform vec4 fs_params[1]; + uniform sampler2D tex_smp; + + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + layout(location = 0) out vec4 frag_color; + + void main() + { + vec4 _28 = texture(tex_smp, uv) * color; + float _37 = _28.w; + frag_color = mix(_28, vec4(_28.xyz * _37, _37) * color, vec4(fs_params[0].x)); + } +*/ +static const uint8_t _sspine_fs_source_glsl410[350] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73, + 0x6d,0x70,0x3b,0x0a,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, + 0x32,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,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,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x34,0x20,0x5f,0x32,0x38,0x20, + 0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d, + 0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x33,0x37,0x20,0x3d,0x20, + 0x5f,0x32,0x38,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x5f,0x32,0x38,0x2c, + 0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x32,0x38,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20, + 0x5f,0x33,0x37,0x2c,0x20,0x5f,0x33,0x37,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x2c,0x20,0x76,0x65,0x63,0x34,0x28,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x30,0x5d,0x2e,0x78,0x29,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +/* + #version 300 es + + uniform vec4 vs_params[4]; + layout(location = 0) in vec2 position; + out vec2 uv; + layout(location = 1) in vec2 texcoord0; + out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = mat4(vs_params[0], vs_params[1], vs_params[2], vs_params[3]) * vec4(position, 0.0, 1.0); + uv = texcoord0; + color = color0; + } +*/ +static const uint8_t _sspine_vs_source_glsl300es[355] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x34,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x76,0x65,0x63, + 0x34,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,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, +}; +/* + #version 300 es + precision mediump float; + precision highp int; + + uniform highp vec4 fs_params[1]; + uniform highp sampler2D tex_smp; + + in highp vec2 uv; + in highp vec4 color; + layout(location = 0) out highp vec4 frag_color; + + void main() + { + highp vec4 _28 = texture(tex_smp, uv) * color; + highp float _37 = _28.w; + frag_color = mix(_28, vec4(_28.xyz * _37, _37) * color, vec4(fs_params[0].x)); + } +*/ +static const uint8_t _sspine_fs_source_glsl300es[399] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63, + 0x34,0x20,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a, + 0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b, + 0x0a,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20, + 0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,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,0x30,0x29,0x20,0x6f,0x75, + 0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68, + 0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x5f,0x32,0x38,0x20,0x3d,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76, + 0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x68, + 0x69,0x67,0x68,0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x33,0x37,0x20,0x3d, + 0x20,0x5f,0x32,0x38,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x5f,0x32,0x38, + 0x2c,0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x32,0x38,0x2e,0x78,0x79,0x7a,0x20,0x2a, + 0x20,0x5f,0x33,0x37,0x2c,0x20,0x5f,0x33,0x37,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x2c,0x20,0x76,0x65,0x63,0x34,0x28,0x66,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x29,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +/* + cbuffer vs_params : register(b0) + { + row_major float4x4 _19_mvp : packoffset(c0); + }; + + + static float4 gl_Position; + static float2 position; + static float2 uv; + static float2 texcoord0; + static float4 color; + static float4 color0; + + struct SPIRV_Cross_Input + { + float2 position : TEXCOORD0; + float2 texcoord0 : TEXCOORD1; + float4 color0 : TEXCOORD2; + }; + + struct SPIRV_Cross_Output + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + float4 gl_Position : SV_Position; + }; + + void vert_main() + { + gl_Position = mul(float4(position, 0.0f, 1.0f), _19_mvp); + uv = texcoord0; + color = color0; + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + position = stage_input.position; + texcoord0 = stage_input.texcoord0; + color0 = stage_input.color0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.uv = uv; + stage_output.color = color; + return stage_output; + } +*/ +static const uint8_t _sspine_vs_bytecode_hlsl4[844] = { + 0x44,0x58,0x42,0x43,0x2a,0xc9,0x57,0x52,0x4b,0x3d,0x3e,0x89,0x00,0xf4,0xfa,0x41, + 0xfd,0xd7,0x63,0xc3,0x01,0x00,0x00,0x00,0x4c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xf4,0x00,0x00,0x00,0x58,0x01,0x00,0x00,0xc8,0x01,0x00,0x00, + 0xd0,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0xb8,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,0x90,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x80,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f,0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00, + 0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,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,0x03,0x03,0x00,0x00,0x50,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,0x03,0x0c,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,0x00,0x01,0x00,0x00, + 0x40,0x00,0x01,0x00,0x40,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,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, + 0x32,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,0x36,0x00,0x00,0x05, + 0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x01,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,0x00,0x00,0x00,0x08, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,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,0x02,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, +}; +/* + cbuffer fs_params : register(b0) + { + float _53_pma : packoffset(c0); + }; + + Texture2D tex : register(t0); + SamplerState smp : register(s0); + + static float2 uv; + static float4 color; + static float4 frag_color; + + struct SPIRV_Cross_Input + { + float2 uv : TEXCOORD0; + float4 color : TEXCOORD1; + }; + + struct SPIRV_Cross_Output + { + float4 frag_color : SV_Target0; + }; + + void frag_main() + { + float4 _28 = tex.Sample(smp, uv) * color; + float _37 = _28.w; + frag_color = lerp(_28, float4(_28.xyz * _37, _37) * color, _53_pma.xxxx); + } + + SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) + { + uv = stage_input.uv; + color = stage_input.color; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.frag_color = frag_color; + return stage_output; + } +*/ +static const uint8_t _sspine_fs_bytecode_hlsl4[868] = { + 0x44,0x58,0x42,0x43,0xfb,0x9d,0xdb,0x19,0x85,0xbc,0x50,0x5d,0x6b,0x03,0xd4,0xc8, + 0x1b,0xea,0x59,0xf5,0x01,0x00,0x00,0x00,0x64,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x3c,0x01,0x00,0x00,0x88,0x01,0x00,0x00,0xbc,0x01,0x00,0x00, + 0xe8,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x90,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0xd8,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x84,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,0x01,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0xab,0xab,0x84,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x35,0x33,0x5f, + 0x70,0x6d,0x61,0x00,0x00,0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,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,0x49,0x53,0x47,0x4e, + 0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52, + 0x24,0x01,0x00,0x00,0x40,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x59,0x00,0x00,0x04, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x5a,0x00,0x00,0x03, + 0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00, + 0x00,0x00,0x00,0x00,0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x02,0x00,0x00,0x00,0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00, + 0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0x72,0x00,0x10,0x00,0x01,0x00,0x00,0x00, + 0xf6,0x0f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x02,0x10,0x00,0x00,0x00,0x00,0x00, + 0x36,0x00,0x00,0x05,0x82,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x3a,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x80,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x80,0x20,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x04,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,0x01,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,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_METAL) +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * float4(in.position, 0.0, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sspine_vs_bytecode_metal_macos[3148] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,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,0x04,0xdb,0x24,0x3a,0x7c,0x95,0x82, + 0x6f,0x4a,0xd6,0x7f,0x57,0x6d,0x3f,0xb7,0x34,0x4c,0x93,0x7d,0x68,0xcc,0x7f,0xc0, + 0x16,0xd5,0x32,0xe9,0xf6,0xe7,0xed,0xdd,0x00,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,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,0x04,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,0x20,0x0b,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xc5,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,0x68,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x1f,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,0x02,0x30, + 0x8c,0x40,0x00,0x76,0x08,0x42,0x24,0x81,0x98,0x89,0x9a,0x07,0x7a,0x90,0x87,0x7a, + 0x18,0x07,0x7a,0x70,0x83,0x76,0x28,0x07,0x7a,0x08,0x07,0x76,0xd0,0x03,0x3d,0x68, + 0x87,0x70,0xa0,0x07,0x79,0x48,0x07,0x7c,0x40,0x01,0x39,0x48,0x9a,0x22,0x4a,0x98, + 0xfc,0x4a,0xfa,0x1f,0x20,0x02,0x18,0x09,0x05,0x65,0x10,0xc1,0x10,0x4a,0x31,0x42, + 0x10,0x87,0xd0,0x40,0xc0,0x1c,0x01,0x18,0xa4,0xc0,0x9a,0x23,0x00,0x85,0x41,0x04, + 0x41,0x18,0x46,0x20,0x96,0x11,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,0x0a,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,0x05,0x51,0x06,0x05,0x42,0x6d,0x04,0x80,0xd8,0x58,0xc2,0x03,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xea,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25, + 0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xa3,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,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04, + 0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66, + 0xa6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8c,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,0x64,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,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d, + 0x6e,0x08,0xa3,0x3c,0x8a,0xa5,0x44,0xca,0xa5,0x4c,0x0a,0x46,0x26,0x2c,0x4d,0xce, + 0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c, + 0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x45,0xd1,0x94,0x48,0xb9,0x94,0x49,0xd9,0x86,0x18, + 0x4a,0xa5,0x64,0x0a,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef, + 0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d, + 0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30, + 0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4, + 0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x43,0xa8,0x45,0x50,0x3c,0xe5,0x5b,0x84,0x25,0x50,0xc0,0x40,0x89,0x14,0x49, + 0x99,0x94,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x5a, + 0x02,0xc5,0x53,0xbe,0x25,0x58,0x02,0x05,0x0c,0x94,0x48,0x91,0x94,0x49,0x19,0x03, + 0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50,0x3c,0xe5,0x5b,0x86,0x25, + 0x50,0xc0,0x40,0x89,0x94,0x4b,0x99,0x94,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56, + 0x67,0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee, + 0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0, + 0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34, + 0x37,0xb3,0x37,0x22,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x1c,0xda,0xec,0xe0, + 0x86,0x28,0x8b,0xb0,0x10,0x8b,0xa0,0xac,0x81,0xc2,0x06,0x8c,0xc2,0xd2,0xe4,0x5c, + 0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xe6,0xd2,0xf4,0xca,0x78,0x85,0xa5, + 0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0x85,0xb1,0xa5,0x9d,0xb9, + 0x7d,0xcd,0xa5,0xe9,0x95,0x31,0xb1,0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b,0xe3, + 0xf0,0x15,0x93,0x33,0x84,0x0c,0x96,0x43,0x39,0x03,0x05,0x0d,0x16,0x42,0xf9,0x16, + 0x61,0x09,0x94,0x34,0x50,0xd4,0x40,0x69,0x03,0xc5,0x0d,0x16,0x42,0x79,0x83,0x05, + 0x51,0x22,0x05,0x0e,0x94,0x49,0x89,0x83,0x21,0x88,0x22,0x06,0x0a,0x19,0x28,0x66, + 0xa0,0xc8,0xc1,0x10,0x23,0x01,0x94,0x4e,0x99,0x03,0x3e,0x6f,0x6d,0x6e,0x69,0x70, + 0x6f,0x74,0x65,0x6e,0x74,0x20,0x63,0x68,0x61,0x72,0x7c,0xa6,0xd2,0xda,0xe0,0xd8, + 0xca,0x40,0x86,0x56,0x56,0x40,0xa8,0x84,0x82,0x82,0x86,0x08,0x8a,0x1d,0x0c,0x31, + 0x94,0x3a,0x50,0xee,0xa0,0x49,0x86,0x18,0x0a,0x1e,0x28,0x78,0xd0,0x24,0x23,0x22, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x77,0x20,0x87,0x7a,0x60,0x87,0x72, + 0x70,0x03,0x73,0x60,0x87,0x70,0x38,0x87,0x79,0x98,0x22,0x04,0xc3,0x08,0x85,0x1d, + 0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xd2,0x81,0x1c,0xca,0xc1,0x1d,0xe8,0x61,0x4a,0x50, + 0x8c,0x58,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xec,0xa1,0x1c,0xe4,0x61,0x1e,0xd2,0xe1, + 0x1d,0xdc,0x61,0x4a,0x60,0x8c,0xa0,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xd8,0x21,0x1c, + 0xdc,0xe1,0x1c,0xea,0x21,0x1c,0xce,0xa1,0x1c,0x7e,0xc1,0x1e,0xca,0x41,0x1e,0xe6, + 0x21,0x1d,0xde,0xc1,0x1d,0xa6,0x04,0xc8,0x88,0x29,0x1c,0xd2,0x41,0x1e,0xdc,0x60, + 0x1c,0xde,0xa1,0x1d,0xe0,0x21,0x1d,0xd8,0xa1,0x1c,0x7e,0xe1,0x1d,0xe0,0x81,0x1e, + 0xd2,0xe1,0x1d,0xdc,0x61,0x1e,0xa6,0x0c,0x0a,0xe3,0x8c,0x50,0xc2,0x21,0x1d,0xe4, + 0xc1,0x0d,0xec,0xa1,0x1c,0xe4,0x81,0x1e,0xca,0x01,0x1f,0xa6,0x04,0x74,0x00,0x00, + 0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3, + 0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c, + 0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19, + 0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5, + 0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0, + 0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81, + 0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d, + 0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39, + 0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77, + 0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef, + 0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07, + 0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73, + 0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0xf4,0xc6,0x22,0x82,0x20,0x08,0x46,0x00,0xa8,0x95,0x40,0x19,0xd0,0x98,0x01,0xa0, + 0x30,0x03,0x00,0x00,0xe3,0x15,0x07,0x33,0x4d,0x0c,0x05,0x65,0x90,0x81,0x19,0x0e, + 0x13,0x02,0xf9,0x8c,0x57,0x2c,0xd0,0x75,0x21,0x14,0x94,0x41,0x06,0xe8,0x60,0x4c, + 0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0xa8,0xe2,0x38,0x86,0x82,0x62,0x43,0x00, + 0x9f,0xd9,0x06,0xa7,0x02,0x66,0x1b,0x82,0x2a,0x98,0x6d,0x08,0x06,0x21,0x83,0x80, + 0x18,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x5b,0x86,0x20,0xc8,0x83,0x2d,0x43,0x11, + 0xe4,0xc1,0x96,0x41,0x09,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct fs_params + { + float pma; + }; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], constant fs_params& _53 [[buffer(0)]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + float4 _28 = tex.sample(smp, in.uv) * in.color; + float _37 = _28.w; + out.frag_color = mix(_28, float4(_28.xyz * _37, _37) * in.color, float4(_53.pma)); + return out; + } +*/ +static const uint8_t _sspine_fs_bytecode_metal_macos[3465] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x89,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xb0,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x08,0x46,0x82,0xf4,0xfd,0xc1,0x65, + 0x5a,0x94,0x87,0x41,0x06,0x5e,0x69,0x32,0x47,0x25,0xad,0x8c,0xb9,0xca,0x2a,0xea, + 0xfa,0xea,0xb8,0x8d,0x01,0x55,0xee,0xb2,0x00,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,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,0x98,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x23,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,0x7b,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,0x60, + 0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda,0x60,0x10,0x05, + 0xb0,0x00,0xd5,0x06,0xa3,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00,0x6a,0x03,0x62, + 0xfc,0xff,0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54,0x1b,0x8c,0x23,0x00,0x16,0xa0, + 0xda,0x60,0x20,0x02,0xb0,0x00,0xd5,0x06,0x24,0xf9,0xff,0xff,0xff,0xff,0x01,0x60, + 0x00,0x09,0xa8,0x36,0x18,0xca,0xff,0xff,0xff,0xff,0x0f,0x80,0x04,0x50,0x00,0x00, + 0x00,0x49,0x18,0x00,0x00,0x05,0x00,0x00,0x00,0x13,0x88,0x40,0x18,0x88,0x09,0x41, + 0x31,0x61,0x30,0x0e,0x64,0xc2,0x90,0x1c,0xc8,0x84,0x40,0x01,0x00,0x89,0x20,0x00, + 0x00,0x27,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,0x6c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x07,0x49, + 0x53,0x44,0x09,0x93,0x5f,0x48,0xff,0x03,0x44,0x00,0x23,0xa1,0x00,0x0c,0x22,0x10, + 0xc2,0x51,0xd2,0x14,0x51,0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0, + 0x4f,0x63,0x04,0xc0,0x20,0x82,0x11,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01, + 0xcc,0xb3,0x10,0xd1,0x3f,0x8d,0x11,0x00,0x83,0x08,0x88,0x50,0x0c,0x31,0x42,0x39, + 0x89,0x54,0x21,0x42,0x08,0x81,0xd8,0x1c,0x41,0x30,0x47,0x00,0x06,0xc3,0x08,0xc2, + 0x53,0x90,0x70,0xd2,0x70,0xd0,0x01,0x8a,0x03,0x01,0x29,0xf0,0x86,0x11,0x86,0x67, + 0x18,0x61,0x00,0x86,0x11,0x88,0x67,0x8e,0x00,0x14,0x06,0x11,0x00,0x61,0x04,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,0x59, + 0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x18,0xc2,0x34,0x40,0x00,0x08,0x00,0x00, + 0x00,0x00,0x00,0x0c,0x61,0x24,0x20,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0xb2,0x40, + 0x00,0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x7a,0x23,0x00,0x25,0x50,0x08,0x45,0x50,0x10,0x65,0x40,0x78, + 0x04,0x80,0xe8,0x58,0xc2,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x13,0x01,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x63,0x4c,0x00,0xa5,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x3c,0xc4,0x24,0x3c,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x46, + 0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x26,0x65,0x88,0x30,0x11,0x43, + 0x8c,0x87,0x78,0x8e,0x67,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x99,0x8e,0x87, + 0x78,0x88,0x67,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,0x98,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,0x69,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,0xa6,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,0xa6,0xe7,0x19,0x26,0x68,0x8a,0x26,0x69,0x9a,0x86,0x08,0x13,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,0x34,0xcc,0xd8,0xde,0xc2,0xe8, + 0x64,0x28,0xd4,0xd9,0x0d,0x91,0x9e,0x61,0xb2,0xa6,0x6b,0xc2,0xa6,0x6c,0x82,0x26, + 0x6d,0x92,0xa6,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a, + 0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x43,0x4c,0xd6,0xd4,0x4d,0xd8,0x94,0x4d, + 0xd0,0x14,0x4d,0xd2,0xe4,0x51,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33,0x2b,0x93, + 0xe3,0x13,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xf7,0x35,0x97,0xa6,0x57, + 0x46,0x29,0x2c,0x4d,0xce,0x85,0xed,0x6d,0x2c,0x8c,0x2e,0xed,0xcd,0xed,0x2b,0xcd, + 0x8d,0xac,0x0c,0x8f,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34, + 0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37, + 0xb3,0x37,0x16,0x66,0x6c,0x6f,0x61,0x74,0x1c,0xe0,0xda,0xc2,0x86,0x28,0xcf,0xf0, + 0x14,0xcf,0x30,0x95,0xc1,0x64,0x06,0x8c,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe, + 0xe8,0xf2,0xe0,0xca,0xbe,0xe6,0xd2,0xf4,0xca,0x78,0x85,0xa5,0xc9,0xb9,0x84,0xc9, + 0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0x85,0xb1,0xa5,0x9d,0xb9,0x7d,0xcd,0xa5,0xe9, + 0x95,0x31,0x31,0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b,0xe3,0xf0,0x55,0x33,0x33, + 0x84,0x0c,0x1e,0x63,0x02,0x83,0x29,0x0c,0x9e,0x62,0x12,0x83,0x67,0x78,0x88,0x69, + 0x0c,0x26,0x32,0x98,0xce,0x60,0x42,0x83,0xa7,0x98,0xd2,0xe0,0x29,0x26,0x68,0x52, + 0x83,0x49,0x9a,0xd6,0x80,0x4b,0x58,0x9a,0x9c,0x0b,0x5d,0x19,0x1e,0x5d,0x9d,0x5c, + 0x19,0x95,0xb0,0x34,0x39,0x97,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x62,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x2c,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x3e,0x1c,0xe8,0xca,0xf0,0x86,0x50,0x0f,0x32,0xb5,0xc1,0x24, + 0x06,0xcf,0xf0,0x10,0x93,0x1b,0x4c,0xd0,0xf4,0x06,0x93,0x34,0xc1,0x01,0x97,0xb0, + 0x34,0x39,0x97,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x1e,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x72,0x1c,0xe6,0xda,0xe0,0x86,0x48,0x4f,0x31,0xc9,0xc1,0x24,0x06,0xcf,0xf0, + 0x10,0x13,0x34,0xcd,0xc1,0x24,0x4d,0x74,0x30,0x44,0x99,0xb8,0xe9,0x9b,0xd8,0x60, + 0x8a,0x83,0xa9,0x0e,0x86,0x18,0x0b,0x30,0x55,0x93,0x1d,0xf0,0x0a,0x4b,0x93,0x6b, + 0x09,0x63,0x4b,0x0b,0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x9b,0x43,0x69,0x0b,0x4b, + 0x73,0x83,0x49,0x19,0x42,0x4c,0x79,0x30,0xe1,0x01,0xb1,0xb0,0x34,0xb9,0x96,0x30, + 0xb6,0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2,0x16,0xba,0x32,0x3c,0xba,0x3a, + 0xb9,0xb2,0xb9,0x21,0xc6,0xb4,0x07,0x53,0x1e,0x4c,0x7a,0x40,0x2c,0x2c,0x4d,0xae, + 0x25,0x8c,0x2d,0x2d,0x6c,0xae,0x65,0x6e,0xec,0x0d,0xae,0xac,0x65,0x2e,0xac,0x0d, + 0x8e,0xad,0x4c,0x6e,0x6e,0x88,0x31,0xf5,0xc1,0x94,0x07,0x13,0x1f,0x0c,0x21,0xa6, + 0x3d,0x98,0xfa,0x80,0xce,0x97,0x16,0xd5,0x54,0x8e,0xd9,0xdc,0x17,0x5c,0x98,0x5c, + 0x58,0xdb,0x1c,0x9f,0xb7,0x36,0xb7,0x34,0xb8,0x37,0xba,0x32,0x37,0x3a,0x90,0x31, + 0xb4,0x30,0x39,0x3e,0x53,0x69,0x6d,0x70,0x6c,0x65,0x20,0x43,0x2b,0x2b,0x20,0x54, + 0x42,0x41,0x41,0x43,0x84,0x29,0x14,0x86,0x18,0x13,0x28,0x4c,0xa2,0x80,0x25,0x43, + 0x8c,0xa9,0x0c,0xa6,0x51,0xc0,0x92,0x21,0xc6,0xf4,0x07,0x13,0x29,0x60,0xc9,0x10, + 0x63,0x2a,0x85,0x89,0x14,0xb0,0x64,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,0x84,0x60,0x18,0xa1,0xb0,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3a, + 0x90,0x43,0x39,0xb8,0x03,0x3d,0x4c,0x09,0x8a,0x11,0x4b,0x38,0xa4,0x83,0x3c,0xb8, + 0x81,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x8c,0x11,0x54, + 0x38,0xa4,0x83,0x3c,0xb8,0x01,0x3b,0x84,0x83,0x3b,0x9c,0x43,0x3d,0x84,0xc3,0x39, + 0x94,0xc3,0x2f,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0x00, + 0x19,0x31,0x85,0x43,0x3a,0xc8,0x83,0x1b,0x8c,0xc3,0x3b,0xb4,0x03,0x3c,0xa4,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x03,0x3c,0xd0,0x43,0x3a,0xbc,0x83,0x3b,0xcc,0xc3,0x94, + 0x41,0x61,0x9c,0x11,0x4c,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xc8,0x43,0x38,0x9c, + 0x43,0x3b,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xe0,0x0e,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43, + 0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4, + 0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6, + 0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0, + 0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1, + 0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f, + 0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc, + 0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19, + 0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3, + 0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87, + 0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77, + 0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x0b,0x00,0x00, + 0x00,0x26,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,0x6d,0x01,0x0d, + 0x80,0x44,0x3e,0x83,0x5c,0x7e,0x85,0x17,0xb7,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x27,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0d,0x00,0x00, + 0x00,0x74,0x47,0x00,0xc6,0x22,0x80,0x40,0x38,0xe6,0x20,0x06,0xc2,0xb0,0xa8,0x8e, + 0x35,0x0c,0xc3,0x40,0xae,0x06,0x46,0x00,0xe8,0xcd,0x00,0x10,0x1c,0x01,0xa0,0x3a, + 0xd6,0x00,0x04,0x02,0x89,0x19,0x00,0xb2,0x33,0x00,0x14,0x66,0x00,0x66,0x00,0x08, + 0x8c,0x00,0x00,0x00,0x00,0x23,0x06,0x0a,0x11,0x70,0xd0,0x33,0x29,0x47,0x12,0x58, + 0x30,0xc9,0x67,0x90,0x21,0x20,0x90,0x41,0x06,0xc1,0x70,0x4c,0x08,0xe4,0x33,0xc8, + 0x10,0x24,0xd1,0x20,0x43,0x50,0x4c,0x16,0x64,0xf2,0x19,0x6f,0xc8,0xba,0x31,0xa0, + 0x60,0xcc,0x31,0x30,0x41,0x19,0x0c,0x32,0x04,0x4d,0x36,0x62,0x60,0x14,0x41,0x1a, + 0x2c,0x45,0x30,0xdb,0x20,0x05,0x40,0x06,0x01,0x31,0x00,0x00,0x00,0x03,0x00,0x00, + 0x00,0x5b,0x06,0xe0,0xf0,0x83,0x2d,0x43,0x12,0x98,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * float4(in.position, 0.0, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sspine_vs_bytecode_metal_ios[3244] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xac,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa0,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,0xc3,0xfa,0xed,0x98,0x72,0x47,0x43, + 0xff,0x08,0xf0,0xb1,0xc3,0x34,0x0d,0xef,0xaf,0xc8,0x0e,0xfa,0x68,0x10,0xed,0xb2, + 0x31,0x82,0x07,0xf2,0x24,0xf9,0x1c,0xaa,0x58,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,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,0x04,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,0x84,0x0b,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xde,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,0x70,0x00,0x00,0x00,0x1b,0x7e,0x24,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x08,0x07,0x78,0x80,0x07,0x79,0x78,0x07,0x7c,0x68,0x03,0x73, + 0xa8,0x07,0x77,0x18,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40, + 0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xda,0x21,0x1d,0xdc,0xa1,0x0d,0xd8, + 0xa1,0x1c,0xce,0x21,0x1c,0xd8,0xa1,0x0d,0xec,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41, + 0x1e,0xda,0xe0,0x1e,0xd2,0x81,0x1c,0xe8,0x01,0x1d,0x80,0x38,0x90,0x03,0x3c,0x00, + 0x06,0x77,0x78,0x87,0x36,0x10,0x87,0x7a,0x48,0x07,0x76,0xa0,0x87,0x74,0x70,0x87, + 0x79,0x00,0x08,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,0x00,0xe8,0x41,0x1e,0xea,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,0xda,0xc0,0x1d,0xde,0xc1,0x1d, + 0xda,0x80,0x1d,0xca,0x21,0x1c,0xcc,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,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,0xdc,0x21,0x1c,0xdc,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,0x83,0x79,0x48,0x87,0x73,0x70,0x87,0x72,0x20,0x87,0x36,0xd0,0x87,0x72,0x90, + 0x87,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,0x80,0x1e,0xe4,0x21, + 0x1c,0xe0,0x01,0x1e,0xd2,0xc1,0x1d,0xce,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d, + 0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x98,0x07,0x7a,0x08,0x87,0x71,0x58,0x87, + 0x36,0x80,0x07,0x79,0x78,0x07,0x7a,0x28,0x87,0x71,0xa0,0x87,0x77,0x90,0x87,0x36, + 0x10,0x87,0x7a,0x30,0x07,0x73,0x28,0x07,0x79,0x68,0x83,0x79,0x48,0x07,0x7d,0x28, + 0x07,0x00,0x0f,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,0x36,0x6c,0x02,0x01,0x2c,0x40,0x35,0x84,0x43,0x3a,0xc8,0x43,0x1b,0x88,0x43, + 0x3d,0x98,0x83,0x39,0x94,0x83,0x3c,0xb4,0x81,0x3b,0xbc,0x43,0x1b,0x84,0x03,0x3b, + 0xa4,0x43,0x38,0xcc,0x03,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x1f,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,0x02,0x30, + 0x8c,0x40,0x00,0x76,0x08,0x42,0x24,0x81,0x98,0x89,0x9a,0x07,0x7a,0x90,0x87,0x7a, + 0x18,0x07,0x7a,0x70,0x83,0x76,0x28,0x07,0x7a,0x08,0x07,0x76,0xd0,0x03,0x3d,0x68, + 0x87,0x70,0xa0,0x07,0x79,0x48,0x07,0x7c,0x40,0x01,0x39,0x48,0x9a,0x22,0x4a,0x98, + 0xfc,0x4a,0xfa,0x1f,0x20,0x02,0x18,0x09,0x05,0x65,0x10,0xc1,0x10,0x4a,0x31,0x42, + 0x10,0x87,0xd0,0x40,0xc0,0x1c,0x01,0x18,0xa4,0xc0,0x9a,0x23,0x00,0x85,0x41,0x04, + 0x41,0x18,0x46,0x20,0x96,0x11,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,0x0a,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,0x05,0x51,0x06,0x05,0x42,0x6d,0x04, + 0x80,0xd8,0x58,0x82,0x04,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xfb,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0xc6,0x32,0x28,0x00,0xa3,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,0x46,0x06, + 0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c, + 0x25,0x58,0x8c,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,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,0x64,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,0xa1,0x61,0xc6,0xf6,0x16,0x46, + 0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0xa3,0x3c,0x8a,0xa5,0x44,0xca,0xa5, + 0x4c,0x0a,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d, + 0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x45,0xd1,0x94, + 0x48,0xb9,0x94,0x49,0xd9,0x86,0x18,0x4a,0xa5,0x64,0x0a,0x47,0x28,0x2c,0x4d,0xce, + 0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a, + 0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e, + 0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9, + 0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22, + 0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x45,0x50,0x3c,0xe5,0x5b,0x84, + 0x25,0x50,0xc0,0x40,0x89,0x14,0x49,0x99,0x94,0x30,0x60,0x42,0x57,0x86,0x37,0xf6, + 0xf6,0x26,0x47,0x06,0x33,0x84,0x5a,0x02,0xc5,0x53,0xbe,0x25,0x58,0x02,0x05,0x0c, + 0x94,0x48,0x91,0x94,0x49,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8, + 0x65,0x50,0x3c,0xe5,0x5b,0x86,0x25,0x50,0xc0,0x40,0x89,0x94,0x4b,0x99,0x94,0x32, + 0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce, + 0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c, + 0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba, + 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x22,0x66,0x6c,0x6f,0x61,0x74, + 0x34,0x78,0x34,0x1c,0xda,0xec,0xe0,0x86,0x28,0x8b,0xb0,0x10,0x8b,0xa0,0xac,0x81, + 0xc2,0x06,0x8c,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe, + 0xe6,0xd2,0xf4,0xca,0x78,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1, + 0x95,0x7d,0x85,0xb1,0xa5,0x9d,0xb9,0x7d,0xcd,0xa5,0xe9,0x95,0x31,0xb1,0x9b,0xfb, + 0x82,0x0b,0x93,0x0b,0x6b,0x9b,0xe3,0xf0,0x15,0x93,0x33,0x84,0x0c,0x96,0x43,0x39, + 0x03,0x05,0x0d,0x16,0x42,0xf9,0x16,0x61,0x09,0x94,0x34,0x50,0xd4,0x40,0x69,0x03, + 0xc5,0x0d,0x16,0x42,0x79,0x83,0x05,0x51,0x22,0x05,0x0e,0x94,0x49,0x89,0x83,0x21, + 0x88,0x22,0x06,0x0a,0x19,0x28,0x66,0xa0,0xc8,0xc1,0x10,0x23,0x01,0x94,0x4e,0x99, + 0x03,0x3e,0x6f,0x6d,0x6e,0x69,0x70,0x6f,0x74,0x65,0x6e,0x74,0x20,0x63,0x68,0x61, + 0x72,0x7c,0xa6,0xd2,0xda,0xe0,0xd8,0xca,0x40,0x86,0x56,0x56,0x40,0xa8,0x84,0x82, + 0x82,0x86,0x08,0x8a,0x1d,0x0c,0x31,0x94,0x3a,0x50,0xee,0xa0,0x49,0x86,0x18,0x0a, + 0x1e,0x28,0x78,0xd0,0x24,0xbc,0xc2,0xd2,0xe4,0x5a,0xc2,0xd8,0xd2,0xc2,0xe6,0x5a, + 0xe6,0xc6,0xde,0xe0,0xca,0xe6,0x50,0xda,0xc2,0xd2,0xdc,0x60,0x52,0x86,0x10,0xca, + 0x1e,0x28,0x7a,0x40,0x2b,0x2c,0x4d,0xae,0x25,0x8c,0x2d,0x2d,0x6c,0xae,0x65,0x6e, + 0xec,0x0d,0xae,0xac,0x25,0x4c,0xee,0x0c,0x65,0x26,0x65,0x88,0xa1,0xf4,0x81,0xb2, + 0x07,0x0a,0x1f,0x0c,0x11,0x94,0x3e,0x18,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,0xa0,0x03,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39, + 0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8,0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71, + 0x08,0x07,0x76,0x60,0x07,0x71,0x08,0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec, + 0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20,0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50, + 0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e,0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e, + 0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2,0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17, + 0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4,0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66, + 0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8,0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70, + 0x70,0x07,0x77,0x78,0x07,0x7a,0x08,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce, + 0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10,0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90, + 0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30,0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a, + 0x70,0x87,0x71,0xa0,0x87,0x74,0x78,0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8, + 0x07,0x78,0x98,0x07,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00, + 0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x20,0x00,0x00,0x00, + 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0xf4,0xc6,0x22,0x82, + 0x20,0x08,0x46,0x00,0xa8,0x95,0x40,0x19,0xd0,0x98,0x01,0xa0,0x30,0x03,0x00,0x00, + 0xe3,0x15,0x07,0x33,0x4d,0x0c,0x05,0x65,0x90,0x81,0x19,0x0e,0x13,0x02,0xf9,0x8c, + 0x57,0x2c,0xd0,0x75,0x21,0x14,0x94,0x41,0x06,0xe8,0x60,0x4c,0x08,0xe4,0x63,0x41, + 0x01,0x9f,0xf1,0x0a,0xa8,0xe2,0x38,0x86,0x82,0x62,0x43,0x00,0x9f,0xd9,0x06,0xa7, + 0x02,0x66,0x1b,0x82,0x2a,0x98,0x6d,0x08,0x06,0x21,0x83,0x80,0x18,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x5b,0x8a,0x20,0xc8,0x83,0xc3,0x0f,0xb6,0x14,0x45,0x90,0x07, + 0x87,0x1f,0x6c,0x29,0x94,0x20,0x0f,0x0e,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct fs_params + { + float pma; + }; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], constant fs_params& _53 [[buffer(0)]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + float4 _28 = tex.sample(smp, in.uv) * in.color; + float _37 = _28.w; + out.frag_color = mix(_28, float4(_28.xyz * _37, _37) * in.color, float4(_53.pma)); + return out; + } +*/ +static const uint8_t _sspine_fs_bytecode_metal_ios[3529] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc9,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xfb,0xc3,0x38,0x07,0x5d,0xf9,0xd9, + 0x68,0x99,0xff,0xba,0x47,0x1d,0x29,0xd8,0x13,0x4a,0xba,0xb3,0x71,0x0a,0x9c,0x4f, + 0x75,0x70,0x45,0x49,0x48,0xd7,0x6b,0xc4,0xf9,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,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,0xd8,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x33,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,0x82,0x00,0x00,0x00,0x1b,0xc2,0x24,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x70,0x80,0x07,0x78,0x90,0x87,0x77,0xc0,0x87,0x36,0x30,0x87,0x7a, + 0x70,0x87,0x71,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00, + 0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1d,0xd2,0xc1,0x1d,0xda,0x80,0x1d,0xca, + 0xe1,0x1c,0xc2,0x81,0x1d,0xda,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d,0xe4,0xa1, + 0x0d,0xee,0x21,0x1d,0xc8,0x81,0x1e,0xd0,0x01,0x88,0x03,0x39,0xc0,0x03,0x60,0x70, + 0x87,0x77,0x68,0x03,0x71,0xa8,0x87,0x74,0x60,0x07,0x7a,0x48,0x07,0x77,0x98,0x07, + 0x80,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, + 0x07,0x80,0x1e,0xe4,0xa1,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,0xa1,0x0d,0xdc,0xe1,0x1d,0xdc,0xa1,0x0d, + 0xd8,0xa1,0x1c,0xc2,0xc1,0x1c,0x00,0x7a,0x90,0x87,0x7a,0x28,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,0xc0,0x1d,0xc2,0xc1,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, + 0x98,0x87,0x74,0x38,0x07,0x77,0x28,0x07,0x72,0x68,0x03,0x7d,0x28,0x07,0x79,0x78, + 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,0xe8,0x41,0x1e,0xc2,0x01, + 0x1e,0xe0,0x21,0x1d,0xdc,0xe1,0x1c,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x79,0xa0,0x87,0x70,0x18,0x87,0x75,0x68,0x03, + 0x78,0x90,0x87,0x77,0xa0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0x68,0x03,0x71, + 0xa8,0x07,0x73,0x30,0x87,0x72,0x90,0x87,0x36,0x98,0x87,0x74,0xd0,0x87,0x72,0x00, + 0xf0,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,0x60, + 0xc3,0x26,0x0c,0xc0,0x02,0x54,0x43,0x38,0xa4,0x83,0x3c,0xb4,0x81,0x38,0xd4,0x83, + 0x39,0x98,0x43,0x39,0xc8,0x43,0x1b,0xb8,0xc3,0x3b,0xb4,0x41,0x38,0xb0,0x43,0x3a, + 0x84,0xc3,0x3c,0x00,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda,0x60,0x10,0x05,0xb0,0x00, + 0xd5,0x06,0xa3,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00,0x6a,0x03,0x62,0xfc,0xff, + 0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54,0x1b,0x8c,0x23,0x00,0x16,0xa0,0xda,0x60, + 0x20,0x02,0xb0,0x00,0xd5,0x06,0x24,0xf9,0xff,0xff,0xff,0xff,0x01,0x60,0x00,0x09, + 0xa8,0x36,0x18,0xca,0xff,0xff,0xff,0xff,0x0f,0x80,0x04,0x50,0x00,0x49,0x18,0x00, + 0x00,0x05,0x00,0x00,0x00,0x13,0x88,0x40,0x18,0x88,0x09,0x41,0x31,0x61,0x30,0x0e, + 0x64,0xc2,0x90,0x1c,0xc8,0x84,0x40,0x01,0x00,0x89,0x20,0x00,0x00,0x27,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,0x6c,0x33, + 0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x07,0x49,0x53,0x44,0x09,0x93, + 0x5f,0x48,0xff,0x03,0x44,0x00,0x23,0xa1,0x00,0x0c,0x22,0x10,0xc2,0x51,0xd2,0x14, + 0x51,0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0, + 0x20,0x82,0x11,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1, + 0x3f,0x8d,0x11,0x00,0x83,0x08,0x88,0x50,0x0c,0x31,0x42,0x39,0x89,0x54,0x21,0x42, + 0x08,0x81,0xd8,0x1c,0x41,0x30,0x47,0x00,0x06,0xc3,0x08,0xc2,0x53,0x90,0x70,0xd2, + 0x70,0xd0,0x01,0x8a,0x03,0x01,0x29,0xf0,0x86,0x11,0x86,0x67,0x18,0x61,0x00,0x86, + 0x11,0x88,0x67,0x8e,0x00,0x14,0x06,0x11,0x00,0x61,0x04,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,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x4c,0x03, + 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0xc0,0x10,0x46,0x02,0x02,0x60,0x00,0x00,0x00, + 0x00,0x00,0x20,0x0b,0x04,0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x7a,0x23,0x00,0x25,0x50,0x08,0x45,0x50, + 0x10,0x65,0x40,0x78,0x04,0x80,0xe8,0x58,0x82,0x04,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0x1c,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x63,0x4c,0x00,0xa5,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x3c,0xc4,0x24,0x3c,0x05,0xe3,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed, + 0xcd,0x0d,0x64,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x46,0x06,0x46,0x66,0xc6,0x65,0x66,0xa6,0x26,0x65, + 0x88,0x30,0x11,0x43,0x8c,0x87,0x78,0x8e,0x67,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36, + 0x04,0x99,0x8e,0x87,0x78,0x88,0x67,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,0x98,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,0x69,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,0xa6, + 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,0xa6,0xe7,0x19,0x26,0x68,0x8a,0x26,0x69,0x9a,0x86, + 0x08,0x13,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,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x9e,0x61,0xb2,0xa6,0x6b,0xc2,0xa6, + 0x6c,0x82,0x26,0x6d,0x92,0xa6,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98, + 0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x43,0x4c,0xd6,0xd4,0x4d, + 0xd8,0x94,0x4d,0xd0,0x14,0x4d,0xd2,0xe4,0x51,0x09,0x4b,0x93,0x73,0x11,0xab,0x33, + 0x33,0x2b,0x93,0xe3,0x13,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xf7,0x35, + 0x97,0xa6,0x57,0x46,0x29,0x2c,0x4d,0xce,0x85,0xed,0x6d,0x2c,0x8c,0x2e,0xed,0xcd, + 0xed,0x2b,0xcd,0x8d,0xac,0x0c,0x8f,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19, + 0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2, + 0xaf,0x34,0x37,0xb3,0x37,0x16,0x66,0x6c,0x6f,0x61,0x74,0x1c,0xe0,0xda,0xc2,0x86, + 0x28,0xcf,0xf0,0x14,0xcf,0x30,0x95,0xc1,0x64,0x06,0x8c,0xc2,0xd2,0xe4,0x5c,0xc2, + 0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xe6,0xd2,0xf4,0xca,0x78,0x85,0xa5,0xc9, + 0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0x85,0xb1,0xa5,0x9d,0xb9,0x7d, + 0xcd,0xa5,0xe9,0x95,0x31,0x31,0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b,0xe3,0xf0, + 0x55,0x33,0x33,0x84,0x0c,0x1e,0x63,0x02,0x83,0x29,0x0c,0x9e,0x62,0x12,0x83,0x67, + 0x78,0x88,0x69,0x0c,0x26,0x32,0x98,0xce,0x60,0x42,0x83,0xa7,0x98,0xd2,0xe0,0x29, + 0x26,0x68,0x52,0x83,0x49,0x9a,0xd6,0x80,0x4b,0x58,0x9a,0x9c,0x0b,0x5d,0x19,0x1e, + 0x5d,0x9d,0x5c,0x19,0x95,0xb0,0x34,0x39,0x97,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x62, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x2c, + 0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3e,0x1c,0xe8,0xca,0xf0,0x86,0x50,0x0f,0x32, + 0xb5,0xc1,0x24,0x06,0xcf,0xf0,0x10,0x93,0x1b,0x4c,0xd0,0xf4,0x06,0x93,0x34,0xc1, + 0x01,0x97,0xb0,0x34,0x39,0x97,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x1e,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x1c,0xe6,0xda,0xe0,0x86,0x48,0x4f,0x31,0xc9,0xc1,0x24, + 0x06,0xcf,0xf0,0x10,0x13,0x34,0xcd,0xc1,0x24,0x4d,0x74,0x30,0x44,0x99,0xb8,0xe9, + 0x9b,0xd8,0x60,0x8a,0x83,0xa9,0x0e,0x86,0x18,0x0b,0x30,0x55,0x93,0x1d,0xf0,0x0a, + 0x4b,0x93,0x6b,0x09,0x63,0x4b,0x0b,0x9b,0x6b,0x99,0x1b,0x7b,0x83,0x2b,0x9b,0x43, + 0x69,0x0b,0x4b,0x73,0x83,0x49,0x19,0x42,0x4c,0x79,0x30,0xe1,0x01,0xb1,0xb0,0x34, + 0xb9,0x96,0x30,0xb6,0xb4,0xb0,0xb9,0x96,0xb9,0xb1,0x37,0xb8,0xb2,0x16,0xba,0x32, + 0x3c,0xba,0x3a,0xb9,0xb2,0xb9,0x21,0xc6,0xb4,0x07,0x53,0x1e,0x4c,0x7a,0x40,0x2c, + 0x2c,0x4d,0xae,0x25,0x8c,0x2d,0x2d,0x6c,0xae,0x65,0x6e,0xec,0x0d,0xae,0xac,0x65, + 0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x6e,0x6e,0x88,0x31,0xf5,0xc1,0x94,0x07,0x13,0x1f, + 0x0c,0x21,0xa6,0x3d,0x98,0xfa,0x80,0x56,0x58,0x9a,0x5c,0x4b,0x18,0x5b,0x5a,0xd8, + 0x5c,0xcb,0xdc,0xd8,0x1b,0x5c,0x59,0x4b,0x98,0xdc,0x19,0x8a,0x4c,0xca,0x10,0x63, + 0x02,0x85,0x29,0x0f,0xa6,0x3f,0x18,0x22,0x4c,0xa0,0x40,0xe7,0x4b,0x8b,0x6a,0x2a, + 0xc7,0x6c,0xee,0x0b,0x2e,0x4c,0x2e,0xac,0x6d,0x8e,0xcf,0x5b,0x9b,0x5b,0x1a,0xdc, + 0x1b,0x5d,0x99,0x1b,0x1d,0xc8,0x18,0x5a,0x98,0x1c,0x9f,0xa9,0xb4,0x36,0x38,0xb6, + 0x32,0x90,0xa1,0x95,0x15,0x10,0x2a,0xa1,0xa0,0xa0,0x21,0xc2,0x44,0x0a,0x43,0x8c, + 0x69,0x14,0xa6,0x52,0xc0,0x92,0x21,0xc6,0x54,0x06,0x93,0x29,0x60,0xc9,0x10,0x63, + 0x12,0x85,0xe9,0x14,0xb0,0x64,0x88,0x31,0xa1,0xc2,0x74,0x0a,0x58,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,0x42,0x30,0x8c,0x50,0xd8,0x81, + 0x1d,0xec,0xa1,0x1d,0xdc,0x20,0x1d,0xc8,0xa1,0x1c,0xdc,0x81,0x1e,0xa6,0x04,0xc5, + 0x88,0x25,0x1c,0xd2,0x41,0x1e,0xdc,0xc0,0x1e,0xca,0x41,0x1e,0xe6,0x21,0x1d,0xde, + 0xc1,0x1d,0xa6,0x04,0xc6,0x08,0x2a,0x1c,0xd2,0x41,0x1e,0xdc,0x80,0x1d,0xc2,0xc1, + 0x1d,0xce,0xa1,0x1e,0xc2,0xe1,0x1c,0xca,0xe1,0x17,0xec,0xa1,0x1c,0xe4,0x61,0x1e, + 0xd2,0xe1,0x1d,0xdc,0x61,0x4a,0x80,0x8c,0x98,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xc6, + 0xe1,0x1d,0xda,0x01,0x1e,0xd2,0x81,0x1d,0xca,0xe1,0x17,0xde,0x01,0x1e,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0xe6,0x61,0xca,0xa0,0x30,0xce,0x08,0x26,0x1c,0xd2,0x41,0x1e, + 0xdc,0xc0,0x1c,0xe4,0x21,0x1c,0xce,0xa1,0x1d,0xca,0xc1,0x1d,0xe8,0x61,0x4a,0x70, + 0x07,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xa5,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,0x48,0x19,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0x84,0xc3,0x38,0x8c,0x43,0x39,0xcc,0xc3,0x3c,0xb8,0xc1,0x39,0xc8, + 0xc3,0x3b,0xd4,0x03,0x3c,0xcc,0x48,0xb4,0x71,0x08,0x07,0x76,0x60,0x07,0x71,0x08, + 0x87,0x71,0x58,0x87,0x19,0xdb,0xc6,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xf0,0x20, + 0x0f,0xe5,0x30,0x0f,0xe5,0x20,0x0f,0xf6,0x50,0x0e,0x6e,0x10,0x0e,0xe3,0x30,0x0e, + 0xe5,0x30,0x0f,0xf3,0xe0,0x06,0xe9,0xe0,0x0e,0xe4,0x50,0x0e,0xf8,0x30,0x23,0xe2, + 0xec,0x61,0x1c,0xc2,0x81,0x1d,0xd8,0xe1,0x17,0xec,0x21,0x1d,0xe6,0x21,0x1d,0xc4, + 0x21,0x1d,0xd8,0x21,0x1d,0xe8,0x21,0x1f,0x66,0x20,0x9d,0x3b,0xbc,0x43,0x3d,0xb8, + 0x03,0x39,0x94,0x83,0x39,0xcc,0x58,0xbc,0x70,0x70,0x07,0x77,0x78,0x07,0x7a,0x08, + 0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x19,0xce,0x87,0x0e,0xe5,0x10,0x0e,0xf0,0x10, + 0x0e,0xec,0xc0,0x0e,0xef,0x30,0x0e,0xf3,0x90,0x0e,0xf4,0x50,0x0e,0x33,0x28,0x30, + 0x08,0x87,0x74,0x90,0x07,0x37,0x30,0x87,0x7a,0x70,0x87,0x71,0xa0,0x87,0x74,0x78, + 0x07,0x77,0xf8,0x85,0x73,0x90,0x87,0x77,0xa8,0x07,0x78,0x98,0x07,0x00,0x00,0x00, + 0x00,0x71,0x20,0x00,0x00,0x0b,0x00,0x00,0x00,0x26,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,0x6d,0x01,0x0d,0x80,0x44,0x3e,0x83,0x5c,0x7e,0x85,0x17, + 0xb7,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x29,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x74,0x47,0x00,0xc6,0x22,0x80,0x40, + 0x38,0xe6,0x20,0x06,0xc2,0xb0,0xa8,0x8e,0x35,0x0c,0xc3,0x40,0xae,0x06,0x46,0x00, + 0xe8,0xcd,0x00,0x10,0x1c,0x01,0xa0,0x3a,0xd6,0x00,0x04,0x02,0x89,0x19,0x00,0xb2, + 0x33,0x00,0x14,0x66,0x00,0x66,0x00,0x08,0x8c,0x00,0x00,0x00,0x00,0x23,0x06,0x0a, + 0x11,0x70,0xd0,0x33,0x29,0x47,0x12,0x58,0x30,0xc9,0x67,0x90,0x21,0x20,0x90,0x41, + 0x06,0xc1,0x70,0x4c,0x08,0xe4,0x33,0xc8,0x10,0x24,0xd1,0x20,0x43,0x50,0x4c,0x16, + 0x64,0xf2,0x19,0x6f,0xc8,0xba,0x31,0xa0,0x60,0xcc,0x31,0x30,0x41,0x19,0x0c,0x32, + 0x04,0x4d,0x36,0x62,0x60,0x14,0x41,0x1a,0x2c,0x45,0x30,0xdb,0x20,0x05,0x40,0x06, + 0x01,0x31,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x5b,0x0a,0xe0,0xf0,0x03,0x24,0x14, + 0xb6,0x1c,0x49,0x90,0x0a,0x47,0x28,0x20,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +/* + #include + #include + + using namespace metal; + + struct vs_params + { + float4x4 mvp; + }; + + struct main0_out + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + float4 gl_Position [[position]]; + }; + + struct main0_in + { + float2 position [[attribute(0)]]; + float2 texcoord0 [[attribute(1)]]; + float4 color0 [[attribute(2)]]; + }; + + vertex main0_out main0(main0_in in [[stage_in]], constant vs_params& _19 [[buffer(0)]]) + { + main0_out out = {}; + out.gl_Position = _19.mvp * float4(in.position, 0.0, 1.0); + out.uv = in.texcoord0; + out.color = in.color0; + return out; + } +*/ +static const uint8_t _sspine_vs_source_metal_sim[624] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x7d,0x3b,0x0a, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75, + 0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75, + 0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,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,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,0x32,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,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,0x31,0x39,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,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39, + 0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e, + 0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b, + 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, + +}; +/* + #include + #include + + using namespace metal; + + struct fs_params + { + float pma; + }; + + struct main0_out + { + float4 frag_color [[color(0)]]; + }; + + struct main0_in + { + float2 uv [[user(locn0)]]; + float4 color [[user(locn1)]]; + }; + + fragment main0_out main0(main0_in in [[stage_in]], constant fs_params& _53 [[buffer(0)]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) + { + main0_out out = {}; + float4 _28 = tex.sample(smp, in.uv) * in.color; + float _37 = _28.w; + out.frag_color = mix(_28, float4(_28.xyz * _37, _37) * in.color, float4(_53.pma)); + return out; + } +*/ +static const uint8_t _sspine_fs_source_metal_sim[619] = { + 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, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x66, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x70,0x6d,0x61,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30, + 0x29,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,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28, + 0x6c,0x6f,0x63,0x6e,0x30,0x29,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, + 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,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x35,0x33,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,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,0x73,0x6d,0x70,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,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x5f,0x32,0x38,0x20,0x3d,0x20,0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x28,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x29,0x20,0x2a,0x20,0x69, + 0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x20,0x5f,0x33,0x37,0x20,0x3d,0x20,0x5f,0x32,0x38,0x2e,0x77,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x5f,0x32,0x38,0x2c,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x28,0x5f,0x32,0x38,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x5f, + 0x33,0x37,0x2c,0x20,0x5f,0x33,0x37,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x5f,0x35,0x33,0x2e, + 0x70,0x6d,0x61,0x29,0x29,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_WGPU) +/* + diagnostic(off, derivative_uniformity); + + struct vs_params { + /_ @offset(0) _/ + mvp : mat4x4f, + } + + @binding(0) @group(0) var x_19 : vs_params; + + var position_1 : vec2f; + + var uv : vec2f; + + var texcoord0 : vec2f; + + var color : vec4f; + + var color0 : vec4f; + + var gl_Position : vec4f; + + fn main_1() { + gl_Position = (x_19.mvp * vec4f(position_1.x, position_1.y, 0.0f, 1.0f)); + uv = texcoord0; + color = color0; + return; + } + + struct main_out { + @builtin(position) + gl_Position : vec4f, + @location(0) + uv_1 : vec2f, + @location(1) + color_1 : vec4f, + } + + @vertex + fn main(@location(0) position_1_param : vec2f, @location(1) texcoord0_param : vec2f, @location(2) color0_param : vec4f) -> main_out { + position_1 = position_1_param; + texcoord0 = texcoord0_param; + color0 = color0_param; + main_1(); + return main_out(gl_Position, uv, color); + } +*/ +static const uint8_t _sspine_vs_source_wgsl[898] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x6d,0x76,0x70,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78,0x34,0x66,0x2c,0x0a, + 0x7d,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40, + 0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x76,0x61,0x72,0x3c,0x75,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70, + 0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74, + 0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a, + 0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20, + 0x7b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x28,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x2e,0x78, + 0x2c,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x2e,0x79,0x2c,0x20, + 0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b, + 0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a, + 0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29, + 0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65, + 0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31, + 0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d, + 0x0a,0x00, +}; +/* + diagnostic(off, derivative_uniformity); + + struct fs_params { + /_ @offset(0) _/ + pma : f32, + } + + @binding(0) @group(1) var tex : texture_2d; + + @binding(32) @group(1) var smp : sampler; + + var uv : vec2f; + + var color : vec4f; + + var frag_color : vec4f; + + @binding(1) @group(0) var x_53 : fs_params; + + fn main_1() { + var c0 : vec4f; + var c1 : vec4f; + let x_23 = uv; + let x_24 = textureSample(tex, smp, x_23); + c0 = (x_24 * color); + let x_38 = (c0.xyz * c0.w); + c1 = (vec4f(x_38.x, x_38.y, x_38.z, c0.w) * color); + frag_color = mix(c0, c1, vec4f(x_53.pma)); + return; + } + + struct main_out { + @location(0) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(0) uv_param : vec2f, @location(1) color_param : vec4f) -> main_out { + uv = uv_param; + color = color_param; + main_1(); + return main_out(frag_color); + } +*/ +static const uint8_t _sspine_fs_source_wgsl[850] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x70,0x6d,0x61,0x20,0x3a,0x20,0x66,0x33,0x32,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a, + 0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32,0x29,0x20,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20, + 0x3a,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c, + 0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74, + 0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x31,0x29, + 0x20,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x76,0x61,0x72,0x3c,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x35,0x33,0x20,0x3a,0x20,0x66, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x63, + 0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72, + 0x20,0x63,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73,0x6d, + 0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x63,0x30,0x20,0x3d, + 0x20,0x28,0x78,0x5f,0x32,0x34,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b, + 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x38,0x20,0x3d,0x20,0x28,0x63, + 0x30,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x63,0x30,0x2e,0x77,0x29,0x3b,0x0a,0x20, + 0x20,0x63,0x31,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x33, + 0x38,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x38,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x33, + 0x38,0x2e,0x7a,0x2c,0x20,0x63,0x30,0x2e,0x77,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x29,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x63,0x30,0x2c,0x20,0x63,0x31,0x2c,0x20, + 0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x35,0x33,0x2e,0x70,0x6d,0x61,0x29,0x29, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b, + 0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a, + 0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72,0x61,0x67, + 0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e, + 0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x75,0x76, + 0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75, + 0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d, + 0x0a,0x00, +}; +#elif defined(SOKOL_VULKAN) +/* + #version 460 + + layout(set = 0, binding = 0, std140) uniform vs_params + { + mat4 mvp; + } _19; + + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = _19.mvp * vec4(position, 0.0, 1.0); + uv = texcoord0; + color = color0; + } + +*/ +static const uint8_t _sspine_vs_bytecode_spirv_vk[1404] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x2b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0c,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xcc,0x01,0x00,0x00,0x05,0x00,0x04,0x00, + 0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0b,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0b,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,0x0b,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,0x0b,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,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00,0x5f,0x31,0x39,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,0x24,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x25,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x47,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x22,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,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x25,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1c,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x1e,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0e,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x20,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x05,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x21,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x22,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x24,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,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, +}; +/* + #version 460 + + layout(set = 0, binding = 1, std140) uniform fs_params + { + float pma; + } _53; + + layout(set = 1, binding = 0) uniform texture2D tex; + layout(set = 1, binding = 32) uniform sampler smp; + + layout(location = 0) in vec2 uv; + layout(location = 1) in vec4 color; + layout(location = 0) out vec4 frag_color; + + void main() + { + vec4 _28 = texture(sampler2D(tex, smp), uv) * color; + float _37 = _28.w; + frag_color = mix(_28, vec4(_28.xyz * _37, _37) * color, vec4(_53.pma)); + } +*/ +static const uint8_t _sspine_fs_bytecode_spirv_vk[1520] = { + 0x03,0x02,0x23,0x07,0x00,0x04,0x01,0x00,0x0b,0x00,0x08,0x00,0x3c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x01,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,0x0b,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x1a,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x10,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00, + 0xcc,0x01,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x5f,0x32,0x38,0x00, + 0x05,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00, + 0x10,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x05,0x00,0x03,0x00,0x16,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, + 0x72,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x5f,0x33,0x37,0x00, + 0x05,0x00,0x05,0x00,0x24,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x00,0x00,0x05,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x32,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x70,0x6d,0x61,0x00,0x05,0x00,0x03,0x00,0x34,0x00,0x00,0x00, + 0x5f,0x35,0x33,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x32,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x34,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x34,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0a,0x00,0x00,0x00,0x06,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,0x20,0x00,0x04,0x00,0x0b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0b,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x0e,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, + 0x1b,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x14,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,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,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x1f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x1f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x26,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x1e,0x00,0x03,0x00, + 0x32,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x35,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x35,0x00,0x00,0x00, + 0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x37,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, + 0x05,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x56,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x14,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x1d,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, + 0x26,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, + 0x26,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x37,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x3d,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x38,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x39,0x00,0x00,0x00, + 0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x0c,0x00,0x08,0x00, + 0x07,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x24,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sspine_vs_source_dummy = ""; +static const char* _sspine_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs + +#define _sspine_def(val, def) (((val) == 0) ? (def) : (val)) +#define _SSPINE_INIT_COOKIE (0xABBAABBA) +#define _SSPINE_INVALID_SLOT_INDEX (0) +#define _SSPINE_DEFAULT_CONTEXT_POOL_SIZE (4) +#define _SSPINE_DEFAULT_ATLAS_POOL_SIZE (64) +#define _SSPINE_DEFAULT_SKELETON_POOL_SIZE (64) +#define _SSPINE_DEFAULT_SKINSET_POOL_SIZE (64) +#define _SSPINE_DEFAULT_INSTANCE_POOL_SIZE (1024) +#define _SSPINE_DEFAULT_MAX_VERTICES (1<<16) +#define _SSPINE_DEFAULT_MAX_COMMANDS (1<<14) +#define _SSPINE_MAX_TRIGGERED_EVENTS (16) +#define _SSPINE_SLOT_SHIFT (16) +#define _SSPINE_MAX_POOL_SIZE (1<<_SSPINE_SLOT_SHIFT) +#define _SSPINE_SLOT_MASK (_SSPINE_MAX_POOL_SIZE-1) + +typedef struct { + float mvp[16]; +} _sspine_vsparams_t; + +typedef struct { + float pma; + uint8_t _pad[12]; +} _sspine_fsparams_t; + +typedef struct { + uint32_t id; + sspine_resource_state state; +} _sspine_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sspine_pool_t; + +typedef struct { + _sspine_slot_t slot; + sspine_atlas_overrides overrides; + spAtlas* sp_atlas; + int num_pages; +} _sspine_atlas_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_atlas_t* items; +} _sspine_atlas_pool_t; + +typedef struct { + uint32_t id; + _sspine_atlas_t* ptr; +} _sspine_atlas_ref_t; + +typedef struct { + sg_image img; + sg_view view; + sg_sampler smp; +} _sspine_image_view_sampler_t; + +typedef struct { + _sspine_slot_t slot; + _sspine_atlas_ref_t atlas; + spSkeletonData* sp_skel_data; + spAnimationStateData* sp_anim_data; + struct { + int cap; + sspine_vec2* ptr; + } tform_buf; +} _sspine_skeleton_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_skeleton_t* items; +} _sspine_skeleton_pool_t; + +typedef struct { + uint32_t id; + _sspine_skeleton_t* ptr; +} _sspine_skeleton_ref_t; + +typedef struct { + _sspine_slot_t slot; + _sspine_skeleton_ref_t skel; + spSkin* sp_skin; +} _sspine_skinset_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_skinset_t* items; +} _sspine_skinset_pool_t; + +typedef struct { + uint32_t id; + _sspine_skinset_t* ptr; +} _sspine_skinset_ref_t; + +typedef struct { + _sspine_slot_t slot; + _sspine_atlas_ref_t atlas; + _sspine_skeleton_ref_t skel; + _sspine_skinset_ref_t skinset; + spSkeleton* sp_skel; + spAnimationState* sp_anim_state; + spSkeletonClipping* sp_clip; + int cur_triggered_event_index; + sspine_triggered_event_info triggered_events[_SSPINE_MAX_TRIGGERED_EVENTS]; +} _sspine_instance_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_instance_t* items; +} _sspine_instance_pool_t; + +typedef struct { + sspine_vec2 pos; + sspine_vec2 uv; + uint32_t color; +} _sspine_vertex_t; + +typedef struct { + _sspine_vertex_t* ptr; + int index; +} _sspine_alloc_vertices_result_t; + +typedef struct { + uint32_t* ptr; + int index; +} _sspine_alloc_indices_result_t; + +typedef struct { + int layer; + sg_pipeline pip; + sg_view view; + sg_sampler smp; + float pma; // pma = 0.0: use texture color as is, pma = 1.0: multiply texture rgb by texture alpha in fragment shader + int base_element; + int num_elements; +} _sspine_command_t; + +typedef struct { + _sspine_slot_t slot; + float transform[16]; + struct { + int cap; + int next; + uint32_t rewind_frame_id; + _sspine_vertex_t* ptr; + } vertices; + struct { + int cap; + int next; + uint32_t rewind_frame_id; + uint32_t* ptr; + } indices; + struct { + int cap; + int next; + uint32_t rewind_frame_id; + _sspine_command_t* ptr; + } commands; + uint32_t update_frame_id; + sg_buffer vbuf; + sg_buffer ibuf; + struct { + sg_pipeline normal_additive; + sg_pipeline multiply; + } pip; + sg_bindings bind; +} _sspine_context_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_context_t* items; +} _sspine_context_pool_t; + +typedef struct { + uint32_t init_cookie; + uint32_t frame_id; + sspine_desc desc; + sspine_context def_ctx_id; + sspine_context cur_ctx_id; + _sspine_context_t* cur_ctx; // may be 0! + sg_shader shd; + _sspine_context_pool_t context_pool; + _sspine_atlas_pool_t atlas_pool; + _sspine_skeleton_pool_t skeleton_pool; + _sspine_skinset_pool_t skinset_pool; + _sspine_instance_pool_t instance_pool; +} _sspine_t; +static _sspine_t _sspine; + +// dummy spine-c platform implementation functions +#if defined(__cplusplus) +extern "C" { +#endif +void _spAtlasPage_createTexture(spAtlasPage* self, const char* path) { + // nothing to do here + (void)self; (void)path; +} + +static void _sspine_delete_image_view_sampler(const _sspine_image_view_sampler_t* ivs); +void _spAtlasPage_disposeTexture(spAtlasPage* self) { + _sspine_delete_image_view_sampler((const _sspine_image_view_sampler_t*) self->rendererObject); +} + +char* _spUtil_readFile(const char* path, int* length) { + (void)path; + *length = 0; + return 0; +} +#if defined(__cplusplus) +} // extern "C" +#endif + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SSPINE_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sspine_log_messages[] = { + _SSPINE_LOG_ITEMS +}; +#undef _SSPINE_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SSPINE_PANIC(code) _sspine_log(SSPINE_LOGITEM_ ##code, 0, __LINE__) +#define _SSPINE_ERROR(code) _sspine_log(SSPINE_LOGITEM_ ##code, 1, __LINE__) +#define _SSPINE_WARN(code) _sspine_log(SSPINE_LOGITEM_ ##code, 2, __LINE__) +#define _SSPINE_INFO(code) _sspine_log(SSPINE_LOGITEM_ ##code, 3, __LINE__) + +static void _sspine_log(sspine_log_item log_item, uint32_t log_level, uint32_t line_nr) { + if (_sspine.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sspine_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sspine.desc.logger.func("sspine", log_level, (uint32_t)log_item, message, line_nr, filename, _sspine.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _sspine_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +/* Copy a string into a fixed size buffer with guaranteed zero- + termination. + + Return false if the string didn't fit into the buffer and had to be clamped. + + FIXME: Currently UTF-8 strings might become invalid if the string + is clamped, because the last zero-byte might be written into + the middle of a multi-byte sequence. +*/ +static bool _sspine_strcpy(const char* src, char* dst, int max_len) { + SOKOL_ASSERT(src && dst && (max_len > 0)); + char* const end = &(dst[max_len-1]); + char c = 0; + for (int i = 0; i < max_len; i++) { + c = *src; + if (c != 0) { + src++; + } + *dst++ = c; + } + // truncated? + if (c != 0) { + *end = 0; + return false; + } else { + return true; + } +} + +static sspine_string _sspine_string(const char* cstr) { + sspine_string res; + _sspine_clear(&res, sizeof(res)); + if (cstr) { + res.valid = true; + res.truncated = !_sspine_strcpy(cstr, res.cstr, sizeof(res.cstr)); + res.len = (uint8_t)strlen(res.cstr); + } + return res; +} + +static void* _sspine_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sspine.desc.allocator.alloc_fn) { + ptr = _sspine.desc.allocator.alloc_fn(size, _sspine.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SSPINE_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _sspine_malloc_clear(size_t size) { + void* ptr = _sspine_malloc(size); + _sspine_clear(ptr, size); + return ptr; +} + +static void _sspine_free(void* ptr) { + if (_sspine.desc.allocator.free_fn) { + _sspine.desc.allocator.free_fn(ptr, _sspine.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ███████ ███████ ███████ +// ██ ██ ██ ██ ██ +// ██████ █████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ███████ +// +// >>refs +static bool _sspine_atlas_ref_valid(const _sspine_atlas_ref_t* ref) { + return ref->ptr && (ref->ptr->slot.id == ref->id); +} + +static bool _sspine_skeleton_ref_valid(const _sspine_skeleton_ref_t* ref) { + return ref->ptr && (ref->ptr->slot.id == ref->id); +} + +static bool _sspine_skinset_ref_valid(const _sspine_skinset_ref_t* ref) { + return ref->ptr && (ref->ptr->slot.id == ref->id); +} + +static bool _sspine_skeleton_and_deps_valid(_sspine_skeleton_t* skeleton) { + return skeleton && _sspine_atlas_ref_valid(&skeleton->atlas); +} + +static bool _sspine_skinset_and_deps_valid(_sspine_skinset_t* skinset) { + return skinset && _sspine_skeleton_ref_valid(&skinset->skel); +} + +static bool _sspine_instance_and_deps_valid(_sspine_instance_t* instance) { + return instance && + _sspine_atlas_ref_valid(&instance->atlas) && + _sspine_skeleton_ref_valid(&instance->skel) && + ((instance->skinset.id == SSPINE_INVALID_ID) || _sspine_skinset_ref_valid(&instance->skinset)); +} + +static sspine_image _sspine_image(uint32_t atlas_id, int index) { + sspine_image img = { atlas_id, index }; + return img; +} + +static sspine_atlas_page _sspine_atlas_page(uint32_t atlas_id, int index) { + sspine_atlas_page page = { atlas_id, index }; + return page; +} + +static sspine_anim _sspine_anim(uint32_t skeleton_id, int index) { + sspine_anim anim = { skeleton_id, index }; + return anim; +} + +static sspine_bone _sspine_bone(uint32_t skeleton_id, int index) { + sspine_bone bone = { skeleton_id, index }; + return bone; +} + +static sspine_slot _sspine_slot(uint32_t skeleton_id, int index) { + sspine_slot slot = { skeleton_id, index }; + return slot; +} + +static sspine_event _sspine_event(uint32_t skeleton_id, int index) { + sspine_event event = { skeleton_id, index }; + return event; +} + +static sspine_iktarget _sspine_iktarget(uint32_t skeleton_id, int index) { + sspine_iktarget iktarget = { skeleton_id, index }; + return iktarget; +} + +static sspine_skin _sspine_skin(uint32_t skeleton_id, int index) { + sspine_skin skin = { skeleton_id, index }; + return skin; +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _sspine_init_pool(_sspine_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 + pool->size = num + 1; + 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*) _sspine_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _sspine_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; + } +} + +static void _sspine_discard_pool(_sspine_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sspine_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sspine_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sspine_pool_alloc_index(_sspine_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SSPINE_INVALID_SLOT_INDEX; + } +} + +static void _sspine_pool_free_index(_sspine_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* initialize a pool slot: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the handle id +*/ +static uint32_t _sspine_slot_init(_sspine_pool_t* pool, _sspine_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SSPINE_RESOURCESTATE_INITIAL) && (slot->id == SSPINE_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SSPINE_SLOT_SHIFT)|(slot_index & _SSPINE_SLOT_MASK); + slot->state = SSPINE_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _sspine_slot_index(uint32_t id) { + int slot_index = (int) (id & _SSPINE_SLOT_MASK); + SOKOL_ASSERT(_SSPINE_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +static void _sspine_init_item_pool(_sspine_pool_t* pool, int pool_size, void** items_ptr, size_t item_size_bytes) { + // NOTE: the pools will have an additional item, since slot 0 is reserved + SOKOL_ASSERT(pool && (pool->size == 0)); + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SSPINE_MAX_POOL_SIZE)); + SOKOL_ASSERT(items_ptr && (*items_ptr == 0)); + SOKOL_ASSERT(item_size_bytes > 0); + _sspine_init_pool(pool, pool_size); + const size_t pool_size_bytes = item_size_bytes * (size_t)pool->size; + *items_ptr = _sspine_malloc_clear(pool_size_bytes); +} + +static void _sspine_discard_item_pool(_sspine_pool_t* pool, void** items_ptr) { + SOKOL_ASSERT(pool && (pool->size != 0)); + SOKOL_ASSERT(items_ptr && (*items_ptr != 0)); + _sspine_free(*items_ptr); *items_ptr = 0; + _sspine_discard_pool(pool); +} + +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ +// +// >>context +static void _sspine_setup_context_pool(int pool_size) { + _sspine_context_pool_t* p = &_sspine.context_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_context_t)); +} + +static void _sspine_discard_context_pool(void) { + _sspine_context_pool_t* p = &_sspine.context_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_context _sspine_make_context_handle(uint32_t id) { + sspine_context handle = { id }; + return handle; +} + +static _sspine_context_t* _sspine_context_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_context_pool_t* p = &_sspine.context_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_context_t* _sspine_lookup_context(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_context_t* ctx = _sspine_context_at(id); + if (ctx->slot.id == id) { + return ctx; + } + } + return 0; +} + +static sspine_context _sspine_alloc_context(void) { + _sspine_context_pool_t* p = &_sspine.context_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_context_handle(id); + } else { + // pool exhausted + return _sspine_make_context_handle(SSPINE_INVALID_ID); + } +} + +static sspine_resource_state _sspine_init_context(_sspine_context_t* ctx, const sspine_context_desc* desc) { + SOKOL_ASSERT(ctx && (ctx->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + // setup vertex, index and command storage + ctx->vertices.cap = desc->max_vertices; + ctx->indices.cap = ctx->vertices.cap * 3; + ctx->commands.cap = desc->max_commands; + + const size_t vbuf_size = (size_t)ctx->vertices.cap * sizeof(_sspine_vertex_t); + const size_t ibuf_size = (size_t)ctx->indices.cap * sizeof(uint32_t); + const size_t cbuf_size = (size_t)ctx->commands.cap * sizeof(_sspine_command_t); + + ctx->vertices.ptr = (_sspine_vertex_t*) _sspine_malloc(vbuf_size); + ctx->indices.ptr = (uint32_t*) _sspine_malloc(ibuf_size); + ctx->commands.ptr = (_sspine_command_t*) _sspine_malloc(cbuf_size); + + sg_buffer_desc vbuf_desc; + _sspine_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.usage.vertex_buffer = true; + vbuf_desc.usage.stream_update = true; + vbuf_desc.size = vbuf_size; + vbuf_desc.label = "sspine-vbuf"; + ctx->vbuf = sg_make_buffer(&vbuf_desc); + ctx->bind.vertex_buffers[0] = ctx->vbuf; + + sg_buffer_desc ibuf_desc; + _sspine_clear(&ibuf_desc, sizeof(ibuf_desc)); + ibuf_desc.usage.index_buffer = true; + ibuf_desc.usage.stream_update = true; + ibuf_desc.size = ibuf_size; + ibuf_desc.label = "sspine-ibuf"; + ctx->ibuf = sg_make_buffer(&ibuf_desc); + ctx->bind.index_buffer = ctx->ibuf; + + // for blend modes, see: https://wiki.libsdl.org/SDL_BlendMode + // + // NOTE: we're configuring the blend mode for premultiplied alpha, + // and then do the premultiplication in the fragment shader + // if needed + sg_pipeline_desc pip_desc; + _sspine_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.shader = _sspine.shd; + pip_desc.layout.buffers[0].stride = sizeof(_sspine_vertex_t); + pip_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2; + pip_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2; + pip_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N; + pip_desc.index_type = SG_INDEXTYPE_UINT32; + pip_desc.sample_count = desc->sample_count; + pip_desc.depth.pixel_format = desc->depth_format; + pip_desc.colors[0].pixel_format = desc->color_format; + pip_desc.colors[0].write_mask = desc->color_write_mask; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + pip_desc.label = "sspine-pip-normal/additive"; + ctx->pip.normal_additive = sg_make_pipeline(&pip_desc); + + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_DST_COLOR; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR; + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_DST_ALPHA; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + + pip_desc.label = "sspine-pip-multiply"; + ctx->pip.multiply = sg_make_pipeline(&pip_desc); + + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_context(_sspine_context_t* ctx) { + // NOTE: it's ok to call sg_destroy functions with invalid handles + sg_destroy_pipeline(ctx->pip.normal_additive); + sg_destroy_pipeline(ctx->pip.multiply); + sg_destroy_buffer(ctx->ibuf); + sg_destroy_buffer(ctx->vbuf); + if (ctx->commands.ptr) { + _sspine_free(ctx->commands.ptr); + ctx->commands.ptr = 0; + } + if (ctx->indices.ptr) { + _sspine_free(ctx->indices.ptr); + ctx->indices.ptr = 0; + } + if (ctx->vertices.ptr) { + _sspine_free(ctx->vertices.ptr); + ctx->vertices.ptr = 0; + } +} + +static void _sspine_destroy_context(sspine_context ctx_id) { + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + _sspine_deinit_context(ctx); + _sspine_context_pool_t* p = &_sspine.context_pool; + _sspine_clear(ctx, sizeof(_sspine_context_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(ctx_id.id)); + } +} + +static void _sspine_destroy_all_contexts(void) { + _sspine_context_pool_t* p = &_sspine.context_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_context_t* ctx = &p->items[i]; + _sspine_destroy_context(_sspine_make_context_handle(ctx->slot.id)); + } +} + +static sspine_context_desc _sspine_context_desc_defaults(const sspine_context_desc* desc) { + sspine_context_desc res = *desc; + res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES); + res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS); + return res; +} + +static bool _sspine_is_default_context(sspine_context ctx_id) { + return ctx_id.id == 0x00010001; +} + +// █████ ████████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██ ██ ███████ +// +// >>atlas +static void _sspine_setup_atlas_pool(int pool_size) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_atlas_t)); +} + +static void _sspine_discard_atlas_pool(void) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_atlas _sspine_make_atlas_handle(uint32_t id) { + sspine_atlas handle = { id }; + return handle; +} + +static _sspine_atlas_t* _sspine_atlas_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_atlas_t* _sspine_lookup_atlas(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_atlas_t* atlas = _sspine_atlas_at(id); + if (atlas->slot.id == id) { + return atlas; + } + } + return 0; +} + +static sspine_atlas _sspine_alloc_atlas(void) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_atlas_handle(id); + } else { + // pool exhausted + return _sspine_make_atlas_handle(SSPINE_INVALID_ID); + } +} + +static const _sspine_image_view_sampler_t* _sspine_new_image_view_sampler(sg_image img, sg_view view, sg_sampler smp) { + _sspine_image_view_sampler_t* ivs = (_sspine_image_view_sampler_t*) _sspine_malloc_clear(sizeof(_sspine_image_view_sampler_t)); + SOKOL_ASSERT(ivs); + ivs->img = img; + ivs->view = view; + ivs->smp = smp; + return ivs; +} + +static void _sspine_delete_image_view_sampler(const _sspine_image_view_sampler_t* ivs) { + if (ivs) { + sg_destroy_sampler(ivs->smp); + sg_destroy_view(ivs->view); + sg_destroy_image(ivs->img); + _sspine_free((void*)ivs); + } +} + +static sg_image _sspine_image_from_renderer_object(void* renderer_object) { + SOKOL_ASSERT(renderer_object); + const _sspine_image_view_sampler_t* ivs = (const _sspine_image_view_sampler_t*)renderer_object; + return ivs->img; +} + +static sg_view _sspine_view_from_renderer_object(void* renderer_object) { + SOKOL_ASSERT(renderer_object); + const _sspine_image_view_sampler_t* ivs = (const _sspine_image_view_sampler_t*)renderer_object; + return ivs->view; +} + +static sg_sampler _sspine_sampler_from_renderer_object(void* renderer_object) { + SOKOL_ASSERT(renderer_object); + const _sspine_image_view_sampler_t* ivs = (const _sspine_image_view_sampler_t*)renderer_object; + return ivs->smp; +} + +static sspine_resource_state _sspine_init_atlas(_sspine_atlas_t* atlas, const sspine_atlas_desc* desc) { + SOKOL_ASSERT(atlas && (atlas->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(atlas->sp_atlas == 0); + + if ((0 == desc->data.ptr) || (0 == desc->data.size)) { + _SSPINE_ERROR(ATLAS_DESC_NO_DATA); + return SSPINE_RESOURCESTATE_FAILED; + } + atlas->overrides = desc->override; + + // NOTE: Spine doesn't detect when invalid or corrupt data is passed here, + // not much we can do about this... + atlas->sp_atlas = spAtlas_create((const char*)desc->data.ptr, (int)desc->data.size, "", 0); + if (0 == atlas->sp_atlas) { + _SSPINE_ERROR(SPINE_ATLAS_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + + // allocate a sokol-gfx image, view and sampler object for each page, but the actual + // initialization needs to be delegated to the application + for (spAtlasPage* page = atlas->sp_atlas->pages; page != 0; page = page->next) { + atlas->num_pages++; + const sg_image img = sg_alloc_image(); + if (sg_query_image_state(img) != SG_RESOURCESTATE_ALLOC) { + _SSPINE_ERROR(SG_ALLOC_IMAGE_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + const sg_view view = sg_alloc_view(); + if (sg_query_view_state(view) != SG_RESOURCESTATE_ALLOC) { + _SSPINE_ERROR(SG_ALLOC_VIEW_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + const sg_sampler smp = sg_alloc_sampler(); + if (sg_query_sampler_state(smp) != SG_RESOURCESTATE_ALLOC) { + _SSPINE_ERROR(SG_ALLOC_SAMPLER_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + + // need to put the image and sampler handle into a heap-alloc unfortunately, + // because a void* isn't big enough to stash two 32-bit handles into + // it directly on platforms with 32-bit pointers (like wasm) + page->rendererObject = (void*) _sspine_new_image_view_sampler(img, view, smp); + + if (desc->override.premul_alpha_enabled) { + // NOTE: -1 is spine-c convention for 'true' + page->pma = -1; + } else if (desc->override.premul_alpha_disabled) { + page->pma = 0; + } + } + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_atlas(_sspine_atlas_t* atlas) { + if (atlas->sp_atlas) { + spAtlas_dispose(atlas->sp_atlas); + atlas->sp_atlas = 0; + } +} + +static void _sspine_destroy_atlas(sspine_atlas atlas_id) { + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + _sspine_deinit_atlas(atlas); + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + _sspine_clear(atlas, sizeof(_sspine_atlas_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(atlas_id.id)); + } +} + +static void _sspine_destroy_all_atlases(void) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_atlas_t* atlas = &p->items[i]; + _sspine_destroy_atlas(_sspine_make_atlas_handle(atlas->slot.id)); + } +} + +static sspine_atlas_desc _sspine_atlas_desc_defaults(const sspine_atlas_desc* desc) { + sspine_atlas_desc res = *desc; + return res; +} + +static spAtlasPage* _sspine_lookup_atlas_page(uint32_t atlas_id, int page_index) { + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id); + if (atlas) { + if ((page_index >= 0) && (page_index < atlas->num_pages)) { + int i = 0; + for (spAtlasPage* page = atlas->sp_atlas->pages; page != 0; page = page->next, i++) { + if (i == page_index) { + return page; + } + } + } + } + return 0; +} + +// ███████ ██ ██ ███████ ██ ███████ ████████ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ███████ █████ █████ ██ █████ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ███████ ███████ ██ ██████ ██ ████ +// +// >>skeleton +static void _sspine_setup_skeleton_pool(int pool_size) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_skeleton_t)); +} + +static void _sspine_discard_skeleton_pool(void) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_skeleton _sspine_make_skeleton_handle(uint32_t id) { + sspine_skeleton handle = { id }; + return handle; +} + +static _sspine_skeleton_t* _sspine_skeleton_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_skeleton_t* _sspine_lookup_skeleton(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_skeleton_t* skeleton = _sspine_skeleton_at(id); + if (skeleton->slot.id == id) { + return skeleton; + } + } + return 0; +} + +static sspine_skeleton _sspine_alloc_skeleton(void) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_skeleton_handle(id); + } else { + // pool exhausted + return _sspine_make_skeleton_handle(SSPINE_INVALID_ID); + } +} + +static sspine_resource_state _sspine_init_skeleton(_sspine_skeleton_t* skeleton, const sspine_skeleton_desc* desc) { + SOKOL_ASSERT(skeleton && (skeleton->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + if ((0 == desc->json_data) && ((0 == desc->binary_data.ptr) || (0 == desc->binary_data.size))) { + _SSPINE_ERROR(SKELETON_DESC_NO_DATA); + return SSPINE_RESOURCESTATE_FAILED; + } + if (desc->atlas.id == SSPINE_INVALID_ID) { + _SSPINE_ERROR(SKELETON_DESC_NO_ATLAS); + return SSPINE_RESOURCESTATE_FAILED; + } + + skeleton->atlas.id = desc->atlas.id; + skeleton->atlas.ptr = _sspine_lookup_atlas(skeleton->atlas.id); + if (!_sspine_atlas_ref_valid(&skeleton->atlas)) { + _SSPINE_ERROR(SKELETON_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + _sspine_atlas_t* atlas = skeleton->atlas.ptr; + if (SSPINE_RESOURCESTATE_VALID != atlas->slot.state) { + _SSPINE_ERROR(SKELETON_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(atlas->sp_atlas); + + if (desc->json_data) { + spSkeletonJson* skel_json = spSkeletonJson_create(atlas->sp_atlas); + SOKOL_ASSERT(skel_json); + skel_json->scale = desc->prescale; + skeleton->sp_skel_data = spSkeletonJson_readSkeletonData(skel_json, desc->json_data); + spSkeletonJson_dispose(skel_json); skel_json = 0; + if (0 == skeleton->sp_skel_data) { + _SSPINE_ERROR(CREATE_SKELETON_DATA_FROM_JSON_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + } else { + spSkeletonBinary* skel_bin = spSkeletonBinary_create(atlas->sp_atlas); + SOKOL_ASSERT(skel_bin); + skel_bin->scale = desc->prescale; + skeleton->sp_skel_data = spSkeletonBinary_readSkeletonData(skel_bin, (const unsigned char*)desc->binary_data.ptr, (int)desc->binary_data.size); + spSkeletonBinary_dispose(skel_bin); skel_bin = 0; + if (0 == skeleton->sp_skel_data) { + _SSPINE_ERROR(CREATE_SKELETON_DATA_FROM_BINARY_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + } + SOKOL_ASSERT(skeleton->sp_skel_data); + + skeleton->sp_anim_data = spAnimationStateData_create(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_anim_data); + skeleton->sp_anim_data->defaultMix = desc->anim_default_mix; + + // get the max number of vertices in any mesh attachment + int max_vertex_count = 4; // number of vertices in a 'region attachment' (a 2-triangle quad) + const spSkeletonData* sp_skel_data = skeleton->sp_skel_data; + for (int skinIndex = 0; skinIndex < sp_skel_data->skinsCount; skinIndex++) { + const spSkin* sp_skin = sp_skel_data->skins[skinIndex]; + const spSkinEntry* skin_entry = spSkin_getAttachments(sp_skin); + if (skin_entry) do { + if (skin_entry->attachment) { + if (skin_entry->attachment->type == SP_ATTACHMENT_MESH) { + const spMeshAttachment* mesh_attachment = (spMeshAttachment*)skin_entry->attachment; + // worldVerticesLength is number of floats + SOKOL_ASSERT((mesh_attachment->super.worldVerticesLength & 1) == 0); + const int num_vertices = mesh_attachment->super.worldVerticesLength / 2; + if (num_vertices > max_vertex_count) { + max_vertex_count = num_vertices; + } + } + } + } while ((skin_entry = skin_entry->next) != 0); + } + + // allocate a shared vertex transform buffer (big enough to hold vertices for biggest mesh attachment) + skeleton->tform_buf.cap = max_vertex_count; + skeleton->tform_buf.ptr = (sspine_vec2*) _sspine_malloc((size_t)skeleton->tform_buf.cap * sizeof(sspine_vec2)); + + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_skeleton(_sspine_skeleton_t* skeleton) { + if (skeleton->tform_buf.ptr) { + _sspine_free(skeleton->tform_buf.ptr); + skeleton->tform_buf.ptr = 0; + } + if (skeleton->sp_anim_data) { + spAnimationStateData_dispose(skeleton->sp_anim_data); + skeleton->sp_anim_data = 0; + } + if (skeleton->sp_skel_data) { + spSkeletonData_dispose(skeleton->sp_skel_data); + skeleton->sp_skel_data = 0; + } +} + +static void _sspine_destroy_skeleton(sspine_skeleton skeleton_id) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (skeleton) { + _sspine_deinit_skeleton(skeleton); + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + _sspine_clear(skeleton, sizeof(_sspine_skeleton_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(skeleton_id.id)); + } +} + +static void _sspine_destroy_all_skeletons(void) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_skeleton_t* skeleton = &p->items[i]; + _sspine_destroy_skeleton(_sspine_make_skeleton_handle(skeleton->slot.id)); + } +} + +static sspine_skeleton_desc _sspine_skeleton_desc_defaults(const sspine_skeleton_desc* desc) { + sspine_skeleton_desc res = *desc; + res.prescale = _sspine_def(desc->prescale, 1.0f); + res.anim_default_mix = _sspine_def(desc->anim_default_mix, 0.2f); + return res; +} + +static spBoneData* _sspine_lookup_bone_data(uint32_t skeleton_id, int bone_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->bones); + if ((bone_index >= 0) && (bone_index <= skeleton->sp_skel_data->bonesCount)) { + return skeleton->sp_skel_data->bones[bone_index]; + } + } + return 0; +} + +static spSlotData* _sspine_lookup_slot_data(uint32_t skeleton_id, int slot_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->slots); + if ((slot_index >= 0) && (slot_index <= skeleton->sp_skel_data->slotsCount)) { + return skeleton->sp_skel_data->slots[slot_index]; + } + } + return 0; +} + +static spEventData* _sspine_lookup_event_data(uint32_t skeleton_id, int event_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->events); + if ((event_index >= 0) && (event_index < skeleton->sp_skel_data->eventsCount)) { + return skeleton->sp_skel_data->events[event_index]; + } + } + return 0; +} + +static spIkConstraintData* _sspine_lookup_ikconstraint_data(uint32_t skeleton_id, int iktarget_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->ikConstraints); + if ((iktarget_index >= 0) && (iktarget_index < skeleton->sp_skel_data->ikConstraintsCount)) { + return skeleton->sp_skel_data->ikConstraints[iktarget_index]; + } + } + return 0; +} + +static spSkin* _sspine_lookup_skin(uint32_t skeleton_id, int skin_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->skins); + if ((skin_index >= 0) && (skin_index < skeleton->sp_skel_data->skinsCount)) { + return skeleton->sp_skel_data->skins[skin_index]; + } + } + return 0; +} + +// ███████ ██ ██ ██ ███ ██ ███████ ███████ ████████ +// ██ ██ ██ ██ ████ ██ ██ ██ ██ +// ███████ █████ ██ ██ ██ ██ ███████ █████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ████ ███████ ███████ ██ +// +// >>skinset +static void _sspine_setup_skinset_pool(int pool_size) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_skinset_t)); +} + +static void _sspine_discard_skinset_pool(void) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_skinset _sspine_make_skinset_handle(uint32_t id) { + sspine_skinset handle = { id }; + return handle; +} + +static _sspine_skinset_t* _sspine_skinset_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_skinset_t* _sspine_lookup_skinset(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_skinset_t* skinset = _sspine_skinset_at(id); + if (skinset->slot.id == id) { + return skinset; + } + } + return 0; +} + +static sspine_skinset _sspine_alloc_skinset(void) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_skinset_handle(id); + } else { + // pool exhausted + return _sspine_make_skinset_handle(SSPINE_INVALID_ID); + } +} + +static sspine_resource_state _sspine_init_skinset(_sspine_skinset_t* skinset, const sspine_skinset_desc* desc) { + SOKOL_ASSERT(skinset && (skinset->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + if (desc->skeleton.id == SSPINE_INVALID_ID) { + _SSPINE_ERROR(SKINSET_DESC_NO_SKELETON); + return SSPINE_RESOURCESTATE_FAILED; + } + skinset->skel.id = desc->skeleton.id; + skinset->skel.ptr = _sspine_lookup_skeleton(desc->skeleton.id); + if (!_sspine_skeleton_ref_valid(&skinset->skel)) { + _SSPINE_ERROR(SKINSET_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + _sspine_skeleton_t* skel = skinset->skel.ptr; + if (SSPINE_RESOURCESTATE_VALID != skel->slot.state) { + _SSPINE_ERROR(SKINSET_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(skel->sp_skel_data); + skinset->sp_skin = spSkin_create("skinset"); + for (int i = 0; i < SSPINE_MAX_SKINSET_SKINS; i++) { + if (desc->skins[i].skeleton_id != SSPINE_INVALID_ID) { + spSkin* skin = _sspine_lookup_skin(desc->skins[i].skeleton_id, desc->skins[i].index); + if (0 == skin) { + _SSPINE_ERROR(SKINSET_INVALID_SKIN_HANDLE); + return SSPINE_RESOURCESTATE_FAILED; + } + spSkin_addSkin(skinset->sp_skin, skin); + } + } + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_skinset(_sspine_skinset_t* skinset) { + if (skinset->sp_skin) { + spSkin_clear(skinset->sp_skin); + spSkin_dispose(skinset->sp_skin); + skinset->sp_skin = 0; + } +} + +static void _sspine_destroy_skinset(sspine_skinset skinset_id) { + _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (skinset) { + _sspine_deinit_skinset(skinset); + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + _sspine_clear(skinset, sizeof(_sspine_skinset_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(skinset_id.id)); + } +} + +static void _sspine_destroy_all_skinsets(void) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_skinset_t* skinset = &p->items[i]; + _sspine_destroy_skinset(_sspine_make_skinset_handle(skinset->slot.id)); + } +} + +static sspine_skinset_desc _sspine_skinset_desc_defaults(const sspine_skinset_desc* desc) { + sspine_skinset_desc res = *desc; + return res; +} + +// ██ ███ ██ ███████ ████████ █████ ███ ██ ██████ ███████ +// ██ ████ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ███████ ██ ███████ ██ ██ ██ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ████ ███████ ██ ██ ██ ██ ████ ██████ ███████ +// +// >>instance +static void _sspine_setup_instance_pool(int pool_size) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_instance_t)); +} + +static void _sspine_discard_instance_pool(void) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_instance _sspine_make_instance_handle(uint32_t id) { + sspine_instance handle = { id }; + return handle; +} + +static _sspine_instance_t* _sspine_instance_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_instance_pool_t* p = &_sspine.instance_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_instance_t* _sspine_lookup_instance(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_instance_t* instance = _sspine_instance_at(id); + if (instance->slot.id == id) { + return instance; + } + } + return 0; +} + +static sspine_instance _sspine_alloc_instance(void) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + sspine_instance res; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + res = _sspine_make_instance_handle(id); + } else { + // pool exhausted + res = _sspine_make_instance_handle(SSPINE_INVALID_ID); + } + return res; +} + +static void _sspine_rewind_triggered_events(_sspine_instance_t* instance) { + instance->cur_triggered_event_index = 0; + _sspine_clear(instance->triggered_events, sizeof(instance->triggered_events)); +} + +static sspine_triggered_event_info* _sspine_next_triggered_event_info(_sspine_instance_t* instance) { + if (instance->cur_triggered_event_index < _SSPINE_MAX_TRIGGERED_EVENTS) { + return &instance->triggered_events[instance->cur_triggered_event_index++]; + } else { + return 0; + } +} + +static void _sspine_event_listener(spAnimationState* sp_anim_state, spEventType sp_event_type, spTrackEntry* sp_track_entry, spEvent* sp_event) { + if (sp_event_type == SP_ANIMATION_EVENT) { + SOKOL_ASSERT(sp_anim_state && sp_track_entry && sp_event); (void)sp_track_entry; + SOKOL_ASSERT(sp_event->data && sp_event->data->name); + _sspine_instance_t* instance = _sspine_lookup_instance((uint32_t)(uintptr_t)sp_anim_state->userData); + if (_sspine_instance_and_deps_valid(instance)) { + sspine_triggered_event_info* info = _sspine_next_triggered_event_info(instance); + if (info) { + // FIXME: this sucks, but we really need the event index + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(instance->skel.id); + SOKOL_ASSERT(skeleton && skeleton->sp_skel_data->events); + const spEventData* sp_event_data = sp_event->data; + for (int i = 0; i < skeleton->sp_skel_data->eventsCount; i++) { + if (sp_event_data == skeleton->sp_skel_data->events[i]) { + info->event = _sspine_event(skeleton->slot.id, i); + break; + } + } + SOKOL_ASSERT(info->event.skeleton_id != SSPINE_INVALID_ID); + info->valid = true; + info->time = sp_event->time; + info->int_value = sp_event->intValue; + info->float_value = sp_event->floatValue; + info->volume = sp_event->volume; + info->balance = sp_event->balance; + info->string_value = _sspine_string(sp_event->stringValue); + if (info->string_value.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + } + } +} + +static sspine_resource_state _sspine_init_instance(_sspine_instance_t* instance, const sspine_instance_desc* desc) { + SOKOL_ASSERT(instance && (instance->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + if (desc->skeleton.id == SSPINE_INVALID_ID) { + _SSPINE_ERROR(INSTANCE_DESC_NO_SKELETON); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->skel.id = desc->skeleton.id; + instance->skel.ptr = _sspine_lookup_skeleton(instance->skel.id); + if (!_sspine_skeleton_ref_valid(&instance->skel)) { + _SSPINE_ERROR(INSTANCE_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + _sspine_skeleton_t* skel = instance->skel.ptr; + if (SSPINE_RESOURCESTATE_VALID != skel->slot.state) { + _SSPINE_ERROR(INSTANCE_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->atlas = skel->atlas; + if (!_sspine_atlas_ref_valid(&instance->atlas)) { + _SSPINE_ERROR(INSTANCE_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + if (SSPINE_RESOURCESTATE_VALID != instance->atlas.ptr->slot.state) { + _SSPINE_ERROR(INSTANCE_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(skel->sp_skel_data); + SOKOL_ASSERT(skel->sp_anim_data); + + instance->sp_skel = spSkeleton_create(skel->sp_skel_data); + if (0 == instance->sp_skel) { + _SSPINE_ERROR(SPINE_SKELETON_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->sp_anim_state = spAnimationState_create(skel->sp_anim_data); + if (0 == instance->sp_anim_state) { + _SSPINE_ERROR(SPINE_ANIMATIONSTATE_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->sp_clip = spSkeletonClipping_create(); + if (0 == instance->sp_clip) { + _SSPINE_ERROR(SPINE_SKELETONCLIPPING_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + + instance->sp_anim_state->userData = (void*)(uintptr_t)instance->slot.id; + instance->sp_anim_state->listener = _sspine_event_listener; + + spSkeleton_setToSetupPose(instance->sp_skel); + spAnimationState_update(instance->sp_anim_state, 0.0f); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + spSkeleton_update(instance->sp_skel, 0.0f); + spSkeleton_updateWorldTransform(instance->sp_skel, SP_PHYSICS_UPDATE); + + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_instance(_sspine_instance_t* instance) { + if (instance->sp_clip) { + spSkeletonClipping_dispose(instance->sp_clip); + instance->sp_clip = 0; + } + if (instance->sp_anim_state) { + spAnimationState_dispose(instance->sp_anim_state); + instance->sp_anim_state = 0; + } + if (instance->sp_skel) { + spSkeleton_dispose(instance->sp_skel); + instance->sp_skel = 0; + } +} + +static void _sspine_destroy_instance(sspine_instance instance_id) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + _sspine_deinit_instance(instance); + _sspine_instance_pool_t* p = &_sspine.instance_pool; + _sspine_clear(instance, sizeof(_sspine_instance_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(instance_id.id)); + } +} + +static void _sspine_destroy_all_instances(void) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_instance_t* instance = &p->items[i]; + _sspine_destroy_instance(_sspine_make_instance_handle(instance->slot.id)); + } +} + +static spAnimation* _sspine_lookup_skeleton_anim(uint32_t skeleton_id, int anim_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + if ((anim_index >= 0) && (anim_index < skeleton->sp_skel_data->animationsCount)) { + return skeleton->sp_skel_data->animations[anim_index]; + } + } + return 0; +} + +static spAnimation* _sspine_lookup_instance_anim(uint32_t instance_id, uint32_t skeleton_id, int anim_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->data); + if ((anim_index >= 0) && (anim_index < instance->sp_skel->data->animationsCount)) { + return instance->sp_skel->data->animations[anim_index]; + } + } + return 0; +} + +static spBone* _sspine_lookup_bone(uint32_t instance_id, uint32_t skeleton_id, int bone_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->bones); + if ((bone_index >= 0) && (bone_index <= instance->sp_skel->bonesCount)) { + return instance->sp_skel->bones[bone_index]; + } + } + return 0; +} + +static spSlot* _sspine_lookup_slot(uint32_t instance_id, uint32_t skeleton_id, int slot_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->slots); + if ((slot_index >= 0) && (slot_index <= instance->sp_skel->slotsCount)) { + return instance->sp_skel->slots[slot_index]; + } + } + return 0; +} + +static spIkConstraint* _sspine_lookup_ikconstraint(uint32_t instance_id, uint32_t skeleton_id, int iktarget_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->ikConstraints); + if ((iktarget_index >= 0) && (iktarget_index < instance->sp_skel->ikConstraintsCount)) { + return instance->sp_skel->ikConstraints[iktarget_index]; + } + } + return 0; +} + +static sspine_instance_desc _sspine_instance_desc_defaults(const sspine_instance_desc* desc) { + sspine_instance_desc res = *desc; + return res; +} + +// ██████ ██████ █████ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██████ ███████ ██ █ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ ██ +// ██████ ██ ██ ██ ██ ███ ███ +// +// >>draw +static void _sspine_check_rewind_commands(_sspine_context_t* ctx) { + if (_sspine.frame_id != ctx->commands.rewind_frame_id) { + ctx->commands.next = 0; + ctx->commands.rewind_frame_id = _sspine.frame_id; + } +} + +static _sspine_command_t* _sspine_next_command(_sspine_context_t* ctx) { + _sspine_check_rewind_commands(ctx); + if (ctx->commands.next < ctx->commands.cap) { + return &(ctx->commands.ptr[ctx->commands.next++]); + } else { + _SSPINE_ERROR(COMMAND_BUFFER_FULL); + return 0; + } +} + +static _sspine_command_t* _sspine_cur_command(_sspine_context_t* ctx) { + _sspine_check_rewind_commands(ctx); + if (ctx->commands.next > 0) { + return &ctx->commands.ptr[ctx->commands.next - 1]; + } else { + return 0; + } +} + +static void _sspine_check_rewind_vertices(_sspine_context_t* ctx) { + if (_sspine.frame_id != ctx->vertices.rewind_frame_id) { + ctx->vertices.next = 0; + ctx->vertices.rewind_frame_id = _sspine.frame_id; + } +} + +static _sspine_alloc_vertices_result_t _sspine_alloc_vertices(_sspine_context_t* ctx, int num) { + _sspine_check_rewind_vertices(ctx); + _sspine_alloc_vertices_result_t res; + _sspine_clear(&res, sizeof(res)); + if ((ctx->vertices.next + num) <= ctx->vertices.cap) { + res.ptr = &(ctx->vertices.ptr[ctx->vertices.next]); + res.index = ctx->vertices.next; + ctx->vertices.next += num; + } else { + _SSPINE_ERROR(VERTEX_BUFFER_FULL); + } + return res; +} + +static void _sspine_check_rewind_indices(_sspine_context_t* ctx) { + if (_sspine.frame_id != ctx->indices.rewind_frame_id) { + ctx->indices.next = 0; + ctx->indices.rewind_frame_id = _sspine.frame_id; + } +} + +static _sspine_alloc_indices_result_t _sspine_alloc_indices(_sspine_context_t* ctx, int num) { + _sspine_check_rewind_indices(ctx); + _sspine_alloc_indices_result_t res; + _sspine_clear(&res, sizeof(res)); + if ((ctx->indices.next + num) <= ctx->indices.cap) { + res.ptr = &(ctx->indices.ptr[ctx->indices.next]); + res.index = ctx->indices.next; + ctx->indices.next += num; + } else { + _SSPINE_ERROR(INDEX_BUFFER_FULL); + } + return res; +} + +static void _sspine_draw_instance(_sspine_context_t* ctx, _sspine_instance_t* instance, int layer) { + SOKOL_ASSERT(_sspine_instance_and_deps_valid(instance)); + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + SOKOL_ASSERT(instance->sp_clip); + + // see: https://github.com/EsotericSoftware/spine-runtimes/blob/4.1/spine-sdl/src/spine-sdl-c.c + const spSkeleton* sp_skel = instance->sp_skel; + float* tform_buf = (float*)instance->skel.ptr->tform_buf.ptr; + const int max_tform_buf_verts = instance->skel.ptr->tform_buf.cap; + SOKOL_UNUSED(max_tform_buf_verts); // only used in asserts + const int tform_buf_stride = 2; // each element is 2 floats + spSkeletonClipping* sp_clip = instance->sp_clip; + for (int slot_index = 0; slot_index < sp_skel->slotsCount; slot_index++) { + spSlot* sp_slot = sp_skel->drawOrder[slot_index]; + if (!sp_slot->attachment) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + // early out if the slot alpha is 0 or the bone is not active + // FIXME: does alpha 0 actually mean 'invisible' for all blend modes? + if ((sp_slot->color.a == 0) || (!sp_slot->bone->active)) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + int num_vertices = 0; + float* uvs = 0; + float* vertices = 0; + int num_indices = 0; + const uint16_t* indices = 0; + const spColor* att_color = 0; + sg_view view = { SG_INVALID_ID }; + sg_sampler smp = { SG_INVALID_ID }; + bool premul_alpha = false; + if (sp_slot->attachment->type == SP_ATTACHMENT_REGION) { + static const uint16_t quad_indices[] = { 0, 1, 2, 2, 3, 0 }; + spRegionAttachment* region = (spRegionAttachment*)sp_slot->attachment; + att_color = ®ion->color; + // FIXME(?) early out if the slot alpha is 0 + if (att_color->a == 0) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + spRegionAttachment_computeWorldVertices(region, sp_slot, tform_buf, 0, tform_buf_stride); + vertices = tform_buf; + num_vertices = 4; + indices = &quad_indices[0]; + num_indices = 6; + uvs = region->uvs; + const spAtlasPage* sp_page = ((spAtlasRegion*)region->rendererObject)->page; + view = _sspine_view_from_renderer_object(sp_page->rendererObject); + smp = _sspine_sampler_from_renderer_object(sp_page->rendererObject); + premul_alpha = sp_page->pma != 0; + } else if (sp_slot->attachment->type == SP_ATTACHMENT_MESH) { + spMeshAttachment* mesh = (spMeshAttachment*)sp_slot->attachment; + att_color = &mesh->color; + // FIXME(?) early out if the slot alpha is 0 + if (att_color->a == 0) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + const int num_floats = mesh->super.worldVerticesLength; + num_vertices = num_floats / 2; + SOKOL_ASSERT(num_vertices <= max_tform_buf_verts); + spVertexAttachment_computeWorldVertices(&mesh->super, sp_slot, 0, num_floats, tform_buf, 0, tform_buf_stride); + vertices = tform_buf; + indices = mesh->triangles; + num_indices = mesh->trianglesCount; // actually indicesCount??? + uvs = mesh->uvs; + const spAtlasPage* sp_page = ((spAtlasRegion*)mesh->rendererObject)->page; + view = _sspine_view_from_renderer_object(sp_page->rendererObject); + smp = _sspine_sampler_from_renderer_object(sp_page->rendererObject); + premul_alpha = sp_page->pma != 0; + } else if (sp_slot->attachment->type == SP_ATTACHMENT_CLIPPING) { + spClippingAttachment* clip_attachment = (spClippingAttachment*) sp_slot->attachment; + spSkeletonClipping_clipStart(sp_clip, sp_slot, clip_attachment); + continue; + } else { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + SOKOL_ASSERT(vertices && (num_vertices > 0)); + SOKOL_ASSERT(indices && (num_indices > 0)); + SOKOL_ASSERT(uvs); + SOKOL_ASSERT(view.id != SG_INVALID_ID); + SOKOL_ASSERT(smp.id != SG_INVALID_ID); + + if (spSkeletonClipping_isClipping(sp_clip)) { + spSkeletonClipping_clipTriangles(sp_clip, tform_buf, num_vertices * 2, (uint16_t*)indices, num_indices, uvs, tform_buf_stride); + vertices = sp_clip->clippedVertices->items; + num_vertices = sp_clip->clippedVertices->size / 2; + uvs = sp_clip->clippedUVs->items; + indices = sp_clip->clippedTriangles->items; + num_indices = sp_clip->clippedTriangles->size; + } + SOKOL_ASSERT(vertices); + SOKOL_ASSERT(indices); + SOKOL_ASSERT(uvs); + + // there might be no geometry to render after clipping + if ((0 == num_vertices) || (0 == num_indices)) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + const _sspine_alloc_vertices_result_t dst_vertices = _sspine_alloc_vertices(ctx, num_vertices); + const _sspine_alloc_indices_result_t dst_indices = _sspine_alloc_indices(ctx, num_indices); + if ((0 == dst_vertices.ptr) || (0 == dst_indices.ptr)) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + // write transformed and potentially clipped vertices and indices + const uint8_t r = (uint8_t)(sp_skel->color.r * sp_slot->color.r * att_color->r * 255.0f); + const uint8_t g = (uint8_t)(sp_skel->color.g * sp_slot->color.g * att_color->g * 255.0f); + const uint8_t b = (uint8_t)(sp_skel->color.b * sp_slot->color.b * att_color->b * 255.0f); + const uint8_t a = (uint8_t)(sp_skel->color.a * sp_slot->color.a * att_color->a * 255.0f); + const uint32_t color = (((uint32_t)a<<24) | ((uint32_t)b<<16) | ((uint32_t)g<<8) | (uint32_t)r); + for (int vi = 0; vi < num_vertices; vi++) { + dst_vertices.ptr[vi].pos.x = vertices[vi*2]; + dst_vertices.ptr[vi].pos.y = vertices[vi*2 + 1]; + dst_vertices.ptr[vi].color = color; + dst_vertices.ptr[vi].uv.x = uvs[vi*2]; + dst_vertices.ptr[vi].uv.y = uvs[vi*2 + 1]; + } + for (int ii = 0; ii < num_indices; ii++) { + dst_indices.ptr[ii] = (uint32_t)indices[ii] + (uint32_t)dst_vertices.index; + } + + sg_pipeline pip = { SG_INVALID_ID }; + // NOTE: pma == 0.0: use color from texture as is + // pma == 1.0: multiply texture rgb by texture alpha in fragment shader + float pma = 0.0f; + switch (sp_slot->data->blendMode) { + case SP_BLEND_MODE_NORMAL: + case SP_BLEND_MODE_ADDITIVE: + case SP_BLEND_MODE_SCREEN: + pip = ctx->pip.normal_additive; + pma = premul_alpha ? 0.0f : 1.0f; // NOT A BUG + break; + case SP_BLEND_MODE_MULTIPLY: + pip = ctx->pip.multiply; + pma = 0.0f; // always use texture color as is + break; + } + + // write new draw command, or merge with current draw command + _sspine_command_t* cur_cmd = _sspine_cur_command(ctx); + if (cur_cmd + && (cur_cmd->layer == layer) + && (cur_cmd->pip.id == pip.id) + && (cur_cmd->view.id == view.id) + && (cur_cmd->smp.id == smp.id) + && (cur_cmd->pma == pma)) + { + // merge with current command + cur_cmd->num_elements += num_indices; + } else { + // record a new command + _sspine_command_t* cmd_ptr = _sspine_next_command(ctx); + if (cmd_ptr) { + cmd_ptr->layer = layer; + cmd_ptr->pip = pip; + cmd_ptr->view = view; + cmd_ptr->smp = smp; + cmd_ptr->pma = pma; + cmd_ptr->base_element = dst_indices.index; + cmd_ptr->num_elements = num_indices; + } + } + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + } + spSkeletonClipping_clipEnd2(sp_clip); +} + +// compute orthographic projection matrix +static void _sspine_layer_transform_to_proj(const sspine_layer_transform* tform, float* res) { + const float left = -tform->origin.x; + const float right = tform->size.x - tform->origin.x; + const float top = -tform->origin.y; + const float bottom = tform->size.y - tform->origin.y; + const float znear = -1.0f; + const float zfar = 1.0f; + res[0] = 2.0f / (right - left); + res[1] = 0.0f; + res[2] = 0.0f; + res[3] = 0.0f; + res[4] = 0.0f; + res[5] = 2.0f / (top - bottom); + res[6] = 0.0f; + res[7] = 0.0f; + res[8] = 0.0f; + res[9] = 0.0f; + res[10] = -2.0f / (zfar - znear); + res[11] = 0.0f; + res[12] = -(right + left) / (right - left); + res[13] = -(top + bottom) / (top - bottom); + res[14] = -(zfar + znear) / (zfar - znear); + res[15] = 1.0f; +} + +static _sspine_vsparams_t _sspine_compute_vsparams(const sspine_layer_transform* tform) { + _sspine_vsparams_t p; + _sspine_clear(&p, sizeof(p)); + _sspine_layer_transform_to_proj(tform, p.mvp); + return p; +} + +static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_layer_transform* tform) { + if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-spine"); + + if (ctx->update_frame_id != _sspine.frame_id) { + ctx->update_frame_id = _sspine.frame_id; + const sg_range vtx_range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sspine_vertex_t) }; + sg_update_buffer(ctx->vbuf, &vtx_range); + const sg_range idx_range = { ctx->indices.ptr, (size_t)ctx->indices.next * sizeof(uint32_t) }; + sg_update_buffer(ctx->ibuf, &idx_range); + } + + _sspine_vsparams_t vsparams = _sspine_compute_vsparams(tform); + const sg_range vsparams_range = { &vsparams, sizeof(vsparams) }; + _sspine_fsparams_t fsparams; + _sspine_clear(&fsparams, sizeof(fsparams)); + const sg_range fsparams_range = { &fsparams, sizeof(fsparams) }; + + uint32_t cur_pip_id = SG_INVALID_ID; + uint32_t cur_view_id = SG_INVALID_ID; + uint32_t cur_smp_id = SG_INVALID_ID; + float cur_pma = -1.0f; + for (int i = 0; i < ctx->commands.next; i++) { + const _sspine_command_t* cmd = &ctx->commands.ptr[i]; + const bool view_valid = sg_query_view_state(cmd->view) == SG_RESOURCESTATE_VALID; + const bool smp_valid = sg_query_sampler_state(cmd->smp) == SG_RESOURCESTATE_VALID; + if ((layer == cmd->layer) && view_valid && smp_valid) { + if (cur_pip_id != cmd->pip.id) { + sg_apply_pipeline(cmd->pip); + cur_pip_id = cmd->pip.id; + sg_apply_uniforms(0, &vsparams_range); + sg_apply_uniforms(1, &fsparams_range); + cur_view_id = SG_INVALID_ID; + } + if ((cur_view_id != cmd->view.id) || (cur_smp_id != cmd->smp.id)) { + ctx->bind.views[0] = cmd->view; + ctx->bind.samplers[0] = cmd->smp; + sg_apply_bindings(&ctx->bind); + cur_view_id = cmd->view.id; + cur_smp_id = cmd->smp.id; + } + if (cur_pma != cmd->pma) { + fsparams.pma = cmd->pma; + sg_apply_uniforms(1, &fsparams_range); + cur_pma = cmd->pma; + } + if (cmd->num_elements > 0) { + sg_draw(cmd->base_element, cmd->num_elements, 1); + } + } + } + sg_pop_debug_group(); + } +} + +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc + +// return sspine_desc with patched defaults +static sspine_desc _sspine_desc_defaults(const sspine_desc* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sspine_desc res = *desc; + res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES); + res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS); + res.context_pool_size = _sspine_def(desc->context_pool_size, _SSPINE_DEFAULT_CONTEXT_POOL_SIZE); + res.atlas_pool_size = _sspine_def(desc->atlas_pool_size, _SSPINE_DEFAULT_ATLAS_POOL_SIZE); + res.skeleton_pool_size = _sspine_def(desc->skeleton_pool_size, _SSPINE_DEFAULT_SKELETON_POOL_SIZE); + res.skinset_pool_size = _sspine_def(desc->skinset_pool_size, _SSPINE_DEFAULT_SKINSET_POOL_SIZE); + res.instance_pool_size = _sspine_def(desc->instance_pool_size, _SSPINE_DEFAULT_INSTANCE_POOL_SIZE); + return res; +} + +static sspine_context_desc _sspine_as_context_desc(const sspine_desc* desc) { + sspine_context_desc ctx_desc; + _sspine_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; + ctx_desc.depth_format = desc->depth_format; + ctx_desc.sample_count = desc->sample_count; + ctx_desc.color_write_mask = desc->color_write_mask; + return ctx_desc; +} + +static sg_filter _sspine_as_sampler_filter(spAtlasFilter filter) { + switch (filter) { + case SP_ATLAS_UNKNOWN_FILTER: return _SG_FILTER_DEFAULT; + case SP_ATLAS_NEAREST: return SG_FILTER_NEAREST; + case SP_ATLAS_LINEAR: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP_NEAREST_NEAREST: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_LINEAR_NEAREST: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP_NEAREST_LINEAR: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_LINEAR_LINEAR: return SG_FILTER_LINEAR; + default: return SG_FILTER_LINEAR; + } +} + +static sg_filter _sspine_as_sampler_mipmap_filter(spAtlasFilter filter) { + switch (filter) { + case SP_ATLAS_UNKNOWN_FILTER: + return _SG_FILTER_DEFAULT; + case SP_ATLAS_NEAREST: + case SP_ATLAS_LINEAR: + case SP_ATLAS_MIPMAP: + case SP_ATLAS_MIPMAP_NEAREST_NEAREST: + case SP_ATLAS_MIPMAP_LINEAR_NEAREST: + return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_NEAREST_LINEAR: + case SP_ATLAS_MIPMAP_LINEAR_LINEAR: + return SG_FILTER_LINEAR; + default: + return SG_FILTER_NEAREST; + } +} + +static sg_wrap _sspine_as_sampler_wrap(spAtlasWrap wrap) { + switch (wrap) { + case SP_ATLAS_MIRROREDREPEAT: return SG_WRAP_MIRRORED_REPEAT; + case SP_ATLAS_CLAMPTOEDGE: return SG_WRAP_CLAMP_TO_EDGE; + case SP_ATLAS_REPEAT: return SG_WRAP_REPEAT; + default: return _SG_WRAP_DEFAULT; + } +} + +static void _sspine_init_image_info(const _sspine_atlas_t* atlas, int index, sspine_image_info* info, bool with_overrides) { + spAtlasPage* page = _sspine_lookup_atlas_page(atlas->slot.id, index); + SOKOL_ASSERT(page); + SOKOL_ASSERT(page->name); + info->valid = true; + info->sgimage = _sspine_image_from_renderer_object(page->rendererObject); + info->sgview = _sspine_view_from_renderer_object(page->rendererObject); + info->sgsampler = _sspine_sampler_from_renderer_object(page->rendererObject); + if (with_overrides && (atlas->overrides.min_filter != _SG_FILTER_DEFAULT)) { + info->min_filter = atlas->overrides.min_filter; + } else { + info->min_filter = _sspine_as_sampler_filter(page->minFilter); + } + if (with_overrides && (atlas->overrides.mag_filter != _SG_FILTER_DEFAULT)) { + info->mag_filter = atlas->overrides.mag_filter; + } else { + info->mag_filter = _sspine_as_sampler_filter(page->magFilter); + } + if (with_overrides && (atlas->overrides.mipmap_filter != _SG_FILTER_DEFAULT)) { + info->mipmap_filter = atlas->overrides.mipmap_filter; + } else { + info->mipmap_filter = _sspine_as_sampler_mipmap_filter(page->minFilter); + } + if (with_overrides && (atlas->overrides.wrap_u != _SG_WRAP_DEFAULT)) { + info->wrap_u = atlas->overrides.wrap_u; + } else { + info->wrap_u = _sspine_as_sampler_wrap(page->uWrap); + } + if (with_overrides && (atlas->overrides.wrap_v != _SG_WRAP_DEFAULT)) { + info->wrap_v = atlas->overrides.wrap_v; + } else { + info->wrap_v = _sspine_as_sampler_wrap(page->vWrap); + } + info->width = page->width; + info->height = page->height; + // NOTE: override already happened in atlas init + info->premul_alpha = page->pma != 0; + info->filename = _sspine_string(page->name); + if (info->filename.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } +} + +static void _sspine_init_shared(void) { + sg_shader_desc shd_desc; + _sspine_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].glsl_name = "position"; + shd_desc.attrs[1].glsl_name = "texcoord0"; + shd_desc.attrs[2].glsl_name = "color0"; + shd_desc.attrs[0].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[0].hlsl_sem_index = 0; + shd_desc.attrs[1].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[1].hlsl_sem_index = 1; + shd_desc.attrs[2].hlsl_sem_name = "TEXCOORD"; + shd_desc.attrs[2].hlsl_sem_index = 2; + shd_desc.attrs[0].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[1].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.attrs[2].base_type = SG_SHADERATTRBASETYPE_FLOAT; + shd_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX; + shd_desc.uniform_blocks[0].size = sizeof(_sspine_vsparams_t); + shd_desc.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140; + shd_desc.uniform_blocks[0].hlsl_register_b_n = 0; + shd_desc.uniform_blocks[0].msl_buffer_n = 0; + shd_desc.uniform_blocks[0].wgsl_group0_binding_n = 0; + shd_desc.uniform_blocks[0].spirv_set0_binding_n = 0; + shd_desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "vs_params"; + shd_desc.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.uniform_blocks[0].glsl_uniforms[0].array_count = 4; + shd_desc.uniform_blocks[1].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.uniform_blocks[1].size = 16; + shd_desc.uniform_blocks[1].layout = SG_UNIFORMLAYOUT_STD140; + shd_desc.uniform_blocks[1].hlsl_register_b_n = 0; + shd_desc.uniform_blocks[1].msl_buffer_n = 0; + shd_desc.uniform_blocks[1].wgsl_group0_binding_n = 1; + shd_desc.uniform_blocks[1].spirv_set0_binding_n = 1; + shd_desc.uniform_blocks[1].glsl_uniforms[0].glsl_name = "fs_params"; + shd_desc.uniform_blocks[1].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.uniform_blocks[1].glsl_uniforms[0].array_count = 1; + shd_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.views[0].texture.image_type = SG_IMAGETYPE_2D; + shd_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.views[0].texture.hlsl_register_t_n = 0; + shd_desc.views[0].texture.msl_texture_n = 0; + shd_desc.views[0].texture.wgsl_group1_binding_n = 0; + shd_desc.views[0].texture.spirv_set1_binding_n = 0; + shd_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.samplers[0].hlsl_register_s_n = 0; + shd_desc.samplers[0].msl_sampler_n = 0; + shd_desc.samplers[0].wgsl_group1_binding_n = 32; + shd_desc.samplers[0].spirv_set1_binding_n = 32; + shd_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT; + shd_desc.texture_sampler_pairs[0].view_slot = 0; + shd_desc.texture_sampler_pairs[0].sampler_slot = 0; + shd_desc.texture_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.label = "sspine-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vertex_func.source = (const char*)_sspine_vs_source_glsl410; + shd_desc.fragment_func.source = (const char*)_sspine_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vertex_func.source = (const char*)_sspine_vs_source_glsl300es; + shd_desc.fragment_func.source = (const char*)_sspine_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vertex_func.entry = "main0"; + shd_desc.fragment_func.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sspine_vs_bytecode_metal_macos); + shd_desc.fragment_func.bytecode = SG_RANGE(_sspine_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vertex_func.bytecode = SG_RANGE(_sspine_vs_bytecode_metal_ios); + shd_desc.fragment_func.bytecode = SG_RANGE(_sspine_fs_bytecode_metal_ios); + break; + default: + shd_desc.vertex_func.source = (const char*)_sspine_vs_source_metal_sim; + shd_desc.fragment_func.source = (const char*)_sspine_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vertex_func.bytecode = SG_RANGE(_sspine_vs_bytecode_hlsl4); + shd_desc.fragment_func.bytecode = SG_RANGE(_sspine_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vertex_func.source = (const char*)_sspine_vs_source_wgsl; + shd_desc.fragment_func.source = (const char*)_sspine_fs_source_wgsl; + #elif defined(SOKOL_VULKAN) + shd_desc.vertex_func.bytecode = SG_RANGE(_sspine_vs_bytecode_spirv_vk); + shd_desc.vertex_func.entry = "main"; + shd_desc.fragment_func.bytecode = SG_RANGE(_sspine_fs_bytecode_spirv_vk); + shd_desc.fragment_func.entry = "main"; + #else + shd_desc.vertex_func.source = _sspine_vs_source_dummy; + shd_desc.fragment_func.source = _sspine_fs_source_dummy; + #endif + _sspine.shd = sg_make_shader(&shd_desc); +} + +static void _sspine_destroy_shared(void) { + sg_destroy_shader(_sspine.shd); +} + +// called from inside sokol-gfx sg_commit() +static void _sspine_commit_listener_func(void* userdata) { + (void)userdata; + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine.frame_id++; +} + +static sg_commit_listener _sspine_make_commit_listener(void) { + sg_commit_listener commit_listener = { _sspine_commit_listener_func, 0 }; + return commit_listener; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sspine_setup(const sspine_desc* desc) { + SOKOL_ASSERT(desc); + spBone_setYDown(1); + _sspine_clear(&_sspine, sizeof(_sspine)); + _sspine.init_cookie = _SSPINE_INIT_COOKIE; + _sspine.desc = _sspine_desc_defaults(desc); + _sspine_init_shared(); + // important, need to setup the frame id with a non-zero value, + // otherwise updates won't trigger in the first frame + _sspine.frame_id = 1; + _sspine_setup_context_pool(_sspine.desc.context_pool_size); + _sspine_setup_atlas_pool(_sspine.desc.atlas_pool_size); + _sspine_setup_skeleton_pool(_sspine.desc.skeleton_pool_size); + _sspine_setup_skinset_pool(_sspine.desc.skinset_pool_size); + _sspine_setup_instance_pool(_sspine.desc.instance_pool_size); + const sspine_context_desc ctx_desc = _sspine_as_context_desc(&_sspine.desc); + _sspine.def_ctx_id = sspine_make_context(&ctx_desc); + SOKOL_ASSERT(_sspine_is_default_context(_sspine.def_ctx_id)); + sspine_set_context(_sspine.def_ctx_id); + if (!sg_add_commit_listener(_sspine_make_commit_listener())) { + _SSPINE_ERROR(ADD_COMMIT_LISTENER_FAILED); + } +} + +SOKOL_API_IMPL void sspine_shutdown(void) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sg_remove_commit_listener(_sspine_make_commit_listener()); + _sspine_destroy_all_instances(); + _sspine_destroy_all_skinsets(); + _sspine_destroy_all_skeletons(); + _sspine_destroy_all_atlases(); + _sspine_destroy_all_contexts(); + _sspine_discard_instance_pool(); + _sspine_discard_skinset_pool(); + _sspine_discard_skeleton_pool(); + _sspine_discard_atlas_pool(); + _sspine_discard_context_pool(); + _sspine_destroy_shared(); + _sspine.init_cookie = 0; +} + +SOKOL_API_IMPL sspine_context sspine_make_context(const sspine_context_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_context_desc desc_def = _sspine_context_desc_defaults(desc); + sspine_context ctx_id = _sspine_alloc_context(); + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + ctx->slot.state = _sspine_init_context(ctx, &desc_def); + SOKOL_ASSERT((ctx->slot.state == SSPINE_RESOURCESTATE_VALID) || (ctx->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (ctx->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_context(ctx); + } + } else { + ctx->slot.state = SSPINE_RESOURCESTATE_FAILED; + _SSPINE_ERROR(CONTEXT_POOL_EXHAUSTED); + } + return ctx_id; +} + +SOKOL_API_IMPL void sspine_destroy_context(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + if (_sspine_is_default_context(ctx_id)) { + _SSPINE_ERROR(CANNOT_DESTROY_DEFAULT_CONTEXT); + return; + } + _sspine_destroy_context(ctx_id); + // re-validate the current context pointer (this will return a nullptr + // if we just destroyed the current context) + _sspine.cur_ctx = _sspine_lookup_context(_sspine.cur_ctx_id.id); +} + +SOKOL_API_IMPL void sspine_set_context(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + if (_sspine_is_default_context(ctx_id)) { + _sspine.cur_ctx_id = _sspine.def_ctx_id; + } else { + _sspine.cur_ctx_id = ctx_id; + } + // this will return null if the handle isn't valid + _sspine.cur_ctx = _sspine_lookup_context(_sspine.cur_ctx_id.id); +} + +SOKOL_API_IMPL sspine_context sspine_get_context(void) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return _sspine.cur_ctx_id; +} + +SOKOL_API_IMPL sspine_context sspine_default_context(void) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return _sspine_make_context_handle(0x00010001); +} + +SOKOL_API_IMPL sspine_context_info sspine_get_context_info(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_context_info res; + _sspine_clear(&res, sizeof(res)); + const _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + res.num_vertices = ctx->vertices.next; + res.num_indices = ctx->indices.next; + res.num_commands = ctx->commands.next; + } + return res; +} + +SOKOL_API_IMPL void sspine_set_skinset(sspine_instance instance_id, sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (_sspine_instance_and_deps_valid(instance) && _sspine_skinset_and_deps_valid(skinset) && (instance->skel.id == skinset->skel.id)) { + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + SOKOL_ASSERT(skinset->sp_skin); + spSkeleton_setSkin(instance->sp_skel, 0); + spSkeleton_setSkin(instance->sp_skel, skinset->sp_skin); + spSkeleton_setSlotsToSetupPose(instance->sp_skel); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + } +} + +SOKOL_API_IMPL void sspine_update_instance(sspine_instance instance_id, float delta_time) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + _sspine_rewind_triggered_events(instance); + spAnimationState_update(instance->sp_anim_state, delta_time); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + spSkeleton_update(instance->sp_skel, delta_time); + spSkeleton_updateWorldTransform(instance->sp_skel, SP_PHYSICS_UPDATE); + } +} + +SOKOL_API_IMPL int sspine_num_triggered_events(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT((instance->cur_triggered_event_index >= 0) && (instance->cur_triggered_event_index <= _SSPINE_MAX_TRIGGERED_EVENTS)); + return instance->cur_triggered_event_index; + } + return 0; +} + +SOKOL_API_IMPL sspine_triggered_event_info sspine_get_triggered_event_info(sspine_instance instance_id, int triggered_event_index) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_triggered_event_info res; + _sspine_clear(&res, sizeof(res)); + if (_sspine_instance_and_deps_valid(instance)) { + if ((triggered_event_index >= 0) && (triggered_event_index < instance->cur_triggered_event_index)) { + res = instance->triggered_events[triggered_event_index]; + } + } + return res; +} + +SOKOL_API_IMPL void sspine_draw_instance_in_layer(sspine_instance instance_id, int layer) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_context_t* ctx = _sspine.cur_ctx; + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (ctx && _sspine_instance_and_deps_valid(instance)) { + _sspine_draw_instance(ctx, instance, layer); + } +} + +SOKOL_API_IMPL sspine_mat4 sspine_layer_transform_to_mat4(const sspine_layer_transform* tform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_mat4 res; + _sspine_layer_transform_to_proj(tform, res.m); + return res; +} + +SOKOL_API_IMPL void sspine_context_draw_instance_in_layer(sspine_context ctx_id, sspine_instance instance_id, int layer) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (ctx && _sspine_instance_and_deps_valid(instance)) { + _sspine_draw_instance(ctx, instance, layer); + } +} + +SOKOL_API_IMPL void sspine_draw_layer(int layer, const sspine_layer_transform* tform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(tform); + _sspine_context_t* ctx = _sspine.cur_ctx; + if (ctx) { + _sspine_draw_layer(ctx, layer, tform); + } +} + +SOKOL_API_IMPL void sspine_context_draw_layer(sspine_context ctx_id, int layer, const sspine_layer_transform* tform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(tform); + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + _sspine_draw_layer(ctx, layer, tform); + } +} + +SOKOL_API_IMPL sspine_atlas sspine_make_atlas(const sspine_atlas_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_atlas_desc desc_def = _sspine_atlas_desc_defaults(desc); + sspine_atlas atlas_id = _sspine_alloc_atlas(); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + atlas->slot.state = _sspine_init_atlas(atlas, &desc_def); + SOKOL_ASSERT((atlas->slot.state == SSPINE_RESOURCESTATE_VALID) || (atlas->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (atlas->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_atlas(atlas); + } + } else { + _SSPINE_ERROR(ATLAS_POOL_EXHAUSTED); + } + return atlas_id; +} + +SOKOL_API_IMPL void sspine_destroy_atlas(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_atlas(atlas_id); +} + +SOKOL_API_IMPL sspine_skeleton sspine_make_skeleton(const sspine_skeleton_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_skeleton_desc desc_def = _sspine_skeleton_desc_defaults(desc); + sspine_skeleton skeleton_id = _sspine_alloc_skeleton(); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (skeleton) { + skeleton->slot.state = _sspine_init_skeleton(skeleton, &desc_def); + SOKOL_ASSERT((skeleton->slot.state == SSPINE_RESOURCESTATE_VALID) || (skeleton->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (skeleton->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_skeleton(skeleton); + } + } else { + _SSPINE_ERROR(SKELETON_POOL_EXHAUSTED); + } + return skeleton_id; +} + +SOKOL_API_IMPL void sspine_destroy_skeleton(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_skeleton(skeleton_id); +} + +SOKOL_API_IMPL sspine_skinset sspine_make_skinset(const sspine_skinset_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_skinset_desc desc_def = _sspine_skinset_desc_defaults(desc); + sspine_skinset skinset_id = _sspine_alloc_skinset(); + _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (skinset) { + skinset->slot.state = _sspine_init_skinset(skinset, &desc_def); + SOKOL_ASSERT((skinset->slot.state == SSPINE_RESOURCESTATE_VALID) || (skinset->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (skinset->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_skinset(skinset); + } + } else { + _SSPINE_ERROR(SKINSET_POOL_EXHAUSTED); + } + return skinset_id; +} + +SOKOL_API_IMPL void sspine_destroy_skinset(sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_skinset(skinset_id); +} + +SOKOL_API_IMPL sspine_instance sspine_make_instance(const sspine_instance_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_instance_desc desc_def = _sspine_instance_desc_defaults(desc); + sspine_instance instance_id = _sspine_alloc_instance(); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + instance->slot.state = _sspine_init_instance(instance, &desc_def); + SOKOL_ASSERT((instance->slot.state == SSPINE_RESOURCESTATE_VALID) || (instance->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (instance->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_instance(instance); + } + } else { + _SSPINE_ERROR(INSTANCE_POOL_EXHAUSTED); + } + return instance_id; +} + +SOKOL_API_IMPL void sspine_destroy_instance(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_instance(instance_id); +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_context_resource_state(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + return ctx->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_atlas_resource_state(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + return atlas->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_skeleton_resource_state(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (skeleton) { + return skeleton->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_skinset_resource_state(sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (skinset) { + return skinset->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_instance_resource_state(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + return instance->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL bool sspine_context_valid(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_context_resource_state(ctx_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_atlas_valid(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_atlas_resource_state(atlas_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_skeleton_valid(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_skeleton_resource_state(skeleton_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_skinset_valid(sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_skinset_resource_state(skinset_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_instance_valid(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_instance_resource_state(instance_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL sspine_atlas sspine_get_skeleton_atlas(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + sspine_atlas res; + _sspine_clear(&res, sizeof(res)); + if (skeleton) { + res.id = skeleton->atlas.id; + } + return res; +} + +SOKOL_API_IMPL sspine_skeleton sspine_get_instance_skeleton(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_skeleton res; + _sspine_clear(&res, sizeof(res)); + if (instance) { + res.id = instance->skel.id; + } + return res; +} + +SOKOL_API_IMPL int sspine_num_images(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + return atlas->num_pages; + } + return 0; +} + +SOKOL_API_IMPL sspine_image sspine_image_by_index(sspine_atlas atlas_id, int index) { + return _sspine_image(atlas_id.id, index); +} + +SOKOL_API_IMPL bool sspine_image_valid(sspine_image image) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(image.atlas_id); + return atlas && (image.index >= 0) && (image.index < atlas->num_pages); +} + +SOKOL_API_IMPL bool sspine_image_equal(sspine_image first, sspine_image second) { + return (first.atlas_id == second.atlas_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_image_info sspine_get_image_info(sspine_image image) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(image.atlas_id); + sspine_image_info img_info; + _sspine_clear(&img_info, sizeof(img_info)); + if (atlas && (image.index >= 0) && (image.index < atlas->num_pages)) { + _sspine_init_image_info(atlas, image.index, &img_info, true); + } + return img_info; +} + +SOKOL_API_IMPL int sspine_num_atlas_pages(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + return atlas->num_pages; + } else { + return 0; + } +} + +SOKOL_API_IMPL sspine_atlas_page sspine_atlas_page_by_index(sspine_atlas atlas_id, int index) { + return _sspine_atlas_page(atlas_id.id, index); +} + +SOKOL_API_IMPL bool sspine_atlas_page_valid(sspine_atlas_page page) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(page.atlas_id); + if (atlas) { + return (page.index >= 0) && (page.index < atlas->num_pages); + } + return false; +} + +SOKOL_API_IMPL bool sspine_atlas_page_equal(sspine_atlas_page first, sspine_atlas_page second) { + return (first.atlas_id == second.atlas_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_atlas_page_info sspine_get_atlas_page_info(sspine_atlas_page page) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_atlas_page_info res; + _sspine_clear(&res, sizeof(res)); + const spAtlasPage* sp_page = _sspine_lookup_atlas_page(page.atlas_id, page.index); + if (sp_page) { + // at this point, atlas is guaranteed to be valid + const _sspine_atlas_t* atlas = _sspine_lookup_atlas(page.atlas_id); + res.valid = true; + res.atlas.id = page.atlas_id; + // write image info without overrides + _sspine_init_image_info(atlas, page.index, &res.image, false); + // ...and provide the overrides separately + res.overrides = atlas->overrides; + } + return res; +} + +SOKOL_API_IMPL void sspine_set_position(sspine_instance instance_id, sspine_vec2 position) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_skel); + instance->sp_skel->x = position.x; + instance->sp_skel->y = position.y; + } +} + +SOKOL_API_IMPL void sspine_set_scale(sspine_instance instance_id, sspine_vec2 scale) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + instance->sp_skel->scaleX = scale.x; + instance->sp_skel->scaleY = scale.y; + } +} + +SOKOL_API_IMPL void sspine_set_color(sspine_instance instance_id, sspine_color color) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + instance->sp_skel->color.r = color.r; + instance->sp_skel->color.g = color.g; + instance->sp_skel->color.b = color.b; + instance->sp_skel->color.a = color.a; + } +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_position(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_vec2 v = { 0.0f, 0.0f }; + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + v.x = instance->sp_skel->x; + v.y = instance->sp_skel->y; + } + return v; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_scale(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_vec2 v = { 0.0f, 0.0f }; + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + v.x = instance->sp_skel->scaleX; + v.y = instance->sp_skel->scaleY; + } + return v; +} + +SOKOL_API_IMPL sspine_color sspine_get_color(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_color c = { 0.0f, 0.0f, 0.0f, 0.0f }; + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + c.r = instance->sp_skel->color.r; + c.g = instance->sp_skel->color.g; + c.b = instance->sp_skel->color.b; + c.a = instance->sp_skel->color.a; + } + return c; +} + +SOKOL_API_IMPL int sspine_num_anims(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->animationsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_anim sspine_anim_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + // NOTE: there's a spSkeletonData_findAnimation function, but that doesn't + // give us access to the index, so we'll need to do the loop ourselves + SOKOL_ASSERT(skeleton->sp_skel_data); + const spSkeletonData* sp_skel_data = skeleton->sp_skel_data; + const int num_anims = sp_skel_data->animationsCount; + SOKOL_ASSERT(sp_skel_data->animations); + for (int i = 0; i < num_anims; i++) { + SOKOL_ASSERT(sp_skel_data->animations[i]); + SOKOL_ASSERT(sp_skel_data->animations[i]->name); + if (0 == strcmp(sp_skel_data->animations[i]->name, name)) { + return _sspine_anim(skeleton_id.id, i); + } + } + } + return _sspine_anim(0, 0); +} + +SOKOL_API_IMPL sspine_anim sspine_anim_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_anim(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_anim_valid(sspine_anim anim) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(anim.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (anim.index >= 0) && (anim.index < skeleton->sp_skel_data->animationsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_anim_equal(sspine_anim first, sspine_anim second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_anim_info sspine_get_anim_info(sspine_anim anim) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_anim_info res; + _sspine_clear(&res, sizeof(res)); + const spAnimation* sp_anim = _sspine_lookup_skeleton_anim(anim.skeleton_id, anim.index); + if (sp_anim) { + res.valid = true; + res.index = anim.index; + res.duration = sp_anim->duration; + res.name = _sspine_string(sp_anim->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_clear_animation_tracks(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_clearTracks(instance->sp_anim_state); + } +} + +SOKOL_API_IMPL void sspine_clear_animation_track(sspine_instance instance_id, int track_index) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_clearTrack(instance->sp_anim_state, track_index); + } +} + +SOKOL_API_IMPL void sspine_set_animation(sspine_instance instance_id, sspine_anim anim, int track_index, bool loop) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spAnimation* sp_anim = _sspine_lookup_instance_anim(instance_id.id, anim.skeleton_id, anim.index); + if (sp_anim) { + // NOTE: at this point, instance is guaranteed to be valid + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + SOKOL_ASSERT(instance); + spAnimationState_setAnimation(instance->sp_anim_state, track_index, sp_anim, loop?1:0); + } +} + +SOKOL_API_IMPL void sspine_add_animation(sspine_instance instance_id, sspine_anim anim, int track_index, bool loop, float delay) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spAnimation* sp_anim = _sspine_lookup_instance_anim(instance_id.id, anim.skeleton_id, anim.index); + if (sp_anim) { + // NOTE: at this point, instance is guaranteed to be valid + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + SOKOL_ASSERT(instance); + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_addAnimation(instance->sp_anim_state, track_index, sp_anim, loop?1:0, delay); + } +} + +SOKOL_API_IMPL void sspine_set_empty_animation(sspine_instance instance_id, int track_index, float mix_duration) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_setEmptyAnimation(instance->sp_anim_state, track_index, mix_duration); + } +} + +SOKOL_API_IMPL void sspine_add_empty_animation(sspine_instance instance_id, int track_index, float mix_duration, float delay) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_addEmptyAnimation(instance->sp_anim_state, track_index, mix_duration, delay); + } +} + +SOKOL_API_IMPL int sspine_num_bones(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->bonesCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_bone sspine_bone_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + spBoneData* sp_bone_data = spSkeletonData_findBone(skeleton->sp_skel_data, name); + if (sp_bone_data) { + return _sspine_bone(skeleton_id.id, sp_bone_data->index); + } + } + return _sspine_bone(0, 0); +} + +SOKOL_API_IMPL sspine_bone sspine_bone_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_bone(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_bone_valid(sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(bone.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (bone.index >= 0) && (bone.index < skeleton->sp_skel_data->bonesCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_bone_equal(sspine_bone first, sspine_bone second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_bone_info sspine_get_bone_info(sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_bone_info res; + _sspine_clear(&res, sizeof(res)); + const spBoneData* sp_bone_data = _sspine_lookup_bone_data(bone.skeleton_id, bone.index); + if (sp_bone_data) { + SOKOL_ASSERT(sp_bone_data->index == bone.index); + SOKOL_ASSERT(sp_bone_data->name); + res.valid = true; + res.index = sp_bone_data->index; + if (sp_bone_data->parent) { + res.parent_bone = _sspine_bone(bone.skeleton_id, sp_bone_data->parent->index); + } + res.length = sp_bone_data->length; + res.pose.position.x = sp_bone_data->x; + res.pose.position.y = sp_bone_data->y; + res.pose.rotation = sp_bone_data->rotation; + res.pose.scale.x = sp_bone_data->scaleX; + res.pose.scale.y = sp_bone_data->scaleY; + res.pose.shear.x = sp_bone_data->shearX; + res.pose.shear.y = sp_bone_data->shearY; + res.color.r = sp_bone_data->color.r; + res.color.g = sp_bone_data->color.g; + res.color.b = sp_bone_data->color.b; + res.color.a = sp_bone_data->color.a; + res.name = _sspine_string(sp_bone_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_set_bone_transform(sspine_instance instance_id, sspine_bone bone, const sspine_bone_transform* transform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(transform); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->x = transform->position.x; + sp_bone->y = transform->position.y; + sp_bone->rotation = transform->rotation; + sp_bone->scaleX = transform->scale.x; + sp_bone->scaleY = transform->scale.y; + sp_bone->shearX = transform->shear.x; + sp_bone->shearY = transform->shear.y; + } +} + +SOKOL_API_IMPL void sspine_set_bone_position(sspine_instance instance_id, sspine_bone bone, sspine_vec2 position) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->x = position.x; + sp_bone->y = position.y; + } +} + +SOKOL_API_IMPL void sspine_set_bone_rotation(sspine_instance instance_id, sspine_bone bone, float rotation) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->rotation = rotation; + } +} + +SOKOL_API_IMPL void sspine_set_bone_scale(sspine_instance instance_id, sspine_bone bone, sspine_vec2 scale) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->scaleX = scale.x; + sp_bone->scaleY = scale.y; + } +} + +SOKOL_API_IMPL void sspine_set_bone_shear(sspine_instance instance_id, sspine_bone bone, sspine_vec2 shear) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->shearX = shear.x; + sp_bone->shearY = shear.y; + } +} + +SOKOL_API_IMPL sspine_bone_transform sspine_get_bone_transform(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_bone_transform res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.position.x = sp_bone->x; + res.position.y = sp_bone->y; + res.rotation = sp_bone->rotation; + res.scale.x = sp_bone->scaleX; + res.scale.y = sp_bone->scaleY; + res.shear.x = sp_bone->shearX; + res.shear.y = sp_bone->shearY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_position(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->x; + res.y = sp_bone->y; + } + return res; +} + +SOKOL_API_IMPL float sspine_get_bone_rotation(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + return sp_bone->rotation; + } else { + return 0.0f; + } +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_scale(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->scaleX; + res.y = sp_bone->scaleY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_shear(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->shearX; + res.y = sp_bone->shearY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_world_position(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->worldX; + res.y = sp_bone->worldY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_bone_local_to_world(sspine_instance instance_id, sspine_bone bone, sspine_vec2 local_pos) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + spBone_localToWorld(sp_bone, local_pos.x, local_pos.y, &res.x, &res.y); + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_bone_world_to_local(sspine_instance instance_id, sspine_bone bone, sspine_vec2 world_pos) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + spBone_worldToLocal(sp_bone, world_pos.x, world_pos.y, &res.x, &res.y); + } + return res; +} + +SOKOL_API_IMPL int sspine_num_slots(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->slotsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_slot sspine_slot_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + spSlotData* sp_slot_data = spSkeletonData_findSlot(skeleton->sp_skel_data, name); + if (sp_slot_data) { + return _sspine_slot(skeleton_id.id, sp_slot_data->index); + } + } + return _sspine_slot(0, 0); +} + +SOKOL_API_IMPL sspine_slot sspine_slot_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_slot(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_slot_valid(sspine_slot slot) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(slot.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (slot.index >= 0) && (slot.index < skeleton->sp_skel_data->slotsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_slot_equal(sspine_slot first, sspine_slot second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_slot_info sspine_get_slot_info(sspine_slot slot) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_slot_info res; + _sspine_clear(&res, sizeof(res)); + const spSlotData* sp_slot_data = _sspine_lookup_slot_data(slot.skeleton_id, slot.index); + if (sp_slot_data) { + SOKOL_ASSERT(sp_slot_data->index == slot.index); + SOKOL_ASSERT(sp_slot_data->name); + SOKOL_ASSERT(sp_slot_data->boneData); + res.valid = true; + res.index = sp_slot_data->index; + res.bone = _sspine_bone(slot.skeleton_id, sp_slot_data->boneData->index); + res.color.r = sp_slot_data->color.r; + res.color.g = sp_slot_data->color.g; + res.color.b = sp_slot_data->color.b; + res.color.a = sp_slot_data->color.a; + res.attachment_name = _sspine_string(sp_slot_data->attachmentName); + if (res.attachment_name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + res.name = _sspine_string(sp_slot_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_set_slot_color(sspine_instance instance_id, sspine_slot slot, sspine_color color) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spSlot* sp_slot = _sspine_lookup_slot(instance_id.id, slot.skeleton_id, slot.index); + if (sp_slot) { + sp_slot->color.r = color.r; + sp_slot->color.g = color.g; + sp_slot->color.b = color.b; + sp_slot->color.a = color.a; + } +} + +SOKOL_API_IMPL sspine_color sspine_get_slot_color(sspine_instance instance_id, sspine_slot slot) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_color color; + _sspine_clear(&color, sizeof(color)); + spSlot* sp_slot = _sspine_lookup_slot(instance_id.id, slot.skeleton_id, slot.index); + if (sp_slot) { + color.r = sp_slot->color.r; + color.g = sp_slot->color.g; + color.b = sp_slot->color.b; + color.a = sp_slot->color.a; + } + return color; +} + +SOKOL_API_IMPL int sspine_num_events(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->eventsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_event sspine_event_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_skel_data->events); + // spEventData has no embedded index, so we need to loop over the events + for (int i = 0; i < skeleton->sp_skel_data->eventsCount; i++) { + SOKOL_ASSERT(skeleton->sp_skel_data->events[i]); + SOKOL_ASSERT(skeleton->sp_skel_data->events[i]->name); + if (0 == strcmp(skeleton->sp_skel_data->events[i]->name, name)) { + return _sspine_event(skeleton_id.id, i); + } + } + } + return _sspine_event(0, 0); +} + +SOKOL_API_IMPL sspine_event sspine_event_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_event(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_event_valid(sspine_event event) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(event.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (event.index >= 0) && (event.index < skeleton->sp_skel_data->eventsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_event_equal(sspine_event first, sspine_event second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_event_info sspine_get_event_info(sspine_event event) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_event_info res; + _sspine_clear(&res, sizeof(res)); + const spEventData* sp_event_data = _sspine_lookup_event_data(event.skeleton_id, event.index); + if (sp_event_data) { + res.valid = true; + res.index = event.index; + res.int_value = sp_event_data->intValue; + res.float_value = sp_event_data->floatValue; + res.volume = sp_event_data->volume; + res.balance = sp_event_data->balance; + res.name = _sspine_string(sp_event_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + res.string_value = _sspine_string(sp_event_data->stringValue); + if (res.string_value.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + res.audio_path = _sspine_string(sp_event_data->audioPath); + if (res.audio_path.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL int sspine_num_iktargets(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->ikConstraintsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_iktarget sspine_iktarget_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_skel_data->ikConstraints); + // spIkConstraintData has no embedded index, so we need to loop over the events + for (int i = 0; i < skeleton->sp_skel_data->ikConstraintsCount; i++) { + SOKOL_ASSERT(skeleton->sp_skel_data->ikConstraints[i]); + SOKOL_ASSERT(skeleton->sp_skel_data->ikConstraints[i]->name); + if (0 == strcmp(skeleton->sp_skel_data->ikConstraints[i]->name, name)) { + return _sspine_iktarget(skeleton_id.id, i); + } + } + } + return _sspine_iktarget(0, 0); +} + +SOKOL_API_IMPL sspine_iktarget sspine_iktarget_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_iktarget(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_iktarget_valid(sspine_iktarget iktarget) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(iktarget.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (iktarget.index >= 0) && (iktarget.index < skeleton->sp_skel_data->ikConstraintsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_iktarget_equal(sspine_iktarget first, sspine_iktarget second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_iktarget_info sspine_get_iktarget_info(sspine_iktarget iktarget) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_iktarget_info res; + _sspine_clear(&res, sizeof(res)); + const spIkConstraintData* ik_data = _sspine_lookup_ikconstraint_data(iktarget.skeleton_id, iktarget.index); + if (ik_data) { + res.valid = true; + res.index = iktarget.index; + res.target_bone = _sspine_bone(iktarget.skeleton_id, ik_data->target->index); + res.name = _sspine_string(ik_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_set_iktarget_world_pos(sspine_instance instance_id, sspine_iktarget iktarget, sspine_vec2 world_pos) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spIkConstraint* ik_data = _sspine_lookup_ikconstraint(instance_id.id, iktarget.skeleton_id, iktarget.index); + if (ik_data) { + spBone* bone = ik_data->target; + spBone* parent_bone = bone->parent; + if (parent_bone) { + spBone_worldToLocal(parent_bone, world_pos.x, world_pos.y, &bone->x, &bone->y); + } + } +} + +SOKOL_API_IMPL int sspine_num_skins(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->skinsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_skin sspine_skin_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_skel_data->skins); + // spSkin has no embedded index, so we need to loop over the skins + for (int i = 0; i < skeleton->sp_skel_data->skinsCount; i++) { + SOKOL_ASSERT(skeleton->sp_skel_data->skins[i]); + SOKOL_ASSERT(skeleton->sp_skel_data->skins[i]->name); + if (0 == strcmp(skeleton->sp_skel_data->skins[i]->name, name)) { + return _sspine_skin(skeleton_id.id, i); + } + } + } + return _sspine_skin(0, 0); +} + +SOKOL_API_IMPL sspine_skin sspine_skin_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_skin(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_skin_valid(sspine_skin skin) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skin.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (skin.index >= 0) && (skin.index < skeleton->sp_skel_data->skinsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_skin_equal(sspine_skin first, sspine_skin second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_skin_info sspine_get_skin_info(sspine_skin skin) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_skin_info res; + _sspine_clear(&res, sizeof(res)); + const spSkin* sp_skin = _sspine_lookup_skin(skin.skeleton_id, skin.index); + if (sp_skin) { + res.valid = true; + res.index = skin.index; + res.name = _sspine_string(sp_skin->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_SPINE_API_DECL void sspine_set_skin(sspine_instance instance_id, sspine_skin skin) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skin.skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + // clear any currently set skinset + instance->skinset.id = SSPINE_INVALID_ID; + instance->skinset.ptr = 0; + spSkin* sp_skin = _sspine_lookup_skin(skin.skeleton_id, skin.index); + if (sp_skin) { + spSkeleton_setSkin(instance->sp_skel, 0); + spSkeleton_setSkin(instance->sp_skel, sp_skin); + spSkeleton_setSlotsToSetupPose(instance->sp_skel); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + } + } +} + +#endif // SOKOL_SPINE_IMPL +#endif // SOKOL_SPINE_INCLUDED