diff options
| author | James Van-As <james.vanas@gmail.com> | 2024-01-26 10:37:17 +1300 |
|---|---|---|
| committer | James Van-As <james.vanas@gmail.com> | 2024-01-26 10:37:17 +1300 |
| commit | fbdbb6ab2f362c2b92f092d36cc3568ab12e36d6 (patch) | |
| tree | 50cefc693d5047da7e52a05fc66a76d770018b6c | |
| parent | 1485e81eb0285d23fd6f633f6b637a1ab21e414b (diff) | |
| parent | f58a78539e6a972700579ee72cb3f2d66f07088f (diff) | |
Merge commit 'f58a78539e6a972700579ee72cb3f2d66f07088f' into metal-vertex-buffer-offset
# Conflicts:
# sokol_gfx.h
| -rw-r--r-- | .github/workflows/gen_bindings.yml | 38 | ||||
| -rw-r--r-- | .github/workflows/main.yml | 12 | ||||
| -rw-r--r-- | CHANGELOG.md | 321 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | bindgen/README.md | 3 | ||||
| -rw-r--r-- | bindgen/gen_nim.py | 8 | ||||
| -rw-r--r-- | bindgen/gen_rust.py | 4 | ||||
| -rw-r--r-- | bindgen/gen_zig.py | 12 | ||||
| -rw-r--r-- | sokol_app.h | 799 | ||||
| -rw-r--r-- | sokol_args.h | 26 | ||||
| -rw-r--r-- | sokol_audio.h | 26 | ||||
| -rw-r--r-- | sokol_fetch.h | 46 | ||||
| -rw-r--r-- | sokol_gfx.h | 4790 | ||||
| -rw-r--r-- | sokol_log.h | 2 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | tests/CMakePresets.json | 28 | ||||
| -rw-r--r-- | tests/functional/sokol_fetch_test.c | 77 | ||||
| -rw-r--r-- | tests/functional/sokol_gfx_test.c | 228 | ||||
| -rw-r--r-- | tests/functional/sokol_spine_test.c | 4 | ||||
| -rwxr-xr-x | tests/test_emscripten.sh | 2 | ||||
| -rw-r--r-- | util/sokol_debugtext.h | 292 | ||||
| -rw-r--r-- | util/sokol_fontstash.h | 336 | ||||
| -rw-r--r-- | util/sokol_gfx_imgui.h | 288 | ||||
| -rw-r--r-- | util/sokol_gl.h | 328 | ||||
| -rw-r--r-- | util/sokol_imgui.h | 526 | ||||
| -rw-r--r-- | util/sokol_memtrack.h | 4 | ||||
| -rw-r--r-- | util/sokol_nuklear.h | 321 | ||||
| -rw-r--r-- | util/sokol_shape.h | 6 | ||||
| -rw-r--r-- | util/sokol_spine.h | 177 |
29 files changed, 5593 insertions, 3130 deletions
diff --git a/.github/workflows/gen_bindings.yml b/.github/workflows/gen_bindings.yml index 57e7d271..712303fd 100644 --- a/.github/workflows/gen_bindings.yml +++ b/.github/workflows/gen_bindings.yml @@ -6,7 +6,7 @@ jobs: test-windows: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: test_win run: | cd tests @@ -16,7 +16,7 @@ jobs: test-mac: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: seanmiddleditch/gha-setup-ninja@master - name: test_macos run: | @@ -26,7 +26,7 @@ jobs: test-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: seanmiddleditch/gha-setup-ninja@master - name: prepare run: | @@ -41,20 +41,20 @@ jobs: needs: [ test-windows, test-mac, test-linux ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: repository: floooh/sokol-zig path: bindgen/sokol-zig - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-nim path: bindgen/sokol-nim - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-odin path: bindgen/sokol-odin - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-rust path: bindgen/sokol-rust @@ -96,12 +96,10 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-zig - uses: goto-bus-stop/setup-zig@v2 - with: - version: 0.11.0 - uses: actions/download-artifact@v3 with: name: ignore-me-zig @@ -125,9 +123,9 @@ jobs: steps: - uses: jiro4989/setup-nim-action@v1 with: - nim-version: devel + nim-version: '2.x' repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-nim - uses: actions/download-artifact@v3 @@ -155,7 +153,7 @@ jobs: os: [ubuntu-latest, windows-latest] runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-odin - uses: actions/download-artifact@v3 @@ -220,14 +218,14 @@ jobs: os: [ubuntu-latest, windows-latest] runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-rust submodules: recursive - uses: actions/download-artifact@v3 with: name: ignore-me-rust - path: src/sokol + path: src - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -247,7 +245,7 @@ jobs: if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-zig ssh-key: ${{ secrets.GHACTIONS_ZIG_PUSH }} @@ -268,7 +266,7 @@ jobs: if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-nim ssh-key: ${{ secrets.GHACTIONS_NIM_PUSH }} @@ -289,7 +287,7 @@ jobs: if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-odin ssh-key: ${{ secrets.GHACTIONS_ODIN_PUSH }} @@ -311,7 +309,7 @@ jobs: if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: floooh/sokol-rust ssh-key: ${{ secrets.GHACTIONS_RUST_PUSH }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c06f3685..4d7ddf39 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: windows: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: test_win run: | cd tests @@ -15,7 +15,7 @@ jobs: mac: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: seanmiddleditch/gha-setup-ninja@master - name: test_macos run: | @@ -24,7 +24,7 @@ jobs: ios: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: test_ios run: | cd tests @@ -32,7 +32,7 @@ jobs: linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: seanmiddleditch/gha-setup-ninja@master - name: prepare run: | @@ -45,7 +45,7 @@ jobs: emscripten: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: seanmiddleditch/gha-setup-ninja@master - name: test_emscripten run: | @@ -54,7 +54,7 @@ jobs: android: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: seanmiddleditch/gha-setup-ninja@master - uses: actions/setup-java@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 350e65d3..e260de9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,324 @@ ## Updates +#### 23-Jan-2034 + +- sokol_app.h android: Touch event coordinates are now using AMotionEvent_getX/Y() instead + of AMotionEvent_getRawX/Y(). The raw functions don't work well in multi-window + scenarios. See PR https://github.com/floooh/sokol/pull/974 for details. + Many thanks to Github user @Comanx! + +#### 19-Jan-2024 + +- sokol_app.h wgpu: tiny fix for a breaking API change in webgpu.h in the Emscripten 3.1.52 SDK +- Merged PR https://github.com/floooh/sokol/pull/970 (many thanks to @waywardmonkeys) which + fixes a couple of strict-prototype warnings (e.g. C functions using func() instead of func(void)). + I also enabled `-Wstrict-prototypes` now in the CI tests for GCC and Clang, so such cases + should be caught in the future. + +#### 18-Jan-2024 + +- sokol_gfx.h: added support for the following pixel formats: + - BC3_SRGBA + - BC7_SRGBA + - ETC2_SRGB8 + - ETC2_SRGB8A8 + - ASTC_4x4_RGBA + - ASTC_4x4_SRGBA + + Related PR: https://github.com/floooh/sokol/pull/967 + + Many thanks to GH user @allcreater! + +#### 07-Jan-2024 + +- sokol_app.h (macos+metal): window content no longer 'wobbles' during window resizing. Many + thanks to @Seb-degraff for picking up and investigating this longstanding issue + (https://github.com/floooh/sokol/issues/700), finding a fix for the remaining problem + and providing a really nice PR (https://github.com/floooh/sokol/pull/963) + +#### 06-Jan-2024 + +> NOTE: if you use sokol_gfx.h and sokol_app.h together, make sure to update both. This is +because the pixel format enum in sokol_gfx.h has been shuffled around a bit, and as a result, some internal +pixel format constants in sokol_app.h had to move too! + +- sokol_gfx.h: some minor new features (non-breaking): + - the struct `sg_pixel_format` has two new items: + - `bool compressed`: true if this is a hardware-compressed pixel format + - `int bytes_per_pixel`: as the name says, with the caveat that this is + zero for compressed pixel formats (because the smallest element in compressed formats is a block, not a pixel) + - two previously private helper functions have been exposed to help with size computations + for texture data, these may be useful when preparing image data for consumption by `sg_make_image()` + and `sg_update_image()`: + - `int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes)`: + Computes the number of bytes in a texture row for a given pixel format. A 'row' has + different meanings for uncompressed vs compressed formats: For uncompressed pixel + formats, a row is a single line of pixels, while for compressed formats, a row is + a line of 'compression blocks'. `width` is always in pixels. + - `int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes)`: + Computes number of bytes in a texture surface (e.g. a single mipmap) for a given + pixel format. `width` and `height` are always in pixels. + + The `row_align_bytes` parameter is for added flexibility. For image data that goes into + the `sg_make_image()` or `sg_update_image()` functions 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. +- Related issue: https://github.com/floooh/sokol/issues/946, and PR: https://github.com/floooh/sokol/pull/962 + +#### 03-Jan-2024 + +- sokol_nuklear.h: `snk_handle_event()` now returns a bool to indicate whether the + event was handled by Nuklear (this allows an application to skip its own event + handling if Nuklear already handled the event). Issue link: https://github.com/floooh/sokol/issues/958, + fixed in PR: https://github.com/floooh/sokol/pull/959. Many thanks to @adamrt for the PR! + +#### 02-Jan-2024 + +Happy New Year! A couple of input-related changes in the sokol_app.h Emscripten backend: + +- Mouse and touch events now bubble up to the HTML document instead of being consumed, in some scenarios this + allows better integration with the surrounding web page. To prevent event bubbling, + call `sapp_consume_event()` from within the sokol_app.h event callback function. +- **NOTE**: wheel/scroll events behave as before and are always consumed. This prevents + an ugly "scroll bumping" effect when a wheel event bubbles up on a page where + scrolling shouldn't be possible. +- The hidden HTML text input field hack for text input on mobile browsers has been + removed. This idea never really worked across all browsers, and it actually + interfered with Dear ImGui text input fields because the hidden HTML text field + generated focus-in/out events which confused the Dear ImGui input handling code. + +Those changes fix a couple of problem when trying to integrate sokol_app.h applications +into VSCode webview panels, see: https://marketplace.visualstudio.com/items?itemName=floooh.vscode-kcide + +Related PR: https://github.com/floooh/sokol/pull/939 + +#### 10-Nov-2023 + +A small change in the sokol_gfx.h GL backend on Windows only: + +PR https://github.com/floooh/sokol/pull/839 has been merged, in debug mode this creates +the GL context with WGL_CONTEXT_DEBUG_BIT_ARB. Thanks to @castano for the PR! + +#### 06-Nov-2023 + +A bugfix in the sokol_gfx.h D3D11 backend, and some related cleanup when creating depth-stencil +render target images and resource views: + +- fixed: render target images with format SG_PIXELFORMAT_DEPTH_STENCIL triggered a validation + error because the pixel format capabilities code marked them as non-renderable. Now + the SG_PIXELFORMAT_DEPTH_STENCIL pixel format is properly reported as renderable. +- the DXGIFormats for SG_PIXELFORMAT_DEPTH_STENCIL images are now as follows: + - D3D11 texture object: DXGI_FORMAT_R24G8_TYPELESS + - D3D11 shader-resource-view object: DXGI_FORMAT_R24_UNORM_X8_TYPELESS + - D3D11 depth-stencil-view object: DXGI_FORMAT_D24_UNORM_S8_UINT + +Related PR: https://github.com/floooh/sokol/pull/937 + +#### 30-Oct-2023 + +Some sokol_gfx.h backend-specific updates and tweaks (very minor chance that this is breaking if you are injecting textures into the D3D11 backend). + +- a new set of public API functions to access the native backend 3D-API resource objects of + sokol-gfx resource objects: + + ``` + sg_[api]_[type]_info sg_[api]_query_[type]_info(sg_[type]) + ``` + ...where `[api]` is any of `[gl, d3d11, mtl, wgpu]` and `[type]` is any of `[buffer, image, sampler, shader, pipeline, pass]`. + + This is mainly useful when mixing native 3D-API code with sokol-gfx code. + + See issue https://github.com/floooh/sokol/issues/931 for details. + +- WebGPU backend: `sg_make_image()` will no longer automatically create a WebGPU texture-view object when injecting a WebGPU texture object, instead +this must now be explicitly provided. + +- D3D11 backend: `sg_make_image()` will no longer automatically create a +shader-resource-view object when injecting a D3D11 texture object, and +vice versa, a texture object will no longer be looked up from an injected +shader-resource-view object (e.g. the injection rules are now more straightforward and explicit). See issue https://github.com/floooh/sokol/issues/930 for details. + +For the detailed changes, see PR https://github.com/floooh/sokol/pull/932. + +#### 27-Oct-2023 + +Fix broken render-to-mipmap in the sokol_gfx.h GL backend. + +There was a subtle bug / "feature gap" lurking in sokol_gfx.h GL backend: trying +to render to any mipmap except the top-level mipmap resulted in a black screen +because of an incomplete-framebuffer error. This is fixed now. The changes in detail: + +- creating a texture in the GL backend now sets the GL_TEXTURE_MAX_LEVEL property + (this is the fix to make everything work) +- the framebuffer completeness check in the GL backend now has more detailed error logging +- in the validation layer, the requirement that a sampler that's used with a + single-mipmap-texture must use `.mipmap_filter = SG_FILTER_NONE` has been + relaxed (a later update will remove SG_FILTER_NONE entirely since it's not needed anymore + and the concept of a "none" mipmap filter only exists in GL and Metal, but not D3D, WebGPU + and Vulkan) + +Ticket: https://github.com/floooh/sokol/issues/923 + +PR: https://github.com/floooh/sokol/pull/924 + +There's also a new render-to-mipmap sample which covers to close this 'feature gap': + +https://floooh.github.io/sokol-html5/miprender-sapp.html + +A couple of similar samples will follow over the next few days +(rendering to texture array layers and 3d texture slices). + +#### 26-Oct-2023 + +- sokol_app.h gl: fix a regression introduced in https://github.com/floooh/sokol/pull/916 + which could select the wrong framebuffer pixel format and break rendering + on some GL drivers (in my case: an older Intel GPU). + + If you are using the GL backend on Windows, please make sure to upgrade! + +#### 23-Oct-2023 + +- sokol_app.h gl: some further startup optimizations in the WGL code path + via PR https://github.com/floooh/sokol/pull/916 + +#### 21-Oct-2023 + +The major topic of this update is the 'finalized' WebGPU support in sokol_gfx.h and sokol_app.h. + +- WebGPU samples are hosted here: + + https://floooh.github.io/sokol-webgpu/ + +- WebGL2 samples remain hosted here: + + https://floooh.github.io/sokol-html5/ + +- Please read the following blog post as introduction: + + https://floooh.github.io/2023/10/16/sokol-webgpu.html + +- ...and the changelog and updated documentation in the sokol-shdc repository: + + https://github.com/floooh/sokol-tools + +- You'll also need to update the sokol-shdc binaries: + + https://github.com/floooh/sokol-tools-bin + +- Please also read the following new or updated sections in the embedded sokol_gfx.h header documentation: + + - `ON SHADER CREATION` + - `ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING` + - `WEBGPU CAVEATS` + + Please do this especially when using any of the following texture pixel formats, as you will most likely encounter new validation layer errors: + + - `SG_PIXELFORMAT_R32F` + - `SG_PIXELFORMAT_RG32F` + - `SG_PIXELFORMAT_RGBA32F` + +- There is a tiny breaking change in the sokol_gfx.h API (only requires action when not using sokol-shdc): + + - the following `sg_sampler_type` enum items have been renamed to better match their WebGPU counterparts: + - SG_SAMPLERTYPE_SAMPLE => SG_SAMPLERTYPE_FILTERING + - SG_SAMPLERTYPE_COMPARE => SG_SAMPLERTYPE_COMPARISON + + - the enum `sg_image_sample_type` gained a new item: + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT + + - the enum `sg_sampler_type` gained a new item: + - SG_SAMPLERTYPE_NONFILTERING + +- The sokol_gfx.h struct `sg_desc` has two new items: + - `.wgpu_bindgroups_cache_size` - must be power-of-2, default: 1024 + - `.wgpu_disable_bindgroups_cache` - default: false + +- sokol_gfx.h gained the following new public API functions to query per-frame information: + - `sg_frame_stats sg_query_frame_stats()` + - `void sg_enable_frame_stats(void)` + - `void sg_disable_frame_stats(void)` + - `bool sg_frame_stats_enabled(void)` + + Frame statistics gathering is enabled after startup, but can be temporarily + disabled and enabled again via `sg_disable_frame_stats()` and `sg_enable_frame_stats`. + +- The sokol_gfx.h validation layer has new validation checks in `sg_make_shader()` + regarding image/sampler pair compatibility (WebGPU is particularly strict about + this stuff). + +- In sokol_app.h, the old wip WebGPU device and swapchain setup code is now implemented + in pure C code (previously this was a mix of Javascript and C). + +- Also note that sokol_app.h currently only supports WebGPU in the Emscripten backend. + If you want to use sokol_gfx.h with the WebGPU backend in a native scenario, you'll have + to use a different window system glue library (like GLFW). The sokol-samples directory + has a handful of examples for using sokol_gfx.h + Dawn + GLFW. + +- The following headers have been made compatible with the sokol_gfx.h WebGPU backend + (mainly by embedding WGSL shader code): + - sokol_debugtext.h + - sokol_fontstash.h + - sokol_gl.h + - sokol_spine.h + - sokol_imgui.h (also required some more changes for embedding `unfilterable-float` + textures, since these now require separate shader and pipeline objects) + - sokol_nuklear.h (works in WebGPU, but doesn't contain the work from sokol_imgui.h + to support `unfilterable-float` user textures) + +- sokol_gfx_imgui.h gained a new function `sg_imgui_draw_menu()` which renders a + menu panel to show/hide all debug windows. Previously this had to be done + outside the header. + +- sokol_gfx_imgui.h gained a new 'frame stats' window, which allows to peak into + sokol_gfx.h frame-rendering internals. This basically visualizes the struct + `sg_frame_stats` returned by the new sokol_gfx.h function `sg_query_frame_stats()`. + +- The sokol-samples repository gained 3 new samples: + - cubemap-jpeg-sapp.c (load a cubemap from seperate JPEG files) + - cubemaprt-sapp.c (render into cubemap faces - this demo actually existed a while but wasn't "official" so far) + - drawcallperf-sapp.c (a sample to explore the performance overhead of sg_apply_bindings, sg_apply_uniforms and sg_draw) + +#### 03-Oct-2023 + +- sokol_app.h win/gl: PR https://github.com/floooh/sokol/pull/886 has been merged, this makes + GL context initialization on Windows slightly more efficient. Many thanks to @dtrebilco! + +#### 25-Sep-2023 + +- The allocator callback functions in all headers that support custom allocators have been renamed + from `alloc` and `free` to `alloc_fn` and `free_fn`, this is because the symbol `free` is quite + likely to collide with a preprocessor macro of the same name if the standard C allocator is + replaced with a custom allocator. + + This is a breaking change only if you've been providing your own allocator functions to + the sokol headers. + + See issue https://github.com/floooh/sokol/issues/903 and PR https://github.com/floooh/sokol/pull/908 + for details. + +#### 23-Sep-2023 + +- sokol_gfx.h gl: Allow to inject an external GL framebuffer id into the sokol-gfx default + pass. See PR https://github.com/floooh/sokol/pull/899 and issue https://github.com/floooh/sokol/issues/892 + for details. Many thanks to @danielchasehooper for the discussion and PR! + + Further down the road I want to make the whole topic more flexible while at the same time + simplifying the sokol-gfx API, see here: https://github.com/floooh/sokol/issues/904 + +#### 22-Sep-2023 + +- sokol_gfx.h: Fixed a Metal validation error on Intel Macs when creating textures (Intel Macs + have unified memory, but don't support textures in shared storage mode). This was a regression + in the image/sampler split update in mid-July 2023. Fixes issue https://github.com/floooh/sokol/issues/905 + via PR https://github.com/floooh/sokol/pull/907. + +#### 19-Sep-2023 + +- sokol_fetch.h: fixed a minor issue where a request that was cancelled before it was dispatched + had an incomplete response state set in the response callback (the `finished`, `failed` and + `error_code` fields were not set). This fixes issue https://github.com/floooh/sokol/issues/882 + via PR https://github.com/floooh/sokol/pull/898 + #### 18-Sep-2023 - PR https://github.com/floooh/sokol/pull/893 has been merged, this fixes a minor issue @@ -102,7 +421,7 @@ The main topic of this update is to separate sampler state from image state in sokol_gfx.h which became possible after GLES2 support had been removed from sokol_gfx.h. -This also causes some 'colateral changes' in shader authoring and +This also causes some 'collateral changes' in shader authoring and other sokol headers, but there was opportunity to fill a few feature gaps in sokol_gfx.h as well: @@ -4,7 +4,7 @@ Simple [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt) cross-platform libraries for C and C++, written in C. -[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**17-Set-2023** debug label support in sokol_gfx.h Metal backend) +[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**06-Jan-2024**: some new size computation helper functions in sokol_gfx.h) [](/../../actions/workflows/main.yml) [](/../../actions/workflows/gen_bindings.yml) [](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[](https://github.com/floooh/sokol-rust/actions/workflows/main.yml) @@ -16,8 +16,6 @@ cross-platform libraries for C and C++, written in C. - [sokol_gp.h](https://github.com/edubart/sokol_gp) a 2D shape drawing library on top of sokol_gfx.h -- [LearnOpenGL examples ported to sokol-gfx](https://www.geertarien.com/learnopengl-examples-html5/) by @geertarien (cool stuff!) - - [Dear ImGui starterkit](https://github.com/floooh/cimgui-sokol-starterkit) a self-contained starterkit for writing Dear ImGui apps in C. - [qoiview](https://github.com/floooh/qoiview) a basic viewer for the new QOI image file format @@ -40,7 +38,7 @@ useful details for integrating the Sokol headers into your own project with your ## Core libraries -- [**sokol\_gfx.h**](https://github.com/floooh/sokol/blob/master/sokol_gfx.h): 3D-API wrapper (GL + Metal + D3D11) +- [**sokol\_gfx.h**](https://github.com/floooh/sokol/blob/master/sokol_gfx.h): 3D-API wrapper (GL/GLES3/WebGL2 + Metal + D3D11 + WebGPU) - [**sokol\_app.h**](https://github.com/floooh/sokol/blob/master/sokol_app.h): app framework wrapper (entry + window + 3D-context + input) - [**sokol\_time.h**](https://github.com/floooh/sokol/blob/master/sokol_time.h): time measurement - [**sokol\_audio.h**](https://github.com/floooh/sokol/blob/master/sokol_audio.h): minimal buffer-streaming audio playback @@ -88,7 +86,7 @@ A blog post with more background info: [A Tour of sokol_gfx.h](http://floooh.git # sokol_gfx.h: -- simple, modern wrapper around GLES3/WebGL2, GL3.3, D3D11 and Metal +- simple, modern wrapper around GLES3/WebGL2, GL3.3, D3D11, Metal, and WebGPU - buffers, images, shaders, pipeline-state-objects and render-passes - does *not* handle window creation or 3D API context initialization - does *not* provide shader dialect cross-translation (**BUT** there's now an 'official' shader-cross-compiler solution which diff --git a/bindgen/README.md b/bindgen/README.md index f90e499c..aef1ec17 100644 --- a/bindgen/README.md +++ b/bindgen/README.md @@ -1,5 +1,7 @@ ## Language Binding Generation Scripts +> REMINDER: we can pass `-fparse-all-comments` to the clang ast-dump command line which adds the following node types to the ast-dump.json: FullComment, ParagraphComment, TextComment. This might allow us to preserve comments in the language bindings (might be useful as part of a bigger change to make sokol header comments autodoc and Intellisense-friendly) + ### Zig First make sure that clang and python3 are in the path: @@ -23,6 +25,7 @@ To update the Zig bindings: > git clone https://github.com/floooh/sokol-zig > git clone https://github.com/floooh/sokol-nim > git clone https://github.com/floooh/sokol-odin +> git clone https://github.com/floooh/sokol-rust > python3 gen_all.py ``` diff --git a/bindgen/gen_nim.py b/bindgen/gen_nim.py index 14d6297d..0bfab70c 100644 --- a/bindgen/gen_nim.py +++ b/bindgen/gen_nim.py @@ -455,15 +455,15 @@ def gen_array_converters(decl, prefix): if util.is_1d_array_type(field['type']): n = array_sizes[0] l(f'converter to{struct_name}{field_name}*[N:static[int]](items: array[N, {array_base_type}]): array[{n}, {array_base_type}] =') - l(f' static: assert(N < {n})') + l(f' static: assert(N <= {n})') l(f' for index,item in items.pairs: result[index]=item') l('') elif util.is_2d_array_type(field['type']): x = array_sizes[1] y = array_sizes[0] l(f'converter to{struct_name}{field_name}*[Y:static[int], X:static[int]](items: array[Y, array[X, {array_base_type}]]): array[{y}, array[{x}, {array_base_type}]] =') - l(f' static: assert(X < {x})') - l(f' static: assert(Y < {y})') + l(f' static: assert(X <= {x})') + l(f' static: assert(Y <= {y})') l(f' for indexY,itemY in items.pairs:') l(f' for indexX, itemX in itemY.pairs:') l(f' result[indexY][indexX] = itemX') @@ -566,6 +566,8 @@ def gen_extra(inp): # l('') c_source_path = '/'.join(c_source_paths[inp['prefix']].split('/')[3:]) l('{.passc:"-DSOKOL_NIM_IMPL".}') + l('when defined(release):') + l(' {.passc:"-DNDEBUG".}') l(f'{{.compile:"{c_source_path}".}}') def gen_module(inp, dep_prefixes): diff --git a/bindgen/gen_rust.py b/bindgen/gen_rust.py index 5578d083..9349152b 100644 --- a/bindgen/gen_rust.py +++ b/bindgen/gen_rust.py @@ -355,6 +355,8 @@ def funcptr_result_c(field_type): res_type = field_type[: field_type.index("(*)")].strip() if res_type == "void": return "" + elif is_prim_type(res_type): + return f" -> {as_rust_prim_type(res_type)}" elif util.is_const_void_ptr(res_type): return " -> *const core::ffi::c_void" elif util.is_void_ptr(res_type): @@ -764,7 +766,7 @@ def gen_helpers(inp): if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_', 'sapp_']: l("/// Helper function to cast a rust slice into a sokol Range") l(f"pub fn slice_as_range<T>(data: &[T]) -> {range_struct_name} {{") - l(f" {range_struct_name} {{ size: data.len() * std::mem::size_of::<T>(), ptr: data.as_ptr() as *const _ }}") + l(f" {range_struct_name} {{ size: std::mem::size_of_val(data), ptr: data.as_ptr() as *const _ }}") l("}") l("/// Helper function to cast a rust reference into a sokol Range") l(f"pub fn value_as_range<T>(value: &T) -> {range_struct_name} {{") diff --git a/bindgen/gen_zig.py b/bindgen/gen_zig.py index b1631ebc..3194fd05 100644 --- a/bindgen/gen_zig.py +++ b/bindgen/gen_zig.py @@ -271,6 +271,8 @@ def funcptr_result_c(field_type): res_type = field_type[:field_type.index('(*)')].strip() if res_type == 'void': return 'void' + elif is_prim_type(res_type): + return as_zig_prim_type(res_type) elif util.is_const_void_ptr(res_type): return '?*const anyopaque' elif util.is_void_ptr(res_type): @@ -485,17 +487,23 @@ def gen_helpers(inp): l(' putc(byte);') l(' }') l(' }') - l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {') + l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: usize) Error!void {') l(' _ = self;') l(' var i: u64 = 0;') l(' while (i < n) : (i += 1) {') l(' putc(byte);') l(' }') l(' }') + l(' pub fn writeBytesNTimes(self: Writer, bytes: []const u8, n: usize) Error!void {') + l(' var i: usize = 0;') + l(' while (i < n) : (i += 1) {') + l(' try self.writeAll(bytes);') + l(' }') + l(' }') l('};') l('// std.fmt-style formatted print') l('pub fn print(comptime fmt: anytype, args: anytype) void {') - l(' var writer: Writer = .{};') + l(' const writer: Writer = .{};') l(' @import("std").fmt.format(writer, fmt, args) catch {};') l('}') l('') diff --git a/sokol_app.h b/sokol_app.h index f3b68ebc..884a2eaa 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -537,7 +537,7 @@ Like clipboard support, drag'n'drop support must be explicitly enabled at startup in the sapp_desc struct. - sapp_desc sokol_main() { + sapp_desc sokol_main(void) { return (sapp_desc) { .enable_dragndrop = true, // default is false ... @@ -547,7 +547,7 @@ 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() { + sapp_desc sokol_main(void) { return (sapp_desc) { .enable_dragndrop = true, // default is false .max_dropped_files = 8, // default is 1 @@ -687,7 +687,7 @@ For instance on a Retina Mac, returning the following sapp_desc struct from sokol_main(): - sapp_desc sokol_main() { + sapp_desc sokol_main(void) { return (sapp_desc) { .width = 640, .height = 480, @@ -767,7 +767,7 @@ 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 unregisters), and then + 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). @@ -926,7 +926,7 @@ 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 the + For an example and test of the window icon feature, check out the 'icon-sapp' sample on the sokol-samples git repository. ONSCREEN KEYBOARD @@ -1039,8 +1039,8 @@ return (sapp_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }; @@ -1493,12 +1493,12 @@ typedef struct sapp_icon_desc { Used in sapp_desc to provide custom memory-alloc and -free functions to sokol_app.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + 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)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sapp_allocator; @@ -1593,6 +1593,18 @@ typedef struct sapp_allocator { _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity sucessfully created") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED, "wgpu: failed to create swapchain object") \ + _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_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(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") \ @@ -1653,7 +1665,7 @@ typedef struct sapp_desc { sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free) sapp_logger logger; // logging callback override (default: NO LOGGING!) - /* backend-specific options */ + // backend-specific options int gl_major_version; // override GL major and minor version (the default GL version is 3.2) int gl_minor_version; bool win32_console_utf8; // if true, set the output console codepage to UTF-8 @@ -1872,7 +1884,20 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include <stddef.h> // size_t #include <math.h> // roundf -/* check if the config defines are alright */ +// helper macros +#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) + +#define _SAPP_MAX_TITLE_LENGTH (128) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) +// NOTE: the pixel format values *must* be compatible with sg_pixel_format +#define _SAPP_PIXELFORMAT_RGBA8 (23) +#define _SAPP_PIXELFORMAT_BGRA8 (28) +#define _SAPP_PIXELFORMAT_DEPTH (43) +#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (44) + +// check if the config defines are alright #if defined(__APPLE__) // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting #if !defined(__cplusplus) @@ -2064,6 +2089,18 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include <time.h> #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 + + // ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ // ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ // █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ @@ -2359,27 +2396,24 @@ typedef struct { #if defined(SOKOL_WGPU) typedef struct { - int state; + WGPUInstance instance; + WGPUAdapter adapter; WGPUDevice device; - WGPUSwapChain swapchain; WGPUTextureFormat render_format; + WGPUSurface surface; + WGPUSwapChain swapchain; WGPUTexture msaa_tex; - WGPUTexture depth_stencil_tex; - WGPUTextureView swapchain_view; WGPUTextureView msaa_view; + WGPUTexture depth_stencil_tex; WGPUTextureView depth_stencil_view; + WGPUTextureView swapchain_view; + bool async_init_done; } _sapp_wgpu_t; #endif typedef struct { - bool textfield_created; - bool wants_show_keyboard; - bool wants_hide_keyboard; bool mouse_lock_requested; uint16_t mouse_buttons; - #if defined(SOKOL_WGPU) - _sapp_wgpu_t wgpu; - #endif } _sapp_emsc_t; #endif // _SAPP_EMSCRIPTEN @@ -2416,7 +2450,7 @@ typedef enum MONITOR_DPI_TYPE { MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; -#endif /*DPI_ENUMS_DECLARED*/ +#endif // DPI_ENUMS_DECLARED typedef struct { bool aware; @@ -2463,6 +2497,7 @@ typedef struct { #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 @@ -2703,30 +2738,6 @@ typedef struct { #endif // _SAPP_LINUX -/* helper macros */ -#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) -#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) - -#define _SAPP_MAX_TITLE_LENGTH (128) -#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640) -#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) -/* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ -#define _SAPP_PIXELFORMAT_RGBA8 (23) -#define _SAPP_PIXELFORMAT_BGRA8 (28) -#define _SAPP_PIXELFORMAT_DEPTH (42) -#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (43) - -#if defined(_SAPP_MACOS) || defined(_SAPP_IOS) - // this is ARC compatible - #if defined(__cplusplus) - #define _SAPP_CLEAR_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 - typedef struct { bool enabled; int buf_size; @@ -2784,6 +2795,9 @@ typedef struct { _sapp_ios_t ios; #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_t emsc; + #if defined(SOKOL_WGPU) + _sapp_wgpu_t wgpu; + #endif #elif defined(_SAPP_WIN32) _sapp_win32_t win32; #if defined(SOKOL_D3D11) @@ -2802,8 +2816,8 @@ typedef struct { #endif #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 */ + 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]; } _sapp_t; static _sapp_t _sapp; @@ -2862,10 +2876,9 @@ _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sapp_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sapp.desc.allocator.alloc) { - ptr = _sapp.desc.allocator.alloc(size, _sapp.desc.allocator.user_data); - } - else { + 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) { @@ -2881,8 +2894,8 @@ _SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _sapp_free(void* ptr) { - if (_sapp.desc.allocator.free) { - _sapp.desc.allocator.free(ptr, _sapp.desc.allocator.user_data); + if (_sapp.desc.allocator.free_fn) { + _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data); } else { free(ptr); @@ -2986,7 +2999,7 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { } _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sapp_desc res = *desc; res.sample_count = _sapp_def(res.sample_count, 1); res.swap_interval = _sapp_def(res.swap_interval, 1); @@ -3548,6 +3561,7 @@ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { else { _sapp.dpi_scale = 1.0f; } + _sapp.macos.view.layer.contentsScale = _sapp.dpi_scale; // NOTE: needed because we set layerContentsPlacement to a non-scaling value in windowWillStartLiveResize. const NSRect bounds = [_sapp.macos.view bounds]; _sapp.window_width = (int)roundf(bounds.size.width); _sapp.window_height = (int)roundf(bounds.size.height); @@ -3903,6 +3917,24 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } } +#if defined(SOKOL_METAL) +- (void)windowWillStartLiveResize:(NSNotification *)notification { + // Work around the MTKView 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(); @@ -4015,7 +4047,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } #endif -_SOKOL_PRIVATE void _sapp_macos_poll_input_events() { +_SOKOL_PRIVATE void _sapp_macos_poll_input_events(void) { /* NOTE: late event polling temporarily out-commented to check if this @@ -4633,13 +4665,6 @@ extern "C" { typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); -/* this function is called from a JS event handler when the user hides - the onscreen keyboard pressing the 'dismiss keyboard key' -*/ -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { - _sapp.onscreen_keyboard_shown = false; -} - EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { if (_sapp.clipboard.enabled) { _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); @@ -4730,27 +4755,6 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int } /* extern "C" */ #endif -/* Javascript helper functions for mobile virtual keyboard input */ -EM_JS(void, sapp_js_create_textfield, (void), { - const _sapp_inp = document.createElement("input"); - _sapp_inp.type = "text"; - _sapp_inp.id = "_sokol_app_input_element"; - _sapp_inp.autocapitalize = "none"; - _sapp_inp.addEventListener("focusout", function(_sapp_event) { - __sapp_emsc_notify_keyboard_hidden() - - }); - document.body.append(_sapp_inp); -}); - -EM_JS(void, sapp_js_focus_textfield, (void), { - document.getElementById("_sokol_app_input_element").focus(); -}); - -EM_JS(void, sapp_js_unfocus_textfield, (void), { - document.getElementById("_sokol_app_input_element").blur(); -}); - EM_JS(void, sapp_js_add_beforeunload_listener, (void), { Module.sokol_beforeunload = (event) => { if (__sapp_html5_get_ask_leave_site() != 0) { @@ -4886,45 +4890,6 @@ EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), canvas.removeEventListener('drop', Module.sokol_drop); }); -/* called from the emscripten event handler to update the keyboard visibility - state, this must happen from an JS input event handler, otherwise - the request will be ignored by the browser -*/ -_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { - if (_sapp.emsc.wants_show_keyboard) { - /* create input text field on demand */ - if (!_sapp.emsc.textfield_created) { - _sapp.emsc.textfield_created = true; - sapp_js_create_textfield(); - } - /* focus the text input field, this will bring up the keyboard */ - _sapp.onscreen_keyboard_shown = true; - _sapp.emsc.wants_show_keyboard = false; - sapp_js_focus_textfield(); - } - if (_sapp.emsc.wants_hide_keyboard) { - /* unfocus the text input field */ - if (_sapp.emsc.textfield_created) { - _sapp.onscreen_keyboard_shown = false; - _sapp.emsc.wants_hide_keyboard = false; - sapp_js_unfocus_textfield(); - } - } -} - -/* actually showing the onscreen keyboard must be initiated from a JS - input event handler, so we'll just keep track of the desired - state, and the actual state change will happen with the next input event -*/ -_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { - if (show) { - _sapp.emsc.wants_show_keyboard = true; - } - else { - _sapp.emsc.wants_hide_keyboard = true; - } -} - EM_JS(void, sapp_js_init, (const char* c_str_target), { // lookup and store canvas object by name const target_str = UTF8ToString(c_str_target); @@ -5051,11 +5016,6 @@ _SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr); } -#if defined(SOKOL_WGPU) -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void); -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void); -#endif - _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; } @@ -5094,6 +5054,10 @@ _SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* return m; } +#if defined(SOKOL_WGPU) +_SOKOL_PRIVATE void _sapp_emsc_wgpu_size_changed(void); +#endif + _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); @@ -5139,9 +5103,8 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); #if defined(SOKOL_WGPU) - /* on WebGPU: recreate size-dependent rendering surfaces */ - _sapp_emsc_wgpu_surfaces_discard(); - _sapp_emsc_wgpu_surfaces_create(); + // on WebGPU: recreate size-dependent rendering surfaces + _sapp_emsc_wgpu_size_changed(); #endif if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_RESIZED); @@ -5152,6 +5115,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU _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 = false; _sapp.emsc.mouse_buttons = emsc_event->buttons; if (_sapp.mouse.locked) { _sapp.mouse.dx = (float) emsc_event->movementX; @@ -5212,15 +5176,14 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE } else { _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; } - _sapp_call_event(&_sapp.event); + 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(); } } - _sapp_emsc_update_keyboard_state(); - return true; + return consume_event; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { @@ -5242,8 +5205,9 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelE _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; _sapp_call_event(&_sapp.event); } - _sapp_emsc_update_keyboard_state(); _sapp_emsc_update_mouse_lock_state(); + // NOTE: wheel events are always consumed because they try to scroll the + // page which looks pretty bad return true; } @@ -5370,7 +5334,7 @@ _SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) { _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(user_data); - bool retval = true; + bool consume_event = false; if (_sapp_events_enabled()) { sapp_event_type type; switch (emsc_type) { @@ -5393,14 +5357,9 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard _sapp.event.key_repeat = emsc_event->repeat; _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); if (type == SAPP_EVENTTYPE_CHAR) { - // FIXME: this doesn't appear to work on Android Chrome + // NOTE: charCode doesn't appear to be supported on Android Chrome _sapp.event.char_code = emsc_event->charCode; - /* workaround to make Cmd+V work on Safari */ - if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { - retval = false; - } - } - else { + } 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) @@ -5413,11 +5372,10 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard _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 - */ + // 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) && @@ -5425,7 +5383,9 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard { send_keyup_followup = true; } - // only forward keys to the browser (can further be suppressed by sapp_consume_event()) + // Only forward alpha-numeric keys to the browser (can further be suppressed by sapp_consume_event()) + // NOTE: it should be possible to disable this behaviour via sapp_desc to give apps more + // controls over input event bubbling. switch (_sapp.event.key_code) { case SAPP_KEYCODE_WORLD_1: case SAPP_KEYCODE_WORLD_2: @@ -5482,34 +5442,34 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard case SAPP_KEYCODE_RIGHT_ALT: case SAPP_KEYCODE_RIGHT_SUPER: case SAPP_KEYCODE_MENU: - /* consume the event */ + // consume the event + consume_event = true; break; default: - /* forward key to browser */ - retval = false; + // forward key to browser + consume_event = false; break; } } if (_sapp_call_event(&_sapp.event)) { // event was consumed via sapp_consume_event() - retval = true; + consume_event = true; } if (send_keyup_followup) { _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; if (_sapp_call_event(&_sapp.event)) { - retval = true; + consume_event = true; } } } } - _sapp_emsc_update_keyboard_state(); _sapp_emsc_update_mouse_lock_state(); - return retval; + 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 retval = true; + bool consume_event = false; if (_sapp_events_enabled()) { sapp_event_type type; switch (emsc_type) { @@ -5527,7 +5487,6 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchE break; default: type = SAPP_EVENTTYPE_INVALID; - retval = false; break; } if (type != SAPP_EVENTTYPE_INVALID) { @@ -5545,11 +5504,10 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchE dst->pos_y = src->targetY * _sapp.dpi_scale; dst->changed = src->isChanged; } - _sapp_call_event(&_sapp.event); + consume_event = _sapp_call_event(&_sapp.event); } } - _sapp_emsc_update_keyboard_state(); - return retval; + return consume_event; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_focus_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { @@ -5612,116 +5570,189 @@ _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { #endif #if defined(SOKOL_WGPU) -#define _SAPP_EMSC_WGPU_STATE_INITIAL (0) -#define _SAPP_EMSC_WGPU_STATE_READY (1) -#define _SAPP_EMSC_WGPU_STATE_RUNNING (2) -#if defined(__cplusplus) -extern "C" { -#endif -/* called when the asynchronous WebGPU device + swapchain init code in JS has finished */ -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) { - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device); - _sapp.emsc.wgpu.device = (WGPUDevice) device_id; - _sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id; - _sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt; - _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY; -} -#if defined(__cplusplus) -} // extern "C" -#endif - -/* embedded JS function to handle all the asynchronous WebGPU setup */ -EM_JS(void, sapp_js_wgpu_init, (), { - WebGPU.initManagers(); - // FIXME: the extension activation must be more clever here - navigator.gpu.requestAdapter().then((adapter) => { - console.log("wgpu adapter extensions: " + adapter.extensions); - adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then((device) => { - var gpuContext = document.getElementById("canvas").getContext("gpupresent"); - console.log("wgpu device extensions: " + adapter.extensions); - gpuContext.getSwapChainPreferredFormat(device).then((fmt) => { - const swapChainDescriptor = { device: device, format: fmt }; - const swapChain = gpuContext.configureSwapChain(swapChainDescriptor); - const deviceId = WebGPU.mgrDevice.create(device); - const swapChainId = WebGPU.mgrSwapChain.create(swapChain); - const fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; }); - console.log("wgpu device: " + device); - console.log("wgpu swap chain: " + swapChain); - console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")"); - __sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId); - }); - }); - }); -}); - -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { - SOKOL_ASSERT(_sapp.emsc.wgpu.device); - SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view); +_SOKOL_PRIVATE void _sapp_emsc_wgpu_create_swapchain(void) { + SOKOL_ASSERT(_sapp.wgpu.instance); + SOKOL_ASSERT(_sapp.wgpu.device); + SOKOL_ASSERT(0 == _sapp.wgpu.surface); + SOKOL_ASSERT(0 == _sapp.wgpu.swapchain); + 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); + SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view); + + WGPUSurfaceDescriptorFromCanvasHTMLSelector canvas_desc; + _sapp_clear(&canvas_desc, sizeof(canvas_desc)); + canvas_desc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector; + canvas_desc.selector = _sapp.html5_canvas_selector; + WGPUSurfaceDescriptor surf_desc; + _sapp_clear(&surf_desc, sizeof(surf_desc)); + surf_desc.nextInChain = &canvas_desc.chain; + _sapp.wgpu.surface = wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc); + if (0 == _sapp.wgpu.surface) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED); + } + _sapp.wgpu.render_format = wgpuSurfaceGetPreferredFormat(_sapp.wgpu.surface, _sapp.wgpu.adapter); + + WGPUSwapChainDescriptor sc_desc; + _sapp_clear(&sc_desc, sizeof(sc_desc)); + sc_desc.usage = WGPUTextureUsage_RenderAttachment; + sc_desc.format = _sapp.wgpu.render_format; + sc_desc.width = (uint32_t)_sapp.framebuffer_width; + sc_desc.height = (uint32_t)_sapp.framebuffer_height; + sc_desc.presentMode = WGPUPresentMode_Fifo; + _sapp.wgpu.swapchain = wgpuDeviceCreateSwapChain(_sapp.wgpu.device, _sapp.wgpu.surface, &sc_desc); + if (0 == _sapp.wgpu.swapchain) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED); + } WGPUTextureDescriptor ds_desc; _sapp_clear(&ds_desc, sizeof(ds_desc)); - ds_desc.usage = WGPUTextureUsage_OutputAttachment; + 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.depth = 1; - ds_desc.arrayLayerCount = 1; - ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8; + 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 = _sapp.sample_count; - _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc); - _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0); + 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) { WGPUTextureDescriptor msaa_desc; _sapp_clear(&msaa_desc, sizeof(msaa_desc)); - msaa_desc.usage = WGPUTextureUsage_OutputAttachment; + 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.depth = 1; - msaa_desc.arrayLayerCount = 1; - msaa_desc.format = _sapp.emsc.wgpu.render_format; + 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 = _sapp.sample_count; - _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc); - _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0); + 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_emsc_wgpu_surfaces_discard(void) { - if (_sapp.emsc.wgpu.msaa_tex) { - wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex); - _sapp.emsc.wgpu.msaa_tex = 0; +_SOKOL_PRIVATE void _sapp_emsc_wgpu_discard_swapchain(void) { + if (_sapp.wgpu.msaa_view) { + wgpuTextureViewRelease(_sapp.wgpu.msaa_view); + _sapp.wgpu.msaa_view = 0; } - if (_sapp.emsc.wgpu.msaa_view) { - wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view); - _sapp.emsc.wgpu.msaa_view = 0; + if (_sapp.wgpu.msaa_tex) { + wgpuTextureRelease(_sapp.wgpu.msaa_tex); + _sapp.wgpu.msaa_tex = 0; } - if (_sapp.emsc.wgpu.depth_stencil_tex) { - wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex); - _sapp.emsc.wgpu.depth_stencil_tex = 0; + if (_sapp.wgpu.depth_stencil_view) { + wgpuTextureViewRelease(_sapp.wgpu.depth_stencil_view); + _sapp.wgpu.depth_stencil_view = 0; } - if (_sapp.emsc.wgpu.depth_stencil_view) { - wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view); - _sapp.emsc.wgpu.depth_stencil_view = 0; + if (_sapp.wgpu.depth_stencil_tex) { + wgpuTextureRelease(_sapp.wgpu.depth_stencil_tex); + _sapp.wgpu.depth_stencil_tex = 0; } + if (_sapp.wgpu.swapchain) { + wgpuSwapChainRelease(_sapp.wgpu.swapchain); + _sapp.wgpu.swapchain = 0; + } + if (_sapp.wgpu.surface) { + wgpuSurfaceRelease(_sapp.wgpu.surface); + _sapp.wgpu.surface = 0; + } +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_size_changed(void) { + _sapp_emsc_wgpu_discard_swapchain(); + _sapp_emsc_wgpu_create_swapchain(); } -_SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) { - if (_sapp.emsc.wgpu.swapchain_view) { - wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view); +_SOKOL_PRIVATE void _sapp_emsc_wgpu_request_device_cb(WGPURequestDeviceStatus status, WGPUDevice device, const char* msg, void* userdata) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata); + SOKOL_ASSERT(!_sapp.wgpu.async_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); + } } - _sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain); + SOKOL_ASSERT(device); + _sapp.wgpu.device = device; + _sapp_emsc_wgpu_create_swapchain(); + _sapp.wgpu.async_init_done = true; } -#endif + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* msg, void* userdata) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata); + 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; + size_t cur_feature_index = 1; + WGPUFeatureName requiredFeatures[8] = { + WGPUFeatureName_Depth32FloatStencil8, + }; + // check for optional features we're interested in + // FIXME: ASTC texture compression + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionBC)) { + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC; + } else if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) { + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2; + } + + WGPUDeviceDescriptor dev_desc; + _sapp_clear(&dev_desc, sizeof(dev_desc)); + dev_desc.requiredFeatureCount = cur_feature_index; + dev_desc.requiredFeatures = requiredFeatures, + wgpuAdapterRequestDevice(adapter, &dev_desc, _sapp_emsc_wgpu_request_device_cb, 0); +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_init(void) { + SOKOL_ASSERT(0 == _sapp.wgpu.instance); + SOKOL_ASSERT(!_sapp.wgpu.async_init_done); + _sapp.wgpu.instance = wgpuCreateInstance(0); + if (0 == _sapp.wgpu.instance) { + _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED); + } + // FIXME: power preference? + wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, _sapp_emsc_wgpu_request_adapter_cb, 0); +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_frame(void) { + if (_sapp.wgpu.async_init_done) { + _sapp.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.wgpu.swapchain); + _sapp_frame(); + wgpuTextureViewRelease(_sapp.wgpu.swapchain_view); + _sapp.wgpu.swapchain_view = 0; + } +} +#endif // SOKOL_WGPU _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); @@ -5752,7 +5783,7 @@ _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { #endif } -_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { +_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); @@ -5788,32 +5819,12 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { _sapp_timing_external(&_sapp.timing, time / 1000.0); #if defined(SOKOL_WGPU) - /* - on WebGPU, the emscripten frame callback will already be called while - the asynchronous WebGPU device and swapchain initialization is still - in progress - */ - switch (_sapp.emsc.wgpu.state) { - case _SAPP_EMSC_WGPU_STATE_INITIAL: - /* async JS init hasn't finished yet */ - break; - case _SAPP_EMSC_WGPU_STATE_READY: - /* perform post-async init stuff */ - _sapp_emsc_wgpu_surfaces_create(); - _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING; - break; - case _SAPP_EMSC_WGPU_STATE_RUNNING: - /* a regular frame */ - _sapp_emsc_wgpu_next_frame(); - _sapp_frame(); - break; - } + _sapp_emsc_wgpu_frame(); #else - /* WebGL code path */ _sapp_frame(); #endif - /* quit-handling */ + // quit-handling if (_sapp.quit_requested) { _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); _sapp_call_event(&_sapp.event); @@ -5853,18 +5864,17 @@ _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { #if defined(SOKOL_GLES3) _sapp_emsc_webgl_init(); #elif defined(SOKOL_WGPU) - sapp_js_wgpu_init(); + _sapp_emsc_wgpu_init(); #endif _sapp.valid = true; _sapp_emsc_register_eventhandlers(); sapp_set_icon(&desc->icon); - /* start the frame loop */ + // start the frame loop emscripten_request_animation_frame_loop(_sapp_emsc_frame, 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 - */ + // 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) @@ -5908,6 +5918,99 @@ _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { 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; @@ -6585,44 +6688,55 @@ _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { 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); - const _sapp_gl_fbconfig* closest; - int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); - _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); - SOKOL_ASSERT(usable_configs); - int usable_count = 0; - for (int i = 0; i < native_count; i++) { - const int n = i + 1; - _sapp_gl_fbconfig* u = usable_configs + usable_count; - _sapp_gl_init_fbconfig(u); - if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) { - continue; - } - if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { - continue; - } - if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { - continue; - } - u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB); - u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB); - u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB); - u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB); - u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB); - u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB); - if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { - u->doublebuffer = true; - } - if (_sapp.wgl.arb_multisample) { - u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); - } - u->handle = (uintptr_t)n; - usable_count++; + #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; } - SOKOL_ASSERT(usable_count > 0); + + 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; @@ -6632,13 +6746,46 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { 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); + desired.samples = (_sapp.sample_count > 1) ? _sapp.sample_count : 0; + int pixel_format = 0; - if (closest) { - pixel_format = (int) closest->handle; + + _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_gl_fbconfig u; + _sapp_clear(&u, sizeof(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; + } + } } - _sapp_free(usable_configs); + return pixel_format; } @@ -6663,7 +6810,11 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { 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 }; @@ -8099,8 +8250,8 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { 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_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; - dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; + dst->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) { @@ -9490,7 +9641,7 @@ _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) } } -_SOKOL_PRIVATE void _sapp_glx_init() { +_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); @@ -9565,7 +9716,7 @@ _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { return value; } -_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { +_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig(void) { GLXFBConfig* native_configs; _sapp_gl_fbconfig* usable_configs; const _sapp_gl_fbconfig* closest; @@ -11050,7 +11201,7 @@ SOKOL_API_IMPL float sapp_heightf(void) { SOKOL_API_IMPL int sapp_color_format(void) { #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - switch (_sapp.emsc.wgpu.render_format) { + switch (_sapp.wgpu.render_format) { case WGPUTextureFormat_RGBA8Unorm: return _SAPP_PIXELFORMAT_RGBA8; case WGPUTextureFormat_BGRA8Unorm: @@ -11107,8 +11258,6 @@ SOKOL_API_IMPL const void* sapp_egl_get_context(void) { SOKOL_API_IMPL void sapp_show_keyboard(bool show) { #if defined(_SAPP_IOS) _sapp_ios_show_keyboard(show); - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_show_keyboard(show); #elif defined(_SAPP_ANDROID) _sapp_android_show_keyboard(show); #else @@ -11478,7 +11627,7 @@ SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - return (const void*) _sapp.emsc.wgpu.device; + return (const void*) _sapp.wgpu.device; #else return 0; #endif @@ -11488,10 +11637,10 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) if (_sapp.sample_count > 1) { - return (const void*) _sapp.emsc.wgpu.msaa_view; + return (const void*) _sapp.wgpu.msaa_view; } else { - return (const void*) _sapp.emsc.wgpu.swapchain_view; + return (const void*) _sapp.wgpu.swapchain_view; } #else return 0; @@ -11502,7 +11651,7 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) if (_sapp.sample_count > 1) { - return (const void*) _sapp.emsc.wgpu.swapchain_view; + return (const void*) _sapp.wgpu.swapchain_view; } else { return 0; @@ -11515,7 +11664,7 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - return (const void*) _sapp.emsc.wgpu.depth_stencil_view; + return (const void*) _sapp.wgpu.depth_stencil_view; #else return 0; #endif diff --git a/sokol_args.h b/sokol_args.h index d33ba3a0..6c560bae 100644 --- a/sokol_args.h +++ b/sokol_args.h @@ -33,7 +33,7 @@ sokol_args.h provides a simple unified argument parsing API for WebAssembly and native apps. - When running as WebAssembly app, arguments are taken from the page URL: + 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 @@ -247,8 +247,8 @@ sargs_setup(&(sargs_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -316,12 +316,12 @@ extern "C" { Used in sargs_desc to provide custom memory-alloc and -free functions to sokol_args.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + 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)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sargs_allocator; @@ -447,10 +447,9 @@ _SOKOL_PRIVATE void _sargs_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sargs_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sargs.allocator.alloc) { - ptr = _sargs.allocator.alloc(size, _sargs.allocator.user_data); - } - else { + if (_sargs.allocator.alloc_fn) { + ptr = _sargs.allocator.alloc_fn(size, _sargs.allocator.user_data); + } else { ptr = malloc(size); } SOKOL_ASSERT(ptr); @@ -464,10 +463,9 @@ _SOKOL_PRIVATE void* _sargs_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _sargs_free(void* ptr) { - if (_sargs.allocator.free) { - _sargs.allocator.free(ptr, _sargs.allocator.user_data); - } - else { + if (_sargs.allocator.free_fn) { + _sargs.allocator.free_fn(ptr, _sargs.allocator.user_data); + } else { free(ptr); } } diff --git a/sokol_audio.h b/sokol_audio.h index bdd69530..6b60937c 100644 --- a/sokol_audio.h +++ b/sokol_audio.h @@ -397,8 +397,8 @@ saudio_setup(&(saudio_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -575,12 +575,12 @@ typedef struct saudio_logger { Used in saudio_desc to provide custom memory-alloc and -free functions to sokol_audio.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + 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)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } saudio_allocator; @@ -1146,10 +1146,9 @@ _SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _saudio_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_saudio.desc.allocator.alloc) { - ptr = _saudio.desc.allocator.alloc(size, _saudio.desc.allocator.user_data); - } - else { + if (_saudio.desc.allocator.alloc_fn) { + ptr = _saudio.desc.allocator.alloc_fn(size, _saudio.desc.allocator.user_data); + } else { ptr = malloc(size); } if (0 == ptr) { @@ -1165,10 +1164,9 @@ _SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _saudio_free(void* ptr) { - if (_saudio.desc.allocator.free) { - _saudio.desc.allocator.free(ptr, _saudio.desc.allocator.user_data); - } - else { + if (_saudio.desc.allocator.free_fn) { + _saudio.desc.allocator.free_fn(ptr, _saudio.desc.allocator.user_data); + } else { free(ptr); } } @@ -2485,7 +2483,7 @@ void _saudio_backend_shutdown(void) { SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { SOKOL_ASSERT(!_saudio.valid); SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); _saudio_clear(&_saudio, sizeof(_saudio)); _saudio.desc = *desc; _saudio.stream_cb = desc->stream_cb; diff --git a/sokol_fetch.h b/sokol_fetch.h index 21e9b5d1..510c5b5b 100644 --- a/sokol_fetch.h +++ b/sokol_fetch.h @@ -820,8 +820,8 @@ sfetch_setup(&(sfetch_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -1024,8 +1024,8 @@ typedef struct sfetch_range_t { override one function but not the other). */ typedef struct sfetch_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sfetch_allocator_t; @@ -1424,10 +1424,9 @@ _SOKOL_PRIVATE void _sfetch_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (allocator->alloc) { - ptr = allocator->alloc(size, allocator->user_data); - } - else { + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { ptr = malloc(size); } if (0 == ptr) { @@ -1447,10 +1446,9 @@ _SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _sfetch_free(void* ptr) { - if (_sfetch->desc.allocator.free) { - _sfetch->desc.allocator.free(ptr, _sfetch->desc.allocator.user_data); - } - else { + if (_sfetch->desc.allocator.free_fn) { + _sfetch->desc.allocator.free_fn(ptr, _sfetch->desc.allocator.user_data); + } else { free(ptr); } } @@ -2457,6 +2455,12 @@ _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { 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) { @@ -2469,9 +2473,16 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_ _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); 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 no buffer provided yet, invoke response callback to do so if (0 == item->buffer.ptr) { _sfetch_invoke_response_callback(item); } @@ -2498,8 +2509,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_ item->user.cont = false; } if (item->user.cancel) { - item->state = _SFETCH_STATE_FAILED; - item->user.finished = true; + _sfetch_cancel_item(item); } switch (item->state) { case _SFETCH_STATE_DISPATCHED: @@ -2541,7 +2551,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_ item->user.fetched_offset = item->thread.fetched_offset; item->user.fetched_size = item->thread.fetched_size; if (item->user.cancel) { - item->user.error_code = SFETCH_ERROR_CANCELLED; + _sfetch_cancel_item(item); } else { item->user.error_code = item->thread.error_code; @@ -2558,7 +2568,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_ } _sfetch_invoke_response_callback(item); - /* when the request is finish, free the lane for another request, + /* when the request is finished, free the lane for another request, otherwise feed it back into the incoming queue */ if (item->user.finished) { @@ -2608,7 +2618,7 @@ _SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_reques } _SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sfetch_desc_t res = *desc; res.max_requests = _sfetch_def(desc->max_requests, 128); res.num_channels = _sfetch_def(desc->num_channels, 1); diff --git a/sokol_gfx.h b/sokol_gfx.h index b678e560..490fd3c3 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -68,7 +68,7 @@ sokol_gfx DOES NOT: =================== - - create a window or the 3D-API context/device, you must do this + - 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 @@ -350,10 +350,36 @@ to sokol_gfx.h internals, and may change more often than other public API functions and structs. + --- you can query frame stats and control stats collection via: + + sg_query_frame_stats() + sg_enable_frame_stats() + sg_disable_frame_stats() + sg_frame_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 parammeter 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: ================== @@ -397,7 +423,7 @@ - https://floooh.github.io/sokol-html5/mrt-sapp.html - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html - A render pass wraps rendering commands into a common set of render target images + A render pass groups rendering commands into a set of render target images (called '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). @@ -595,9 +621,9 @@ ================== 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 information about the shader interface - needed in the sokol-gfx validation layer and to properly bind shader resources - on the CPU-side to be consumable by the GPU-side. + creating a shader object, along with information about the shader resource + binding interface needed in the sokol-gfx validation layer and to properly + bind shader resources on the CPU-side to be consumable by the GPU-side. 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 @@ -636,6 +662,7 @@ 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, shader 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") @@ -644,6 +671,8 @@ vertex shader: - Metal: no information needed since vertex attributes are always bound by their attribute location defined in the shader via '[[attribute(N)]]' + - WebGPU: no 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)', PLEASE NOTE that the name-lookup method @@ -664,23 +693,42 @@ and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! - A description of each texture/image used in the shader: - - the expected image type (e.g. 2D, 3D, etc...) - - the 'image sample type' (e.g. float, depth, signed- or unsigned-int) + - 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 (currently it's not supported to fetch data from multisampled textures in shaders, but this is planned for a later time) - A description of each sampler used in the shader: - - just wether the sampler is a regular 'sampling sampler', - or a 'comparison sampler' (which is usually used for - shadow mapping) + - SG_SAMPLERTYPE_FILTERING, + - SG_SAMPLERTYPE_NONFILTERING, + - SG_SAMPLERTYPE_COMPARISON, - An array of 'image-sampler-pairs' used by the shader to sample textures, - for D3D11 and Metal this is only used for validation purposes to check - whether the texture and sampler are compatible with each other. 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 tradtional GLSL 'sampler object'. + 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 tradtional 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 For example code of how to create backend-specific shader objects, please refer to the following samples: @@ -689,6 +737,47 @@ - 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 WebGPI: 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. UNIFORM DATA LAYOUT: @@ -939,8 +1028,8 @@ sg_setup(&(sg_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -1199,6 +1288,102 @@ result in a pipeline object in FAILED state. Same when trying to create a pass object with invalid image objects. + + 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 WebGPU code is 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)`, with up to + 4 uniform blocks per shader stage. + - Vertex shader uniform block bindings must start at `@group(0) @binding(0)` + - Fragment shader uniform blocks bindings must start at `@group(0) @binding(4)` + + All textures and samplers must use `@group(1)` and start at specific + offsets depending on resource type and shader stage. + - Vertex shader textures must start at `@group(1) @binding(0)` + - Vertex shader samplers must start at `@group(1) @binding(16)` + - Fragment shader textures must start at `@group(1) @binding(32)` + - Fragment shader samplers must start at `@group(1) @binding(48)` + + Note that the actual number of allowed per-stage texture- and sampler-bindings + in sokol-gfx is currently lower than the above ranges (currently only up to + 12 textures and 8 samplers per shader stage are allowed). + + If you use sokol-shdc to generate WGSL shader code, you don't need to worry + about the above binding convention since sokol-shdc assigns bind slots + automatically. + + - 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 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 numbers, with the default being + 1024. The bindgroups cache behaviour can be observed by calling the new + function sg_query_frame_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_vs_hash_key_mismatch + + The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`, + if this number if 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. + + - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);` + into the WGSL output. Currently only the Chrome WebGPU implementation seems + to accept this. + + - The vertex format SG_VERTEXFORMAT_UINT10_N2 is currently not supported because + WebGPU lacks a matching vertex format (this is currently being worked on though, + as soon as the vertex format shows up in webgpu.h, sokol_gfx.h will add support. + + - Likewise, the following sokol-gfx vertex formats are not supported in WebGPU: + R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN and all PVRTC compressed format. + Unlike unsupported vertex formats, unsupported pixel formats can be queried + in cross-backend code via sg_query_pixel_format() 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 supported widely supported) + + - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be + enabled (this would exclude all iOS devices) + + LICENSE ======= zlib/libpng license @@ -1252,7 +1437,7 @@ extern "C" { sg_buffer: vertex- and index-buffers sg_image: images used as textures and render targets sg_sampler sampler object describing how a texture is sampled in a shader - sg_shader: vertex- and fragment-shaders, uniform blocks + sg_shader: vertex- and fragment-shaders and shader interface information sg_pipeline: associated shader and vertex-layouts, and render states sg_pass: a bundle of render targets and actions on them sg_context: a 'context handle' for switching between 3D-API contexts @@ -1362,7 +1547,7 @@ typedef enum sg_backend { 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 bool members: + an sg_pixelformat_info struct with the following members: - sample: the pixelformat can be sampled as texture at least with nearest filtering @@ -1374,6 +1559,8 @@ typedef enum sg_backend { - msaa: multisample-antialiasing is supported when using the pixelformat for render targets - 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. @@ -1421,6 +1608,7 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_BGRA8, SG_PIXELFORMAT_RGB10A2, SG_PIXELFORMAT_RG11B10F, + SG_PIXELFORMAT_RGB9E5, SG_PIXELFORMAT_RG32UI, SG_PIXELFORMAT_RG32SI, @@ -1435,12 +1623,15 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_RGBA32SI, SG_PIXELFORMAT_RGBA32F, + // NOTE: when adding/removing pixel formats before DEPTH, also update sokol_app.h/SAPP_PIXELFORMAT_* 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, @@ -1448,17 +1639,21 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_BC6H_RGBF, SG_PIXELFORMAT_BC6H_RGBUF, SG_PIXELFORMAT_BC7_RGBA, + SG_PIXELFORMAT_BC7_SRGBA, SG_PIXELFORMAT_PVRTC_RGB_2BPP, SG_PIXELFORMAT_PVRTC_RGB_4BPP, SG_PIXELFORMAT_PVRTC_RGBA_2BPP, SG_PIXELFORMAT_PVRTC_RGBA_4BPP, SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_SRGB8, SG_PIXELFORMAT_ETC2_RGB8A1, SG_PIXELFORMAT_ETC2_RGBA8, + SG_PIXELFORMAT_ETC2_SRGB8A8, SG_PIXELFORMAT_ETC2_RG11, SG_PIXELFORMAT_ETC2_RG11SN, - SG_PIXELFORMAT_RGB9E5, + SG_PIXELFORMAT_ASTC_4x4_RGBA, + SG_PIXELFORMAT_ASTC_4x4_SRGBA, _SG_PIXELFORMAT_NUM, _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF @@ -1469,15 +1664,14 @@ typedef enum sg_pixel_format { 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 target - bool blend; // alpha-blending is supported - bool msaa; // pixel format can be used as MSAA render target - bool depth; // pixel format is a depth format - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif + 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 target + bool blend; // alpha-blending is supported + bool msaa; // pixel format can be used as MSAA render target + bool depth; // pixel format is a depth format + bool compressed; // true if this is a hardware-compressed format + 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; /* @@ -1489,9 +1683,6 @@ typedef struct sg_features { 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 - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif } sg_features; /* @@ -1642,6 +1833,17 @@ typedef enum sg_image_type { layer in sg_apply_bindings() to check if the provided image object is compatible with what the shader expects, and also required by the WebGPU backend. + + 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 tags `@image_sample_type` + and `@sampler_type`) */ typedef enum sg_image_sample_type { _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init @@ -1649,6 +1851,7 @@ typedef enum sg_image_sample_type { 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; @@ -1658,11 +1861,22 @@ typedef enum sg_image_sample_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_SAMPLE, - SG_SAMPLERTYPE_COMPARE, + SG_SAMPLERTYPE_FILTERING, + SG_SAMPLERTYPE_NONFILTERING, + SG_SAMPLERTYPE_COMPARISON, _SG_SAMPLERTYPE_NUM, _SG_SAMPLERTYPE_FORCE_U32, } sg_sampler_type; @@ -1731,13 +1945,6 @@ typedef enum sg_primitive_type { For min_filter and mag_filter the default is SG_FILTER_NEAREST. For mipmap_filter the default is SG_FILTER_NONE. - - The following restrictions apply: - - - an image object with (num_mipmaps == 1) must use SG_FILTER_NONE - - min_filter and mag_filter cannot be SG_FILTER_NONE - - Those restrictions are checked in the validation layer. */ typedef enum sg_filter { _SG_FILTER_DEFAULT, // value 0 reserved for default-init @@ -2347,15 +2554,16 @@ typedef struct sg_image_data { .mtl_textures[SG_NUM_INFLIGHT_FRAMES] .d3d11_texture .d3d11_shader_resource_view + .wgpu_texture + .wgpu_texture_view 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) - For D3D11, you can provide either a D3D11 texture, or a - shader-resource-view, or both. If only a texture is provided, a matching - shader-resource-view will be created. If only a shader-resource-view is - provided, the texture will be looked up from the shader-resource-view. + For D3D11 and WebGPU, either only provide a texture, or both a texture and + shader-resource-view / texture-view object. If you want to use access the + injected texture in a shader you *must* provide a shader-resource-view. The same rules apply as for injecting native buffers (see sg_buffer_desc documentation for more details). @@ -2380,6 +2588,7 @@ typedef struct sg_image_desc { const void* d3d11_texture; const void* d3d11_shader_resource_view; const void* wgpu_texture; + const void* wgpu_texture_view; uint32_t _end_canary; } sg_image_desc; @@ -2594,18 +2803,12 @@ typedef struct sg_vertex_buffer_layout_state { int stride; sg_vertex_step step_func; int step_rate; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[2]; - #endif } sg_vertex_buffer_layout_state; typedef struct sg_vertex_attr_state { int buffer_index; int offset; sg_vertex_format format; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[2]; - #endif } sg_vertex_attr_state; typedef struct sg_vertex_layout_state { @@ -2853,6 +3056,162 @@ typedef struct sg_pass_info { } sg_pass_info; /* + sg_frame_stats + + Allows to track generic and backend-specific tracking stats about a + render frame. Obtained by calling sg_query_frame_stats(). The returned + struct will contains information about the *previous* frame. +*/ +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_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; +} 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; +} 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_ps_set_shader_resources; + uint32_t num_vs_set_samplers; + uint32_t num_ps_set_samplers; +} 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_texture; + uint32_t num_set_vertex_sampler_state; + uint32_t num_set_fragment_texture; + uint32_t num_set_fragment_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; +} 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_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 { + 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_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_stats_gl gl; + sg_frame_stats_d3d11 d3d11; + sg_frame_stats_metal metal; + sg_frame_stats_wgpu wgpu; +} sg_frame_stats; + +/* sg_log_item An enum with a unique item for each log message, warning, error @@ -2868,8 +3227,12 @@ typedef struct sg_pass_info { _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \ _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \ _SG_LOGITEM_XMACRO(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, "texture name not found in shader (gl)") \ - _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_INCOMPLETE, "framebuffer completeness check failed (gl)") \ - _SG_LOGITEM_XMACRO(GL_MSAA_FRAMEBUFFER_INCOMPLETE, "completeness check failed for msaa resolve framebuffer (gl)") \ + _SG_LOGITEM_XMACRO(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_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ @@ -2906,11 +3269,21 @@ typedef struct sg_pass_info { _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_MAP_UNIFORM_BUFFER_FAILED, "mapping uniform buffer failed (wgpu)") \ - _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER, "per frame staging buffer full when copying to buffer (wgpu)") \ - _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE, "per frame staging buffer full when copying to texture (wgpu)") \ - _SG_LOGITEM_XMACRO(WGPU_RESET_STATE_CACHE_FIXME, "_sg_wgpu_reset_state_cache: fixme") \ - _SG_LOGITEM_XMACRO(WGPU_ACTIVATE_CONTEXT_FIXME, "_sg_wgpu_activate_context: fixme") \ + _SG_LOGITEM_XMACRO(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_TOO_MANY_IMAGES, "shader uses too many sampled images on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_SAMPLERS, "shader uses too many samplers on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \ + _SG_LOGITEM_XMACRO(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create pass") \ _SG_LOGITEM_XMACRO(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in buffer uninit (must be same as for creation)") \ _SG_LOGITEM_XMACRO(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in image uninit (must be same as for creation)") \ _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in sampler uninit (must be same as for creation)") \ @@ -2976,6 +3349,7 @@ typedef struct sg_pass_info { _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MINFILTER_NONE, "sg_sampler_desc.min_filter cannot be SG_FILTER_NONE") \ _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MAGFILTER_NONE, "sg_sampler_desc.mag_filter cannot be SG_FILTER_NONE") \ + _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_SOURCE, "shader source code required") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \ @@ -2996,6 +3370,8 @@ typedef struct sg_pass_info { _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED, "shader stage: image-sampler-pair has name but .used field not true") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED, "shader stage: image-sampler-pair has .image_slot != 0 but .used field not true") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED, "shader stage: image-sampler-pair .sampler_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED, "shader stage: image sample type UNFILTERABLE_FLOAT, UINT, SINT can only be used with NONFILTERING sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED, "shader stage: image sample type DEPTH can only be used with COMPARISON sampler") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS, "shader stage image-sampler-pairs must occupy continuous slots (sg_shader_desc.vs|fs.image_samplers[])") \ @@ -3062,31 +3438,35 @@ typedef struct sg_pass_info { _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: missing image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on vertex stage is missing or the image handle is invalid") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_EXISTS, "sg_apply_bindings: image bound to vertex stage no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to vertex stage doesn't match shader desc") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected on vertex stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on vertex stage") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: missing sampler binding on vertex stage") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARE on vertex stage but sampler has SG_COMPAREFUNC_NEVER") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_SAMPLE on vertex stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding on vertex stage is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON on vertex stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING on vertex stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on vertex stage, but sampler has SG_FILTER_LINEAR filters") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on vertex stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_SMP_EXISTS, "sg_apply_bindings: sampler bound to vertex stage no longer alive") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to vertex stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: missing image binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on fragment stage is missing or the image handle is invalid") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: image bound to fragment stage no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to fragment stage doesn't match shader desc") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected on fragment stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on fragment stage") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: missing sampler binding on fragment stage") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARE on fragment stage but sampler has SG_COMPAREFUNC_NEVER") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_SAMPLE on fragment stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding on fragment stage is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON on fragment stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING on fragment stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on fragment stage, but sampler has SG_FILTER_LINEAR filters") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on fragment stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_SMP_EXISTS, "sg_apply_bindings: sampler bound to fragment stage no longer alive") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to fragment stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ - _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size exceeds declared uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \ _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_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") \ @@ -3127,18 +3507,20 @@ typedef enum sg_log_item { .pass_pool_size 16 .context_pool_size 16 .uniform_buffer_size 4 MB (4*1024*1024) - .staging_buffer_size 8 MB (8*1024*1024) .max_commit_listeners 1024 .disable_validation false + .mtl_force_managed_storage_mode false + .wgpu_disable_bindgroups_cache false + .wgpu_bindgroups_cache_size 1024 - .allocator.alloc 0 (in this case, malloc() will be called) - .allocator.free 0 (in this case, free() will be called) + .allocator.alloc_fn 0 (in this case, malloc() will be called) + .allocator.free_fn 0 (in this case, free() will be called) .allocator.user_data 0 .context.color_format: default value depends on selected backend: all GL backends: SG_PIXELFORMAT_RGBA8 Metal and D3D11: SG_PIXELFORMAT_BGRA8 - WGPU: *no default* (must be queried from WGPU swapchain) + WebGPU: *no default* (must be queried from swapchain) .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL .context.sample_count 1 @@ -3150,6 +3532,10 @@ typedef enum sg_log_item { must hold a strong reference to the Objective-C object for the duration of the sokol_gfx call! + .mtl_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) .context.metal.device a pointer to the MTLDevice object .context.metal.renderpass_descriptor_cb @@ -3189,6 +3575,19 @@ typedef enum sg_log_item { callback functions 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 occurence, and increase the cache size as + needed (the default is 1024). + NOTE: wgpu_bindgroups_cache_size must be a power-of-2 number! .context.wgpu.device a WGPUDevice handle .context.wgpu.render_format @@ -3204,7 +3603,7 @@ typedef enum sg_log_item { .context.wgpu.depth_stencil_view_cb .context.wgpu.depth_stencil_view_userdata_cb callback to get current default-pass depth-stencil-surface WGPUTextureView - the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 + the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth32FloatStencil8 .context.wgpu.user_data optional user data pointer passed to the userdata versions of callback functions @@ -3240,11 +3639,17 @@ typedef struct sg_wgpu_context_desc { const void* (*render_view_userdata_cb)(void*); const void* (*resolve_view_cb)(void); // returns WGPUTextureView const void* (*resolve_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); // returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 + const void* (*depth_stencil_view_cb)(void); // returns WGPUTextureView const void* (*depth_stencil_view_userdata_cb)(void*); void* user_data; } sg_wgpu_context_desc; +typedef struct sg_gl_context_desc { + uint32_t (*default_framebuffer_cb)(void); + uint32_t (*default_framebuffer_userdata_cb)(void*); + void* user_data; +} sg_gl_context_desc; + typedef struct sg_context_desc { sg_pixel_format color_format; sg_pixel_format depth_format; @@ -3252,6 +3657,7 @@ typedef struct sg_context_desc { sg_metal_context_desc metal; sg_d3d11_context_desc d3d11; sg_wgpu_context_desc wgpu; + sg_gl_context_desc gl; } sg_context_desc; /* @@ -3273,12 +3679,12 @@ typedef struct sg_commit_listener { Used in sg_desc to provide custom memory-alloc and -free functions to sokol_gfx.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + 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)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sg_allocator; @@ -3314,10 +3720,11 @@ typedef struct sg_desc { int pass_pool_size; int context_pool_size; int uniform_buffer_size; - int staging_buffer_size; int max_commit_listeners; bool disable_validation; // disable validation layer even in debug mode, useful for tests bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA + bool wgpu_disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache + int wgpu_bindgroups_cache_size; // number of slots in the WebGPU bindgroup cache (must be 2^N) sg_allocator allocator; sg_logger logger; // optional log function override sg_context_desc context; @@ -3375,6 +3782,8 @@ 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); @@ -3436,25 +3845,201 @@ SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd); SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip); SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass); +// frame stats +SOKOL_GFX_API_DECL void sg_enable_frame_stats(void); +SOKOL_GFX_API_DECL void sg_disable_frame_stats(void); +SOKOL_GFX_API_DECL bool sg_frame_stats_enabled(void); +SOKOL_GFX_API_DECL sg_frame_stats sg_query_frame_stats(void); + // rendering contexts (optional) SOKOL_GFX_API_DECL sg_context sg_setup_context(void); SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); -/* Backend-specific helper functions, these may come in handy for mixing +/* 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) + const void* srv; // ID3D11ShaderResourceView* +} 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* vs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (vertex stage constant buffers) + const void* fs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11BUffer* (fragment stage constant buffers) + 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_pass_info { + const void* color_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView + const void* resolve_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView + const void* dsv; // ID3D11DepthStencilView +} sg_d3d11_pass_info; + +typedef struct sg_mtl_buffer_info { + const void* buf[SG_NUM_INFLIGHT_FRAMES]; // id<MTLBuffer> + int active_slot; +} sg_mtl_buffer_info; + +typedef struct sg_mtl_image_info { + const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id<MTLTexture> + int active_slot; +} sg_mtl_image_info; + +typedef struct sg_mtl_sampler_info { + const void* smp; // id<MTLSamplerState> +} sg_mtl_sampler_info; + +typedef struct sg_mtl_shader_info { + const void* vs_lib; // id<MTLLibrary> + const void* fs_lib; // id<MTLLibrary> + const void* vs_func; // id<MTLFunction> + const void* fs_func; // id<MTLFunction> +} sg_mtl_shader_info; + +typedef struct sg_mtl_pipeline_info { + const void* rps; // id<MTLRenderPipelineState> + const void* dss; // id<MTLDepthStencilState> +} 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 + const void* view; // WGPUTextureView +} 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* pip; // WGPURenderPipeline +} sg_wgpu_pipeline_info; + +typedef struct sg_wgpu_pass_info { + const void* color_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView + const void* resolve_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView + const void* ds_view; // WGPUTextureView +} sg_wgpu_pass_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; + uint32_t msaa_render_buffer; + 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_pass_info { + uint32_t frame_buffer; + uint32_t msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; +} sg_gl_pass_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 pass resource objects +SOKOL_GFX_API_DECL sg_d3d11_pass_info sg_d3d11_query_pass_info(sg_pass pass); // Metal: return __bridge-casted MTLDevice SOKOL_GFX_API_DECL const void* sg_mtl_device(void); - // Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(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 currrent pass +SOKOL_GFX_API_DECL const void* sg_wgpu_render_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 pass resource objects +SOKOL_GFX_API_DECL sg_wgpu_pass_info sg_wgpu_query_pass_info(sg_pass pass); + +// 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 pass resource objects +SOKOL_GFX_API_DECL sg_gl_pass_info sg_gl_query_pass_info(sg_pass pass); #ifdef __cplusplus } // extern "C" @@ -3618,10 +4203,9 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #import <Metal/Metal.h> #elif defined(SOKOL_WGPU) + #include <webgpu/webgpu.h> #if defined(__EMSCRIPTEN__) - #include <webgpu/webgpu.h> - #else - #include <dawn/webgpu.h> + #include <emscripten/emscripten.h> #endif #elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) #define _SOKOL_ANY_GL (1) @@ -3901,6 +4485,13 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #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 #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -3924,6 +4515,9 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #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 @@ -3963,9 +4557,15 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #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 @@ -3975,6 +4575,12 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #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 @@ -3987,7 +4593,9 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifndef GL_LUMINANCE #define GL_LUMINANCE 0x1909 #endif + #ifndef _SG_GL_CHECK_ERROR #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } + #endif #endif // ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ @@ -4004,6 +4612,22 @@ typedef struct { 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; + +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num); +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool); +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool); +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index); +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot); +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index); +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id); + // constants enum { _SG_STRING_SIZE = 16, @@ -4018,8 +4642,8 @@ enum { _SG_DEFAULT_PASS_POOL_SIZE = 16, _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, - _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, + _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE = 1024, }; // fixed-size string @@ -4034,6 +4658,8 @@ typedef struct { #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.key+=val;}} _SOKOL_PRIVATE void* _sg_malloc_clear(size_t size); _SOKOL_PRIVATE void _sg_free(void* ptr); @@ -4769,14 +5395,12 @@ typedef struct { typedef struct { bool valid; - bool has_unified_memory; - bool force_managed_storage_mode; + bool use_shared_storage_mode; const void*(*renderpass_descriptor_cb)(void); const void*(*renderpass_descriptor_userdata_cb)(void*); const void*(*drawable_cb)(void); const void*(*drawable_userdata_cb)(void*); void* user_data; - uint32_t frame_index; uint32_t cur_frame_rotate_index; int ub_size; int cur_ub_offset; @@ -4797,11 +5421,11 @@ typedef struct { #elif defined(SOKOL_WGPU) -#define _SG_WGPU_STAGING_ALIGN (256) -#define _SG_WGPU_STAGING_PIPELINE_SIZE (8) #define _SG_WGPU_ROWPITCH_ALIGN (256) -#define _SG_WGPU_MAX_SHADERSTAGE_IMAGES (8) -#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) // also see WGPULimits.maxUniformBufferBindingSize +#define _SG_WGPU_NUM_BINDGROUPS (2) // 0: uniforms, 1: images and sampler on both shader stages +#define _SG_WGPU_UNIFORM_BINDGROUP_INDEX (0) +#define _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX (1) typedef struct { _sg_slot_t slot; @@ -4817,16 +5441,22 @@ typedef struct { _sg_image_common_t cmn; struct { WGPUTexture tex; - WGPUTextureView tex_view; - WGPUTexture msaa_tex; - WGPUSampler sampler; + WGPUTextureView view; } wgpu; } _sg_wgpu_image_t; typedef _sg_wgpu_image_t _sg_image_t; typedef struct { + _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; - WGPUBindGroupLayout bind_group_layout; _sg_str_t entry; } _sg_wgpu_shader_stage_t; @@ -4835,6 +5465,7 @@ typedef struct { _sg_shader_common_t cmn; struct { _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + WGPUBindGroupLayout bind_group_layout; } wgpu; } _sg_wgpu_shader_t; typedef _sg_wgpu_shader_t _sg_shader_t; @@ -4845,15 +5476,14 @@ typedef struct { _sg_shader_t* shader; struct { WGPURenderPipeline pip; - uint32_t stencil_ref; + WGPUColor blend_color; } wgpu; } _sg_wgpu_pipeline_t; typedef _sg_wgpu_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - WGPUTextureView render_tex_view; - WGPUTextureView resolve_tex_view; + WGPUTextureView view; } _sg_wgpu_attachment_t; typedef struct { @@ -4861,6 +5491,7 @@ typedef struct { _sg_pass_common_t cmn; struct { _sg_wgpu_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; _sg_wgpu_attachment_t ds_att; } wgpu; } _sg_wgpu_pass_t; @@ -4874,37 +5505,59 @@ typedef _sg_wgpu_context_t _sg_context_t; // a pool of per-frame uniform buffers typedef struct { - WGPUBindGroupLayout bindgroup_layout; uint32_t num_bytes; - uint32_t offset; // current offset into current frame's mapped uniform buffer - uint32_t bind_offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + uint32_t offset; // current offset into buf + uint8_t* staging; // intermediate buffer for uniform data updates WGPUBuffer buf; // the GPU-side uniform buffer - WGPUBindGroup bindgroup; struct { - int num; - int cur; - WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; // CPU-side staging buffers - uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; // if != 0, staging buffer currently mapped - } stage; -} _sg_wgpu_ubpool_t; - -// ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers + WGPUBindGroupLayout group_layout; + WGPUBindGroup group; + uint32_t offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + } bind; +} _sg_wgpu_uniform_buffer_t; + typedef struct { - uint32_t num_bytes; - uint32_t offset; // current offset into current frame's staging buffer - int num; // number of staging buffers - int cur; // this frame's staging buffer - WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; // CPU-side staging buffers - uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; // if != 0, staging buffer currently mapped -} _sg_wgpu_stagingpool_t; + uint32_t id; +} _sg_wgpu_bindgroup_handle_t; + +#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS)) +typedef struct { + uint64_t hash; + uint32_t items[_SG_WGPU_BINDGROUPSCACHE_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; + int offset; + } vbs[SG_MAX_VERTEX_BUFFERS]; + struct { + sg_buffer buffer; + int offset; + } ib; + _sg_wgpu_bindgroup_handle_t bg; +} _sg_wgpu_bindings_cache_t; // the WGPU backend state typedef struct { bool valid; - bool in_pass; - bool draw_indexed; - int cur_width; - int cur_height; WGPUDevice dev; WGPUTextureView (*render_view_cb)(void); WGPUTextureView (*render_view_userdata_cb)(void*); @@ -4913,15 +5566,21 @@ typedef struct { WGPUTextureView (*depth_stencil_view_cb)(void); WGPUTextureView (*depth_stencil_view_userdata_cb)(void*); void* user_data; + bool in_pass; + bool use_indexed_draw; + int cur_width; + int cur_height; + WGPUSupportedLimits limits; WGPUQueue queue; - WGPUCommandEncoder render_cmd_enc; - WGPUCommandEncoder staging_cmd_enc; + WGPUCommandEncoder cmd_enc; WGPURenderPassEncoder pass_enc; WGPUBindGroup empty_bind_group; const _sg_pipeline_t* cur_pipeline; sg_pipeline cur_pipeline_id; - _sg_wgpu_ubpool_t ub; - _sg_wgpu_stagingpool_t staging; + _sg_wgpu_uniform_buffer_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; #endif @@ -4931,13 +5590,6 @@ typedef struct { #define _SG_INVALID_SLOT_INDEX (0) typedef struct { - int size; - int queue_top; - uint32_t* gen_ctrs; - int* free_queue; -} _sg_pool_t; - -typedef struct { _sg_pool_t buffer_pool; _sg_pool_t image_pool; _sg_pool_t sampler_pool; @@ -4960,6 +5612,33 @@ typedef struct { sg_commit_listener* items; } _sg_commit_listeners_t; +// resolved resource bindings struct +typedef struct { + _sg_pipeline_t* pip; + int num_vbs; + int num_vs_imgs; + int num_vs_smps; + int num_fs_imgs; + int num_fs_smps; + int vb_offsets[SG_MAX_VERTEX_BUFFERS]; + int ib_offset; + _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS]; + _sg_buffer_t* ib; + _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; +} _sg_bindings_t; + +typedef struct { + bool sample; + bool filter; + bool render; + bool blend; + bool msaa; + bool depth; +} _sg_pixelformat_info_t; + typedef struct { bool valid; sg_desc desc; // original desc with default values patched in @@ -4968,7 +5647,7 @@ typedef struct { sg_pass cur_pass; sg_pipeline cur_pipeline; bool pass_valid; - bool bindings_valid; + bool bindings_applied; bool next_draw_valid; #if defined(SOKOL_DEBUG) sg_log_item validate_error; @@ -4977,7 +5656,10 @@ typedef struct { sg_backend backend; sg_features features; sg_limits limits; - sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM]; + _sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM]; + bool stats_enabled; + sg_frame_stats stats; + sg_frame_stats prev_stats; #if defined(_SOKOL_ANY_GL) _sg_gl_backend_t gl; #elif defined(SOKOL_METAL) @@ -5061,8 +5743,8 @@ _SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sg_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sg.desc.allocator.alloc) { - ptr = _sg.desc.allocator.alloc(size, _sg.desc.allocator.user_data); + if (_sg.desc.allocator.alloc_fn) { + ptr = _sg.desc.allocator.alloc_fn(size, _sg.desc.allocator.user_data); } else { ptr = malloc(size); } @@ -5079,8 +5761,8 @@ _SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _sg_free(void* ptr) { - if (_sg.desc.allocator.free) { - _sg.desc.allocator.free(ptr, _sg.desc.allocator.user_data); + if (_sg.desc.allocator.free_fn) { + _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data); } else { free(ptr); } @@ -5120,6 +5802,23 @@ _SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { return (val + (align - 1)) & ~(align - 1); } +typedef struct { int x, y, w, h; } _sg_recti_t; + +_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; +} + _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return 4; @@ -5245,6 +5944,7 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format 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: @@ -5252,15 +5952,20 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC6H_RGBF: case SG_PIXELFORMAT_BC6H_RGBUF: case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: case SG_PIXELFORMAT_PVRTC_RGB_2BPP: case SG_PIXELFORMAT_PVRTC_RGB_4BPP: case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: 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_ETC2_RG11: case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return true; default: return false; @@ -5335,6 +6040,9 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { 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; @@ -5345,6 +6053,18 @@ _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; +} + /* return row pitch for an image see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp @@ -5369,20 +6089,26 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) 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: pitch = ((width + 3) / 4) * 8; pitch = pitch < 8 ? 8 : pitch; break; 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_ETC2_RG11: case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: pitch = ((width + 3) / 4) * 16; pitch = pitch < 16 ? 16 : pitch; break; @@ -5410,17 +6136,23 @@ _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { 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_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: case SG_PIXELFORMAT_ETC2_RG11: case SG_PIXELFORMAT_ETC2_RG11SN: 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_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: num_rows = ((height + 3) / 4); break; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: @@ -5459,7 +6191,7 @@ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, } // capability table pixel format helper functions -_SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->filter = true; pfi->blend = true; @@ -5467,53 +6199,53 @@ _SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { pfi->msaa = true; } -_SOKOL_PRIVATE void _sg_pixelformat_s(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) { pfi->sample = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sf(sg_pixelformat_info* pfi) { +_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* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->render = true; } -_SOKOL_PRIVATE void _sg_pixelformat_srmd(sg_pixelformat_info* pfi) { +_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* pfi) { +_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* pfi) { +_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* pfi) { +_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* pfi) { +_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* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->filter = true; pfi->blend = true; @@ -5755,28 +6487,11 @@ _SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { _SOKOL_UNUSED(pip); } -_SOKOL_PRIVATE void _sg_dummy_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs, - _sg_sampler_t** vs_smps, int num_vs_smps, - _sg_sampler_t** fs_smps, int num_fs_smps) -{ - SOKOL_ASSERT(pip); - SOKOL_ASSERT(vbs && vb_offsets); - SOKOL_ASSERT(vs_imgs); - SOKOL_ASSERT(fs_imgs); - SOKOL_ASSERT(vs_smps); - SOKOL_ASSERT(fs_smps); - _SOKOL_UNUSED(pip); - _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); - _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); - _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); - _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); - _SOKOL_UNUSED(vs_smps); _SOKOL_UNUSED(num_vs_smps); - _SOKOL_UNUSED(fs_smps); _SOKOL_UNUSED(num_fs_smps); +_SOKOL_PRIVATE bool _sg_dummy_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SOKOL_UNUSED(bnd); + return true; } _SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -5799,7 +6514,7 @@ _SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* d } } -_SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { +_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) { @@ -5807,8 +6522,7 @@ _SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* da buf->cmn.active_slot = 0; } } - // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend - return _sg_roundup((int)data->size, 4); + return true; } _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { @@ -6324,6 +7038,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { 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: @@ -6338,6 +7054,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { 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_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: @@ -6348,14 +7066,22 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; 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_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; case SG_PIXELFORMAT_ETC2_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; } @@ -6414,6 +7140,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { 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; @@ -6421,15 +7148,20 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { 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_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; 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_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; case SG_PIXELFORMAT_ETC2_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; } } @@ -6542,6 +7274,7 @@ _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) { @@ -6555,6 +7288,7 @@ _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_pvrtc(void) { @@ -6566,12 +7300,19 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { _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_ETC2_RG11]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_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_limits(void) { _SG_GL_CHECK_ERROR(); GLint gl_int; @@ -6624,6 +7365,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { bool has_bptc = false; // BC6H and BC7 bool has_pvrtc = false; 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++) { @@ -6641,6 +7383,8 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { 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; } } } @@ -6672,6 +7416,9 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { if (has_etc2) { _sg_gl_init_pixelformats_etc2(); } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } } #endif @@ -6693,6 +7440,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { #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; @@ -6716,6 +7464,8 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { has_pvrtc = 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")) { @@ -6762,6 +7512,9 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { if (has_etc2) { _sg_gl_init_pixelformats_etc2(); } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } } #endif @@ -6770,10 +7523,12 @@ _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_add(gl.num_bind_buffer, 1); } if (force || (_sg.gl.cache.index_buffer != 0)) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); _sg.gl.cache.index_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); } } @@ -6783,11 +7538,13 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { if (_sg.gl.cache.vertex_buffer != buffer) { _sg.gl.cache.vertex_buffer = buffer; glBindBuffer(target, buffer); + _sg_stats_add(gl.num_bind_buffer, 1); } } else { if (_sg.gl.cache.index_buffer != buffer) { _sg.gl.cache.index_buffer = buffer; glBindBuffer(target, buffer); + _sg_stats_add(gl.num_bind_buffer, 1); } } } @@ -6821,10 +7578,12 @@ _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_add(gl.num_bind_buffer, 1); } if (buf == _sg.gl.cache.index_buffer) { _sg.gl.cache.index_buffer = 0; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); } if (buf == _sg.gl.cache.stored_vertex_buffer) { _sg.gl.cache.stored_vertex_buffer = 0; @@ -6844,6 +7603,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { if (_sg.gl.cache.cur_active_texture != texture) { _sg.gl.cache.cur_active_texture = texture; glActiveTexture(texture); + _sg_stats_add(gl.num_active_texture, 1); } _SG_GL_CHECK_ERROR(); } @@ -6854,11 +7614,14 @@ _SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) { if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) { GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i); glActiveTexture(gl_texture_unit); + _sg_stats_add(gl.num_active_texture, 1); 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_add(gl.num_bind_sampler, 1); _sg.gl.cache.texture_samplers[i].target = 0; _sg.gl.cache.texture_samplers[i].texture = 0; _sg.gl.cache.texture_samplers[i].sampler = 0; @@ -6885,15 +7648,18 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int slot_index, GLenum tar if ((target != slot->target) && (slot->target != 0)) { glBindTexture(slot->target, 0); _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); } // apply new binding (can be 0 to unbind) if (target != 0) { glBindTexture(target, texture); _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); } // apply new sampler (can be 0 to unbind) glBindSampler((GLuint)slot_index, sampler); _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_sampler, 1); slot->target = target; slot->texture = texture; @@ -6928,8 +7694,10 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint s _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); glBindTexture(slot->target, 0); _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); glBindSampler((GLuint)i, 0); _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_sampler, 1); slot->target = 0; slot->texture = 0; slot->sampler = 0; @@ -6947,6 +7715,7 @@ _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_add(gl.num_use_program, 1); } } @@ -6974,6 +7743,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { attr->divisor = -1; glDisableVertexAttribArray((GLuint)i); _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); } _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; @@ -6998,6 +7768,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { 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; @@ -7010,6 +7781,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { 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++) { @@ -7028,15 +7800,19 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { 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_GLCORE33) 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); + SOKOL_ASSERT(desc->context.gl.default_framebuffer_cb == 0 || desc->context.gl.default_framebuffer_userdata_cb == 0); + // assumes that _sg.gl is already zero-initialized _sg.gl.valid = true; @@ -7083,6 +7859,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { _SG_GL_CHECK_ERROR(); // incoming texture data is generally expected to be packed tightly glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + #if defined(SOKOL_GLCORE33) + // enable seamless cubemap sampling (only desktop GL) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + #endif return SG_RESOURCESTATE_VALID; } @@ -7181,6 +7961,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ 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); + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_LEVEL, img->cmn.num_mipmaps - 1); const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; int data_index = 0; for (int face_index = 0; face_index < num_faces; face_index++) { @@ -7619,9 +8400,31 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ } // check if framebuffer is complete - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - _SG_ERROR(GL_FRAMEBUFFER_INCOMPLETE); - return SG_RESOURCESTATE_FAILED; + { + 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 SG_RESOURCESTATE_FAILED; + } } // setup color attachments for the framebuffer @@ -7643,8 +8446,28 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.msaa_resolve_framebuffer[i]); _sg_gl_fb_attach_texture(gl_resolve_att, cmn_resolve_att, GL_COLOR_ATTACHMENT0); // check if framebuffer is complete - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - _SG_ERROR(GL_MSAA_FRAMEBUFFER_INCOMPLETE); + 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 SG_RESOURCESTATE_FAILED; } // setup color attachments for the framebuffer @@ -7725,6 +8548,12 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac #if defined(SOKOL_GLCORE33) glDisable(GL_FRAMEBUFFER_SRGB); #endif + if (_sg.desc.context.gl.default_framebuffer_userdata_cb) { + _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_userdata_cb(_sg.desc.context.gl.user_data); + } else if (_sg.desc.context.gl.default_framebuffer_cb) { + _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_cb(); + } + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); } glViewport(0, 0, w, h); @@ -7902,10 +8731,12 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { if (state_ds->compare != cache_ds->compare) { cache_ds->compare = state_ds->compare; glDepthFunc(_sg_gl_compare_func(state_ds->compare)); + _sg_stats_add(gl.num_render_state, 1); } if (state_ds->write_enabled != cache_ds->write_enabled) { cache_ds->write_enabled = state_ds->write_enabled; glDepthMask(state_ds->write_enabled); + _sg_stats_add(gl.num_render_state, 1); } 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)) @@ -7918,6 +8749,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { 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_add(gl.num_render_state, 1); 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)) @@ -7931,6 +8763,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } else { glDisable(GL_POLYGON_OFFSET_FILL); } + _sg_stats_add(gl.num_render_state, 1); } } } @@ -7946,10 +8779,12 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } else { glDisable(GL_STENCIL_TEST); } + _sg_stats_add(gl.num_render_state, 1); } if (state_ss->write_mask != cache_ss->write_mask) { cache_ss->write_mask = state_ss->write_mask; glStencilMask(state_ss->write_mask); + _sg_stats_add(gl.num_render_state, 1); } for (int i = 0; i < 2; i++) { const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; @@ -7964,6 +8799,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg_gl_compare_func(state_sfs->compare), state_ss->ref, state_ss->read_mask); + _sg_stats_add(gl.num_render_state, 1); } if ((state_sfs->fail_op != cache_sfs->fail_op) || (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || @@ -7976,6 +8812,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _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_add(gl.num_render_state, 1); } } cache_ss->read_mask = state_ss->read_mask; @@ -7994,6 +8831,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } else { glDisable(GL_BLEND); } + _sg_stats_add(gl.num_render_state, 1); } if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || @@ -8008,11 +8846,13 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _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_add(gl.num_render_state, 1); } 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_add(gl.num_render_state, 1); } // standalone color target state @@ -8034,6 +8874,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { (cm & SG_COLORMASK_A) != 0); } #endif + _sg_stats_add(gl.num_render_state, 1); } } @@ -8045,6 +8886,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { sg_color c = pip->cmn.blend_color; _sg.gl.cache.blend_color = c; glBlendColor(c.r, c.g, c.b, c.a); + _sg_stats_add(gl.num_render_state, 1); } } // pip->cmn.color_count > 0 @@ -8052,16 +8894,19 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.cull_mode = pip->gl.cull_mode; if (SG_CULLMODE_NONE == pip->gl.cull_mode) { glDisable(GL_CULL_FACE); + _sg_stats_add(gl.num_render_state, 1); } 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_add(gl.num_render_state, 1); } 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; @@ -8070,6 +8915,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } else { glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); } + _sg_stats_add(gl.num_render_state, 1); } #ifdef SOKOL_GLCORE33 if (pip->gl.sample_count != _sg.gl.cache.sample_count) { @@ -8079,6 +8925,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } else { glDisable(GL_MULTISAMPLE); } + _sg_stats_add(gl.num_render_state, 1); } #endif @@ -8086,33 +8933,26 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { if (pip->shader->gl.prog != _sg.gl.cache.prog) { _sg.gl.cache.prog = pip->shader->gl.prog; glUseProgram(pip->shader->gl.prog); + _sg_stats_add(gl.num_use_program, 1); } } _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_gl_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs, - _sg_sampler_t** vs_smps, int num_vs_smps, - _sg_sampler_t** fs_smps, int num_fs_smps) -{ - SOKOL_ASSERT(pip); - _SOKOL_UNUSED(num_vbs); +_SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); _SG_GL_CHECK_ERROR(); // bind combined image-samplers _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; - const _sg_gl_shader_stage_t* gl_stage = &pip->shader->gl.stage[stage_index]; - _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS) ? vs_imgs : fs_imgs; - _sg_sampler_t** smps = (stage_index == SG_SHADERSTAGE_VS) ? vs_smps : fs_smps; - const int num_imgs = (stage_index == SG_SHADERSTAGE_VS) ? num_vs_imgs : num_fs_imgs; - const int num_smps = (stage_index == SG_SHADERSTAGE_VS) ? num_vs_smps : num_fs_smps; + const _sg_shader_stage_t* stage = &bnd->pip->shader->cmn.stage[stage_index]; + const _sg_gl_shader_stage_t* gl_stage = &bnd->pip->shader->gl.stage[stage_index]; + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS) ? bnd->vs_imgs : bnd->fs_imgs; + _sg_sampler_t** smps = (stage_index == SG_SHADERSTAGE_VS) ? bnd->vs_smps : bnd->fs_smps; + const int num_imgs = (stage_index == SG_SHADERSTAGE_VS) ? bnd->num_vs_imgs : bnd->num_fs_imgs; + const int num_smps = (stage_index == SG_SHADERSTAGE_VS) ? bnd->num_vs_smps : bnd->num_fs_smps; SOKOL_ASSERT(num_imgs == stage->num_images); _SOKOL_UNUSED(num_imgs); SOKOL_ASSERT(num_smps == stage->num_samplers); _SOKOL_UNUSED(num_smps); for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { @@ -8134,24 +8974,24 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( _SG_GL_CHECK_ERROR(); // index buffer (can be 0) - const GLuint gl_ib = ib ? ib->gl.buf[ib->cmn.active_slot] : 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 = ib_offset; + _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 = &pip->gl.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 < num_vbs); - _sg_buffer_t* vb = vbs[attr->vb_index]; + SOKOL_ASSERT(attr->vb_index < bnd->num_vbs); + _sg_buffer_t* vb = bnd->vbs[attr->vb_index]; SOKOL_ASSERT(vb); gl_vb = vb->gl.buf[vb->cmn.active_slot]; - vb_offset = vb_offsets[attr->vb_index] + attr->offset; + 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) || @@ -8161,20 +9001,22 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( (cache_attr->gl_attr.divisor != attr->divisor)) { _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); - glVertexAttribPointer(attr_index, attr->size, attr->type, - attr->normalized, attr->stride, - (const GLvoid*)(GLintptr)vb_offset); + glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); + _sg_stats_add(gl.num_vertex_attrib_pointer, 1); glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); + _sg_stats_add(gl.num_vertex_attrib_divisor, 1); cache_attr_dirty = true; } if (cache_attr->gl_attr.vb_index == -1) { glEnableVertexAttribArray(attr_index); + _sg_stats_add(gl.num_enable_vertex_attrib_array, 1); cache_attr_dirty = true; } } else { // attribute is disabled if (cache_attr->gl_attr.vb_index != -1) { glDisableVertexAttribArray(attr_index); + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); cache_attr_dirty = true; } } @@ -8185,6 +9027,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( } } _SG_GL_CHECK_ERROR(); + return true; } _SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -8201,6 +9044,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_in if (u->gl_loc == -1) { continue; } + _sg_stats_add(gl.num_uniform, 1); GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset); switch (u->type) { @@ -8289,7 +9133,7 @@ _SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { +_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) { @@ -8306,8 +9150,6 @@ _SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); _sg_gl_cache_restore_buffer_binding(gl_tgt); _SG_GL_CHECK_ERROR(); - // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend - return _sg_roundup((int)data->size, 4); } _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { @@ -8817,10 +9659,11 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { 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_D24_UNORM_S8_UINT; + 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; @@ -8828,6 +9671,7 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { 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; }; } @@ -8835,6 +9679,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { _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); } @@ -8843,6 +9689,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_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); } @@ -8851,6 +9699,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_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); } @@ -9073,13 +9923,15 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt)); const UINT rtv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_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* info = &_sg.formats[fmt]; + _sg_pixelformat_info_t* info = &_sg.formats[fmt]; + const bool render = 0 != (rtv_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 = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + info->render = render || depth; info->blend = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); info->msaa = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); - info->depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + info->depth = depth; } } @@ -9204,7 +10056,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res) && (0 == img->d3d11.srv)); HRESULT hr; - const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); + const bool injected = (0 != desc->d3d11_texture); const bool msaa = (img->cmn.sample_count > 1); img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { @@ -9223,23 +10075,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const // first check for injected texture and/or resource view if (injected) { img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex2d); img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex2d) { - _sg_d3d11_AddRef(img->d3d11.tex2d); - } else { - // if only a shader-resource-view was provided, but no texture, lookup - // the texture from the shader-resource-view, this also bumps the refcount - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); - SOKOL_ASSERT(img->d3d11.tex2d); - } if (img->d3d11.srv) { _sg_d3d11_AddRef(img->d3d11.srv); } - } - - if (0 == img->d3d11.tex2d) { - // if not injected, create texture + } else { + // if not injected, create 2D texture D3D11_TEXTURE2D_DESC d3d11_tex_desc; _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; @@ -9276,58 +10118,51 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } + + // create shader-resource-view for 2D texture + // FIXME: currently we don't support setting MSAA texture as shader resource + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } } SOKOL_ASSERT(img->d3d11.tex2d); img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; _sg_d3d11_AddRef(img->d3d11.res); - - // ...and similar, if not injected, create shader-resource-view - // FIXME: currently we don't support setting MSAA texture as shader resource - if ((0 == img->d3d11.srv) && !msaa) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); - switch (img->cmn.type) { - case SG_IMAGETYPE_2D: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_CUBE: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_ARRAY: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; - break; - default: - SOKOL_UNREACHABLE; break; - } - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); - return SG_RESOURCESTATE_FAILED; - } - } } 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); img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex3d) { - _sg_d3d11_AddRef(img->d3d11.tex3d); - } else { - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); - SOKOL_ASSERT(img->d3d11.tex3d); - } if (img->d3d11.srv) { _sg_d3d11_AddRef(img->d3d11.srv); } - } - - if (0 == img->d3d11.tex3d) { + } else { + // not injected, create 3d texture D3D11_TEXTURE3D_DESC d3d11_tex_desc; _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; @@ -9353,23 +10188,24 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } + + // create shader-resource-view for 3D texture + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } } SOKOL_ASSERT(img->d3d11.tex3d); img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d; _sg_d3d11_AddRef(img->d3d11.res); - - if ((0 == img->d3d11.srv) && !msaa) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); - return SG_RESOURCESTATE_FAILED; - } - } } return SG_RESOURCESTATE_VALID; } @@ -9934,6 +10770,7 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* } // apply the render-target- and depth-stencil-views _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); + _sg_stats_add(d3d11.pass.num_om_set_render_targets, 1); // set viewport and scissor rect to cover whole screen D3D11_VIEWPORT vp; @@ -9953,6 +10790,7 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].clear_value.r); + _sg_stats_add(d3d11.pass.num_clear_render_target_view, 1); } } UINT ds_flags = 0; @@ -9964,6 +10802,7 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* } if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); + _sg_stats_add(d3d11.pass.num_clear_depth_stencil_view, 1); } } @@ -10003,6 +10842,7 @@ _SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { color_img->d3d11.res, src_subres, color_img->d3d11.format); + _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); } } } @@ -10063,56 +10903,65 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); _sg_d3d11_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); -} - -_SOKOL_PRIVATE void _sg_d3d11_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs, - _sg_sampler_t** vs_smps, int num_vs_smps, - _sg_sampler_t** fs_smps, int num_fs_smps) -{ - SOKOL_ASSERT(pip); + _sg_stats_add(d3d11.pipeline.num_rs_set_state, 1); + _sg_stats_add(d3d11.pipeline.num_om_set_depth_stencil_state, 1); + _sg_stats_add(d3d11.pipeline.num_om_set_blend_state, 1); + _sg_stats_add(d3d11.pipeline.num_ia_set_primitive_topology, 1); + _sg_stats_add(d3d11.pipeline.num_ia_set_input_layout, 1); + _sg_stats_add(d3d11.pipeline.num_vs_set_shader, 1); + _sg_stats_add(d3d11.pipeline.num_vs_set_constant_buffers, 1); + _sg_stats_add(d3d11.pipeline.num_ps_set_shader, 1); + _sg_stats_add(d3d11.pipeline.num_ps_set_constant_buffers, 1); +} + +_SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); // gather all the D3D11 resources into arrays - ID3D11Buffer* d3d11_ib = ib ? ib->d3d11.buf : 0; + ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0; ID3D11Buffer* d3d11_vbs[SG_MAX_VERTEX_BUFFERS] = {0}; UINT d3d11_vb_offsets[SG_MAX_VERTEX_BUFFERS] = {0}; ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; - for (int i = 0; i < num_vbs; i++) { - SOKOL_ASSERT(vbs[i]->d3d11.buf); - d3d11_vbs[i] = vbs[i]->d3d11.buf; - d3d11_vb_offsets[i] = (UINT)vb_offsets[i]; + for (int i = 0; i < bnd->num_vbs; i++) { + SOKOL_ASSERT(bnd->vbs[i]->d3d11.buf); + d3d11_vbs[i] = bnd->vbs[i]->d3d11.buf; + d3d11_vb_offsets[i] = (UINT)bnd->vb_offsets[i]; } - for (int i = 0; i < num_vs_imgs; i++) { - SOKOL_ASSERT(vs_imgs[i]->d3d11.srv); - d3d11_vs_srvs[i] = vs_imgs[i]->d3d11.srv; + for (int i = 0; i < bnd->num_vs_imgs; i++) { + SOKOL_ASSERT(bnd->vs_imgs[i]->d3d11.srv); + d3d11_vs_srvs[i] = bnd->vs_imgs[i]->d3d11.srv; } - for (int i = 0; i < num_fs_imgs; i++) { - SOKOL_ASSERT(fs_imgs[i]->d3d11.srv); - d3d11_fs_srvs[i] = fs_imgs[i]->d3d11.srv; + for (int i = 0; i < bnd->num_fs_imgs; i++) { + SOKOL_ASSERT(bnd->fs_imgs[i]->d3d11.srv); + d3d11_fs_srvs[i] = bnd->fs_imgs[i]->d3d11.srv; } - for (int i = 0; i < num_vs_smps; i++) { - SOKOL_ASSERT(vs_smps[i]->d3d11.smp); - d3d11_vs_smps[i] = vs_smps[i]->d3d11.smp; + for (int i = 0; i < bnd->num_vs_smps; i++) { + SOKOL_ASSERT(bnd->vs_smps[i]->d3d11.smp); + d3d11_vs_smps[i] = bnd->vs_smps[i]->d3d11.smp; } - for (int i = 0; i < num_fs_smps; i++) { - SOKOL_ASSERT(fs_smps[i]->d3d11.smp); - d3d11_fs_smps[i] = fs_smps[i]->d3d11.smp; + for (int i = 0; i < bnd->num_fs_smps; i++) { + SOKOL_ASSERT(bnd->fs_smps[i]->d3d11.smp); + d3d11_fs_smps[i] = bnd->fs_smps[i]->d3d11.smp; } - _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); - _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, (UINT)ib_offset); + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, bnd->pip->d3d11.vb_strides, d3d11_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_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_vs_smps); _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_fs_smps); + _sg_stats_add(d3d11.bindings.num_ia_set_vertex_buffers, 1); + _sg_stats_add(d3d11.bindings.num_ia_set_index_buffer, 1); + _sg_stats_add(d3d11.bindings.num_vs_set_shader_resources, 1); + _sg_stats_add(d3d11.bindings.num_ps_set_shader_resources, 1); + _sg_stats_add(d3d11.bindings.num_vs_set_samplers, 1); + _sg_stats_add(d3d11.bindings.num_ps_set_samplers, 1); + return true; } _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -10124,6 +10973,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; SOKOL_ASSERT(cb); _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data->ptr, 0, 0); + _sg_stats_add(d3d11.uniforms.num_update_subresource, 1); } _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { @@ -10131,14 +10981,18 @@ _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_i if (_sg.d3d11.use_indexed_draw) { if (_sg.d3d11.use_instanced_draw) { _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); + _sg_stats_add(d3d11.draw.num_draw_indexed_instanced, 1); } else { _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); + _sg_stats_add(d3d11.draw.num_draw_indexed, 1); } } else { if (_sg.d3d11.use_instanced_draw) { _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); + _sg_stats_add(d3d11.draw.num_draw_instanced, 1); } else { _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + _sg_stats_add(d3d11.draw.num_draw, 1); } } } @@ -10153,30 +11007,32 @@ _SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* d SOKOL_ASSERT(buf->d3d11.buf); D3D11_MAPPED_SUBRESOURCE d3d11_msr; HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); if (SUCCEEDED(hr)) { memcpy(d3d11_msr.pData, data->ptr, data->size); _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + _sg_stats_add(d3d11.num_unmap, 1); } else { _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); } } -_SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { +_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_add(d3d11.num_map, 1); 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_add(d3d11.num_unmap, 1); } else { _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); } - // NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend - return _sg_roundup((int)data->size, 4); } _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { @@ -10200,6 +11056,7 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data const size_t slice_offset = slice_size * (size_t)slice_index; const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); if (SUCCEEDED(hr)) { // FIXME: need to handle difference in depth-pitch for 3D textures as well! if (src_pitch == (int)d3d11_msr.RowPitch) { @@ -10215,6 +11072,7 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data } } _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); + _sg_stats_add(d3d11.num_unmap, 1); } else { _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); } @@ -10272,12 +11130,13 @@ _SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resol _SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) { #if defined(_SG_TARGET_MACOS) - if (_sg.mtl.force_managed_storage_mode || !_sg.mtl.has_unified_memory) { - return MTLResourceStorageModeManaged; - } else { + if (_sg.mtl.use_shared_storage_mode) { return MTLResourceStorageModeShared; + } else { + return MTLResourceStorageModeManaged; } #else + // MTLResourceStorageModeManaged is not even defined on iOS SDK return MTLResourceStorageModeShared; #endif } @@ -10386,6 +11245,7 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { 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; @@ -10393,16 +11253,21 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { 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_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; 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_ETC2_RG11: return MTLPixelFormatEAC_RG11Unorm; case SG_PIXELFORMAT_ETC2_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; } @@ -10657,6 +11522,7 @@ _SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { if (nil == res) { return _SG_MTL_INVALID_SLOT_INDEX; } + _sg_stats_add(metal.idpool.num_added, 1); 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]); @@ -10673,6 +11539,7 @@ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_inde if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { return; } + _sg_stats_add(metal.idpool.num_released, 1); 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++; @@ -10695,6 +11562,7 @@ _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { // don't need to check further, release-items past this are too young break; } + _sg_stats_add(metal.idpool.num_garbage_collected, 1); // 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)); @@ -10739,16 +11607,19 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.features.mrt_independent_write_mask = 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:MTLGPUFamilyApple8] || [_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; @@ -10848,6 +11719,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(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]); _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]); @@ -10855,16 +11727,22 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(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]); #else _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); _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_ETC2_RG11]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + #endif } @@ -10884,7 +11762,6 @@ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; _sg.mtl.drawable_userdata_cb = desc->context.metal.drawable_userdata_cb; _sg.mtl.user_data = desc->context.metal.user_data; - _sg.mtl.frame_index = 1; _sg.mtl.ub_size = desc->uniform_buffer_size; _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); _sg.mtl.device = (__bridge id<MTLDevice>) desc->context.metal.device; @@ -10900,16 +11777,24 @@ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { #endif } - if (@available(macOS 10.15, iOS 13.0, *)) { - _sg.mtl.has_unified_memory = _sg.mtl.device.hasUnifiedMemory; + if (desc->mtl_force_managed_storage_mode) { + _sg.mtl.use_shared_storage_mode = false; + } else if (@available(macOS 10.15, iOS 13.0, *)) { + // on Intel Macs, always use managed resources even though the + // device says it supports unified memory (because of texture restrictions) + const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1]; + if (!is_apple_gpu) { + _sg.mtl.use_shared_storage_mode = false; + } else { + _sg.mtl.use_shared_storage_mode = true; + } } else { #if defined(_SG_TARGET_MACOS) - _sg.mtl.has_unified_memory = false; + _sg.mtl.use_shared_storage_mode = false; #else - _sg.mtl.has_unified_memory = true; + _sg.mtl.use_shared_storage_mode = true; #endif } - _sg.mtl.force_managed_storage_mode = desc->mtl_force_managed_storage_mode; _sg_mtl_init_caps(); } @@ -10923,7 +11808,7 @@ _SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { dispatch_semaphore_signal(_sg.mtl.sem); } - _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_garbage_collect(_sg.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); _sg_mtl_destroy_pool(); _sg.mtl.valid = false; @@ -11013,7 +11898,7 @@ _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.mtl.frame_index, buf->mtl.buf[slot]); + _sg_mtl_release_resource(_sg.frame_index, buf->mtl.buf[slot]); } } @@ -11167,7 +12052,7 @@ _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.mtl.frame_index, img->mtl.tex[slot]); + _sg_mtl_release_resource(_sg.frame_index, img->mtl.tex[slot]); } } @@ -11217,7 +12102,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, cons _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.mtl.frame_index, smp->mtl.sampler_state); + _sg_mtl_release_resource(_sg.frame_index, smp->mtl.sampler_state); } _SOKOL_PRIVATE id<MTLLibrary> _sg_mtl_compile_library(const char* src) { @@ -11320,10 +12205,10 @@ failed: _SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); // it is valid to call _sg_mtl_release_resource with a 'null resource' - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); } _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { @@ -11464,8 +12349,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s _SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); // it's valid to call release resource with a 'null resource' - _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); - _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.dss); + _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_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { @@ -11577,7 +12462,7 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a // 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 default_pass_desc_ref = _sg_mtl_add_resource(pass_desc); - _sg_mtl_release_resource(_sg.mtl.frame_index, default_pass_desc_ref); + _sg_mtl_release_resource(_sg.frame_index, default_pass_desc_ref); } if (pass_desc) { _sg.mtl.pass_valid = true; @@ -11723,13 +12608,12 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { [_sg.mtl.cmd_buffer commit]; // garbage-collect resources pending for release - _sg_mtl_garbage_collect(_sg.mtl.frame_index); + _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.frame_index++; _sg.mtl.cur_ub_offset = 0; _sg.mtl.cur_ub_base_ptr = 0; // NOTE: MTLCommandBuffer is autoreleased @@ -11759,22 +12643,12 @@ _SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); // clip against framebuffer rect - x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); - y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); - if ((x + w) > _sg.mtl.cur_width) { - w = _sg.mtl.cur_width - x; - } - if ((y + h) > _sg.mtl.cur_height) { - h = _sg.mtl.cur_height - y; - } - w = _sg_max(w, 1); - h = _sg_max(h, 1); - + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.mtl.cur_width, _sg.mtl.cur_height); MTLScissorRect r; - r.x = (NSUInteger)x; - r.y = (NSUInteger) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); - r.width = (NSUInteger)w; - r.height = (NSUInteger)h; + r.x = (NSUInteger)clip.x; + r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.mtl.cur_height - (clip.y + clip.h))); + r.width = (NSUInteger)clip.w; + r.height = (NSUInteger)clip.h; [_sg.mtl.cmd_encoder setScissorRect:r]; } @@ -11792,108 +12666,116 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; sg_color c = pip->cmn.blend_color; [_sg.mtl.cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; + _sg_stats_add(metal.pipeline.num_set_blend_color, 1); [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; + _sg_stats_add(metal.pipeline.num_set_cull_mode, 1); [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + _sg_stats_add(metal.pipeline.num_set_front_facing_winding, 1); [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + _sg_stats_add(metal.pipeline.num_set_stencil_reference_value, 1); [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp]; + _sg_stats_add(metal.pipeline.num_set_depth_bias, 1); SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + _sg_stats_add(metal.pipeline.num_set_render_pipeline_state, 1); SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; + _sg_stats_add(metal.pipeline.num_set_depth_stencil_state, 1); } } -_SOKOL_PRIVATE void _sg_mtl_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs, - _sg_sampler_t** vs_smps, int num_vs_smps, - _sg_sampler_t** fs_smps, int num_fs_smps) -{ - _SOKOL_UNUSED(pip); +_SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { - return; + return false; } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); // store index buffer binding, this will be needed later in sg_draw() - _sg.mtl.state_cache.cur_indexbuffer = ib; - _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; - if (ib) { - SOKOL_ASSERT(pip->cmn.index_type != SG_INDEXTYPE_NONE); - _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; + _sg.mtl.state_cache.cur_indexbuffer = bnd->ib; + _sg.mtl.state_cache.cur_indexbuffer_offset = bnd->ib_offset; + if (bnd->ib) { + SOKOL_ASSERT(bnd->pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = bnd->ib->slot.id; } else { - SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); + SOKOL_ASSERT(bnd->pip->cmn.index_type == SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; } // apply vertex buffers - for (NSUInteger slot = 0; slot < (NSUInteger)num_vbs; slot++) { - const _sg_buffer_t* vb = vbs[slot]; + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vbs; slot++) { + const _sg_buffer_t* vb = bnd->vbs[slot]; + const int vb_offset = bnd->vb_offsets[slot]; if ((_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) || - (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot])) + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offset)) { _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; - _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; + _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offset; const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; if (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) { _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) - offset:(NSUInteger)vb_offsets[slot] - atIndex:mtl_slot]; + offset:(NSUInteger)vb_offset + atIndex:mtl_slot]; } else { - [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)vb_offsets[slot] atIndex:mtl_slot]; + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)vb_offset atIndex:mtl_slot]; } + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); } } // apply vertex shader images - for (NSUInteger slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { - const _sg_image_t* img = vs_imgs[slot]; + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_imgs; slot++) { + const _sg_image_t* img = bnd->vs_imgs[slot]; if (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id) { _sg.mtl.state_cache.cur_vs_images[slot] = img; _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; - } - } - - // apply fragment shader images - for (NSUInteger slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { - const _sg_image_t* img = fs_imgs[slot]; - if (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id) { - _sg.mtl.state_cache.cur_fs_images[slot] = img; - _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; - SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_vertex_texture, 1); } } // apply vertex shader samplers - for (NSUInteger slot = 0; slot < (NSUInteger)num_vs_smps; slot++) { - const _sg_sampler_t* smp = vs_smps[slot]; + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_smps; slot++) { + const _sg_sampler_t* smp = bnd->vs_smps[slot]; if (_sg.mtl.state_cache.cur_vs_sampler_ids[slot].id != smp->slot.id) { _sg.mtl.state_cache.cur_vs_samplers[slot] = smp; _sg.mtl.state_cache.cur_vs_sampler_ids[slot].id = smp->slot.id; SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_vertex_sampler_state, 1); + } + } + + // apply fragment shader images + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_imgs; slot++) { + const _sg_image_t* img = bnd->fs_imgs[slot]; + if (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id) { + _sg.mtl.state_cache.cur_fs_images[slot] = img; + _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_fragment_texture, 1); } } // apply fragment shader samplers - for (NSUInteger slot = 0; slot < (NSUInteger)num_fs_smps; slot++) { - const _sg_sampler_t* smp = fs_smps[slot]; + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_smps; slot++) { + const _sg_sampler_t* smp = bnd->fs_smps[slot]; if (_sg.mtl.state_cache.cur_fs_sampler_ids[slot].id != smp->slot.id) { _sg.mtl.state_cache.cur_fs_samplers[slot] = smp; _sg.mtl.state_cache.cur_fs_sampler_ids[slot].id = smp->slot.id; SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_fragment_sampler_state, 1); } } + return true; } _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -11915,8 +12797,10 @@ _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_i memcpy(dst, data->ptr, data->size); if (stage_index == SG_SHADERSTAGE_VS) { [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + _sg_stats_add(metal.uniforms.num_set_vertex_buffer_offset, 1); } else { [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + _sg_stats_add(metal.uniforms.num_set_fragment_buffer_offset, 1); } _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); } @@ -11958,13 +12842,13 @@ _SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* dat 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() == MTLStorageModeManaged) { + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; } #endif } -_SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { +_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) { @@ -11976,12 +12860,10 @@ _SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data 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() == MTLStorageModeManaged) { + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; } #endif - // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends - return _sg_roundup((int)data->size, 4); } _SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { @@ -12012,7 +12894,8 @@ _SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { // ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ // ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ // -// >>webgpu backend +// >>webgpu +// >>wgpu #elif defined(SOKOL_WGPU) _SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { @@ -12028,20 +12911,36 @@ _SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_u return res; } -_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(sg_action a) { - switch (a) { - case SG_ACTION_CLEAR: - case SG_ACTION_DONTCARE: +_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_ACTION_LOAD: + case SG_LOADACTION_LOAD: return WGPULoadOp_Load; default: SOKOL_UNREACHABLE; - return (WGPULoadOp)0; + return WGPULoadOp_Force32; } } -_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { +_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; @@ -12051,17 +12950,7 @@ _SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { } } -_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_image_sample_type t) { - // FIXME - switch (t) { - case SG_IMAGESAMPLETYPE_FLOAT: return WGPUTextureComponentType_Float; - case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureComponentType_Sint; - case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureComponentType_Uint; - default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; - } -} - -_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_texture_dimension(sg_image_type t) { if (SG_IMAGETYPE_3D == t) { return WGPUTextureDimension_3D; } else { @@ -12069,7 +12958,27 @@ _SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { } } -_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_addrmode(sg_wrap m) { +_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t) { + switch (t) { + case SG_IMAGESAMPLETYPE_FLOAT: return 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; @@ -12084,15 +12993,11 @@ _SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_addrmode(sg_wrap m) { } } -_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmagfilter(sg_filter f) { +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) { switch (f) { case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_LINEAR: return WGPUFilterMode_Nearest; case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: return WGPUFilterMode_Linear; default: SOKOL_UNREACHABLE; @@ -12100,19 +13005,16 @@ _SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmagfilter(sg_filter f) { } } -_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { +_SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) { switch (f) { + case SG_FILTER_NONE: case SG_FILTER_NEAREST: + return WGPUMipmapFilterMode_Nearest; case SG_FILTER_LINEAR: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return WGPUFilterMode_Nearest; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return WGPUFilterMode_Linear; + return WGPUMipmapFilterMode_Linear; default: SOKOL_UNREACHABLE; - return WGPUFilterMode_Force32; + return WGPUMipmapFilterMode_Force32; } } @@ -12121,30 +13023,40 @@ _SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; } -_SOKOL_PRIVATE WGPUInputStepMode _sg_wgpu_stepmode(sg_vertex_step s) { - return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUInputStepMode_Vertex : WGPUInputStepMode_Instance; +_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_Float; - case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float2; - case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float3; - case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float4; - case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Char4; - case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Char4Norm; - case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_UChar4; - case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_UChar4Norm; - case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Short2; - case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Short2Norm; - case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_UShort2Norm; - case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Short4; - case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Short4Norm; - case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; - case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Half2; - case SG_VERTEXFORMAT_HALF3: return WGPUVertexFormat_Half4; - // FIXME! UINT10_N2 - case SG_VERTEXFORMAT_UINT10_N2: + 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_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_USHORT2N: return WGPUVertexFormat_Unorm16x2; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Sint16x4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Snorm16x4; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_Unorm16x4; + case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Float16x2; + case SG_VERTEXFORMAT_HALF4: return WGPUVertexFormat_Float16x4; + // FIXME! UINT10_N2 (see https://github.com/gpuweb/gpuweb/issues/4275) + case SG_VERTEXFORMAT_UINT10_N2: return WGPUVertexFormat_Undefined; default: SOKOL_UNREACHABLE; return WGPUVertexFormat_Force32; @@ -12158,7 +13070,9 @@ _SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { 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; + default: + SOKOL_UNREACHABLE; + return WGPUPrimitiveTopology_Force32; } } @@ -12171,7 +13085,9 @@ _SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode 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; + default: + SOKOL_UNREACHABLE; + return WGPUCullMode_Force32; } } @@ -12196,12 +13112,13 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { 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_RG11B10Float; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Ufloat; case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; @@ -12212,18 +13129,29 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { 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_Depth24PlusStencil8; + 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_BC6HRGBSfloat; + 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_ETC2_RG11: return WGPUTextureFormat_EACR11Unorm; + case SG_PIXELFORMAT_ETC2_RG11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat; + 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: @@ -12231,35 +13159,18 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_RG16SN: case SG_PIXELFORMAT_RGBA16: case SG_PIXELFORMAT_RGBA16SN: - case SG_PIXELFORMAT_SRGB8A8: - case SG_PIXELFORMAT_RGB9E5: case SG_PIXELFORMAT_PVRTC_RGB_2BPP: case SG_PIXELFORMAT_PVRTC_RGB_4BPP: case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - case SG_PIXELFORMAT_ETC2_RGB8: - case SG_PIXELFORMAT_ETC2_RGB8A1: - case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + return WGPUTextureFormat_Undefined; + default: SOKOL_UNREACHABLE; return WGPUTextureFormat_Force32; } } -/* -FIXME ??? this isn't needed anywhere? -_SOKOL_PRIVATE WGPUTextureAspect _sg_wgpu_texture_aspect(sg_pixel_format fmt) { - if (_sg_is_valid_rendertarget_depth_format(fmt)) { - if (!_sg_is_depth_stencil_format(fmt)) { - return WGPUTextureAspect_DepthOnly; - } - } - return WGPUTextureAspect_All; -} -*/ - _SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { switch (f) { case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; @@ -12270,7 +13181,9 @@ _SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { 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; + default: + SOKOL_UNREACHABLE; + return WGPUCompareFunction_Force32; } } @@ -12284,7 +13197,9 @@ _SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { 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; + default: + SOKOL_UNREACHABLE; + return WGPUStencilOperation_Force32; } } @@ -12293,7 +13208,9 @@ _SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; - default: SOKOL_UNREACHABLE; return WGPUBlendOperation_Force32; + default: + SOKOL_UNREACHABLE; + return WGPUBlendOperation_Force32; } } @@ -12301,22 +13218,23 @@ _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_SrcColor; - case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrcColor; + 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_DstColor; - case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDstColor; + 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_BlendColor; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusBlendColor; + 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_BlendColor; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusConstant; default: - SOKOL_UNREACHABLE; return WGPUBlendFactor_Force32; + SOKOL_UNREACHABLE; + return WGPUBlendFactor_Force32; } } @@ -12337,6 +13255,40 @@ _SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { return res; } +// image/sampler binding on wgpu follows this convention: +// +// - all images and sampler are in @group(1) +// - vertex stage images start at @binding(0) +// - vertex stage samplers start at @binding(16) +// - fragment stage images start at @binding(32) +// - fragment stage samplers start at @binding(48) +// +_SOKOL_PRIVATE uint32_t _sg_wgpu_image_binding(sg_shader_stage stage, int img_slot) { + SOKOL_ASSERT((img_slot >= 0) && (img_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 0 + (uint32_t)img_slot; + } else { + return 32 + (uint32_t)img_slot; + } +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_sampler_binding(sg_shader_stage stage, int smp_slot) { + SOKOL_ASSERT((smp_slot >= 0) && (smp_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 16 + (uint32_t)smp_slot; + } else { + return 48 + (uint32_t)smp_slot; + } +} + +_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return WGPUShaderStage_Vertex; + case SG_SHADERSTAGE_FS: return WGPUShaderStage_Fragment; + 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; @@ -12344,483 +13296,625 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; - // FIXME: max images size??? - _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; + wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); + + const WGPULimits* l = &_sg.wgpu.limits.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; + // NOTE: no WGPUTextureFormat_R16Unorm _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]); - _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_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]); - _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); - _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_sf(&_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_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]); - // FIXME: missing SG_PIXELFORMAT_RG11B10F + + _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_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + + // FIXME: can be made filterable with extension + _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]); - /* FIXME FIXME FIXME: need to check if BC texture compression is - actually supported, currently the WebGPU C-API doesn't allow this - */ - _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_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]); -} - -/* - WGPU uniform buffer pool implementation: - - At start of frame, a mapped buffer is grabbed from the pool, - or a new buffer is created if there is no mapped buffer available. + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); - At end of frame, the current buffer is unmapped before queue submit, - and async-mapped immediately again. + 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_ETC2_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + } - UNIFORM BUFFER FIXME: + 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]); + } +} - - As per WebGPU spec, it should be possible to create a Uniform|MapWrite - buffer, but this isn't currently allowed in Dawn. -*/ -_SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_init(const sg_desc* desc) { + SOKOL_ASSERT(0 == _sg.wgpu.uniform.staging); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.buf); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.bind.group_layout); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.bind.group); - /* 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 - */ - _sg.wgpu.ub.num_bytes = desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + // 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); WGPUBufferDescriptor ub_desc; _sg_clear(&ub_desc, sizeof(ub_desc)); - ub_desc.size = _sg.wgpu.ub.num_bytes; + ub_desc.size = _sg.wgpu.uniform.num_bytes; ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; - _sg.wgpu.ub.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); - SOKOL_ASSERT(_sg.wgpu.ub.buf); + _sg.wgpu.uniform.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.buf); - WGPUBindGroupLayoutBinding ub_bglb_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - _sg_clear(ub_bglb_desc, sizeof(ub_bglb_desc)); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + WGPUBindGroupLayoutEntry ub_bgle_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + _sg_clear(ub_bgle_desc, sizeof(ub_bgle_desc)); + for (uint32_t stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; - ub_bglb_desc[stage_index][ub_index].binding = bind_index; - ub_bglb_desc[stage_index][ub_index].visibility = vis; - ub_bglb_desc[stage_index][ub_index].type = WGPUBindingType_UniformBuffer; - ub_bglb_desc[stage_index][ub_index].hasDynamicOffset = true; + for (uint32_t ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + uint32_t bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bgle_desc[stage_index][ub_index].binding = bind_index; + ub_bgle_desc[stage_index][ub_index].visibility = vis; + ub_bgle_desc[stage_index][ub_index].buffer.type = WGPUBufferBindingType_Uniform; + ub_bgle_desc[stage_index][ub_index].buffer.hasDynamicOffset = true; } } WGPUBindGroupLayoutDescriptor ub_bgl_desc; _sg_clear(&ub_bgl_desc, sizeof(ub_bgl_desc)); - ub_bgl_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; - ub_bgl_desc.bindings = &ub_bglb_desc[0][0]; - _sg.wgpu.ub.bindgroup_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); - SOKOL_ASSERT(_sg.wgpu.ub.bindgroup_layout); + ub_bgl_desc.entryCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + ub_bgl_desc.entries = &ub_bgle_desc[0][0]; + _sg.wgpu.uniform.bind.group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.bind.group_layout); - WGPUBindGroupBinding ub_bgb[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - _sg_clear(ub_bgb, sizeof(ub_bgb)); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; - ub_bgb[stage_index][ub_index].binding = bind_index; - ub_bgb[stage_index][ub_index].buffer = _sg.wgpu.ub.buf; - // FIXME FIXME FIXME FIXME: HACK FOR VALIDATION BUG IN DAWN - ub_bgb[stage_index][ub_index].size = (1<<16); + WGPUBindGroupEntry ub_bge[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + _sg_clear(ub_bge, sizeof(ub_bge)); + for (uint32_t stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + for (uint32_t ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + uint32_t bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bge[stage_index][ub_index].binding = bind_index; + ub_bge[stage_index][ub_index].buffer = _sg.wgpu.uniform.buf; + ub_bge[stage_index][ub_index].size = _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; } } WGPUBindGroupDescriptor bg_desc; _sg_clear(&bg_desc, sizeof(bg_desc)); - bg_desc.layout = _sg.wgpu.ub.bindgroup_layout; - bg_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; - bg_desc.bindings = &ub_bgb[0][0]; - _sg.wgpu.ub.bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); - SOKOL_ASSERT(_sg.wgpu.ub.bindgroup); + bg_desc.layout = _sg.wgpu.uniform.bind.group_layout; + bg_desc.entryCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + bg_desc.entries = &ub_bge[0][0]; + _sg.wgpu.uniform.bind.group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.bind.group); } -_SOKOL_PRIVATE void _sg_wgpu_ubpool_discard(void) { - if (_sg.wgpu.ub.buf) { - wgpuBufferRelease(_sg.wgpu.ub.buf); - _sg.wgpu.ub.buf = 0; +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_discard(void) { + if (_sg.wgpu.uniform.buf) { + wgpuBufferRelease(_sg.wgpu.uniform.buf); + _sg.wgpu.uniform.buf = 0; } - if (_sg.wgpu.ub.bindgroup) { - wgpuBindGroupRelease(_sg.wgpu.ub.bindgroup); - _sg.wgpu.ub.bindgroup = 0; + if (_sg.wgpu.uniform.bind.group) { + wgpuBindGroupRelease(_sg.wgpu.uniform.bind.group); + _sg.wgpu.uniform.bind.group = 0; } - if (_sg.wgpu.ub.bindgroup_layout) { - wgpuBindGroupLayoutRelease(_sg.wgpu.ub.bindgroup_layout); - _sg.wgpu.ub.bindgroup_layout = 0; + if (_sg.wgpu.uniform.bind.group_layout) { + wgpuBindGroupLayoutRelease(_sg.wgpu.uniform.bind.group_layout); + _sg.wgpu.uniform.bind.group_layout = 0; } - for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { - if (_sg.wgpu.ub.stage.buf[i]) { - wgpuBufferRelease(_sg.wgpu.ub.stage.buf[i]); - _sg.wgpu.ub.stage.buf[i] = 0; - _sg.wgpu.ub.stage.ptr[i] = 0; - } + if (_sg.wgpu.uniform.staging) { + _sg_free(_sg.wgpu.uniform.staging); + _sg.wgpu.uniform.staging = 0; } } -_SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { - if (!_sg.wgpu.valid) { - return; - } - // FIXME: better handling for this - if (WGPUBufferMapAsyncStatus_Success != status) { - _SG_ERROR(WGPU_MAP_UNIFORM_BUFFER_FAILED); - SOKOL_ASSERT(false); - } - SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); - int index = (int)(intptr_t) user_data; - SOKOL_ASSERT(index < _sg.wgpu.ub.stage.num); - SOKOL_ASSERT(0 == _sg.wgpu.ub.stage.ptr[index]); - _sg.wgpu.ub.stage.ptr[index] = (uint8_t*) data; +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_on_begin_pass(void) { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, // groupIndex 0 is reserved for uniform buffers + _sg.wgpu.uniform.bind.group, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.uniform.bind.offsets[0][0]); } -_SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_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[0][0], sizeof(_sg.wgpu.uniform.bind.offsets)); +} - // immediately request a new mapping for the last frame's current staging buffer - if (!first_frame) { - WGPUBuffer ub_src = _sg.wgpu.ub.stage.buf[_sg.wgpu.ub.stage.cur]; - wgpuBufferMapWriteAsync(ub_src, _sg_wgpu_ubpool_mapped_callback, (void*)(intptr_t)_sg.wgpu.ub.stage.cur); - } +_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_init_pool(&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_discard_pool(&p->pool); +} - // rewind per-frame offsets - _sg.wgpu.ub.offset = 0; - _sg_clear(&_sg.wgpu.ub.bind_offsets, sizeof(_sg.wgpu.ub.bind_offsets)); +_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]; +} - // check if a mapped staging buffer is available, otherwise create one - for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { - if (_sg.wgpu.ub.stage.ptr[i]) { - _sg.wgpu.ub.stage.cur = i; - return; +_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; +} - // no mapped uniform buffer available, create one - SOKOL_ASSERT(_sg.wgpu.ub.stage.num < _SG_WGPU_STAGING_PIPELINE_SIZE); - _sg.wgpu.ub.stage.cur = _sg.wgpu.ub.stage.num++; - const int cur = _sg.wgpu.ub.stage.cur; +_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; +} - WGPUBufferDescriptor desc; - _sg_clear(&desc, sizeof(desc)); - desc.size = _sg.wgpu.ub.num_bytes; - desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; - WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); - _sg.wgpu.ub.stage.buf[cur] = res.buffer; - _sg.wgpu.ub.stage.ptr[cur] = (uint8_t*) res.data; - SOKOL_ASSERT(_sg.wgpu.ub.stage.buf[cur]); - SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); - SOKOL_ASSERT(res.dataLength == _sg.wgpu.ub.num_bytes); -} - -_SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { - // unmap staging buffer and copy to uniform buffer - const int cur = _sg.wgpu.ub.stage.cur; - SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); - _sg.wgpu.ub.stage.ptr[cur] = 0; - WGPUBuffer src_buf = _sg.wgpu.ub.stage.buf[cur]; - wgpuBufferUnmap(src_buf); - if (_sg.wgpu.ub.offset > 0) { - WGPUBuffer dst_buf = _sg.wgpu.ub.buf; - wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.render_cmd_enc, src_buf, 0, dst_buf, 0, _sg.wgpu.ub.offset); - } -} - -// helper function to compute number of bytes needed in staging buffer to copy image data -_SOKOL_PRIVATE uint32_t _sg_wgpu_image_data_buffer_size(const _sg_image_t* img) { - uint32_t num_bytes = 0; - const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { - const uint32_t mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); - const uint32_t mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); - // row-pitch must be 256-aligend - const uint32_t bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); - num_bytes += bytes_per_slice * num_slices * num_faces; - } - return num_bytes; -} - -/* helper function to copy image data into a texture via a staging buffer, returns number of - bytes copied -*/ -_SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* stg_base_ptr, uint32_t stg_base_offset, _sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - SOKOL_ASSERT(stg_buf && stg_base_ptr); - SOKOL_ASSERT(img); - SOKOL_ASSERT(data); - uint32_t stg_offset = stg_base_offset; - const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; - const sg_pixel_format fmt = img->cmn.pixel_format; - WGPUBufferCopyView src_view; - _sg_clear(&src_view, sizeof(src_view)); - src_view.buffer = stg_buf; - WGPUTextureCopyView dst_view; - _sg_clear(&dst_view, sizeof(dst_view)); - dst_view.texture = img->wgpu.tex; - WGPUExtent3D extent; - _sg_clear(&extent, sizeof(extent)); - - for (uint32_t face_index = 0; face_index < num_faces; face_index++) { - for (uint32_t mip_index = 0; mip_index < (uint32_t)img->cmn.num_mipmaps; mip_index++) { - SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); - SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); - const uint8_t* src_base_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; - SOKOL_ASSERT(src_base_ptr); - uint8_t* dst_base_ptr = stg_base_ptr + stg_offset; - - const uint32_t mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); - const uint32_t mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); - const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : 1; - const uint32_t num_rows = _sg_num_rows(fmt, mip_height); - const uint32_t src_bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); - const uint32_t dst_bytes_per_row = _sg_row_pitch(fmt, mip_width, _SG_WGPU_ROWPITCH_ALIGN); - const uint32_t src_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); - const uint32_t dst_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); - SOKOL_ASSERT((uint32_t)data->subimage[face_index][mip_index].size == (src_bytes_per_slice * num_slices)); - SOKOL_ASSERT(src_bytes_per_row <= dst_bytes_per_row); - SOKOL_ASSERT(src_bytes_per_slice == (src_bytes_per_row * num_rows)); - SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); - _SOKOL_UNUSED(src_bytes_per_slice); - - // copy data into mapped staging buffer - if (src_bytes_per_row == dst_bytes_per_row) { - // can do a single memcpy - uint32_t num_bytes = data->subimage[face_index][mip_index].size; - memcpy(dst_base_ptr, src_base_ptr, num_bytes); - } else { - // src/dst pitch doesn't match, need to copy row by row - uint8_t* dst_ptr = dst_base_ptr; - const uint8_t* src_ptr = src_base_ptr; - for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { - SOKOL_ASSERT(dst_ptr == dst_base_ptr + slice_index * dst_bytes_per_slice); - for (uint32_t row_index = 0; row_index < num_rows; row_index++) { - memcpy(dst_ptr, src_ptr, src_bytes_per_row); - src_ptr += src_bytes_per_row; - dst_ptr += dst_bytes_per_row; - } - } - } +_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_reset_slot(&bg->slot); +} - // record the staging copy operation into command encoder - src_view.imageHeight = mip_height; - src_view.rowPitch = dst_bytes_per_row; - dst_view.mipLevel = mip_index; - extent.width = mip_width; - extent.height = mip_height; - extent.depth = mip_depth; - SOKOL_ASSERT((img->cmn.type != SG_IMAGETYPE_CUBE) || (num_slices == 1)); - for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { - const uint32_t layer_index = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? slice_index : face_index; - src_view.offset = stg_offset; - dst_view.arrayLayer = layer_index; - wgpuCommandEncoderCopyBufferToTexture(_sg.wgpu.staging_cmd_enc, &src_view, &dst_view, &extent); - stg_offset += dst_bytes_per_slice; - SOKOL_ASSERT(stg_offset <= _sg.wgpu.staging.num_bytes); - } - } +_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); + case 2: h2 ^= (uint32_t)(((unsigned char*)data)[1] << 8); + case 1: h2 ^= ((unsigned char*)data)[0]; + 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 void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(bnd->num_vs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT(bnd->num_vs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_fs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT(bnd->num_fs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + + _sg_clear(key->items, sizeof(key->items)); + key->items[0] = bnd->pip->slot.id; + const int vs_imgs_offset = 1; + const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; + const int fs_imgs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; + SOKOL_ASSERT((fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); + for (int i = 0; i < bnd->num_vs_imgs; i++) { + SOKOL_ASSERT(bnd->vs_imgs[i]); + key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + SOKOL_ASSERT(bnd->vs_smps[i]); + key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + SOKOL_ASSERT(bnd->fs_imgs[i]); + key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + SOKOL_ASSERT(bnd->fs_smps[i]); + key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id; + } + 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_add(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch, 1); + return false; } - SOKOL_ASSERT(stg_offset >= stg_base_offset); - return (stg_offset - stg_base_offset); + return true; } -/* - The WGPU staging buffer implementation: +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); + _sg_stats_add(wgpu.bindings.num_create_bindgroup, 1); + _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 + WGPUBindGroupLayout bgl = bnd->pip->shader->wgpu.bind_group_layout; + SOKOL_ASSERT(bgl); + WGPUBindGroupEntry wgpu_entries[SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_clear(&wgpu_entries, sizeof(wgpu_entries)); + int bge_index = 0; + for (int i = 0; i < bnd->num_vs_imgs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->textureView = bnd->vs_imgs[i]->wgpu.view; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->sampler = bnd->vs_smps[i]->wgpu.smp; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->textureView = bnd->fs_imgs[i]->wgpu.view; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->sampler = bnd->fs_smps[i]->wgpu.smp; + } + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = bgl; + bg_desc.entryCount = (size_t)bge_index; + bg_desc.entries = &wgpu_entries[0]; + 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; + } - Very similar to the uniform buffer pool, there's a pool of big - per-frame staging buffers, each must be big enough to hold - all data uploaded to dynamic resources for one frame. + _sg_wgpu_init_bindgroups_cache_key(&bg->key, bnd); - Staging buffers are created on demand and reused, because the - 'frame pipeline depth' of WGPU isn't predictable. + bg->slot.state = SG_RESOURCESTATE_VALID; + return bg; +} - The difference to the uniform buffer system is that there isn't - a 1:1 relationship for source- and destination for the - data-copy operation. There's always one staging buffer as copy-source - per frame, but many copy-destinations (regular vertex/index buffers - or images). Instead of one big copy-operation at the end of the frame, - multiple copy-operations will be written throughout the frame. -*/ -_SOKOL_PRIVATE void _sg_wgpu_staging_init(const sg_desc* desc) { - SOKOL_ASSERT(desc && (desc->staging_buffer_size > 0)); - _sg.wgpu.staging.num_bytes = desc->staging_buffer_size; - // there's actually nothing more to do here +_SOKOL_PRIVATE void _sg_wgpu_discard_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_stats_add(wgpu.bindings.num_discard_bindgroup, 1); + 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_staging_discard(void) { - for (int i = 0; i < _sg.wgpu.staging.num; i++) { - if (_sg.wgpu.staging.buf[i]) { - wgpuBufferRelease(_sg.wgpu.staging.buf[i]); - _sg.wgpu.staging.buf[i] = 0; - _sg.wgpu.staging.ptr[i] = 0; +_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_staging_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { - if (!_sg.wgpu.valid) { - return; +_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); } - // FIXME: better handling for this - if (WGPUBufferMapAsyncStatus_Success != status) { - SOKOL_ASSERT("Mapping staging buffer failed!\n"); - SOKOL_ASSERT(false); + if (!_sg_ispow2(num)) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_POW2); } - SOKOL_ASSERT(data && (data_len == _sg.wgpu.staging.num_bytes)); - int index = (int)(intptr_t) user_data; - SOKOL_ASSERT(index < _sg.wgpu.staging.num); - SOKOL_ASSERT(0 == _sg.wgpu.staging.ptr[index]); - _sg.wgpu.staging.ptr[index] = (uint8_t*) data; + _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_staging_next_frame(bool first_frame) { - - // immediately request a new mapping for the last frame's current staging buffer - if (!first_frame) { - WGPUBuffer cur_buf = _sg.wgpu.staging.buf[_sg.wgpu.staging.cur]; - wgpuBufferMapWriteAsync(cur_buf, _sg_wgpu_staging_mapped_callback, (void*)(intptr_t)_sg.wgpu.staging.cur); +_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; +} - // rewind staging-buffer offset - _sg.wgpu.staging.offset = 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; +} - // check if mapped staging buffer is available, otherwise create one - for (int i = 0; i < _sg.wgpu.staging.num; i++) { - if (_sg.wgpu.staging.ptr[i]) { - _sg.wgpu.staging.cur = i; - return; - } +_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; +} + +_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(int index, const _sg_buffer_t* vb, int offset) { + SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEX_BUFFERS)); + 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; } +} - // no mapped buffer available, create one - SOKOL_ASSERT(_sg.wgpu.staging.num < _SG_WGPU_STAGING_PIPELINE_SIZE); - _sg.wgpu.staging.cur = _sg.wgpu.staging.num++; - const int cur = _sg.wgpu.staging.cur; +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_vb_update(int index, const _sg_buffer_t* vb, int offset) { + SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEX_BUFFERS)); + 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; + } +} - WGPUBufferDescriptor desc; - _sg_clear(&desc, sizeof(desc)); - desc.size = _sg.wgpu.staging.num_bytes; - desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; - WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); - _sg.wgpu.staging.buf[cur] = res.buffer; - _sg.wgpu.staging.ptr[cur] = (uint8_t*) res.data; - SOKOL_ASSERT(_sg.wgpu.staging.buf[cur]); - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - SOKOL_ASSERT(res.dataLength == _sg.wgpu.staging.num_bytes); +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_ib_dirty(const _sg_buffer_t* ib, int 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 uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint32_t dst_buf_offset, const void* data, uint32_t data_num_bytes) { - /* Copy a chunk of data into the staging buffer, and record a blit-operation into - the command encoder, bump the offset for the next data chunk, return 0 if there - was not enough room in the staging buffer, return the number of actually - copied bytes on success. +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_ib_update(const _sg_buffer_t* ib, int 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; + } +} - NOTE: that the number of staging bytes to be copied must be a multiple of 4. +_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_ASSERT(_sg.wgpu.staging_cmd_enc); - SOKOL_ASSERT((dst_buf_offset & 3) == 0); - SOKOL_ASSERT(data_num_bytes > 0); - uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); - if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { - _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER); - return false; +_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; } - const int cur = _sg.wgpu.staging.cur; - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - uint32_t stg_buf_offset = _sg.wgpu.staging.offset; - uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur] + stg_buf_offset; - memcpy(stg_ptr, data, data_num_bytes); - WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; - wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.staging_cmd_enc, stg_buf, stg_buf_offset, dst_buf, dst_buf_offset, copy_num_bytes); - _sg.wgpu.staging.offset = stg_buf_offset + copy_num_bytes; - return copy_num_bytes; -} - -_SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_data* data) { - // similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - uint32_t num_bytes = _sg_wgpu_image_data_buffer_size(img); - if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { - _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE); - return false; +} + +_SOKOL_PRIVATE void _sg_wgpu_set_image_sampler_bindgroup(_sg_wgpu_bindgroup_t* bg) { + if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { + _sg_wgpu_bindings_cache_bg_update(bg); + _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); + if (bg) { + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(bg->bindgroup); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, bg->bindgroup, 0, 0); + } else { + // a nullptr bindgroup means setting the empty bindgroup + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, _sg.wgpu.empty_bind_group, 0, 0); + } + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_bindgroup, 1); + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { + if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_fs_imgs + bnd->num_fs_smps) > 0) { + 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_add(wgpu.bindings.num_bindgroup_cache_collisions, 1); + _sg_wgpu_discard_bindgroup(bg); + _sg_wgpu_bindgroups_cache_set(key.hash, SG_INVALID_ID); + bg = 0; + } else { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_hits, 1); + } + } else { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_misses, 1); + } + 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_image_sampler_bindgroup(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_image_sampler_bindgroup(bg); + } + _sg_wgpu_discard_bindgroup(bg); + } else { + return false; + } + } + } else { + _sg_wgpu_set_image_sampler_bindgroup(0); + } + return true; +} +_SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_t* bnd) { + if (_sg_wgpu_bindings_cache_ib_dirty(bnd->ib, bnd->ib_offset)) { + _sg_wgpu_bindings_cache_ib_update(bnd->ib, bnd->ib_offset); + if (bnd->ib) { + const WGPUIndexFormat format = _sg_wgpu_indexformat(bnd->pip->cmn.index_type); + const uint64_t buf_size = (uint64_t)bnd->ib->cmn.size; + const uint64_t offset = (uint64_t)bnd->ib_offset; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, bnd->ib->wgpu.buf, format, offset, max_bytes); + _sg_stats_add(wgpu.bindings.num_set_index_buffer, 1); + } + // FIXME: else-path should actually set a null index buffer (this was just recently implemented in WebGPU) + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_index_buffer, 1); } - const int cur = _sg.wgpu.staging.cur; - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - uint32_t stg_offset = _sg.wgpu.staging.offset; - uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur]; - WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; - uint32_t bytes_copied = _sg_wgpu_copy_image_data(stg_buf, stg_ptr, stg_offset, img, data); - _SOKOL_UNUSED(bytes_copied); - SOKOL_ASSERT(bytes_copied == num_bytes); - _sg.wgpu.staging.offset = _sg_roundup(stg_offset + num_bytes, _SG_WGPU_STAGING_ALIGN); return true; } -_SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { - // called at end of frame before queue-submit - const int cur = _sg.wgpu.staging.cur; - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - _sg.wgpu.staging.ptr[cur] = 0; - wgpuBufferUnmap(_sg.wgpu.staging.buf[cur]); -} - -_SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc) { - SOKOL_ASSERT(img_desc); - // create a new WGPU sampler - // FIXME: anisotropic filtering not supported? - WGPUSamplerDescriptor smp_desc; - _sg_clear(&smp_desc, sizeof(smp_desc)); - smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); - smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); - smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); - smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); - smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); - smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); - smp_desc.lodMinClamp = img_desc->min_lod; - smp_desc.lodMaxClamp = img_desc->max_lod; - WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); - SOKOL_ASSERT(smp); - return smp; +_SOKOL_PRIVATE bool _sg_wgpu_apply_vertex_buffers(_sg_bindings_t* bnd) { + for (int slot = 0; slot < bnd->num_vbs; slot++) { + if (_sg_wgpu_bindings_cache_vb_dirty(slot, bnd->vbs[slot], bnd->vb_offsets[slot])) { + _sg_wgpu_bindings_cache_vb_update(slot, bnd->vbs[slot], bnd->vb_offsets[slot]); + const uint64_t buf_size = (uint64_t)bnd->vbs[slot]->cmn.size; + const uint64_t offset = (uint64_t)bnd->vb_offsets[slot]; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, (uint32_t)slot, bnd->vbs[slot]->wgpu.buf, offset, max_bytes); + _sg_stats_add(wgpu.bindings.num_set_vertex_buffer, 1); + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_vertex_buffer, 1); + } + } + // FIXME: remaining vb slots should actually set a null vertex buffer (this was just recently implemented in WebGPU) + return true; } -//--- WGPU backend API functions --- _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->context.wgpu.device); @@ -12828,7 +13922,6 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(desc->context.wgpu.resolve_view_cb || desc->context.wgpu.resolve_view_userdata_cb); SOKOL_ASSERT(desc->context.wgpu.depth_stencil_view_cb || desc->context.wgpu.depth_stencil_view_userdata_cb); SOKOL_ASSERT(desc->uniform_buffer_size > 0); - SOKOL_ASSERT(desc->staging_buffer_size > 0); _sg.backend = SG_BACKEND_WGPU; _sg.wgpu.valid = true; _sg.wgpu.dev = (WGPUDevice) desc->context.wgpu.device; @@ -12839,19 +13932,17 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { _sg.wgpu.depth_stencil_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.depth_stencil_view_cb; _sg.wgpu.depth_stencil_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.depth_stencil_view_userdata_cb; _sg.wgpu.user_data = desc->context.wgpu.user_data; - _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); + _sg.wgpu.queue = wgpuDeviceGetQueue(_sg.wgpu.dev); SOKOL_ASSERT(_sg.wgpu.queue); - // setup WebGPU features and limits _sg_wgpu_init_caps(); - - // setup the uniform and staging buffer pools - _sg_wgpu_ubpool_init(desc); - _sg_wgpu_ubpool_next_frame(true); - _sg_wgpu_staging_init(desc); - _sg_wgpu_staging_next_frame(true); + _sg_wgpu_uniform_buffer_init(desc); + _sg_wgpu_bindgroups_pool_init(desc); + _sg_wgpu_bindgroups_cache_init(desc); + _sg_wgpu_bindings_cache_clear(); // create an empty bind group for shader stages without bound images + // FIXME: once WebGPU supports setting null objects, this can be removed WGPUBindGroupLayoutDescriptor bgl_desc; _sg_clear(&bgl_desc, sizeof(bgl_desc)); WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); @@ -12863,35 +13954,28 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(_sg.wgpu.empty_bind_group); wgpuBindGroupLayoutRelease(empty_bgl); - // create initial per-frame command encoders + // create initial per-frame command encoder WGPUCommandEncoderDescriptor cmd_enc_desc; _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); - _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); } _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { SOKOL_ASSERT(_sg.wgpu.valid); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); _sg.wgpu.valid = false; - _sg_wgpu_ubpool_discard(); - _sg_wgpu_staging_discard(); - wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); - wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); - _sg.wgpu.render_cmd_enc = 0; - wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); - _sg.wgpu.staging_cmd_enc = 0; - if (_sg.wgpu.queue) { - wgpuQueueRelease(_sg.wgpu.queue); - _sg.wgpu.queue = 0; - } + _sg_wgpu_discard_all_bindgroups(); + _sg_wgpu_bindgroups_cache_discard(); + _sg_wgpu_bindgroups_pool_discard(); + _sg_wgpu_uniform_buffer_discard(); + wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); _sg.wgpu.empty_bind_group = 0; + 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_WARN(WGPU_RESET_STATE_CACHE_FIXME); + _sg_wgpu_bindings_cache_clear(); } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { @@ -12907,7 +13991,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { (void)ctx; - _SG_WARN(WGPU_ACTIVATE_CONTEXT_FIXME); + // FIXME } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { @@ -12917,19 +14001,30 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; wgpuBufferReference(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 = (SG_USAGE_IMMUTABLE == buf->cmn.usage); + WGPUBufferDescriptor wgpu_buf_desc; _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); - wgpu_buf_desc.size = buf->cmn.size; - if (SG_USAGE_IMMUTABLE == buf->cmn.usage) { - SOKOL_ASSERT(desc->data.ptr); - WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); - buf->wgpu.buf = res.buffer; - SOKOL_ASSERT(res.data && (res.dataLength == buf->cmn.size)); - memcpy(res.data, desc->data.ptr, buf->cmn.size); - wgpuBufferUnmap(res.buffer); - } else { - buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + wgpu_buf_desc.size = wgpu_buf_size; + wgpu_buf_desc.mappedAtCreation = map_at_creation; + wgpu_buf_desc.label = 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; @@ -12937,347 +14032,411 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const _SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); - WGPUBuffer wgpu_buf = buf->wgpu.buf; - if (0 != wgpu_buf) { - wgpuBufferRelease(wgpu_buf); - } -} - -_SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex_desc, const sg_image_desc* desc) { - wgpu_tex_desc->usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_CopyDst; - wgpu_tex_desc->dimension = _sg_wgpu_tex_dim(desc->type); - wgpu_tex_desc->size.width = desc->width; - wgpu_tex_desc->size.height = desc->height; - if (desc->type == SG_IMAGETYPE_3D) { - wgpu_tex_desc->size.depth = desc->num_slices; - wgpu_tex_desc->arrayLayerCount = 1; - } else if (desc->type == SG_IMAGETYPE_CUBE) { - wgpu_tex_desc->size.depth = 1; - wgpu_tex_desc->arrayLayerCount = 6; - } else { - wgpu_tex_desc->size.depth = 1; - wgpu_tex_desc->arrayLayerCount = desc->num_slices; + if (buf->wgpu.buf) { + wgpuBufferDestroy(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 }; + 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_src_ptr, 4); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, WGPUTexture wgpu_tex, const sg_image_data* data) { + WGPUTextureDataLayout wgpu_layout; + _sg_clear(&wgpu_layout, sizeof(wgpu_layout)); + WGPUImageCopyTexture wgpu_copy_tex; + _sg_clear(&wgpu_copy_tex, sizeof(wgpu_copy_tex)); + wgpu_copy_tex.texture = wgpu_tex; + wgpu_copy_tex.aspect = WGPUTextureAspect_All; + WGPUExtent3D wgpu_extent; + _sg_clear(&wgpu_extent, sizeof(wgpu_extent)); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + wgpu_copy_tex.mipLevel = (uint32_t)mip_index; + wgpu_copy_tex.origin.z = (uint32_t)face_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; + switch (img->cmn.type) { + case SG_IMAGETYPE_CUBE: + mip_slices = 1; + break; + case SG_IMAGETYPE_3D: + mip_slices = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + break; + default: + mip_slices = img->cmn.num_slices; + break; + } + 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.offset = 0; + 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->subimage[face_index][mip_index]; + wgpuQueueWriteTexture(_sg.wgpu.queue, &wgpu_copy_tex, mip_data->ptr, mip_data->size, &wgpu_layout, &wgpu_extent); + } } - wgpu_tex_desc->format = _sg_wgpu_textureformat(desc->pixel_format); - wgpu_tex_desc->mipLevelCount = desc->num_mipmaps; - wgpu_tex_desc->sampleCount = 1; } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - const bool injected = (0 != desc->wgpu_texture); - const bool is_msaa = desc->sample_count > 1; - WGPUTextureDescriptor wgpu_tex_desc; - _sg_clear(&wgpu_tex_desc, sizeof(wgpu_tex_desc)); - _sg_wgpu_init_texdesc_common(&wgpu_tex_desc, desc); - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - SOKOL_ASSERT(img->cmn.render_target); - SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); - SOKOL_ASSERT(img->cmn.num_mipmaps == 1); - SOKOL_ASSERT(!injected); - /* NOTE: a depth-stencil texture will never be MSAA-resolved, so there - won't be a separate MSAA- and resolve-texture - */ - wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; - wgpu_tex_desc.sampleCount = desc->sample_count; - img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); - SOKOL_ASSERT(img->wgpu.tex); + if (injected) { + img->wgpu.tex = (WGPUTexture)desc->wgpu_texture; + wgpuTextureReference(img->wgpu.tex); + img->wgpu.view = (WGPUTextureView)desc->wgpu_texture_view; + if (img->wgpu.view) { + wgpuTextureViewReference(img->wgpu.view); + } } else { - if (injected) { - img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; - wgpuTextureReference(img->wgpu.tex); + WGPUTextureDescriptor wgpu_tex_desc; + _sg_clear(&wgpu_tex_desc, sizeof(wgpu_tex_desc)); + wgpu_tex_desc.label = desc->label; + wgpu_tex_desc.usage = WGPUTextureUsage_TextureBinding|WGPUTextureUsage_CopyDst; + if (desc->render_target) { + wgpu_tex_desc.usage |= WGPUTextureUsage_RenderAttachment; + } + 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; + if (desc->type == SG_IMAGETYPE_CUBE) { + wgpu_tex_desc.size.depthOrArrayLayers = 6; } else { - /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* - the resolve texture need OutputAttachment usage - */ - if (img->cmn.render_target) { - wgpu_tex_desc.usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_OutputAttachment; - } - img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); - SOKOL_ASSERT(img->wgpu.tex); - - // copy content into texture via a throw-away staging buffer - if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { - WGPUBufferDescriptor wgpu_buf_desc; - _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); - wgpu_buf_desc.size = _sg_wgpu_image_data_buffer_size(img); - wgpu_buf_desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_CopyDst; - WGPUCreateBufferMappedResult map = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); - SOKOL_ASSERT(map.buffer && map.data); - uint32_t num_bytes = _sg_wgpu_copy_image_data(map.buffer, (uint8_t*)map.data, 0, img, &desc->data); - _SOKOL_UNUSED(num_bytes); - SOKOL_ASSERT(num_bytes == wgpu_buf_desc.size); - wgpuBufferUnmap(map.buffer); - wgpuBufferRelease(map.buffer); - } + wgpu_tex_desc.size.depthOrArrayLayers = (uint32_t) img->cmn.num_slices; } - - // create texture view object - WGPUTextureViewDescriptor wgpu_view_desc; - _sg_clear(&wgpu_view_desc, sizeof(wgpu_view_desc)); - wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); - img->wgpu.tex_view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_view_desc); - - /* if render target and MSAA, then a separate texture in MSAA format is needed - which will be resolved into the regular texture at the end of the - offscreen-render pass - */ - if (desc->render_target && is_msaa) { - wgpu_tex_desc.dimension = WGPUTextureDimension_2D; - wgpu_tex_desc.size.depth = 1; - wgpu_tex_desc.arrayLayerCount = 1; - wgpu_tex_desc.mipLevelCount = 1; - wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; - wgpu_tex_desc.sampleCount = desc->sample_count; - img->wgpu.msaa_tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); - SOKOL_ASSERT(img->wgpu.msaa_tex); + 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 ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_wgpu_copy_image_data(img, img->wgpu.tex, &desc->data); + } + WGPUTextureViewDescriptor wgpu_texview_desc; + _sg_clear(&wgpu_texview_desc, sizeof(wgpu_texview_desc)); + wgpu_texview_desc.label = desc->label; + wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type); + wgpu_texview_desc.mipLevelCount = (uint32_t)img->cmn.num_mipmaps; + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + wgpu_texview_desc.arrayLayerCount = 6; + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + wgpu_texview_desc.arrayLayerCount = (uint32_t)img->cmn.num_slices; + } else { + wgpu_texview_desc.arrayLayerCount = 1; + } + 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; + } + img->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc); + if (0 == img->wgpu.view) { + _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; } - - // create sampler via shared-sampler-cache - img->wgpu.sampler = _sg_wgpu_create_sampler(desc); - SOKOL_ASSERT(img->wgpu.sampler); } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); + if (img->wgpu.view) { + wgpuTextureViewRelease(img->wgpu.view); + img->wgpu.view = 0; + } if (img->wgpu.tex) { + wgpuTextureDestroy(img->wgpu.tex); wgpuTextureRelease(img->wgpu.tex); img->wgpu.tex = 0; } - if (img->wgpu.tex_view) { - wgpuTextureViewRelease(img->wgpu.tex_view); - img->wgpu.tex_view = 0; - } - if (img->wgpu.msaa_tex) { - wgpuTextureRelease(img->wgpu.msaa_tex); - img->wgpu.msaa_tex = 0; - } - // NOTE: do *not* destroy the sampler from the shared-sampler-cache - img->wgpu.sampler = 0; } -/* - How BindGroups work in WebGPU: - - - up to 4 bind groups can be bound simultaneously - - up to 16 bindings per bind group - - 'binding' slots are local per bind group - - in the shader: - layout(set=0, binding=1) corresponds to bind group 0, binding 1 - - Now how to map this to sokol-gfx's bind model: - - Reduce SG_MAX_SHADERSTAGE_IMAGES to 8, then: - - 1 bind group for all 8 uniform buffers - 1 bind group for vertex shader textures + samplers - 1 bind group for fragment shader textures + samples - - Alternatively: +_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; + wgpuSamplerReference(smp->wgpu.smp); + } else { + WGPUSamplerDescriptor wgpu_desc; + _sg_clear(&wgpu_desc, sizeof(wgpu_desc)); + wgpu_desc.label = 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; +} - 1 bind group for 8 uniform buffer slots - 1 bind group for 8 vs images + 8 vs samplers - 1 bind group for 12 fs images - 1 bind group for 12 fs samplers +_SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->wgpu.smp) { + wgpuSamplerRelease(smp->wgpu.smp); + smp->wgpu.smp = 0; + } +} - I guess this means that we need to create BindGroups on the - fly during sg_apply_bindings() :/ -*/ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - SOKOL_ASSERT(desc->vs.bytecode.ptr && desc->fs.bytecode.ptr); + SOKOL_ASSERT(desc->vs.source && desc->fs.source); - bool success = true; + #define _sg_wgpu_create_shader_max_bgl_entries (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS)) + WGPUBindGroupLayoutEntry wgpu_bgl_entries[_sg_wgpu_create_shader_max_bgl_entries]; + _sg_clear(wgpu_bgl_entries, sizeof(wgpu_bgl_entries)); + int bgl_index = 0; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; - SOKOL_ASSERT((stage_desc->bytecode.size & 3) == 0); _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; - _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); + + WGPUShaderModuleWGSLDescriptor wgpu_shdmod_wgsl_desc; + _sg_clear(&wgpu_shdmod_wgsl_desc, sizeof(wgpu_shdmod_wgsl_desc)); + wgpu_shdmod_wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + wgpu_shdmod_wgsl_desc.code = stage_desc->source; + WGPUShaderModuleDescriptor wgpu_shdmod_desc; _sg_clear(&wgpu_shdmod_desc, sizeof(wgpu_shdmod_desc)); - wgpu_shdmod_desc.codeSize = stage_desc->bytecode.size >> 2; - wgpu_shdmod_desc.code = (const uint32_t*) stage_desc->bytecode.ptr; + wgpu_shdmod_desc.nextInChain = &wgpu_shdmod_wgsl_desc.chain; + wgpu_shdmod_desc.label = desc->label; + wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); if (0 == wgpu_stage->module) { - success = false; + _SG_ERROR(WGPU_CREATE_SHADER_MODULE_FAILED); + return SG_RESOURCESTATE_FAILED; } - // create image/sampler bind group for the shader stage - WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; - int num_imgs = cmn_stage->num_images; - if (num_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { - num_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + const int num_images = cmn_stage->num_images; + if (num_images > (int)_sg.wgpu.limits.limits.maxSampledTexturesPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_IMAGES); + return SG_RESOURCESTATE_FAILED; } - WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; - _sg_clear(bglb_desc, sizeof(bglb_desc)); - for (int img_index = 0; img_index < num_imgs; img_index++) { - // texture- and sampler-bindings - WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; - WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; - - tex_desc->binding = img_index; - tex_desc->visibility = vis; - tex_desc->type = WGPUBindingType_SampledTexture; - tex_desc->textureDimension = _sg_wgpu_tex_viewdim(cmn_stage->images[img_index].image_type); - tex_desc->textureComponentType = _sg_wgpu_tex_comptype(cmn_stage->images[img_index].sampler_type); - - smp_desc->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - smp_desc->visibility = vis; - smp_desc->type = WGPUBindingType_Sampler; + const int num_samplers = cmn_stage->num_samplers; + if (num_samplers > (int)_sg.wgpu.limits.limits.maxSamplersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_SAMPLERS); + return SG_RESOURCESTATE_FAILED; + } + for (int img_index = 0; img_index < num_images; img_index++) { + SOKOL_ASSERT(bgl_index < _sg_wgpu_create_shader_max_bgl_entries); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + wgpu_bgl_entry->binding = _sg_wgpu_image_binding((sg_shader_stage)stage_index, img_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(img_desc->image_type); + wgpu_bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(img_desc->sample_type); + wgpu_bgl_entry->texture.multisampled = img_desc->multisampled; + } + for (int smp_index = 0; smp_index < num_samplers; smp_index++) { + SOKOL_ASSERT(bgl_index < _sg_wgpu_create_shader_max_bgl_entries); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + wgpu_bgl_entry->binding =_sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(smp_desc->sampler_type); } - WGPUBindGroupLayoutDescriptor img_bgl_desc; - _sg_clear(&img_bgl_desc, sizeof(img_bgl_desc)); - img_bgl_desc.bindingCount = num_imgs * 2; - img_bgl_desc.bindings = &bglb_desc[0]; - wgpu_stage->bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &img_bgl_desc); - SOKOL_ASSERT(wgpu_stage->bind_group_layout); } - return success ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; + + WGPUBindGroupLayoutDescriptor wgpu_bgl_desc; + _sg_clear(&wgpu_bgl_desc, sizeof(wgpu_bgl_desc)); + wgpu_bgl_desc.entryCount = (size_t)bgl_index; + wgpu_bgl_desc.entries = &wgpu_bgl_entries[0]; + shd->wgpu.bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &wgpu_bgl_desc); + if (shd->wgpu.bind_group_layout == 0) { + _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + #undef _sg_wgpu_create_shader_max_bgl_entries + return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_wgpu_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); + if (shd->wgpu.bind_group_layout) { + wgpuBindGroupLayoutRelease(shd->wgpu.bind_group_layout); + shd->wgpu.bind_group_layout = 0; + } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; if (wgpu_stage->module) { wgpuShaderModuleRelease(wgpu_stage->module); wgpu_stage->module = 0; } - if (wgpu_stage->bind_group_layout) { - wgpuBindGroupLayoutRelease(wgpu_stage->bind_group_layout); - wgpu_stage->bind_group_layout = 0; - } } } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); SOKOL_ASSERT(desc->shader.id == shd->slot.id); - SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout); - SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout); + SOKOL_ASSERT(shd->wgpu.bind_group_layout); pip->shader = shd; - pip->wgpu.stencil_ref = (uint32_t) desc->stencil.ref; - WGPUBindGroupLayout pip_bgl[3] = { - _sg.wgpu.ub.bindgroup_layout, - shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout, - shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout - }; - WGPUPipelineLayoutDescriptor pl_desc; - _sg_clear(&pl_desc, sizeof(pl_desc)); - pl_desc.bindGroupLayoutCount = 3; - pl_desc.bindGroupLayouts = &pip_bgl[0]; - WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); - - WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_VERTEX_BUFFERS]; - _sg_clear(&vb_desc, sizeof(vb_desc)); - WGPUVertexAttributeDescriptor va_desc[SG_MAX_VERTEX_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; - _sg_clear(&va_desc, sizeof(va_desc)); - int vb_idx = 0; - for (; vb_idx < SG_MAX_VERTEX_BUFFERS; vb_idx++) { - const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; - if (0 == src_vb_desc->stride) { + 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 and sampler resources + WGPUBindGroupLayout wgpu_bgl[_SG_WGPU_NUM_BINDGROUPS]; + _sg_clear(&wgpu_bgl, sizeof(wgpu_bgl)); + wgpu_bgl[_SG_WGPU_UNIFORM_BINDGROUP_INDEX] = _sg.wgpu.uniform.bind.group_layout; + wgpu_bgl[_SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX] = shd->wgpu.bind_group_layout; + WGPUPipelineLayoutDescriptor wgpu_pl_desc; + _sg_clear(&wgpu_pl_desc, sizeof(wgpu_pl_desc)); + wgpu_pl_desc.bindGroupLayoutCount = _SG_WGPU_NUM_BINDGROUPS; + 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); + + WGPUVertexBufferLayout wgpu_vb_layouts[SG_MAX_VERTEX_BUFFERS]; + _sg_clear(wgpu_vb_layouts, sizeof(wgpu_vb_layouts)); + WGPUVertexAttribute wgpu_vtx_attrs[SG_MAX_VERTEX_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + _sg_clear(wgpu_vtx_attrs, sizeof(wgpu_vtx_attrs)); + int wgpu_vb_num = 0; + for (int vb_idx = 0; vb_idx < SG_MAX_VERTEX_BUFFERS; vb_idx++, wgpu_vb_num++) { + const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vb_idx]; + if (0 == vbl_state->stride) { break; } - vb_desc[vb_idx].arrayStride = src_vb_desc->stride; - vb_desc[vb_idx].stepMode = _sg_wgpu_stepmode(src_vb_desc->step_func); - /* NOTE: WebGPU has no support for vertex step rate (because that's - not supported by Core Vulkan - */ - int va_idx = 0; - for (int va_loc = 0; va_loc < SG_MAX_VERTEX_ATTRIBUTES; va_loc++) { - const sg_vertex_attr_desc* src_va_desc = &desc->layout.attrs[va_loc]; - if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { - break; - } - pip->cmn.vertex_buffer_layout_active[src_va_desc->buffer_index] = true; - if (vb_idx == src_va_desc->buffer_index) { - va_desc[vb_idx][va_idx].format = _sg_wgpu_vertexformat(src_va_desc->format); - va_desc[vb_idx][va_idx].offset = src_va_desc->offset; - va_desc[vb_idx][va_idx].shaderLocation = va_loc; - va_idx++; + 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_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[vb_idx] = true; + 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; + } + + WGPURenderPipelineDescriptor wgpu_pip_desc; + _sg_clear(&wgpu_pip_desc, sizeof(wgpu_pip_desc)); + WGPUDepthStencilState wgpu_ds_state; + _sg_clear(&wgpu_ds_state, sizeof(wgpu_ds_state)); + WGPUFragmentState wgpu_frag_state; + _sg_clear(&wgpu_frag_state, sizeof(wgpu_frag_state)); + WGPUColorTargetState wgpu_ctgt_state[SG_MAX_COLOR_ATTACHMENTS]; + _sg_clear(&wgpu_ctgt_state, sizeof(wgpu_ctgt_state)); + WGPUBlendState wgpu_blend_state[SG_MAX_COLOR_ATTACHMENTS]; + _sg_clear(&wgpu_blend_state, sizeof(wgpu_blend_state)); + wgpu_pip_desc.label = desc->label; + wgpu_pip_desc.layout = wgpu_pip_layout; + wgpu_pip_desc.vertex.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + wgpu_pip_desc.vertex.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].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 = 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.stage[SG_SHADERSTAGE_FS].module; + wgpu_frag_state.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_FS].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); } } - vb_desc[vb_idx].attributeCount = va_idx; - vb_desc[vb_idx].attributes = &va_desc[vb_idx][0]; + wgpu_pip_desc.fragment = &wgpu_frag_state; } - WGPUVertexStateDescriptor vx_state_desc; - _sg_clear(&vx_state_desc, sizeof(vx_state_desc)); - vx_state_desc.indexFormat = _sg_wgpu_indexformat(desc->index_type); - vx_state_desc.vertexBufferCount = vb_idx; - vx_state_desc.vertexBuffers = vb_desc; - - WGPURasterizationStateDescriptor rs_desc; - _sg_clear(&rs_desc, sizeof(rs_desc)); - rs_desc.frontFace = _sg_wgpu_frontface(desc->face_winding); - rs_desc.cullMode = _sg_wgpu_cullmode(desc->cull_mode); - rs_desc.depthBias = (int32_t) desc->depth.bias; - rs_desc.depthBiasClamp = desc->depth.bias_clamp; - rs_desc.depthBiasSlopeScale = desc->depth.bias_slope_scale; - - WGPUDepthStencilStateDescriptor ds_desc; - _sg_clear(&ds_desc, sizeof(ds_desc)); - ds_desc.format = _sg_wgpu_textureformat(desc->depth.pixel_format); - ds_desc.depthWriteEnabled = desc->depth.write_enabled; - ds_desc.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); - ds_desc.stencilReadMask = desc->stencil.read_mask; - ds_desc.stencilWriteMask = desc->stencil.write_mask; - ds_desc.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); - ds_desc.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); - ds_desc.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); - ds_desc.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); - ds_desc.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); - ds_desc.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); - ds_desc.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); - ds_desc.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); - - WGPUProgrammableStageDescriptor fs_desc; - _sg_clear(&fs_desc, sizeof(fs_desc)); - fs_desc.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; - fs_desc.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; - - WGPUColorStateDescriptor cs_desc[SG_MAX_COLOR_ATTACHMENTS]; - _sg_clear(cs_desc, sizeof(cs_desc)); - for (uint32_t i = 0; i < desc->color_count; i++) { - SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); - cs_desc[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); - cs_desc[i].colorBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); - cs_desc[i].colorBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); - cs_desc[i].colorBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); - cs_desc[i].alphaBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); - cs_desc[i].alphaBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); - cs_desc[i].alphaBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); - cs_desc[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); - } - - WGPURenderPipelineDescriptor pip_desc; - _sg_clear(&pip_desc, sizeof(pip_desc)); - pip_desc.layout = pip_layout; - pip_desc.vertexStage.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; - pip_desc.vertexStage.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; - pip_desc.fragmentStage = &fs_desc; - pip_desc.vertexState = &vx_state_desc; - pip_desc.primitiveTopology = _sg_wgpu_topology(desc->primitive_type); - pip_desc.rasterizationState = &rs_desc; - pip_desc.sampleCount = desc->sample_count; - if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { - pip_desc.depthStencilState = &ds_desc; + pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &wgpu_pip_desc); + wgpuPipelineLayoutRelease(wgpu_pip_layout); + if (0 == pip->wgpu.pip) { + _SG_ERROR(WGPU_CREATE_RENDER_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; } - pip_desc.colorStateCount = desc->color_count; - pip_desc.colorStates = cs_desc; - pip_desc.sampleMask = 0xFFFFFFFF; // FIXME: ??? - pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); - SOKOL_ASSERT(0 != pip->wgpu.pip); - wgpuPipelineLayoutRelease(pip_layout); - return SG_RESOURCESTATE_VALID; } @@ -13285,7 +14444,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); if (pip == _sg.wgpu.cur_pipeline) { _sg.wgpu.cur_pipeline = 0; - _Sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; } if (pip->wgpu.pip) { wgpuRenderPipelineRelease(pip->wgpu.pip); @@ -13293,79 +14452,92 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { } } -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); - - // copy image pointers and create render-texture views - const sg_pass_attachment_desc* att_desc; - for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); - _sg_image_t* img = att_images[i]; - SOKOL_ASSERT(img && (img->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format)); - pass->wgpu.color_atts[i].image = img; - // create a render-texture-view to render into the right sub-surface - const bool is_msaa = img->cmn.sample_count > 1; - WGPUTextureViewDescriptor view_desc; - _sg_clear(&view_desc, sizeof(view_desc)); - view_desc.baseMipLevel = is_msaa ? 0 : att_desc->mip_level; - view_desc.mipLevelCount = 1; - view_desc.baseArrayLayer = is_msaa ? 0 : att_desc->slice; - view_desc.arrayLayerCount = 1; - WGPUTexture wgpu_tex = is_msaa ? img->wgpu.msaa_tex : img->wgpu.tex; - SOKOL_ASSERT(wgpu_tex); - pass->wgpu.color_atts[i].render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); - SOKOL_ASSERT(pass->wgpu.color_atts[i].render_tex_view); - // ... and if needed a separate resolve texture view - if (is_msaa) { - view_desc.baseMipLevel = att_desc->mip_level; - view_desc.baseArrayLayer = att_desc->slice; - WGPUTexture wgpu_tex = img->wgpu.tex; - pass->wgpu.color_atts[i].resolve_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); - SOKOL_ASSERT(pass->wgpu.color_atts[i].resolve_tex_view); + SOKOL_ASSERT(color_images && resolve_images); + + // copy image pointers and create renderable wgpu texture views + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + SOKOL_ASSERT(color_images[i]->wgpu.tex); + pass->wgpu.color_atts[i].image = color_images[i]; + + WGPUTextureViewDescriptor wgpu_color_view_desc; + _sg_clear(&wgpu_color_view_desc, sizeof(wgpu_color_view_desc)); + wgpu_color_view_desc.baseMipLevel = (uint32_t) color_desc->mip_level; + wgpu_color_view_desc.mipLevelCount = 1; + wgpu_color_view_desc.baseArrayLayer = (uint32_t) color_desc->slice; + wgpu_color_view_desc.arrayLayerCount = 1; + pass->wgpu.color_atts[i].view = wgpuTextureCreateView(color_images[i]->wgpu.tex, &wgpu_color_view_desc); + if (0 == pass->wgpu.color_atts[i].view) { + _SG_ERROR(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->wgpu.resolve_atts[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + SOKOL_ASSERT(resolve_images[i]->wgpu.tex); + pass->wgpu.resolve_atts[i].image = resolve_images[i]; + + WGPUTextureViewDescriptor wgpu_resolve_view_desc; + _sg_clear(&wgpu_resolve_view_desc, sizeof(wgpu_resolve_view_desc)); + wgpu_resolve_view_desc.baseMipLevel = (uint32_t) resolve_desc->mip_level; + wgpu_resolve_view_desc.mipLevelCount = 1; + wgpu_resolve_view_desc.baseArrayLayer = (uint32_t) resolve_desc->slice; + wgpu_resolve_view_desc.arrayLayerCount = 1; + pass->wgpu.resolve_atts[i].view = wgpuTextureCreateView(resolve_images[i]->wgpu.tex, &wgpu_resolve_view_desc); + if (0 == pass->wgpu.resolve_atts[i].view) { + _SG_ERROR(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; } } } SOKOL_ASSERT(0 == pass->wgpu.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - _sg_image_t* ds_img = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + SOKOL_ASSERT(ds_img->wgpu.tex); pass->wgpu.ds_att.image = ds_img; - // create a render-texture view - SOKOL_ASSERT(0 == att_desc->mip_level); - SOKOL_ASSERT(0 == att_desc->slice); - WGPUTextureViewDescriptor view_desc; - _sg_clear(&view_desc, sizeof(view_desc)); - WGPUTexture wgpu_tex = ds_img->wgpu.tex; - SOKOL_ASSERT(wgpu_tex); - pass->wgpu.ds_att.render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); - SOKOL_ASSERT(pass->wgpu.ds_att.render_tex_view); + + WGPUTextureViewDescriptor wgpu_ds_view_desc; + _sg_clear(&wgpu_ds_view_desc, sizeof(wgpu_ds_view_desc)); + wgpu_ds_view_desc.baseMipLevel = (uint32_t) ds_desc->mip_level; + wgpu_ds_view_desc.mipLevelCount = 1; + wgpu_ds_view_desc.baseArrayLayer = (uint32_t) ds_desc->slice; + wgpu_ds_view_desc.arrayLayerCount = 1; + pass->wgpu.ds_att.view = wgpuTextureCreateView(ds_img->wgpu.tex, &wgpu_ds_view_desc); + if (0 == pass->wgpu.ds_att.view) { + _SG_ERROR(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_wgpu_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); - for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { - if (pass->wgpu.color_atts[i].render_tex_view) { - wgpuTextureViewRelease(pass->wgpu.color_atts[i].render_tex_view); - pass->wgpu.color_atts[i].render_tex_view = 0; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + if (pass->wgpu.color_atts[i].view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].view); + pass->wgpu.color_atts[i].view = 0; } - if (pass->wgpu.color_atts[i].resolve_tex_view) { - wgpuTextureViewRelease(pass->wgpu.color_atts[i].resolve_tex_view); - pass->wgpu.color_atts[i].resolve_tex_view = 0; + if (pass->wgpu.resolve_atts[i].view) { + wgpuTextureViewRelease(pass->wgpu.resolve_atts[i].view); + pass->wgpu.resolve_atts[i].view = 0; } } - if (pass->wgpu.ds_att.render_tex_view) { - wgpuTextureViewRelease(pass->wgpu.ds_att.render_tex_view); - pass->wgpu.ds_att.render_tex_view = 0; + if (pass->wgpu.ds_att.view) { + wgpuTextureViewRelease(pass->wgpu.ds_att.view); + pass->wgpu.ds_att.view = 0; } } @@ -13375,16 +14547,50 @@ _SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_color_image(const _sg_pass_t* pass, in return pass->wgpu.color_atts[index].image; } +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_resolve_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + // NOTE: may return null + return pass->wgpu.resolve_atts[index].image; +} + _SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { // NOTE: may return null SOKOL_ASSERT(pass); return pass->wgpu.ds_att.image; } +_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->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_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); SOKOL_ASSERT(_sg.wgpu.dev); SOKOL_ASSERT(_sg.wgpu.render_view_cb || _sg.wgpu.render_view_userdata_cb); SOKOL_ASSERT(_sg.wgpu.resolve_view_cb || _sg.wgpu.resolve_view_userdata_cb); @@ -13395,160 +14601,101 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* _sg.wgpu.cur_pipeline = 0; _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + WGPURenderPassDescriptor wgpu_pass_desc; + WGPURenderPassColorAttachment wgpu_color_att[SG_MAX_COLOR_ATTACHMENTS]; + WGPURenderPassDepthStencilAttachment wgpu_ds_att; + _sg_clear(&wgpu_pass_desc, sizeof(wgpu_pass_desc)); + _sg_clear(&wgpu_color_att, sizeof(wgpu_color_att)); + _sg_clear(&wgpu_ds_att, sizeof(wgpu_ds_att)); if (pass) { - WGPURenderPassDescriptor wgpu_pass_desc; - _sg_clear(&wgpu_pass_desc, sizeof(wgpu_pass_desc)); - WGPURenderPassColorAttachmentDescriptor wgpu_color_att_desc[SG_MAX_COLOR_ATTACHMENTS]; - _sg_clear(&wgpu_color_att_desc, sizeof(wgpu_color_att_desc)); SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); - for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { - const _sg_wgpu_attachment_t* wgpu_att = &pass->wgpu.color_atts[i]; - wgpu_color_att_desc[i].loadOp = _sg_wgpu_load_op(action->colors[i].action); - wgpu_color_att_desc[i].storeOp = WGPUStoreOp_Store; - wgpu_color_att_desc[i].clearColor.r = action->colors[i].value.r; - wgpu_color_att_desc[i].clearColor.g = action->colors[i].value.g; - wgpu_color_att_desc[i].clearColor.b = action->colors[i].value.b; - wgpu_color_att_desc[i].clearColor.a = action->colors[i].value.a; - wgpu_color_att_desc[i].attachment = wgpu_att->render_tex_view; - if (wgpu_att->image->cmn.sample_count > 1) { - wgpu_color_att_desc[i].resolveTarget = wgpu_att->resolve_tex_view; - } + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], pass->wgpu.color_atts[i].view, pass->wgpu.resolve_atts[i].view); } - wgpu_pass_desc.colorAttachmentCount = pass->cmn.num_color_atts; - wgpu_pass_desc.colorAttachments = &wgpu_color_att_desc[0]; + wgpu_pass_desc.colorAttachmentCount = (size_t)pass->cmn.num_color_atts; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; if (pass->wgpu.ds_att.image) { - WGPURenderPassDepthStencilAttachmentDescriptor wgpu_ds_att_desc; - _sg_clear(&wgpu_ds_att_desc, sizeof(wgpu_ds_att_desc)); - wgpu_ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); - wgpu_ds_att_desc.clearDepth = action->depth.value; - wgpu_ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); - wgpu_ds_att_desc.clearStencil = action->stencil.value; - wgpu_ds_att_desc.attachment = pass->wgpu.ds_att.render_tex_view; - wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att_desc; - _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &wgpu_pass_desc); + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, pass->wgpu.ds_att.image->cmn.pixel_format, pass->wgpu.ds_att.view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; } } else { - // default render pass - WGPUTextureView wgpu_render_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); + WGPUTextureView wgpu_color_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb ? _sg.wgpu.resolve_view_cb() : _sg.wgpu.resolve_view_userdata_cb(_sg.wgpu.user_data); WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); - - WGPURenderPassDescriptor pass_desc; - _sg_clear(&pass_desc, sizeof(pass_desc)); - WGPURenderPassColorAttachmentDescriptor color_att_desc; - _sg_clear(&color_att_desc, sizeof(color_att_desc)); - color_att_desc.loadOp = _sg_wgpu_load_op(action->colors[0].action); - color_att_desc.clearColor.r = action->colors[0].value.r; - color_att_desc.clearColor.g = action->colors[0].value.g; - color_att_desc.clearColor.b = action->colors[0].value.b; - color_att_desc.clearColor.a = action->colors[0].value.a; - color_att_desc.attachment = wgpu_render_view; - color_att_desc.resolveTarget = wgpu_resolve_view; // null if no MSAA rendering - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_att_desc; - WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; - _sg_clear(&ds_att_desc, sizeof(ds_att_desc)); - ds_att_desc.attachment = wgpu_depth_stencil_view; - SOKOL_ASSERT(0 != ds_att_desc.attachment); - ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); - ds_att_desc.clearDepth = action->depth.value; - ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); - ds_att_desc.clearStencil = action->stencil.value; - pass_desc.depthStencilAttachment = &ds_att_desc; - _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &pass_desc); + _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) { + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, _sg.desc.context.depth_format, wgpu_depth_stencil_view); + } + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; } + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.cmd_enc, &wgpu_pass_desc); SOKOL_ASSERT(_sg.wgpu.pass_enc); + // clear bindings cache and apply an empty image-sampler bindgroup + _sg_wgpu_bindings_cache_clear(); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, _sg.wgpu.empty_bind_group, 0, 0); + _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); + // initial uniform buffer binding (required even if no uniforms are set in the frame) - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, - 0, // groupIndex 0 is reserved for uniform buffers - _sg.wgpu.ub.bindgroup, - SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, - &_sg.wgpu.ub.bind_offsets[0][0]); + _sg_wgpu_uniform_buffer_on_begin_pass(); } _SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); _sg.wgpu.in_pass = false; - wgpuRenderPassEncoderEndPass(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderEnd(_sg.wgpu.pass_enc); wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); _sg.wgpu.pass_enc = 0; } _SOKOL_PRIVATE void _sg_wgpu_commit(void) { SOKOL_ASSERT(!_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.queue); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - - // finish and submit this frame's work - _sg_wgpu_ubpool_flush(); - _sg_wgpu_staging_unmap(); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); - WGPUCommandBuffer cmd_bufs[2]; + _sg_wgpu_uniform_buffer_on_commit(); WGPUCommandBufferDescriptor cmd_buf_desc; _sg_clear(&cmd_buf_desc, sizeof(cmd_buf_desc)); - cmd_bufs[0] = wgpuCommandEncoderFinish(_sg.wgpu.staging_cmd_enc, &cmd_buf_desc); - SOKOL_ASSERT(cmd_bufs[0]); - wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); - _sg.wgpu.staging_cmd_enc = 0; + 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; - cmd_bufs[1] = wgpuCommandEncoderFinish(_sg.wgpu.render_cmd_enc, &cmd_buf_desc); - SOKOL_ASSERT(cmd_bufs[1]); - wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); - _sg.wgpu.render_cmd_enc = 0; + wgpuQueueSubmit(_sg.wgpu.queue, 1, &wgpu_cmd_buf); + wgpuCommandBufferRelease(wgpu_cmd_buf); - wgpuQueueSubmit(_sg.wgpu.queue, 2, &cmd_bufs[0]); - - wgpuCommandBufferRelease(cmd_bufs[0]); - wgpuCommandBufferRelease(cmd_bufs[1]); - - // create a new render- and staging-command-encoders for next frame + // create a new render-command-encoder for next frame WGPUCommandEncoderDescriptor cmd_enc_desc; _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); - _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - - // grab new staging buffers for uniform- and vertex/image-updates - _sg_wgpu_ubpool_next_frame(false); - _sg_wgpu_staging_next_frame(false); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); } _SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - float xf = (float) x; - float yf = (float) (origin_top_left ? y : (_sg.wgpu.cur_height - (y + h))); - float wf = (float) w; - float hf = (float) h; + // FIXME FIXME FIXME: CLIPPING THE VIEWPORT HERE IS WRONG!!! + // (but currently required because WebGPU insists that the viewport rectangle must be + // fully contained inside the framebuffer, but this doesn't make any sense, and also + // isn't required by the backend APIs) + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.wgpu.cur_width, _sg.wgpu.cur_height); + float xf = (float) clip.x; + float yf = (float) (origin_top_left ? clip.y : (_sg.wgpu.cur_height - (clip.y + clip.h))); + float wf = (float) clip.w; + float hf = (float) clip.h; wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_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.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - - // clip against framebuffer rect - x = _sg_min(_sg_max(0, x), _sg.wgpu.cur_width-1); - y = _sg_min(_sg_max(0, y), _sg.wgpu.cur_height-1); - if ((x + w) > _sg.wgpu.cur_width) { - w = _sg.wgpu.cur_width - x; - } - if ((y + h) > _sg.wgpu.cur_height) { - h = _sg.wgpu.cur_height - y; - } - w = _sg_max(w, 1); - h = _sg_max(h, 1); - - uint32_t sx = (uint32_t) x; - uint32_t sy = origin_top_left ? y : (_sg.wgpu.cur_height - (y + h)); - uint32_t sw = w; - uint32_t sh = h; + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.wgpu.cur_width, _sg.wgpu.cur_height); + uint32_t sx = (uint32_t) clip.x; + uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.wgpu.cur_height - (clip.y + clip.h))); + uint32_t sw = (uint32_t) clip.w; + uint32_t sh = (uint32_t) clip.h; wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); } @@ -13557,138 +14704,77 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip->wgpu.pip); SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - _sg.wgpu.draw_indexed = (pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.wgpu.use_indexed_draw = (pip->cmn.index_type != SG_INDEXTYPE_NONE); _sg.wgpu.cur_pipeline = pip; _sg.wgpu.cur_pipeline_id.id = pip->slot.id; wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); - wgpuRenderPassEncoderSetBlendColor(_sg.wgpu.pass_enc, (WGPUColor*)&pip->cmn.blend_color); - wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->wgpu.stencil_ref); + wgpuRenderPassEncoderSetBlendConstant(_sg.wgpu.pass_enc, &pip->wgpu.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->cmn.stencil.ref); } -_SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayout bgl, _sg_image_t** imgs, int num_imgs) { - SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(num_imgs <= _SG_WGPU_MAX_SHADERSTAGE_IMAGES); - WGPUBindGroupBinding img_bgb[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; - _sg_clear(&img_bgb, sizeof(img_bgb)); - for (int img_index = 0; img_index < num_imgs; img_index++) { - WGPUBindGroupBinding* tex_bdg = &img_bgb[img_index*2 + 0]; - WGPUBindGroupBinding* smp_bdg = &img_bgb[img_index*2 + 1]; - tex_bdg->binding = img_index; - tex_bdg->textureView = imgs[img_index]->wgpu.tex_view; - smp_bdg->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - smp_bdg->sampler = imgs[img_index]->wgpu.sampler; - } - WGPUBindGroupDescriptor bg_desc; - _sg_clear(&bg_desc, sizeof(bg_desc)); - bg_desc.layout = bgl; - bg_desc.bindingCount = 2 * num_imgs; - bg_desc.bindings = &img_bgb[0]; - WGPUBindGroup bg = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); - SOKOL_ASSERT(bg); - return bg; -} - -_SOKOL_PRIVATE void _sg_wgpu_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_t* bnd) { SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - - // index buffer - if (ib) { - wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); - } - - // vertex buffers - for (uint32_t slot = 0; slot < (uint32_t)num_vbs; slot++) { - wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, slot, vbs[slot]->wgpu.buf, (uint64_t)vb_offsets[slot]); - } - - // need to create throw-away bind groups for images - if (num_vs_imgs > 0) { - if (num_vs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { - num_vs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - } - WGPUBindGroupLayout vs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout; - SOKOL_ASSERT(vs_bgl); - WGPUBindGroup vs_img_bg = _sg_wgpu_create_images_bindgroup(vs_bgl, vs_imgs, num_vs_imgs); - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, vs_img_bg, 0, 0); - wgpuBindGroupRelease(vs_img_bg); - } else { - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); - } - if (num_fs_imgs > 0) { - if (num_fs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { - num_fs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - } - WGPUBindGroupLayout fs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout; - SOKOL_ASSERT(fs_bgl); - WGPUBindGroup fs_img_bg = _sg_wgpu_create_images_bindgroup(fs_bgl, fs_imgs, num_fs_imgs); - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, fs_img_bg, 0, 0); - wgpuBindGroupRelease(fs_img_bg); - } else { - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); - } + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); + bool retval = true; + retval &= _sg_wgpu_apply_index_buffer(bnd); + retval &= _sg_wgpu_apply_vertex_buffers(bnd); + retval &= _sg_wgpu_apply_bindgroup(bnd); + return retval; } _SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + const uint32_t alignment = _sg.wgpu.limits.limits.minUniformBufferOffsetAlignment; SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - SOKOL_ASSERT((_sg.wgpu.ub.offset + data->size) <= _sg.wgpu.ub.num_bytes); - SOKOL_ASSERT((_sg.wgpu.ub.offset & (_SG_WGPU_STAGING_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.wgpu.uniform.staging); + SOKOL_ASSERT((_sg.wgpu.uniform.offset + data->size) <= _sg.wgpu.uniform.num_bytes); + SOKOL_ASSERT((_sg.wgpu.uniform.offset & (alignment - 1)) == 0); SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); SOKOL_ASSERT(data->size <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); - SOKOL_ASSERT(0 != _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur]); - uint8_t* dst_ptr = _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur] + _sg.wgpu.ub.offset; - memcpy(dst_ptr, data->ptr, data->size); - _sg.wgpu.ub.bind_offsets[stage_index][ub_index] = _sg.wgpu.ub.offset; + _sg_stats_add(wgpu.uniforms.num_set_bindgroup, 1); + memcpy(_sg.wgpu.uniform.staging + _sg.wgpu.uniform.offset, data->ptr, data->size); + _sg.wgpu.uniform.bind.offsets[stage_index][ub_index] = _sg.wgpu.uniform.offset; + _sg.wgpu.uniform.offset = _sg_roundup_u32(_sg.wgpu.uniform.offset + (uint32_t)data->size, alignment); wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, - 0, // groupIndex 0 is reserved for uniform buffers - _sg.wgpu.ub.bindgroup, + _SG_WGPU_UNIFORM_BINDGROUP_INDEX, + _sg.wgpu.uniform.bind.group, SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, - &_sg.wgpu.ub.bind_offsets[0][0]); - _sg.wgpu.ub.offset = _sg_roundup(_sg.wgpu.ub.offset + data->size, _SG_WGPU_STAGING_ALIGN); + &_sg.wgpu.uniform.bind.offsets[0][0]); } _SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - if (_sg.wgpu.draw_indexed) { - wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0, 0); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && (_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.wgpu.cur_pipeline->cmn.index_type) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0, 0); } else { - wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); + wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0); } } _SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, 0, data->ptr, data->size); - SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(buf); + _sg_wgpu_copy_buffer_data(buf, 0, data); } -_SOKOL_PRIVATE int _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_PRIVATE void _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); _SOKOL_UNUSED(new_frame); - uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, buf->cmn.append_pos, data->ptr, data->size); - SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); - return (int)copied_num_bytes; + _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); - bool success = _sg_wgpu_staging_copy_to_texture(img, data); - SOKOL_ASSERT(success); - _SOKOL_UNUSED(success); + _sg_wgpu_copy_image_data(img, img->wgpu.tex, data); } #endif @@ -14115,25 +15201,17 @@ static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { #endif } -static inline void _sg_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs, - _sg_sampler_t** vs_smps, int num_vs_smps, - _sg_sampler_t** fs_smps, int num_fs_smps) -{ +static inline bool _sg_apply_bindings(_sg_bindings_t* bnd) { #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); + return _sg_gl_apply_bindings(bnd); #elif defined(SOKOL_METAL) - _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); + return _sg_mtl_apply_bindings(bnd); #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); + return _sg_d3d11_apply_bindings(bnd); #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); + return _sg_wgpu_apply_bindings(bnd); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); + return _sg_dummy_apply_bindings(bnd); #else #error("INVALID BACKEND"); #endif @@ -14203,17 +15281,17 @@ static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { #endif } -static inline int _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { +static inline void _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_append_buffer(buf, data, new_frame); + _sg_gl_append_buffer(buf, data, new_frame); #elif defined(SOKOL_METAL) - return _sg_mtl_append_buffer(buf, data, new_frame); + _sg_mtl_append_buffer(buf, data, new_frame); #elif defined(SOKOL_D3D11) - return _sg_d3d11_append_buffer(buf, data, new_frame); + _sg_d3d11_append_buffer(buf, data, new_frame); #elif defined(SOKOL_WGPU) - return _sg_wgpu_append_buffer(buf, data, new_frame); + _sg_wgpu_append_buffer(buf, data, new_frame); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_append_buffer(buf, data, new_frame); + _sg_dummy_append_buffer(buf, data, new_frame); #else #error("INVALID BACKEND"); #endif @@ -14445,7 +15523,8 @@ _SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int s */ SOKOL_ASSERT(pool && pool->gen_ctrs); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); - SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + 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; @@ -14805,6 +15884,13 @@ _SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY); _SG_VALIDATE(desc->min_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MINFILTER_NONE); _SG_VALIDATE(desc->mag_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MAGFILTER_NONE); + // 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 } @@ -14824,18 +15910,14 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { #if defined(SOKOL_D3D11) _SG_VALIDATE(0 != desc->attrs[0].sem_name, VALIDATE_SHADERDESC_ATTR_SEMANTICS); #endif - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) - // on GL, must provide shader source code + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) + // on GL or WebGPU, must provide shader source code _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) // on Metal or D3D11, must provide shader source code or byte code _SG_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); _SG_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); - #elif defined(SOKOL_WGPU) - // on WGPU byte code must be provided - _SG_VALIDATE((0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); - _SG_VALIDATE((0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); #else // Dummy Backend, don't require source or bytecode #endif @@ -14935,6 +16017,20 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { #if defined(_SOKOL_ANY_GL) _SG_VALIDATE(img_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL); #endif + if (img_slot_in_range && smp_slot_in_range) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_smp_desc->image_slot]; + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[img_smp_desc->sampler_slot]; + const bool needs_nonfiltering = (img_desc->sample_type == SG_IMAGESAMPLETYPE_UINT) + || (img_desc->sample_type == SG_IMAGESAMPLETYPE_SINT) + || (img_desc->sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT); + const bool needs_comparison = img_desc->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); + } + } } else { _SG_VALIDATE(img_smp_desc->glsl_name == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED); _SG_VALIDATE(img_smp_desc->image_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED); @@ -14977,7 +16073,7 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { if (l_state->stride == 0) { continue; } - _SG_VALIDATE((l_state->stride & 3) == 0, VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } _SG_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, VALIDATE_PIPELINEDESC_NO_ATTRS); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); @@ -15254,6 +16350,17 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { if (img && img->slot.state == SG_RESOURCESTATE_VALID) { _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH); _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_VS_IMAGE_MSAA); + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (stage->images[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_VS_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } } } } else { @@ -15270,11 +16377,17 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); _SG_VALIDATE(smp != 0, VALIDATE_ABND_VS_SMP_EXISTS); if (smp) { - if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARE) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER); } else { _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER); } + if (stage->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_VS_EXPECTED_NONFILTERING_SAMPLER); + } } } } else { @@ -15293,6 +16406,17 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { if (img && img->slot.state == SG_RESOURCESTATE_VALID) { _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH); _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_FS_IMAGE_MSAA); + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (stage->images[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_FS_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } } } } else { @@ -15309,45 +16433,23 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); _SG_VALIDATE(smp != 0, VALIDATE_ABND_FS_SMP_EXISTS); if (smp) { - if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARE) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER); } else { _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER); } + if (stage->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_FS_EXPECTED_NONFILTERING_SAMPLER); + } } } } else { _SG_VALIDATE(bindings->fs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING); } } - - // if image-sampler-pair info was provided in shader desc, check that that the mipmap filter matches image num mipmaps - for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_VS].num_image_samplers; img_smp_index++) { - const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; - const int img_index = stage->image_samplers[img_smp_index].image_slot; - const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[img_index].id); - const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[smp_index].id); - if (img && smp) { - if (img->cmn.num_mipmaps == 1) { - _SG_VALIDATE(smp->cmn.mipmap_filter == SG_FILTER_NONE, VALIDATE_ABND_VS_IMG_SMP_MIPMAPS); - } - } - } - for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_FS].num_image_samplers; img_smp_index++) { - const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; - const int img_index = stage->image_samplers[img_smp_index].image_slot; - const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; - SOKOL_ASSERT(img_index < stage->num_images); - SOKOL_ASSERT(smp_index < stage->num_samplers); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[img_index].id); - const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[smp_index].id); - if (img && smp) { - if (img->cmn.num_mipmaps == 1) { - _SG_VALIDATE(smp->cmn.mipmap_filter == SG_FILTER_NONE, VALIDATE_ABND_FS_IMG_SMP_MIPMAPS); - } - } - } return _sg_validate_end(); #endif } @@ -15374,7 +16476,7 @@ _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT); - // check that the provided data size doesn't exceed the uniform block size + // check that the provided data size matches the uniform block size _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE); return _sg_validate_end(); @@ -15538,7 +16640,7 @@ _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* des if (!smp_desc->used) { break; } - smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_SAMPLE); + smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_FILTERING); } } return def; @@ -16021,8 +17123,8 @@ _SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { res.pass_pool_size = _sg_def(res.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); res.context_pool_size = _sg_def(res.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); - res.staging_buffer_size = _sg_def(res.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); res.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); return res; } @@ -16036,12 +17138,13 @@ _SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + 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.valid = true; sg_setup_context(); @@ -16093,7 +17196,41 @@ 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)); - return _sg.formats[fmt_index]; + const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index]; + sg_pixelformat_info res; + _sg_clear(&res, sizeof(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); + 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_frame_stats sg_query_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.prev_stats; } SOKOL_API_IMPL sg_context sg_setup_context(void) { @@ -16791,6 +17928,7 @@ SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_ac SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); + _sg_stats_add(num_apply_viewport, 1); if (!_sg.pass_valid) { return; } @@ -16804,6 +17942,7 @@ SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float heig SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); + _sg_stats_add(num_apply_scissor_rect, 1); if (!_sg.pass_valid) { return; } @@ -16817,7 +17956,8 @@ SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); - _sg.bindings_valid = false; + _sg_stats_add(num_apply_pipeline, 1); + _sg.bindings_applied = false; if (!_sg_validate_apply_pipeline(pip_id)) { _sg.next_draw_valid = false; return; @@ -16838,88 +17978,100 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(bindings); SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + _sg_stats_add(num_apply_bindings, 1); if (!_sg_validate_apply_bindings(bindings)) { _sg.next_draw_valid = false; return; } - _sg.bindings_valid = true; + _sg.bindings_applied = true; - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_ASSERT(pip); + _sg_bindings_t bnd; + _sg_clear(&bnd, sizeof(bnd)); + bnd.pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + if (0 == bnd.pip) { + _sg.next_draw_valid = false; + } - _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS] = { 0 }; - int num_vbs = 0; - for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++, num_vbs++) { + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++, bnd.num_vbs++) { if (bindings->vertex_buffers[i].id) { - vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); - SOKOL_ASSERT(vbs[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); - _sg.next_draw_valid &= !vbs[i]->cmn.append_overflow; + bnd.vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + bnd.vb_offsets[i] = bindings->vertex_buffer_offsets[i]; + if (bnd.vbs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vbs[i]->slot.state); + _sg.next_draw_valid &= !bnd.vbs[i]->cmn.append_overflow; + } else { + _sg.next_draw_valid = false; + } } else { break; } } - _sg_buffer_t* ib = 0; if (bindings->index_buffer.id) { - ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); - SOKOL_ASSERT(ib); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); - _sg.next_draw_valid &= !ib->cmn.append_overflow; + bnd.ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + bnd.ib_offset = bindings->index_buffer_offset; + if (bnd.ib) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.ib->slot.state); + _sg.next_draw_valid &= !bnd.ib->cmn.append_overflow; + } else { + _sg.next_draw_valid = false; + } } - _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; - int num_vs_imgs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_vs_imgs++) { if (bindings->vs.images[i].id) { - vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); - SOKOL_ASSERT(vs_imgs[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); + bnd.vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + if (bnd.vs_imgs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_imgs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } } else { break; } } - _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; - int num_fs_imgs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { - if (bindings->fs.images[i].id) { - fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); - SOKOL_ASSERT(fs_imgs[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, bnd.num_vs_smps++) { + if (bindings->vs.samplers[i].id) { + bnd.vs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + if (bnd.vs_smps[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_smps[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } } else { break; } } - _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = { 0 }; - int num_vs_smps = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, num_vs_smps++) { - if (bindings->vs.samplers[i].id) { - vs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); - SOKOL_ASSERT(vs_smps[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_smps[i]->slot.state); + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_fs_imgs++) { + if (bindings->fs.images[i].id) { + bnd.fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + if (bnd.fs_imgs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_imgs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } } else { break; } } - _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = { 0 }; - int num_fs_smps = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, num_fs_smps++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, bnd.num_fs_smps++) { if (bindings->fs.samplers[i].id) { - fs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); - SOKOL_ASSERT(fs_smps[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_smps[i]->slot.state); + bnd.fs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + if (bnd.fs_smps[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_smps[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } } else { break; } } if (_sg.next_draw_valid) { - const int* vb_offsets = bindings->vertex_buffer_offsets; - int ib_offset = bindings->index_buffer_offset; - _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); + _sg.next_draw_valid &= _sg_apply_bindings(&bnd); _SG_TRACE_ARGS(apply_bindings, bindings); } } @@ -16929,6 +18081,8 @@ SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_add(num_apply_uniforms, 1); + _sg_stats_add(size_apply_uniforms, (uint32_t)data->size); if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { _sg.next_draw_valid = false; return; @@ -16948,8 +18102,9 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_ASSERT(base_element >= 0); SOKOL_ASSERT(num_elements >= 0); SOKOL_ASSERT(num_instances >= 0); + _sg_stats_add(num_draw, 1); #if defined(SOKOL_DEBUG) - if (!_sg.bindings_valid) { + if (!_sg.bindings_applied) { _SG_WARN(DRAW_WITHOUT_BINDINGS); } #endif @@ -16959,7 +18114,7 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance if (!_sg.next_draw_valid) { return; } - if (!_sg.bindings_valid) { + if (!_sg.bindings_applied) { return; } /* attempting to draw with zero elements or instances is not technically an @@ -16974,6 +18129,7 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_API_IMPL void sg_end_pass(void) { SOKOL_ASSERT(_sg.valid); + _sg_stats_add(num_passes, 1); if (!_sg.pass_valid) { return; } @@ -16987,6 +18143,9 @@ SOKOL_API_IMPL void sg_end_pass(void) { SOKOL_API_IMPL void sg_commit(void) { SOKOL_ASSERT(_sg.valid); _sg_commit(); + _sg.stats.frame_index = _sg.frame_index; + _sg.prev_stats = _sg.stats; + _sg_clear(&_sg.stats, sizeof(_sg.stats)); _sg_notify_commit_listeners(); _SG_TRACE_NOARGS(commit); _sg.frame_index++; @@ -17001,6 +18160,8 @@ SOKOL_API_IMPL void sg_reset_state_cache(void) { 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_add(num_update_buffer, 1); + _sg_stats_add(size_update_buffer, (uint32_t)data->size); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { if (_sg_validate_update_buffer(buf, data)) { @@ -17019,6 +18180,8 @@ SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* 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_add(num_append_buffer, 1); + _sg_stats_add(size_append_buffer, (uint32_t)data->size); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); int result; if (buf) { @@ -17027,17 +18190,20 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { buf->cmn.append_pos = 0; buf->cmn.append_overflow = false; } - if ((buf->cmn.append_pos + _sg_roundup((int)data->size, 4)) > buf->cmn.size) { + 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); - int copied_num_bytes = _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); - buf->cmn.append_pos += copied_num_bytes; + _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; } } @@ -17077,6 +18243,15 @@ SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { SOKOL_ASSERT(_sg.valid); + _sg_stats_add(num_update_image, 1); + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + if (data->subimage[face_index][mip_index].size == 0) { + break; + } + _sg_stats_add(size_update_image, (uint32_t)data->subimage[face_index][mip_index].size); + } + } _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_update_image(img, data)) { @@ -17111,6 +18286,20 @@ SOKOL_API_IMPL bool sg_remove_commit_listener(sg_commit_listener listener) { return _sg_remove_commit_listener(&listener); } +SOKOL_API_IMPL void sg_enable_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = true; +} + +SOKOL_API_IMPL void sg_disable_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = false; +} + +SOKOL_API_IMPL bool sg_frame_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_buffer_info info; @@ -17376,23 +18565,136 @@ SOKOL_API_IMPL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) { } SOKOL_API_IMPL const void* sg_d3d11_device(void) { -#if defined(SOKOL_D3D11) - return (const void*) _sg.d3d11.dev; -#else - return 0; -#endif + #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_d3d11_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, 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_d3d11_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, 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; + res.srv = (const void*) img->d3d11.srv; + } + #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_d3d11_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, 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_d3d11_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + res.vs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_VS].cbufs[i]; + res.fs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_FS].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_d3d11_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, 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_pass_info sg_d3d11_query_pass_info(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_pass_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_rtv[i] = (const void*) pass->d3d11.color_atts[i].view.rtv; + res.resolve_rtv[i] = (const void*) pass->d3d11.resolve_atts[i].view.rtv; + } + res.dsv = (const void*) pass->d3d11.ds_att.view.dsv; + } + #else + _SOKOL_UNUSED(pass_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 { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } else { + return 0; + } + #else return 0; - } -#else - return 0; -#endif + #endif } SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { @@ -17407,6 +18709,328 @@ SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { #endif } +SOKOL_API_IMPL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, 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_mtl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, 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_mtl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, 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_mtl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + const int vs_lib = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib; + const int vs_func = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func; + const int fs_lib = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib; + const int fs_func = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func; + if (vs_lib != 0) { + res.vs_lib = (__bridge void*) _sg_mtl_id(vs_lib); + } + if (fs_lib != 0) { + res.fs_lib = (__bridge void*) _sg_mtl_id(fs_lib); + } + if (vs_func != 0) { + res.vs_func = (__bridge void*) _sg_mtl_id(vs_func); + } + if (fs_func != 0) { + res.fs_func = (__bridge void*) _sg_mtl_id(fs_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_mtl_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, 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.pass_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_wgpu_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, 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_wgpu_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex = (const void*) img->wgpu.tex; + res.view = (const void*) img->wgpu.view; + } + #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_wgpu_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, 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_wgpu_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.vs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + res.fs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + res.bgl = (const void*) shd->wgpu.bind_group_layout; + } + #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_wgpu_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.pip = (const void*) pip->wgpu.pip; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_pass_info sg_wgpu_query_pass_info(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_pass_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_view[i] = (const void*) pass->wgpu.color_atts[i].view; + res.resolve_view[i] = (const void*) pass->wgpu.resolve_atts[i].view; + } + res.ds_view = (const void*) pass->wgpu.ds_att.view; + } + #else + _SOKOL_UNUSED(pass_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_gl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, 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_gl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, 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.msaa_render_buffer = img->gl.msaa_render_buffer; + 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_gl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, 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_gl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.prog = shd->gl.prog; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_pass_info sg_gl_query_pass_info(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_pass_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + res.frame_buffer = pass->gl.fb; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.msaa_resolve_framebuffer[i] = pass->gl.msaa_resolve_framebuffer[i]; + } + } + #else + _SOKOL_UNUSED(pass_id); + #endif + return res; +} + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/sokol_log.h b/sokol_log.h index 87f78d75..58ff30bf 100644 --- a/sokol_log.h +++ b/sokol_log.h @@ -122,7 +122,7 @@ extern "C" { #endif /* - Plug this function into the 'logger.func' struct item when initializating any of the sokol + Plug this function into the 'logger.func' struct item when initializing any of the sokol headers. For instance for sokol_audio.h it would loom like this: saudio_setup(&(saudio_desc){ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3545b758..cfaf7d46 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,9 +51,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(c_flags ${c_flags} /W4 /WX /D_CRT_SECURE_NO_WARNINGS) set(cxx_flags ${cxx_flags} /W4 /WX /EHsc /D_CRT_SECURE_NO_WARNINGS) else() - set(c_flags ${c_flags} -Wall -Wextra -Werror -Wsign-conversion) + set(c_flags ${c_flags} -Wall -Wextra -Werror -Wsign-conversion -Wstrict-prototypes) set(cxx_flags ${cxx_flags} -Wall -Wextra -Werror -Wsign-conversion -fno-rtti -fno-exceptions) - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(c_flags ${c_flags} -Wno-missing-field-initializers) set(cxx_flags ${cxx_flags} -Wno-missing-field-initializers) endif() @@ -69,7 +69,12 @@ endif() if (EMSCRIPTEN) set(CMAKE_EXECUTABLE_SUFFIX ".html") - set(link_flags ${link-flags} -sNO_FILESYSTEM=1 -sASSERTIONS=0 -sMALLOC=emmalloc -sINITIAL_MEMORY=33554432 --closure=1) + set(link_flags ${link_flags} -sNO_FILESYSTEM=1 -sASSERTIONS=0 -sMALLOC=emmalloc -sINITIAL_MEMORY=33554432 --closure=1) + if (SOKOL_BACKEND STREQUAL SOKOL_WGPU) + set(link_flags ${link_flags} -sUSE_WEBGPU=1) + else() + set(link_flags ${link_flags} -sUSE_WEBGL2=1) + endif() elseif (OSX_IOS) set(exe_type MACOSX_BUNDLE) if (USE_ARC) diff --git a/tests/CMakePresets.json b/tests/CMakePresets.json index df7a5659..fe0b8964 100644 --- a/tests/CMakePresets.json +++ b/tests/CMakePresets.json @@ -391,6 +391,26 @@ } }, { + "name": "emsc_wgpu_debug", + "generator": "Ninja", + "binaryDir": "build/emsc_wgpu_debug", + "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_WGPU", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "emsc_wgpu_release", + "generator": "Ninja", + "binaryDir": "build/emsc_wgpu_release", + "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_WGPU", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { "name": "android_debug", "generator": "Ninja", "binaryDir": "build/android_debug", @@ -645,6 +665,14 @@ "configurePreset": "emsc_webgl2_release" }, { + "name": "emsc_wgpu_debug", + "configurePreset": "emsc_wgpu_debug" + }, + { + "name": "emsc_wgpu_release", + "configurePreset": "emsc_wgpu_release" + }, + { "name": "android_debug", "configurePreset": "android_debug" }, diff --git a/tests/functional/sokol_fetch_test.c b/tests/functional/sokol_fetch_test.c index 417979d5..8318a9e2 100644 --- a/tests/functional/sokol_fetch_test.c +++ b/tests/functional/sokol_fetch_test.c @@ -612,9 +612,9 @@ UTEST(sokol_fetch, load_file_chunked) { /* load N big files in small chunks interleaved on the same channel via lanes */ #define LOAD_FILE_LANES_NUM_LANES (4) -uint8_t load_file_lanes_chunk_buf[LOAD_FILE_LANES_NUM_LANES][8192]; -uint8_t load_file_lanes_content[LOAD_FILE_LANES_NUM_LANES][500000]; -int load_file_lanes_passed[LOAD_FILE_LANES_NUM_LANES]; +static uint8_t load_file_lanes_chunk_buf[LOAD_FILE_LANES_NUM_LANES][8192]; +static uint8_t load_file_lanes_content[LOAD_FILE_LANES_NUM_LANES][500000]; +static int load_file_lanes_passed[LOAD_FILE_LANES_NUM_LANES]; static void load_file_lanes_callback(const sfetch_response_t* response) { assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES)); if (response->fetched) { @@ -669,9 +669,9 @@ UTEST(sokol_fetch, load_file_lanes) { #define LOAD_FILE_THROTTLE_NUM_PASSES (3) #define LOAD_FILE_THROTTLE_NUM_REQUESTS (12) // lanes * passes -uint8_t load_file_throttle_chunk_buf[LOAD_FILE_THROTTLE_NUM_LANES][128000]; -uint8_t load_file_throttle_content[LOAD_FILE_THROTTLE_NUM_PASSES][LOAD_FILE_THROTTLE_NUM_LANES][500000]; -int load_file_throttle_passed[LOAD_FILE_THROTTLE_NUM_LANES]; +static uint8_t load_file_throttle_chunk_buf[LOAD_FILE_THROTTLE_NUM_LANES][128000]; +static uint8_t load_file_throttle_content[LOAD_FILE_THROTTLE_NUM_PASSES][LOAD_FILE_THROTTLE_NUM_LANES][500000]; +static int load_file_throttle_passed[LOAD_FILE_THROTTLE_NUM_LANES]; static void load_file_throttle_callback(const sfetch_response_t* response) { assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES)); @@ -733,8 +733,8 @@ UTEST(sokol_fetch, load_file_throttle) { /* test parallel fetches on multiple channels */ #define LOAD_CHANNEL_NUM_CHANNELS (16) -uint8_t load_channel_buf[LOAD_CHANNEL_NUM_CHANNELS][500000]; -bool load_channel_passed[LOAD_CHANNEL_NUM_CHANNELS]; +static uint8_t load_channel_buf[LOAD_CHANNEL_NUM_CHANNELS][500000]; +static bool load_channel_passed[LOAD_CHANNEL_NUM_CHANNELS]; void load_channel_callback(const sfetch_response_t* response) { assert(response->channel < LOAD_CHANNEL_NUM_CHANNELS); @@ -781,15 +781,13 @@ UTEST(sokol_fetch, load_channel) { sfetch_shutdown(); } -bool load_file_cancel_passed = false; -void load_file_cancel_callback(const sfetch_response_t* response) { +static bool load_file_cancel_passed = false; +static void load_file_cancel_callback(const sfetch_response_t* response) { if (response->dispatched) { sfetch_cancel(response->handle); } - if (response->failed) { - if (response->cancelled && response->finished && (response->error_code == SFETCH_ERROR_CANCELLED)) { - load_file_cancel_passed = true; - } + if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) { + load_file_cancel_passed = true; } } @@ -811,3 +809,54 @@ UTEST(sokol_fetch, load_file_cancel) { T(load_file_cancel_passed); sfetch_shutdown(); } + +static bool load_file_cancel_before_dispatch_passed = false; +static void load_file_cancel_before_dispatch_callback(const sfetch_response_t* response) { + // cancelled, finished, failed and error code must all be set + if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) { + load_file_cancel_before_dispatch_passed = true; + } +} + +UTEST(sokol_fetch, load_file_cancel_before_dispatch) { + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1, + }); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_cancel_before_dispatch_callback, + }); + sfetch_cancel(h); + sfetch_dowork(); + T(load_file_cancel_before_dispatch_passed); + sfetch_shutdown(); +} + +static bool load_file_cancel_after_dispatch_passed = false; +static void load_file_cancel_after_dispatch_callback(const sfetch_response_t* response) { + // when cancelled, then finished, failed and error code must all be set + if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) { + load_file_cancel_after_dispatch_passed = true; + } +} + +UTEST(sokol_fetch, load_file_cancel_after_dispatch) { + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1, + }); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_cancel_after_dispatch_callback, + .buffer = SFETCH_RANGE(load_file_buf), + }); + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sfetch_cancel(h); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_cancel_after_dispatch_passed); + sfetch_shutdown(); +} diff --git a/tests/functional/sokol_gfx_test.c b/tests/functional/sokol_gfx_test.c index 28084e0f..471e9c8c 100644 --- a/tests/functional/sokol_gfx_test.c +++ b/tests/functional/sokol_gfx_test.c @@ -1016,6 +1016,7 @@ UTEST(sokol_gfx, query_sampler_desc) { setup(&(sg_desc){0}); sg_sampler s0 = sg_make_sampler(&(sg_sampler_desc){ .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, .mipmap_filter = SG_FILTER_LINEAR, .wrap_v = SG_WRAP_MIRRORED_REPEAT, .max_anisotropy = 8, @@ -1024,7 +1025,7 @@ UTEST(sokol_gfx, query_sampler_desc) { }); const sg_sampler_desc s0_desc = sg_query_sampler_desc(s0); T(s0_desc.min_filter == SG_FILTER_LINEAR); - T(s0_desc.mag_filter == SG_FILTER_NEAREST); + T(s0_desc.mag_filter == SG_FILTER_LINEAR); T(s0_desc.mipmap_filter == SG_FILTER_LINEAR); T(s0_desc.wrap_u == SG_WRAP_REPEAT); T(s0_desc.wrap_v == SG_WRAP_MIRRORED_REPEAT); @@ -1063,18 +1064,18 @@ UTEST(sokol_gfx, query_shader_desc) { } }, .images[0] = { .used = true, .image_type = SG_IMAGETYPE_2D, .sample_type = SG_IMAGESAMPLETYPE_FLOAT, .multisampled = true }, - .images[1] = { .used = true, .image_type = SG_IMAGETYPE_3D, .sample_type = SG_IMAGESAMPLETYPE_SINT }, - .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_SAMPLE }, - .samplers[1] = { .used = true, .sampler_type = SG_SAMPLERTYPE_COMPARE }, - .image_sampler_pairs[0] = { .used = true, .image_slot = 0, .sampler_slot = 1, .glsl_name = "img0" }, - .image_sampler_pairs[1] = { .used = true, .image_slot = 1, .sampler_slot = 0, .glsl_name = "img1" }, + .images[1] = { .used = true, .image_type = SG_IMAGETYPE_3D, .sample_type = SG_IMAGESAMPLETYPE_DEPTH }, + .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_FILTERING }, + .samplers[1] = { .used = true, .sampler_type = SG_SAMPLERTYPE_COMPARISON }, + .image_sampler_pairs[0] = { .used = true, .image_slot = 0, .sampler_slot = 0, .glsl_name = "img0" }, + .image_sampler_pairs[1] = { .used = true, .image_slot = 1, .sampler_slot = 1, .glsl_name = "img1" }, }, .fs = { .source = "fs_source", .images[0] = { .used = true, .image_type = SG_IMAGETYPE_ARRAY, .sample_type = SG_IMAGESAMPLETYPE_DEPTH }, - .images[1] = { .used = true, .image_type = SG_IMAGETYPE_CUBE, .sample_type = SG_IMAGESAMPLETYPE_FLOAT }, - .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_COMPARE }, - .samplers[1] = { .used = true, .sampler_type = SG_SAMPLERTYPE_SAMPLE }, + .images[1] = { .used = true, .image_type = SG_IMAGETYPE_CUBE, .sample_type = SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT }, + .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_COMPARISON }, + .samplers[1] = { .used = true, .sampler_type = SG_SAMPLERTYPE_NONFILTERING }, .image_sampler_pairs[0] = { .used = true, .image_slot = 0, .sampler_slot = 0, .glsl_name = "img3" }, .image_sampler_pairs[1] = { .used = true, .image_slot = 1, .sampler_slot = 1, .glsl_name = "img4" }, }, @@ -1096,19 +1097,19 @@ UTEST(sokol_gfx, query_shader_desc) { T(s0_desc.vs.images[0].multisampled); T(s0_desc.vs.images[1].used); T(s0_desc.vs.images[1].image_type == SG_IMAGETYPE_3D); - T(s0_desc.vs.images[1].sample_type == SG_IMAGESAMPLETYPE_SINT); + T(s0_desc.vs.images[1].sample_type == SG_IMAGESAMPLETYPE_DEPTH); T(s0_desc.vs.images[1].multisampled == false); T(s0_desc.vs.samplers[0].used); - T(s0_desc.vs.samplers[0].sampler_type == SG_SAMPLERTYPE_SAMPLE); + T(s0_desc.vs.samplers[0].sampler_type == SG_SAMPLERTYPE_FILTERING); T(s0_desc.vs.samplers[1].used); - T(s0_desc.vs.samplers[1].sampler_type == SG_SAMPLERTYPE_COMPARE); + T(s0_desc.vs.samplers[1].sampler_type == SG_SAMPLERTYPE_COMPARISON); T(s0_desc.vs.image_sampler_pairs[0].used); T(s0_desc.vs.image_sampler_pairs[0].image_slot == 0); - T(s0_desc.vs.image_sampler_pairs[0].sampler_slot == 1); + T(s0_desc.vs.image_sampler_pairs[0].sampler_slot == 0); T(s0_desc.vs.image_sampler_pairs[0].glsl_name == 0); T(s0_desc.vs.image_sampler_pairs[1].used); T(s0_desc.vs.image_sampler_pairs[1].image_slot == 1); - T(s0_desc.vs.image_sampler_pairs[1].sampler_slot == 0); + T(s0_desc.vs.image_sampler_pairs[1].sampler_slot == 1); T(s0_desc.vs.image_sampler_pairs[1].glsl_name == 0); T(s0_desc.fs.source == 0); T(s0_desc.fs.uniform_blocks[0].size == 0); @@ -1122,12 +1123,12 @@ UTEST(sokol_gfx, query_shader_desc) { T(s0_desc.fs.images[0].multisampled == false); T(s0_desc.fs.images[1].used); T(s0_desc.fs.images[1].image_type == SG_IMAGETYPE_CUBE); - T(s0_desc.fs.images[1].sample_type == SG_IMAGESAMPLETYPE_FLOAT); + T(s0_desc.fs.images[1].sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT); T(s0_desc.fs.images[1].multisampled == false); T(s0_desc.fs.samplers[0].used); - T(s0_desc.fs.samplers[0].sampler_type == SG_SAMPLERTYPE_COMPARE); + T(s0_desc.fs.samplers[0].sampler_type == SG_SAMPLERTYPE_COMPARISON); T(s0_desc.fs.samplers[1].used); - T(s0_desc.fs.samplers[1].sampler_type == SG_SAMPLERTYPE_SAMPLE); + T(s0_desc.fs.samplers[1].sampler_type == SG_SAMPLERTYPE_NONFILTERING); T(s0_desc.fs.image_sampler_pairs[0].used); T(s0_desc.fs.image_sampler_pairs[0].image_slot == 0); T(s0_desc.fs.image_sampler_pairs[0].sampler_slot == 0); @@ -2150,6 +2151,91 @@ UTEST(sokol_gfx, make_image_validate_wrong_mipsize) { sg_shutdown(); } +UTEST(sokol_gfx, make_sampler_validate_start_canary) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + ._start_canary = 1234, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_CANARY); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_minfilter_none) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_NONE, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_MINFILTER_NONE); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_magfilter_none) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + .mag_filter = SG_FILTER_NONE, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_MAGFILTER_NONE); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_anistropic_requires_linear_filtering) { + setup(&(sg_desc){0}); + sg_sampler smp; + + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_NONE, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_NEAREST, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_LINEAR, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_NEAREST, + .mipmap_filter = SG_FILTER_LINEAR, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_LINEAR, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_VALID); + + sg_shutdown(); +} + UTEST(sokol_gfx, make_pass_validate_start_canary) { setup(&(sg_desc){0}); sg_pass pass = sg_make_pass(&(sg_pass_desc){ @@ -2789,3 +2875,111 @@ UTEST(sokol_gfx, make_pass_validate_depth_image_sample_count) { T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); sg_shutdown(); } + +UTEST(sokol_gfx, query_pixelformat_bytesperpixel) { + setup(&(sg_desc){0}); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8SN).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8UI).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8SI).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16SN).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16UI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16SI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16F).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8SN).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8UI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8SI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R32UI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_R32SI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_R32F).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16SN).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16UI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16SI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16F).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_SRGB8A8).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8SN).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8UI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8SI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_BGRA8).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGB10A2).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG11B10F).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGB9E5).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG32UI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG32SI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG32F).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16SN).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16UI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16SI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16F).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA32UI).bytes_per_pixel == 16); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA32SI).bytes_per_pixel == 16); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA32F).bytes_per_pixel == 16); + T(sg_query_pixelformat(SG_PIXELFORMAT_DEPTH).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_DEPTH_STENCIL).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC1_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC2_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC3_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC4_R).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC4_RSN).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC5_RG).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC5_RGSN).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC6H_RGBF).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC6H_RGBUF).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC7_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGB_2BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGB_4BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGBA_2BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGBA_4BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RGB8).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RGB8A1).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RGBA8).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RG11).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RG11SN).bytes_per_pixel == 0); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_pixelformat_compressed) { + setup(&(sg_desc){0}); + int i = SG_PIXELFORMAT_NONE + 1; + for (; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + T(sg_query_pixelformat((sg_pixel_format)i).compressed == false); + } + for (; i < _SG_PIXELFORMAT_NUM; i++) { + T(sg_query_pixelformat((sg_pixel_format)i).compressed == true); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, query_row_pitch) { + setup(&(sg_desc){0}); + T(sg_query_row_pitch(SG_PIXELFORMAT_R8, 13, 1) == 13); + T(sg_query_row_pitch(SG_PIXELFORMAT_R8, 13, 32) == 32); + T(sg_query_row_pitch(SG_PIXELFORMAT_RG8SN, 256, 16) == 512); + T(sg_query_row_pitch(SG_PIXELFORMAT_RGBA8, 256, 16) == 1024); + T(sg_query_row_pitch(SG_PIXELFORMAT_BC1_RGBA, 1024, 1) == 2048); + T(sg_query_row_pitch(SG_PIXELFORMAT_BC1_RGBA, 1, 1) == 8); + T(sg_query_row_pitch(SG_PIXELFORMAT_DEPTH, 256, 4) == 1024); + T(sg_query_row_pitch(SG_PIXELFORMAT_DEPTH_STENCIL, 256, 4) == 1024); + sg_shutdown(); +} + +UTEST(sokol_gfx, sg_query_surface_pitch) { + setup(&(sg_desc){0}); + T(sg_query_surface_pitch(SG_PIXELFORMAT_R8, 256, 256, 1) == (256 * 256)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_R8, 256, 256, 1024) == (256 * 1024)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_RG8, 1, 1, 1) == 2); + T(sg_query_surface_pitch(SG_PIXELFORMAT_RG8, 256, 256, 4) == (256 * 256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_RGBA32F, 256, 256, 1) == (256 * 256 * 16)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 256, 1) == (256 * 2 * 64)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 1, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 2, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 3, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 4, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 5, 1) == (256 * 2 * 2)); + sg_shutdown(); +} diff --git a/tests/functional/sokol_spine_test.c b/tests/functional/sokol_spine_test.c index d32bd27c..435b32b2 100644 --- a/tests/functional/sokol_spine_test.c +++ b/tests/functional/sokol_spine_test.c @@ -15,7 +15,7 @@ static void log_func(const char* tag, uint32_t log_level, uint32_t log_item, con last_logitem = log_item; } -static void init() { +static void init(void) { last_logitem = SSPINE_LOGITEM_OK; sg_setup(&(sg_desc){0}); sspine_setup(&(sspine_desc){ .logger = { .func = log_func } }); @@ -29,7 +29,7 @@ static void init_with_desc(const sspine_desc* desc) { sspine_setup(&desc1); } -static void shutdown() { +static void shutdown(void) { sspine_shutdown(); sg_shutdown(); } diff --git a/tests/test_emscripten.sh b/tests/test_emscripten.sh index 74f0ad1b..aff4b08e 100755 --- a/tests/test_emscripten.sh +++ b/tests/test_emscripten.sh @@ -4,3 +4,5 @@ source test_common.sh setup_emsdk build emsc_webgl2_debug emsc_webgl2_debug build emsc_webgl2_release emsc_webgl2_release +build emsc_wgpu_debug emsc_wgpu_debug +build emsc_wgpu_release emsc_wgpu_release diff --git a/util/sokol_debugtext.h b/util/sokol_debugtext.h index 4d4ee6e1..09da6019 100644 --- a/util/sokol_debugtext.h +++ b/util/sokol_debugtext.h @@ -427,8 +427,8 @@ sdtx_setup(&(sdtx_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -658,12 +658,12 @@ typedef struct sdtx_context_desc_t { Used in sdtx_desc_t to provide custom memory-alloc and -free functions to sokol_debugtext.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + alloc_fn and free_fn function must be provided (e.g. it's not valid to override one function but not the other). */ typedef struct sdtx_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sdtx_allocator_t; @@ -3351,171 +3351,109 @@ static const uint8_t _sdtx_fs_bytecode_hlsl4[608] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; #elif defined(SOKOL_WGPU) -static const uint8_t _sdtx_vs_bytecode_wgpu[1648] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x2e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x2c,0x00,0x00,0x00, - 0x2d,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, - 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, - 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, - 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, - 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, - 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, - 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x13,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, - 0x05,0x00,0x05,0x00,0x2c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, - 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x2d,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, - 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x13,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,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00, - 0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00, - 0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00, - 0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x15,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x40, - 0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0xc0, - 0x2c,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x16,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00, - 0x00,0x00,0x80,0xbf,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x00,0x00,0x80,0x3f,0x2c,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x23,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x23,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,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,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x28,0x00,0x00,0x00, - 0x29,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2b,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2b,0x00,0x00,0x00, - 0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2b,0x00,0x00,0x00, - 0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x11,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x22,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x24,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x27,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _sdtx_vs_source_wgsl[922] = { + 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,0x31,0x39,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f, + 0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x28,0x28,0x78,0x5f,0x31,0x39,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,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x33,0x37,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x34,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x78,0x5f,0x34,0x31,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,0x0a,0x00, }; -static const uint8_t _sdtx_fs_bytecode_wgpu[940] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1a,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, - 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, - 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, - 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, - 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, - 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, - 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, - 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, - 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, - 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, - 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x75,0x76,0x00,0x00, - 0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x07,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, - 0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x0c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x57,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x4f,0x00,0x09,0x00,0x08,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x17,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x19,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00, - 0x19,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _sdtx_fs_source_wgsl[663] = { + 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,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 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,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x34,0x38,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,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,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,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x32, + 0x34,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32, + 0x34,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x29,0x20,0x2a,0x20,0x78, + 0x5f,0x32,0x38,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,0x0a,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _sdtx_vs_src_dummy = ""; @@ -3660,8 +3598,8 @@ static void _sdtx_clear(void* ptr, size_t size) { static void* _sdtx_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sdtx.desc.allocator.alloc) { - ptr = _sdtx.desc.allocator.alloc(size, _sdtx.desc.allocator.user_data); + if (_sdtx.desc.allocator.alloc_fn) { + ptr = _sdtx.desc.allocator.alloc_fn(size, _sdtx.desc.allocator.user_data); } else { ptr = malloc(size); } @@ -3678,8 +3616,8 @@ static void* _sdtx_malloc_clear(size_t size) { } static void _sdtx_free(void* ptr) { - if (_sdtx.desc.allocator.free) { - _sdtx.desc.allocator.free(ptr, _sdtx.desc.allocator.user_data); + if (_sdtx.desc.allocator.free_fn) { + _sdtx.desc.allocator.free_fn(ptr, _sdtx.desc.allocator.user_data); } else { free(ptr); } @@ -4007,7 +3945,7 @@ static void _sdtx_setup_common(void) { shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; shd_desc.fs.samplers[0].used = true; - shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; shd_desc.fs.image_sampler_pairs[0].used = true; shd_desc.fs.image_sampler_pairs[0].image_slot = 0; shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; @@ -4039,8 +3977,8 @@ static void _sdtx_setup_common(void) { shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_wgpu); - shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_wgpu); + shd_desc.vs.source = _sdtx_vs_source_wgsl; + shd_desc.fs.source = _sdtx_fs_source_wgsl; #else shd_desc.vs.source = _sdtx_vs_src_dummy; shd_desc.fs.source = _sdtx_fs_src_dummy; @@ -4259,7 +4197,7 @@ SOKOL_API_IMPL void _sdtx_draw_layer(_sdtx_context_t* ctx, int layer_id) { static sdtx_desc_t _sdtx_desc_defaults(const sdtx_desc_t* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sdtx_desc_t res = *desc; 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); diff --git a/util/sokol_fontstash.h b/util/sokol_fontstash.h index 9719e380..048c8215 100644 --- a/util/sokol_fontstash.h +++ b/util/sokol_fontstash.h @@ -147,8 +147,8 @@ FONScontext* fons_context = sfons_create(&(sfons_desc_t){ ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -213,15 +213,15 @@ extern "C" { Used in sfons_desc_t to provide custom memory-alloc and -free functions to sokol_fontstash.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + 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)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sfons_allocator_t; @@ -300,7 +300,9 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui 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; } @@ -1395,195 +1397,125 @@ static const uint8_t _sfons_fs_bytecode_hlsl4[628] = { 0x00,0x00,0x00,0x00, }; #elif defined(SOKOL_WGPU) -static const uint8_t _sfons_vs_bytecode_wgpu[1968] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x35,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x33,0x00,0x00,0x00, - 0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00, - 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, - 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, - 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, - 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, - 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, - 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, - 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0d,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x13,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, - 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, - 0x05,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x5f,0x32,0x31,0x00,0x05,0x00,0x05,0x00, - 0x1a,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x70,0x73,0x69,0x7a,0x65,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, - 0x2a,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x47,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x25,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00, - 0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00, - 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x15,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x18,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x1e,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, - 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x91,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x1b,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1e,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x24,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x26,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x27,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x28,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x08,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x2f,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x91,0x00,0x05,0x00, - 0x09,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x09,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x32,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _sfons_vs_source_wgsl[1162] = { + 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,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29, + 0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,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,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32,0x20,0x3a,0x20,0x6d,0x61, + 0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f, + 0x31,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x32,0x20,0x2a,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x32,0x20,0x3a,0x20,0x6d, + 0x61,0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x74,0x6d, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x36,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x28,0x78,0x5f,0x33,0x32,0x20,0x2a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x33,0x36,0x2e,0x78,0x2c,0x20,0x78, + 0x5f,0x33,0x36,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, + 0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x35,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x35, + 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,0x0a,0x00, }; -static const uint8_t fs_bytecode_wgpu[1000] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1f,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00,0x03,0x00,0x37,0x00, - 0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30, - 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, - 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72, - 0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e, - 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, - 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e, - 0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f, - 0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e, - 0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23, - 0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00, - 0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, - 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00, - 0x10,0x00,0x00,0x00,0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00, - 0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x1c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, - 0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, - 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x08,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x00,0x00,0x80,0x3f,0x19,0x00,0x09,0x00,0x0d,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0e,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00, - 0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x4f,0x00,0x07,0x00,0x14,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00, - 0x09,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x1d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _sfons_fs_source_wgsl[674] = { + 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,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 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,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x34,0x38,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,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,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,0x76,0x65,0x63,0x32,0x66,0x28,0x78,0x5f,0x32,0x34,0x2e,0x78, + 0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x79,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,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,0x78,0x5f,0x33,0x32,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, + 0x0a,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _sfons_vs_source_dummy = ""; @@ -1610,8 +1542,8 @@ static void _sfons_clear(void* ptr, size_t size) { static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) { SOKOL_ASSERT(allocator && (size > 0)); void* ptr; - if (allocator->alloc) { - ptr = allocator->alloc(size, allocator->user_data); + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); } else { ptr = malloc(size); } @@ -1627,8 +1559,8 @@ static void* _sfons_malloc_clear(const sfons_allocator_t* allocator, size_t size static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) { SOKOL_ASSERT(allocator); - if (allocator->free) { - allocator->free(ptr, allocator->user_data); + if (allocator->free_fn) { + allocator->free_fn(ptr, allocator->user_data); } else { free(ptr); } @@ -1663,7 +1595,7 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; shd_desc.fs.samplers[0].used = true; - shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; shd_desc.fs.image_sampler_pairs[0].used = true; shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; shd_desc.fs.image_sampler_pairs[0].image_slot = 0; @@ -1696,10 +1628,8 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.byte_code = _sfons_vs_bytecode_wgpu; - shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bytecode_wgpu); - shd_desc.fs.byte_code = _sfons_fs_bytecode_wgpu; - shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bytecode_wgpu); + shd_desc.vs.source = _sfons_vs_source_wgsl; + shd_desc.fs.source = _sfons_fs_source_wgsl; #else shd_desc.vs.source = _sfons_vs_source_dummy; shd_desc.fs.source = _sfons_fs_source_dummy; @@ -1809,7 +1739,7 @@ static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) { SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) { SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t)); sfons->desc = _sfons_desc_defaults(desc); FONSparams params; diff --git a/util/sokol_gfx_imgui.h b/util/sokol_gfx_imgui.h index f9106f1b..394760da 100644 --- a/util/sokol_gfx_imgui.h +++ b/util/sokol_gfx_imgui.h @@ -73,8 +73,8 @@ sg_imgui_init(&sg_imgui, &(sg_imgui_desc_t){ .allocator = { - .alloc = my_malloc, - .free = my_free, + .alloc_fn = my_malloc, + .free_fn = my_free, } }); @@ -84,9 +84,16 @@ this won't draw anything yet, since no windows are open. - --- open and close windows directly by setting the following public + --- call the convenience function sg_imgui_draw_menu(ctx, title) + to render a menu which allows to open/close the provided debug windows + + sg_imgui_draw_menu(&sg_imgui, "sokol-gfx"); + + --- alternative, open and close windows directly by setting the following public booleans in the sg_imgui_t struct: + sg_imgui.caps.open = true; + sg_imgui.frame_stats.open = true; sg_imgui.buffers.open = true; sg_imgui.images.open = true; sg_imgui.samplers.open = true; @@ -94,12 +101,15 @@ sg_imgui.pipelines.open = true; sg_imgui.passes.open = true; sg_imgui.capture.open = true; + sg_imgui.frame_stats.open = true; ...for instance, to control the window visibility through menu items, the following code can be used: if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("sokol-gfx")) { + ImGui::MenuItem("Capabilities", 0, &sg_imgui.caps.open); + ImGui::MenuItem("Frame Stats", 0, &sg_imgui.frame_stats.open); ImGui::MenuItem("Buffers", 0, &sg_imgui.buffers.open); ImGui::MenuItem("Images", 0, &sg_imgui.images.open); ImGui::MenuItem("Samplers", 0, &sg_imgui.samplers.open); @@ -169,8 +179,8 @@ sg_imgui_init(&(&ctx, &(sg_imgui_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -709,6 +719,14 @@ typedef struct sg_imgui_caps_t { bool open; } sg_imgui_caps_t; +typedef struct sg_imgui_frame_stats_t { + bool open; + bool disable_sokol_imgui_stats; + bool in_sokol_imgui; + sg_frame_stats stats; + // FIXME: add a ringbuffer for a stats history here +} sg_imgui_frame_stats_t; + /* sg_imgui_allocator_t @@ -718,8 +736,8 @@ typedef struct sg_imgui_caps_t { override one function but not the other). */ typedef struct sg_imgui_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sg_imgui_allocator_t; @@ -743,6 +761,7 @@ typedef struct sg_imgui_t { sg_imgui_passes_t passes; sg_imgui_capture_t capture; sg_imgui_caps_t caps; + sg_imgui_frame_stats_t frame_stats; sg_pipeline cur_pipeline; sg_trace_hooks hooks; } sg_imgui_t; @@ -751,6 +770,8 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_init(sg_imgui_t* ctx, const sg_imgui_desc SOKOL_GFX_IMGUI_API_DECL void sg_imgui_discard(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw(sg_imgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_menu(sg_imgui_t* ctx, const char* title); + SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_buffers_content(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_images_content(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_samplers_content(sg_imgui_t* ctx); @@ -759,6 +780,7 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_pipelines_content(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_passes_content(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capture_content(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capabilities_content(sg_imgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_frame_stats_content(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_buffers_window(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_images_window(sg_imgui_t* ctx); @@ -768,6 +790,7 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_pipelines_window(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_passes_window(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capture_window(sg_imgui_t* ctx); SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capabilities_window(sg_imgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_frame_stats_window(sg_imgui_t* ctx); #if defined(__cplusplus) } /* extern "C" */ @@ -820,6 +843,8 @@ SOKOL_GFX_IMGUI_API_DECL void sg_imgui_draw_capabilities_window(sg_imgui_t* ctx) #define _SG_IMGUI_LIST_WIDTH (192) #define _SG_IMGUI_COLOR_OTHER 0xFFCCCCCC #define _SG_IMGUI_COLOR_RSRC 0xFF00FFFF +#define _SG_IMGUI_COLOR_PASS 0xFFFFFF00 +#define _SG_IMGUI_COLOR_APPLY 0xFFCCCC00 #define _SG_IMGUI_COLOR_DRAW 0xFF00FF00 #define _SG_IMGUI_COLOR_ERR 0xFF8888FF @@ -900,6 +925,39 @@ _SOKOL_PRIVATE bool igBegin(const char* name,bool* p_open,ImGuiWindowFlags flags _SOKOL_PRIVATE void igEnd() { return ImGui::End(); } +_SOKOL_PRIVATE bool igBeginMenu(const char* label, bool enabled) { + return ImGui::BeginMenu(label, enabled); +} +_SOKOL_PRIVATE void igEndMenu(void) { + ImGui::EndMenu(); +} +_SOKOL_PRIVATE bool igMenuItem_BoolPtr(const char* label, const char* shortcut, bool* p_selected, bool enabled) { + return ImGui::MenuItem(label, shortcut, p_selected, enabled); +} +_SOKOL_PRIVATE bool igBeginTable(const char* str_id, int column, ImGuiTableFlags flags, const ImVec2 outer_size, float inner_width) { + return ImGui::BeginTable(str_id, column, flags, outer_size, inner_width); +} +_SOKOL_PRIVATE void igEndTable(void) { + ImGui::EndTable(); +} +_SOKOL_PRIVATE void igTableSetupScrollFreeze(int cols, int rows) { + ImGui::TableSetupScrollFreeze(cols, rows); +} +_SOKOL_PRIVATE void igTableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) { + ImGui::TableSetupColumn(label, flags, init_width_or_weight, user_id); +} +_SOKOL_PRIVATE void igTableHeadersRow(void) { + ImGui::TableHeadersRow(); +} +_SOKOL_PRIVATE void igTableNextRow(ImGuiTableRowFlags row_flags, float min_row_height) { + ImGui::TableNextRow(row_flags, min_row_height); +} +_SOKOL_PRIVATE bool igTableSetColumnIndex(int column_n) { + return ImGui::TableSetColumnIndex(column_n); +} +_SOKOL_PRIVATE bool igCheckbox(const char* label, bool* v) { + return ImGui::Checkbox(label, v); +} #else #define IMVEC2(x,y) (ImVec2){x,y} #define IMVEC4(x,y,z,w) (ImVec4){x,y,z,w} @@ -914,8 +972,8 @@ _SOKOL_PRIVATE void _sg_imgui_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sg_imgui_malloc(const sg_imgui_allocator_t* allocator, size_t size) { SOKOL_ASSERT(allocator && (size > 0)); void* ptr; - if (allocator->alloc) { - ptr = allocator->alloc(size, allocator->user_data); + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); } else { ptr = malloc(size); } @@ -931,8 +989,8 @@ _SOKOL_PRIVATE void* _sg_imgui_malloc_clear(const sg_imgui_allocator_t* allocato _SOKOL_PRIVATE void _sg_imgui_free(const sg_imgui_allocator_t* allocator, void* ptr) { SOKOL_ASSERT(allocator); - if (allocator->free) { - allocator->free(ptr, allocator->user_data); + if (allocator->free_fn) { + allocator->free_fn(ptr, allocator->user_data); } else { free(ptr); } @@ -1098,6 +1156,7 @@ _SOKOL_PRIVATE const char* _sg_imgui_backend_string(sg_backend b) { 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_DUMMY: return "SG_BACKEND_DUMMY"; default: return "???"; } @@ -1136,15 +1195,17 @@ _SOKOL_PRIVATE const char* _sg_imgui_imagesampletype_string(sg_image_sample_type 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* _sg_imgui_samplertype_string(sg_sampler_type t) { switch (t) { - case SG_SAMPLERTYPE_SAMPLE: return "SG_SAMPLERTYPE_SAMPLE"; - case SG_SAMPLERTYPE_COMPARE: return "SG_SAMPLERTYPE_COMPARE"; - default: return "???"; + 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 "???"; } } @@ -1221,6 +1282,12 @@ _SOKOL_PRIVATE const char* _sg_imgui_pixelformat_string(sg_pixel_format fmt) { case SG_PIXELFORMAT_ETC2_RG11: return "SG_PIXELFORMAT_ETC2_RG11"; case SG_PIXELFORMAT_ETC2_RG11SN: return "SG_PIXELFORMAT_ETC2_RG11SN"; case SG_PIXELFORMAT_RGB9E5: return "SG_PIXELFORMAT_RGB9E5"; + 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 "???"; } } @@ -2438,7 +2505,7 @@ _SOKOL_PRIVATE void _sg_imgui_begin_default_pass(const sg_pass_action* pass_acti if (item) { SOKOL_ASSERT(pass_action); item->cmd = SG_IMGUI_CMD_BEGIN_DEFAULT_PASS; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_PASS; item->args.begin_default_pass.action = *pass_action; item->args.begin_default_pass.width = width; item->args.begin_default_pass.height = height; @@ -2455,7 +2522,7 @@ _SOKOL_PRIVATE void _sg_imgui_begin_pass(sg_pass pass, const sg_pass_action* pas if (item) { SOKOL_ASSERT(pass_action); item->cmd = SG_IMGUI_CMD_BEGIN_PASS; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_PASS; item->args.begin_pass.pass = pass; item->args.begin_pass.action = *pass_action; } @@ -2470,7 +2537,7 @@ _SOKOL_PRIVATE void _sg_imgui_apply_viewport(int x, int y, int width, int height sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_APPLY_VIEWPORT; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_APPLY; item->args.apply_viewport.x = x; item->args.apply_viewport.y = y; item->args.apply_viewport.width = width; @@ -2488,7 +2555,7 @@ _SOKOL_PRIVATE void _sg_imgui_apply_scissor_rect(int x, int y, int width, int he sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_APPLY_SCISSOR_RECT; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_APPLY; item->args.apply_scissor_rect.x = x; item->args.apply_scissor_rect.y = y; item->args.apply_scissor_rect.width = width; @@ -2507,7 +2574,7 @@ _SOKOL_PRIVATE void _sg_imgui_apply_pipeline(sg_pipeline pip, void* user_data) { sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_APPLY_PIPELINE; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_APPLY; item->args.apply_pipeline.pipeline = pip; } if (ctx->hooks.apply_pipeline) { @@ -2522,7 +2589,7 @@ _SOKOL_PRIVATE void _sg_imgui_apply_bindings(const sg_bindings* bindings, void* if (item) { SOKOL_ASSERT(bindings); item->cmd = SG_IMGUI_CMD_APPLY_BINDINGS; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_APPLY; item->args.apply_bindings.bindings = *bindings; } if (ctx->hooks.apply_bindings) { @@ -2537,7 +2604,7 @@ _SOKOL_PRIVATE void _sg_imgui_apply_uniforms(sg_shader_stage stage, int ub_index sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_APPLY_UNIFORMS; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_APPLY; sg_imgui_args_apply_uniforms_t* args = &item->args.apply_uniforms; args->stage = stage; args->ub_index = ub_index; @@ -2573,7 +2640,7 @@ _SOKOL_PRIVATE void _sg_imgui_end_pass(void* user_data) { sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_END_PASS; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_PASS; } if (ctx->hooks.end_pass) { ctx->hooks.end_pass(ctx->hooks.user_data); @@ -2586,7 +2653,7 @@ _SOKOL_PRIVATE void _sg_imgui_commit(void* user_data) { sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_COMMIT; - item->color = _SG_IMGUI_COLOR_DRAW; + item->color = _SG_IMGUI_COLOR_OTHER; } _sg_imgui_capture_next_frame(ctx); if (ctx->hooks.commit) { @@ -3053,6 +3120,12 @@ _SOKOL_PRIVATE void _sg_imgui_fail_pass(sg_pass pass_id, void* user_data) { _SOKOL_PRIVATE void _sg_imgui_push_debug_group(const char* name, void* user_data) { sg_imgui_t* ctx = (sg_imgui_t*) user_data; SOKOL_ASSERT(ctx); + if (0 == strcmp(name, "sokol-imgui")) { + ctx->frame_stats.in_sokol_imgui = true; + if (ctx->frame_stats.disable_sokol_imgui_stats) { + sg_disable_frame_stats(); + } + } sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_PUSH_DEBUG_GROUP; @@ -3067,6 +3140,12 @@ _SOKOL_PRIVATE void _sg_imgui_push_debug_group(const char* name, void* user_data _SOKOL_PRIVATE void _sg_imgui_pop_debug_group(void* user_data) { sg_imgui_t* ctx = (sg_imgui_t*) user_data; SOKOL_ASSERT(ctx); + if (ctx->frame_stats.in_sokol_imgui) { + ctx->frame_stats.in_sokol_imgui = false; + if (ctx->frame_stats.disable_sokol_imgui_stats) { + sg_enable_frame_stats(); + } + } sg_imgui_capture_item_t* item = _sg_imgui_capture_next_write_item(ctx); if (item) { item->cmd = SG_IMGUI_CMD_POP_DEBUG_GROUP; @@ -4142,10 +4221,135 @@ _SOKOL_PRIVATE void _sg_imgui_draw_caps_panel(void) { } } +_SOKOL_PRIVATE void _sg_imgui_frame_add_stats_row(const char* key, uint32_t value) { + igTableNextRow(0, 0.0f); + igTableSetColumnIndex(0); + igText(key); + igTableSetColumnIndex(1); + igText("%d", value); +} + +#define _sg_imgui_frame_stats(key) _sg_imgui_frame_add_stats_row(#key, stats->key) + +_SOKOL_PRIVATE void _sg_imgui_draw_frame_stats_panel(sg_imgui_t* ctx) { + _SOKOL_UNUSED(ctx); + igCheckbox("Ignore sokol_imgui.h", &ctx->frame_stats.disable_sokol_imgui_stats); + const sg_frame_stats* stats = &ctx->frame_stats.stats; + const ImGuiTableFlags flags = + ImGuiTableFlags_Resizable | + ImGuiTableFlags_ScrollY | + ImGuiTableFlags_SizingFixedFit | + ImGuiTableFlags_Borders; + if (igBeginTable("##frame_stats_table", 2, flags, IMVEC2(0, 0), 0)) { + igTableSetupScrollFreeze(0, 2); + igTableSetupColumn("key", ImGuiTableColumnFlags_None, 0, 0); + igTableSetupColumn("value", ImGuiTableColumnFlags_None, 0, 0); + igTableHeadersRow(); + _sg_imgui_frame_stats(frame_index); + _sg_imgui_frame_stats(num_passes); + _sg_imgui_frame_stats(num_apply_viewport); + _sg_imgui_frame_stats(num_apply_scissor_rect); + _sg_imgui_frame_stats(num_apply_pipeline); + _sg_imgui_frame_stats(num_apply_bindings); + _sg_imgui_frame_stats(num_apply_uniforms); + _sg_imgui_frame_stats(num_draw); + _sg_imgui_frame_stats(num_update_buffer); + _sg_imgui_frame_stats(num_append_buffer); + _sg_imgui_frame_stats(num_update_image); + _sg_imgui_frame_stats(size_apply_uniforms); + _sg_imgui_frame_stats(size_update_buffer); + _sg_imgui_frame_stats(size_append_buffer); + _sg_imgui_frame_stats(size_update_image); + switch (sg_query_backend()) { + case SG_BACKEND_GLCORE33: + case SG_BACKEND_GLES3: + _sg_imgui_frame_stats(gl.num_bind_buffer); + _sg_imgui_frame_stats(gl.num_active_texture); + _sg_imgui_frame_stats(gl.num_bind_texture); + _sg_imgui_frame_stats(gl.num_bind_sampler); + _sg_imgui_frame_stats(gl.num_use_program); + _sg_imgui_frame_stats(gl.num_render_state); + _sg_imgui_frame_stats(gl.num_vertex_attrib_pointer); + _sg_imgui_frame_stats(gl.num_vertex_attrib_divisor); + _sg_imgui_frame_stats(gl.num_enable_vertex_attrib_array); + _sg_imgui_frame_stats(gl.num_disable_vertex_attrib_array); + _sg_imgui_frame_stats(gl.num_uniform); + break; + case SG_BACKEND_WGPU: + _sg_imgui_frame_stats(wgpu.uniforms.num_set_bindgroup); + _sg_imgui_frame_stats(wgpu.uniforms.size_write_buffer); + _sg_imgui_frame_stats(wgpu.bindings.num_set_vertex_buffer); + _sg_imgui_frame_stats(wgpu.bindings.num_skip_redundant_vertex_buffer); + _sg_imgui_frame_stats(wgpu.bindings.num_set_index_buffer); + _sg_imgui_frame_stats(wgpu.bindings.num_skip_redundant_index_buffer); + _sg_imgui_frame_stats(wgpu.bindings.num_create_bindgroup); + _sg_imgui_frame_stats(wgpu.bindings.num_discard_bindgroup); + _sg_imgui_frame_stats(wgpu.bindings.num_set_bindgroup); + _sg_imgui_frame_stats(wgpu.bindings.num_skip_redundant_bindgroup); + _sg_imgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hits); + _sg_imgui_frame_stats(wgpu.bindings.num_bindgroup_cache_misses); + _sg_imgui_frame_stats(wgpu.bindings.num_bindgroup_cache_collisions); + _sg_imgui_frame_stats(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: + _sg_imgui_frame_stats(metal.idpool.num_added); + _sg_imgui_frame_stats(metal.idpool.num_released); + _sg_imgui_frame_stats(metal.idpool.num_garbage_collected); + _sg_imgui_frame_stats(metal.pipeline.num_set_blend_color); + _sg_imgui_frame_stats(metal.pipeline.num_set_cull_mode); + _sg_imgui_frame_stats(metal.pipeline.num_set_front_facing_winding); + _sg_imgui_frame_stats(metal.pipeline.num_set_stencil_reference_value); + _sg_imgui_frame_stats(metal.pipeline.num_set_depth_bias); + _sg_imgui_frame_stats(metal.pipeline.num_set_render_pipeline_state); + _sg_imgui_frame_stats(metal.pipeline.num_set_depth_stencil_state); + _sg_imgui_frame_stats(metal.bindings.num_set_vertex_buffer); + _sg_imgui_frame_stats(metal.bindings.num_set_vertex_texture); + _sg_imgui_frame_stats(metal.bindings.num_set_vertex_sampler_state); + _sg_imgui_frame_stats(metal.bindings.num_set_fragment_texture); + _sg_imgui_frame_stats(metal.bindings.num_set_fragment_sampler_state); + _sg_imgui_frame_stats(metal.uniforms.num_set_vertex_buffer_offset); + _sg_imgui_frame_stats(metal.uniforms.num_set_fragment_buffer_offset); + break; + case SG_BACKEND_D3D11: + _sg_imgui_frame_stats(d3d11.pass.num_om_set_render_targets); + _sg_imgui_frame_stats(d3d11.pass.num_clear_render_target_view); + _sg_imgui_frame_stats(d3d11.pass.num_clear_depth_stencil_view); + _sg_imgui_frame_stats(d3d11.pass.num_resolve_subresource); + _sg_imgui_frame_stats(d3d11.pipeline.num_rs_set_state); + _sg_imgui_frame_stats(d3d11.pipeline.num_om_set_depth_stencil_state); + _sg_imgui_frame_stats(d3d11.pipeline.num_om_set_blend_state); + _sg_imgui_frame_stats(d3d11.pipeline.num_ia_set_primitive_topology); + _sg_imgui_frame_stats(d3d11.pipeline.num_ia_set_input_layout); + _sg_imgui_frame_stats(d3d11.pipeline.num_vs_set_shader); + _sg_imgui_frame_stats(d3d11.pipeline.num_vs_set_constant_buffers); + _sg_imgui_frame_stats(d3d11.pipeline.num_ps_set_shader); + _sg_imgui_frame_stats(d3d11.pipeline.num_ps_set_constant_buffers); + _sg_imgui_frame_stats(d3d11.bindings.num_ia_set_vertex_buffers); + _sg_imgui_frame_stats(d3d11.bindings.num_ia_set_index_buffer); + _sg_imgui_frame_stats(d3d11.bindings.num_vs_set_shader_resources); + _sg_imgui_frame_stats(d3d11.bindings.num_ps_set_shader_resources); + _sg_imgui_frame_stats(d3d11.bindings.num_vs_set_samplers); + _sg_imgui_frame_stats(d3d11.bindings.num_ps_set_samplers); + _sg_imgui_frame_stats(d3d11.uniforms.num_update_subresource); + _sg_imgui_frame_stats(d3d11.draw.num_draw_indexed_instanced); + _sg_imgui_frame_stats(d3d11.draw.num_draw_indexed); + _sg_imgui_frame_stats(d3d11.draw.num_draw_instanced); + _sg_imgui_frame_stats(d3d11.draw.num_draw); + _sg_imgui_frame_stats(d3d11.num_map); + _sg_imgui_frame_stats(d3d11.num_unmap); + break; + default: break; + } + igEndTable(); + } +} + #define _sg_imgui_def(val, def) (((val) == 0) ? (def) : (val)) _SOKOL_PRIVATE sg_imgui_desc_t _sg_imgui_desc_defaults(const sg_imgui_desc_t* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sg_imgui_desc_t res = *desc; // FIXME: any additional default overrides would go here return res; @@ -4323,6 +4527,24 @@ SOKOL_API_IMPL void sg_imgui_draw(sg_imgui_t* ctx) { sg_imgui_draw_passes_window(ctx); sg_imgui_draw_capture_window(ctx); sg_imgui_draw_capabilities_window(ctx); + sg_imgui_draw_frame_stats_window(ctx); +} + +SOKOL_API_IMPL void sg_imgui_draw_menu(sg_imgui_t* ctx, const char* title) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + SOKOL_ASSERT(title); + if (igBeginMenu(title, true)) { + igMenuItem_BoolPtr("Capabilities", 0, &ctx->caps.open, true); + igMenuItem_BoolPtr("Frame Stats", 0, &ctx->frame_stats.open, true); + igMenuItem_BoolPtr("Buffers", 0, &ctx->buffers.open, true); + igMenuItem_BoolPtr("Images", 0, &ctx->images.open, true); + igMenuItem_BoolPtr("Samplers", 0, &ctx->samplers.open, true); + igMenuItem_BoolPtr("Shaders", 0, &ctx->shaders.open, true); + igMenuItem_BoolPtr("Pipelines", 0, &ctx->pipelines.open, true); + igMenuItem_BoolPtr("Passes", 0, &ctx->passes.open, true); + igMenuItem_BoolPtr("Calls", 0, &ctx->capture.open, true); + igEndMenu(); + } } SOKOL_API_IMPL void sg_imgui_draw_buffers_window(sg_imgui_t* ctx) { @@ -4421,6 +4643,18 @@ SOKOL_API_IMPL void sg_imgui_draw_capabilities_window(sg_imgui_t* ctx) { igEnd(); } +SOKOL_API_IMPL void sg_imgui_draw_frame_stats_window(sg_imgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->frame_stats.open) { + return; + } + igSetNextWindowSize(IMVEC2(512, 400), ImGuiCond_Once); + if (igBegin("Frame Stats", &ctx->frame_stats.open, 0)) { + sg_imgui_draw_frame_stats_content(ctx); + } + igEnd(); +} + SOKOL_API_IMPL void sg_imgui_draw_buffers_content(sg_imgui_t* ctx) { SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); _sg_imgui_draw_buffer_list(ctx); @@ -4476,4 +4710,10 @@ SOKOL_API_IMPL void sg_imgui_draw_capabilities_content(sg_imgui_t* ctx) { _sg_imgui_draw_caps_panel(); } +SOKOL_API_IMPL void sg_imgui_draw_frame_stats_content(sg_imgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + ctx->frame_stats.stats = sg_query_frame_stats(); + _sg_imgui_draw_frame_stats_panel(ctx); +} + #endif /* SOKOL_GFX_IMGUI_IMPL */ diff --git a/util/sokol_gl.h b/util/sokol_gl.h index f17da478..88a62bad 100644 --- a/util/sokol_gl.h +++ b/util/sokol_gl.h @@ -606,8 +606,8 @@ sgl_setup(&(sgl_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -796,8 +796,8 @@ typedef struct sgl_context_desc_t { override one function but not the other). */ typedef struct sgl_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sgl_allocator_t; @@ -833,7 +833,7 @@ SOKOL_GL_API_DECL sgl_context sgl_get_context(void); SOKOL_GL_API_DECL sgl_context sgl_default_context(void); /* draw recorded commands (call inside a sokol-gfx render pass) */ -SOKOL_GL_API_DECL void sgl_draw(); +SOKOL_GL_API_DECL void sgl_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); @@ -991,7 +991,9 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline 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; } @@ -2078,191 +2080,123 @@ static const uint8_t _sgl_fs_bytecode_hlsl4[608] = { }; #elif defined(SOKOL_WGPU) -static const uint8_t _sgl_vs_bytecode_wgpu[1968] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x35,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x33,0x00,0x00,0x00, - 0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00,0x00,0x00, - 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, - 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, - 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, - 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, - 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, - 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, - 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0d,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x13,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, - 0x06,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, - 0x05,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x5f,0x32,0x31,0x00,0x05,0x00,0x05,0x00, - 0x1a,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x70,0x73,0x69,0x7a,0x65,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, - 0x2a,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0d,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x47,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x13,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x25,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00, - 0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00, - 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x15,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x18,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x1e,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, - 0x3b,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x91,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x1b,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x1d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1e,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x24,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x26,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x27,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x28,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x08,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x2f,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x91,0x00,0x05,0x00, - 0x09,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x09,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x32,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _sgl_vs_source_wgsl[1162] = { + 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,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29, + 0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,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,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32,0x20,0x3a,0x20,0x6d,0x61, + 0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f, + 0x31,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x32,0x20,0x2a,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x32,0x20,0x3a,0x20,0x6d, + 0x61,0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x74,0x6d, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x36,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x28,0x78,0x5f,0x33,0x32,0x20,0x2a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x33,0x36,0x2e,0x78,0x2c,0x20,0x78, + 0x5f,0x33,0x36,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, + 0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x35,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x35, + 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,0x0a,0x00, }; -static const uint8_t _sgl_fs_bytecode_wgpu[936] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1a,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00, - 0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30, - 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, - 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72, - 0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e, - 0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f, - 0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e, - 0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f, - 0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e, - 0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23, - 0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00, - 0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, - 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00, - 0x0f,0x00,0x00,0x00,0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00, - 0x75,0x76,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, - 0x72,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, - 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x08,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0d,0x00,0x00,0x00, - 0x0c,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00, - 0x08,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x4f,0x00,0x07,0x00,0x13,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00, - 0x09,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x18,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x19,0x00,0x00,0x00, - 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _sgl_fs_source_wgsl[647] = { + 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,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 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,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x34,0x38,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,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,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,0x76,0x65,0x63,0x32,0x66,0x28,0x78,0x5f,0x32,0x33,0x2e,0x78, + 0x2c,0x20,0x78,0x5f,0x32,0x33,0x2e,0x79,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,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,0x78, + 0x5f,0x32,0x37,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,0x0a,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _sgl_vs_source_dummy = ""; @@ -2503,8 +2437,8 @@ static void _sgl_clear(void* ptr, size_t size) { static void* _sgl_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sgl.desc.allocator.alloc) { - ptr = _sgl.desc.allocator.alloc(size, _sgl.desc.allocator.user_data); + if (_sgl.desc.allocator.alloc_fn) { + ptr = _sgl.desc.allocator.alloc_fn(size, _sgl.desc.allocator.user_data); } else { ptr = malloc(size); } @@ -2521,8 +2455,8 @@ static void* _sgl_malloc_clear(size_t size) { } static void _sgl_free(void* ptr) { - if (_sgl.desc.allocator.free) { - _sgl.desc.allocator.free(ptr, _sgl.desc.allocator.user_data); + if (_sgl.desc.allocator.free_fn) { + _sgl.desc.allocator.free_fn(ptr, _sgl.desc.allocator.user_data); } else { free(ptr); } @@ -3283,7 +3217,7 @@ static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) { // return sg_context_desc_t with patched defaults static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + 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); @@ -3344,7 +3278,7 @@ static void _sgl_setup_common(void) { shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; shd_desc.fs.samplers[0].used = true; - shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; shd_desc.fs.image_sampler_pairs[0].used = true; shd_desc.fs.image_sampler_pairs[0].image_slot = 0; shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; @@ -3377,8 +3311,8 @@ static void _sgl_setup_common(void) { shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_wgpu); - shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_wgpu); + shd_desc.vs.source = _sgl_vs_source_wgsl; + shd_desc.fs.source = _sgl_fs_source_wgsl; #else shd_desc.vs.source = _sgl_vs_source_dummy; shd_desc.fs.source = _sgl_fs_source_dummy; diff --git a/util/sokol_imgui.h b/util/sokol_imgui.h index 2d4cd945..59c150b3 100644 --- a/util/sokol_imgui.h +++ b/util/sokol_imgui.h @@ -32,8 +32,8 @@ SOKOL_METAL SOKOL_WGPU - Optionally provide the following configuration defines before including the - implementation: + 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) @@ -288,8 +288,8 @@ simgui_setup(&(simgui_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -467,12 +467,12 @@ typedef enum simgui_log_item_t { Used in simgui_desc_t to provide custom memory-alloc and -free functions to sokol_imgui.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + alloc_fn and free_fn function must be provided (e.g. it's not valid to override one function but not the other). */ typedef struct simgui_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } simgui_allocator_t; @@ -639,6 +639,7 @@ typedef struct { _simgui_slot_t slot; sg_image image; sg_sampler sampler; + sg_pipeline pip; // this will either be _simgui.def_pip or _simgui.pip_unfilterable } _simgui_image_t; typedef struct { @@ -657,8 +658,11 @@ typedef struct { simgui_image_t default_font; sg_image def_img; // used as default image for user images sg_sampler def_smp; // used as default sampler for user images - sg_shader shd; - sg_pipeline pip; + 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; @@ -1704,183 +1708,117 @@ static const uint8_t _simgui_fs_bytecode_hlsl4[608] = { }; #elif defined(SOKOL_WGPU) -static const uint8_t _simgui_vs_bytecode_wgpu[1868] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x34,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x32,0x00,0x00,0x00, - 0x33,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, - 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, - 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, - 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, - 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, - 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, - 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x13,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x05,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, - 0x73,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x17,0x00,0x00,0x00,0x5f,0x32,0x33,0x00,0x05,0x00,0x03,0x00,0x2a,0x00,0x00,0x00, - 0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00,0x2b,0x00,0x00,0x00,0x74,0x65,0x78,0x63, - 0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00, - 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2f,0x00,0x00,0x00, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00,0x05,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65,0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00, - 0x33,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49, - 0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, - 0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0f,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0f,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x11,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1e,0x00,0x03,0x00,0x15,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x3f,0x2c,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x2c,0x00,0x05,0x00,0x11,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x20,0x00,0x04,0x00, - 0x27,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x29,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x27,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x2e,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x31,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x31,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, - 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x18,0x00,0x00,0x00, - 0x19,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x88,0x00,0x05,0x00, - 0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x83,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x1d,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x27,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x28,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x2a,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2d,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _simgui_vs_source_wgsl[1083] = { + 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,0x67,0x72,0x6f,0x75,0x70,0x28, + 0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,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,0x31,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x32,0x32, + 0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x28,0x28,0x28,0x78,0x5f,0x31,0x39,0x20,0x2f,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,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,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x33,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x34,0x33,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x37,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,0x0a,0x00, }; -static const uint8_t _simgui_fs_bytecode_wgpu[904] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x19,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, - 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, - 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, - 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, - 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, - 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, - 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, - 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, - 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, - 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, - 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x75,0x76,0x00,0x00, - 0x05,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x07,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, - 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x0c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x57,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x17,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _simgui_fs_source_wgsl[630] = { + 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,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 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,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x34,0x38,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,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,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,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,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,0x78,0x5f, + 0x32,0x37,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,0x0a,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _simgui_vs_source_dummy = ""; @@ -1965,8 +1903,8 @@ static void _simgui_clear(void* ptr, size_t size) { static void* _simgui_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_simgui.desc.allocator.alloc) { - ptr = _simgui.desc.allocator.alloc(size, _simgui.desc.allocator.user_data); + if (_simgui.desc.allocator.alloc_fn) { + ptr = _simgui.desc.allocator.alloc_fn(size, _simgui.desc.allocator.user_data); } else { ptr = malloc(size); } @@ -1983,8 +1921,8 @@ static void* _simgui_malloc_clear(size_t size) { } static void _simgui_free(void* ptr) { - if (_simgui.desc.allocator.free) { - _simgui.desc.allocator.free(ptr, _simgui.desc.allocator.user_data); + if (_simgui.desc.allocator.free_fn) { + _simgui.desc.allocator.free_fn(ptr, _simgui.desc.allocator.user_data); } else { free(ptr); } @@ -2147,8 +2085,15 @@ static simgui_image_t _simgui_alloc_image(void) { static _simgui_resource_state _simgui_init_image(_simgui_image_t* img, const simgui_image_desc_t* desc) { SOKOL_ASSERT(img && (img->slot.state == _SIMGUI_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); + SOKOL_ASSERT(_simgui.def_pip.id != SIMGUI_INVALID_ID); + SOKOL_ASSERT(_simgui.pip_unfilterable.id != SIMGUI_INVALID_ID); img->image = desc->image; img->sampler = desc->sampler; + if (sg_query_pixelformat(sg_query_image_desc(desc->image).pixel_format).filter) { + img->pip = _simgui.def_pip; + } else { + img->pip = _simgui.pip_unfilterable; + } return _SIMGUI_RESOURCESTATE_VALID; } @@ -2156,6 +2101,7 @@ static void _simgui_deinit_image(_simgui_image_t* img) { SOKOL_ASSERT(img); img->image.id = SIMGUI_INVALID_ID; img->sampler.id = SIMGUI_INVALID_ID; + img->pip.id = SIMGUI_INVALID_ID; } static void _simgui_destroy_image(simgui_image_t img_id) { @@ -2197,7 +2143,7 @@ static bool _simgui_is_osx(void) { } static simgui_desc_t _simgui_desc_defaults(const simgui_desc_t* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); simgui_desc_t res = *desc; res.max_vertices = _simgui_def(res.max_vertices, 65536); res.image_pool_size = _simgui_def(res.image_pool_size, 256); @@ -2264,84 +2210,6 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { // create sokol-gfx resources sg_push_debug_group("sokol-imgui"); - // NOTE: since we're in C++ mode here we can't use C99 designated init - sg_buffer_desc vb_desc; - _simgui_clear(&vb_desc, sizeof(vb_desc)); - vb_desc.usage = SG_USAGE_STREAM; - vb_desc.size = _simgui.vertices.size; - vb_desc.label = "sokol-imgui-vertices"; - _simgui.vbuf = sg_make_buffer(&vb_desc); - - sg_buffer_desc ib_desc; - _simgui_clear(&ib_desc, sizeof(ib_desc)); - ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER; - ib_desc.usage = SG_USAGE_STREAM; - ib_desc.size = _simgui.indices.size; - ib_desc.label = "sokol-imgui-indices"; - _simgui.ibuf = sg_make_buffer(&ib_desc); - - // a default font sampler - sg_sampler_desc font_smp_desc; - _simgui_clear(&font_smp_desc, sizeof(font_smp_desc)); - font_smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; - font_smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; - font_smp_desc.min_filter = SG_FILTER_LINEAR; - font_smp_desc.mag_filter = SG_FILTER_LINEAR; - font_smp_desc.mipmap_filter = SG_FILTER_NONE; - font_smp_desc.label = "sokol-imgui-font-sampler"; - _simgui.font_smp = sg_make_sampler(&font_smp_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); - - // default font texture - if (!_simgui.desc.no_default_font) { - unsigned char* font_pixels; - int font_width, font_height; - #if defined(__cplusplus) - io->Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height); - #else - int bytes_per_pixel; - ImFontAtlas_GetTexDataAsRGBA32(io->Fonts, &font_pixels, &font_width, &font_height, &bytes_per_pixel); - #endif - sg_image_desc font_img_desc; - _simgui_clear(&font_img_desc, sizeof(font_img_desc)); - font_img_desc.width = font_width; - font_img_desc.height = font_height; - font_img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; - font_img_desc.data.subimage[0][0].ptr = font_pixels; - font_img_desc.data.subimage[0][0].size = (size_t)(font_width * font_height) * sizeof(uint32_t); - font_img_desc.label = "sokol-imgui-font-image"; - _simgui.font_img = sg_make_image(&font_img_desc); - - simgui_image_desc_t img_desc; - _simgui_clear(&img_desc, sizeof(img_desc)); - img_desc.image = _simgui.font_img; - img_desc.sampler = _simgui.font_smp; - _simgui.default_font = simgui_make_image(&img_desc); - io->Fonts->TexID = simgui_imtextureid(_simgui.default_font); - } - - // a default user image - static uint32_t def_pixels[64]; - memset(def_pixels, 0xFF, sizeof(def_pixels)); - sg_image_desc def_image_desc; - _simgui_clear(&def_image_desc, sizeof(def_image_desc)); - def_image_desc.width = 8; - def_image_desc.height = 8; - def_image_desc.pixel_format = SG_PIXELFORMAT_RGBA8; - def_image_desc.data.subimage[0][0].ptr = def_pixels; - def_image_desc.data.subimage[0][0].size = sizeof(def_pixels); - def_image_desc.label = "sokol-imgui-default-image"; - _simgui.def_img = sg_make_image(&def_image_desc); - // shader object for using the embedded shader source (or bytecode) sg_shader_desc shd_desc; _simgui_clear(&shd_desc, sizeof(shd_desc)); @@ -2363,7 +2231,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; shd_desc.fs.samplers[0].used = true; - shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; shd_desc.fs.image_sampler_pairs[0].used = true; shd_desc.fs.image_sampler_pairs[0].image_slot = 0; shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; @@ -2396,13 +2264,13 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_wgpu); - shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_wgpu); + shd_desc.vs.source = _simgui_vs_source_wgsl; + shd_desc.fs.source = _simgui_fs_source_wgsl; #else shd_desc.vs.source = _simgui_vs_source_dummy; shd_desc.fs.source = _simgui_fs_source_dummy; #endif - _simgui.shd = sg_make_shader(&shd_desc); + _simgui.def_shd = sg_make_shader(&shd_desc); // pipeline object for imgui rendering sg_pipeline_desc pip_desc; @@ -2423,7 +2291,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { attr->offset = offsetof(ImDrawVert, col); attr->format = SG_VERTEXFORMAT_UBYTE4N; } - pip_desc.shader = _simgui.shd; + 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; @@ -2437,7 +2305,94 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE; } pip_desc.label = "sokol-imgui-pipeline"; - _simgui.pip = sg_make_pipeline(&pip_desc); + _simgui.def_pip = sg_make_pipeline(&pip_desc); + + // create a unfilterable/nonfiltering variants of the shader and pipeline + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT; + shd_desc.fs.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 = SG_USAGE_STREAM; + vb_desc.size = _simgui.vertices.size; + vb_desc.label = "sokol-imgui-vertices"; + _simgui.vbuf = sg_make_buffer(&vb_desc); + + sg_buffer_desc ib_desc; + _simgui_clear(&ib_desc, sizeof(ib_desc)); + ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER; + ib_desc.usage = SG_USAGE_STREAM; + ib_desc.size = _simgui.indices.size; + ib_desc.label = "sokol-imgui-indices"; + _simgui.ibuf = sg_make_buffer(&ib_desc); + + // a default font sampler + sg_sampler_desc font_smp_desc; + _simgui_clear(&font_smp_desc, sizeof(font_smp_desc)); + font_smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + font_smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + font_smp_desc.min_filter = SG_FILTER_LINEAR; + font_smp_desc.mag_filter = SG_FILTER_LINEAR; + font_smp_desc.mipmap_filter = SG_FILTER_NONE; + font_smp_desc.label = "sokol-imgui-font-sampler"; + _simgui.font_smp = sg_make_sampler(&font_smp_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); + + // a default user image + static uint32_t def_pixels[64]; + memset(def_pixels, 0xFF, sizeof(def_pixels)); + sg_image_desc def_image_desc; + _simgui_clear(&def_image_desc, sizeof(def_image_desc)); + def_image_desc.width = 8; + def_image_desc.height = 8; + def_image_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + def_image_desc.data.subimage[0][0].ptr = def_pixels; + def_image_desc.data.subimage[0][0].size = sizeof(def_pixels); + def_image_desc.label = "sokol-imgui-default-image"; + _simgui.def_img = sg_make_image(&def_image_desc); + + // default font texture + if (!_simgui.desc.no_default_font) { + unsigned char* font_pixels; + int font_width, font_height; + #if defined(__cplusplus) + io->Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height); + #else + int bytes_per_pixel; + ImFontAtlas_GetTexDataAsRGBA32(io->Fonts, &font_pixels, &font_width, &font_height, &bytes_per_pixel); + #endif + sg_image_desc font_img_desc; + _simgui_clear(&font_img_desc, sizeof(font_img_desc)); + font_img_desc.width = font_width; + font_img_desc.height = font_height; + font_img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + font_img_desc.data.subimage[0][0].ptr = font_pixels; + font_img_desc.data.subimage[0][0].size = (size_t)(font_width * font_height) * sizeof(uint32_t); + font_img_desc.label = "sokol-imgui-font-image"; + _simgui.font_img = sg_make_image(&font_img_desc); + + simgui_image_desc_t img_desc; + _simgui_clear(&img_desc, sizeof(img_desc)); + img_desc.image = _simgui.font_img; + img_desc.sampler = _simgui.font_smp; + _simgui.default_font = simgui_make_image(&img_desc); + io->Fonts->TexID = simgui_imtextureid(_simgui.default_font); + } sg_pop_debug_group(); } @@ -2450,8 +2405,10 @@ SOKOL_API_IMPL void simgui_shutdown(void) { igDestroyContext(0); #endif // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID - sg_destroy_pipeline(_simgui.pip); - sg_destroy_shader(_simgui.shd); + 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.font_smp); sg_destroy_image(_simgui.font_img); sg_destroy_sampler(_simgui.def_smp); @@ -2562,8 +2519,8 @@ SOKOL_API_IMPL void simgui_new_frame(const simgui_frame_desc_t* desc) { #endif } -static void _simgui_bind_image_sampler(sg_bindings* bindings, ImTextureID tex_id) { - _simgui_image_t* img = _simgui_lookup_image((uint32_t)(uintptr_t)tex_id); +static const _simgui_image_t* _simgui_bind_image_sampler(sg_bindings* bindings, ImTextureID tex_id) { + const _simgui_image_t* img = _simgui_lookup_image((uint32_t)(uintptr_t)tex_id); if (img) { bindings->fs.images[0] = img->image; bindings->fs.samplers[0] = img->sampler; @@ -2571,6 +2528,7 @@ static void _simgui_bind_image_sampler(sg_bindings* bindings, ImTextureID tex_id bindings->fs.images[0] = _simgui.def_img; bindings->fs.samplers[0] = _simgui.def_smp; } + return img; } static ImDrawList* _simgui_imdrawlist_at(ImDrawData* draw_data, int cl_index) { @@ -2657,7 +2615,7 @@ SOKOL_API_IMPL void simgui_render(void) { 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.pip); + 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; @@ -2690,14 +2648,20 @@ SOKOL_API_IMPL void simgui_render(void) { pcmd->UserCallback(cl, pcmd); // need to re-apply all state after calling a user callback sg_apply_viewport(0, 0, fb_width, fb_height, true); - sg_apply_pipeline(_simgui.pip); + sg_apply_pipeline(_simgui.def_pip); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); sg_apply_bindings(&bind); } else { if ((tex_id != pcmd->TextureId) || (vtx_offset != pcmd->VtxOffset)) { tex_id = pcmd->TextureId; vtx_offset = pcmd->VtxOffset; - _simgui_bind_image_sampler(&bind, tex_id); + const _simgui_image_t* img = _simgui_bind_image_sampler(&bind, tex_id); + if (img) { + sg_apply_pipeline(img->pip); + } else { + sg_apply_pipeline(_simgui.def_pip); + } + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); bind.vertex_buffer_offsets[0] = vb_offset + (int)(pcmd->VtxOffset * sizeof(ImDrawVert)); sg_apply_bindings(&bind); } diff --git a/util/sokol_memtrack.h b/util/sokol_memtrack.h index dc0b469f..47f70fca 100644 --- a/util/sokol_memtrack.h +++ b/util/sokol_memtrack.h @@ -27,8 +27,8 @@ sg_setup(&(sg_desc){ //... .allocator = { - .alloc = smemtrack_alloc, - .free = smemtrack_free, + .alloc_fn = smemtrack_alloc, + .free_fn = smemtrack_free, } }); diff --git a/util/sokol_nuklear.h b/util/sokol_nuklear.h index ae4e9ae0..25293252 100644 --- a/util/sokol_nuklear.h +++ b/util/sokol_nuklear.h @@ -145,7 +145,11 @@ --- if you're using sokol_app.h, from inside the sokol_app.h event callback, call: - void snk_handle_event(const sapp_event* ev); + 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 @@ -223,8 +227,8 @@ snk_setup(&(snk_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -383,12 +387,12 @@ typedef enum snk_log_item_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 and free function must be provided (e.g. it's not valid to + alloc_fn and free_fn function must be provided (e.g. it's not valid to override one function but not the other). */ typedef struct snk_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } snk_allocator_t; @@ -436,7 +440,7 @@ 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 void snk_handle_event(const sapp_event* ev); +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); @@ -1612,183 +1616,117 @@ static const uint8_t _snk_fs_bytecode_hlsl4[608] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; #elif defined(SOKOL_WGPU) -static const uint8_t _snk_vs_bytecode_wgpu[1520] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x27,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x32,0x00,0x00,0x00, - 0x33,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, - 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, - 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, - 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, - 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, - 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, - 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, - 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, - 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, - 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, - 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, - 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, - 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x13,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x05,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, - 0x73,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x17,0x00,0x00,0x00,0x5f,0x32,0x33,0x00,0x05,0x00,0x03,0x00,0x2a,0x00,0x00,0x00, - 0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00,0x2b,0x00,0x00,0x00,0x74,0x65,0x78,0x63, - 0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00, - 0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2f,0x00,0x00,0x00, - 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00,0x05,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65,0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00, - 0x33,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49, - 0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00, - 0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0f,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x0f,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x11,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1e,0x00,0x03,0x00,0x15,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x3f,0x2c,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x2c,0x00,0x05,0x00,0x11,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x20,0x00,0x04,0x00, - 0x27,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x29,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x27,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x2e,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x31,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x31,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, - 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x18,0x00,0x00,0x00, - 0x19,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x88,0x00,0x05,0x00, - 0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x83,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x1d,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x27,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x28,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x2a,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, - 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2d,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _snk_vs_source_wgsl[1083] = { + 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,0x67,0x72,0x6f,0x75,0x70,0x28, + 0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,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,0x31,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x32,0x32, + 0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x28,0x28,0x28,0x78,0x5f,0x31,0x39,0x20,0x2f,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,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,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x33,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x34,0x33,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x37,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,0x0a,0x00, }; -static const uint8_t _snk_fs_bytecode_wgpu[904] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x19,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, - 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, - 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, - 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, - 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, - 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, - 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, - 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, - 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, - 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, - 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, - 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, - 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, - 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, - 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x75,0x76,0x00,0x00, - 0x05,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x07,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, - 0x20,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x3b,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, - 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x0c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x57,0x00,0x05,0x00, - 0x08,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x17,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +static const char _snk_fs_source_wgsl[630] = { + 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,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 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,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x34,0x38,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,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,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,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,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,0x78,0x5f, + 0x32,0x37,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,0x0a,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _snk_vs_source_dummy = ""; @@ -1891,8 +1829,8 @@ static void _snk_clear(void* ptr, size_t size) { static void* _snk_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_snuklear.desc.allocator.alloc) { - ptr = _snuklear.desc.allocator.alloc(size, _snuklear.desc.allocator.user_data); + if (_snuklear.desc.allocator.alloc_fn) { + ptr = _snuklear.desc.allocator.alloc_fn(size, _snuklear.desc.allocator.user_data); } else { ptr = malloc(size); } @@ -1909,8 +1847,8 @@ static void* _snk_malloc_clear(size_t size) { } static void _snk_free(void* ptr) { - if (_snuklear.desc.allocator.free) { - _snuklear.desc.allocator.free(ptr, _snuklear.desc.allocator.user_data); + if (_snuklear.desc.allocator.free_fn) { + _snuklear.desc.allocator.free_fn(ptr, _snuklear.desc.allocator.user_data); } else { free(ptr); } @@ -2261,8 +2199,8 @@ SOKOL_API_IMPL void snk_setup(const snk_desc_t* desc) { vs_bytecode = SG_RANGE(_snk_vs_bytecode_hlsl4); fs_bytecode = SG_RANGE(_snk_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - vs_bytecode = SG_RANGE(_snk_vs_bytecode_wgpu); - fs_bytecode = SG_RANGE(_snk_fs_bytecode_wgpu); + vs_source = _snk_vs_source_wgsl; + fs_source = _snk_fs_source_wgsl; #else vs_source = _snk_vs_source_dummy; fs_source = _snk_fs_source_dummy; @@ -2293,7 +2231,7 @@ SOKOL_API_IMPL void snk_setup(const snk_desc_t* desc) { .entry = fs_entry, .d3d11_target = "ps_4_0", .images[0] = { .used = true, .image_type = SG_IMAGETYPE_2D, .sample_type = SG_IMAGESAMPLETYPE_FLOAT }, - .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_SAMPLE }, + .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_FILTERING }, .image_sampler_pairs[0] = { .used = true, .glsl_name = "tex_smp", .image_slot = 0, .sampler_slot = 0 }, }, .label = "sokol-nuklear-shader" @@ -2592,7 +2530,7 @@ _SOKOL_PRIVATE enum nk_keys _snk_event_to_nuklearkey(const sapp_event* ev) { } } -SOKOL_API_IMPL void snk_handle_event(const sapp_event* ev) { +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) { @@ -2710,6 +2648,7 @@ SOKOL_API_IMPL void snk_handle_event(const sapp_event* ev) { 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) { diff --git a/util/sokol_shape.h b/util/sokol_shape.h index a02b4339..d1676dd3 100644 --- a/util/sokol_shape.h +++ b/util/sokol_shape.h @@ -421,18 +421,12 @@ typedef struct sshape_vertex_t { typedef struct sshape_element_range_t { int base_element; int num_elements; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif } 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 - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif } sshape_sizes_item_t; typedef struct sshape_sizes_t { diff --git a/util/sokol_spine.h b/util/sokol_spine.h index 5a9125ae..48295a20 100644 --- a/util/sokol_spine.h +++ b/util/sokol_spine.h @@ -126,7 +126,7 @@ ========================== During initialization: - - call sspine_setup() after initializating sokol-gfx + - 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 objects referenced by the atlas - create a skeleton object from a Spine skeleton file with sspine_make_skeleton() @@ -202,8 +202,8 @@ sspine_setup(&(sspine_desc){ .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., }, .logger = { @@ -424,7 +424,7 @@ sspine_shutdown(); sg_shutdown(); - - You can explicitely destroy the base object types if you don't need them + - 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: @@ -578,10 +578,10 @@ - sspine_context_draw_layer_in_instance(ctx, ...) - sspine_context_draw_layer(ctx, ...) - These explicitely take a context argument, completely ignore + These explicitly take a context argument, completely ignore and don't change the active context. - You can query some information about the a context with the function: + You can query some information about a context with the function: sspine_context_info info = ssgpine_get_context_info(ctx); @@ -604,7 +604,7 @@ - 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 the handle hasn't been + 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: @@ -908,8 +908,8 @@ sspine_setup(&(sspine_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -1198,8 +1198,8 @@ typedef struct sspine_instance_desc { } sspine_instance_desc; typedef struct sspine_allocator { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sspine_allocator; @@ -2571,7 +2571,144 @@ static const char _sspine_fs_source_metal_sim[619] = { 0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_WGPU) -#error "FIXME: wgpu shaders" +static const char _sspine_vs_source_wgsl[1003] = { + 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,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,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,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32,0x20,0x3a,0x20,0x6d, + 0x61,0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76, + 0x70,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x36,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x32,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x28,0x78,0x5f,0x32,0x36,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x36,0x2e,0x79, + 0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x38,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x33,0x38,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x32,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,0x0a,0x00, +}; +static const char _sspine_fs_source_wgsl[1125] = { + 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, + 0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e, + 0x67,0x28,0x33,0x32,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,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e, + 0x64,0x69,0x6e,0x67,0x28,0x34,0x38,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,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20, + 0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,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,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,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,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x63,0x30,0x20,0x3d,0x20,0x28,0x78, + 0x5f,0x32,0x34,0x20,0x2a,0x20,0x78,0x5f,0x32,0x37,0x29,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x33,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x30,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x37, + 0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x63,0x30,0x2e,0x77,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x33, + 0x66,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x33,0x66,0x28,0x78,0x5f,0x33,0x31,0x2e, + 0x78,0x2c,0x20,0x78,0x5f,0x33,0x31,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x33,0x31,0x2e, + 0x7a,0x29,0x20,0x2a,0x20,0x78,0x5f,0x33,0x37,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x34,0x30,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x63, + 0x30,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x35,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,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,0x78,0x5f,0x34,0x30,0x29,0x20,0x2a,0x20,0x78, + 0x5f,0x34,0x35,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x30,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x20,0x3d,0x20,0x63,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, + 0x35,0x38,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x35,0x33,0x2e, + 0x70,0x6d,0x61,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x78,0x5f,0x34,0x39,0x2c,0x20,0x78,0x5f, + 0x35,0x30,0x2c,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x35,0x38,0x2c,0x20, + 0x78,0x5f,0x35,0x38,0x2c,0x20,0x78,0x5f,0x35,0x38,0x2c,0x20,0x78,0x5f,0x35,0x38, + 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,0x0a,0x00, +}; #elif defined(SOKOL_DUMMY_BACKEND) static const char* _sspine_vs_source_dummy = ""; static const char* _sspine_fs_source_dummy = ""; @@ -2893,8 +3030,8 @@ static sspine_string _sspine_string(const char* cstr) { static void* _sspine_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sspine.desc.allocator.alloc) { - ptr = _sspine.desc.allocator.alloc(size, _sspine.desc.allocator.user_data); + if (_sspine.desc.allocator.alloc_fn) { + ptr = _sspine.desc.allocator.alloc_fn(size, _sspine.desc.allocator.user_data); } else { ptr = malloc(size); } @@ -2911,8 +3048,8 @@ static void* _sspine_malloc_clear(size_t size) { } static void _sspine_free(void* ptr) { - if (_sspine.desc.allocator.free) { - _sspine.desc.allocator.free(ptr, _sspine.desc.allocator.user_data); + if (_sspine.desc.allocator.free_fn) { + _sspine.desc.allocator.free_fn(ptr, _sspine.desc.allocator.user_data); } else { free(ptr); } @@ -4392,7 +4529,7 @@ static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_l // return sspine_desc with patched defaults static sspine_desc _sspine_desc_defaults(const sspine_desc* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sspine_desc res = *desc; res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES); res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS); @@ -4521,7 +4658,7 @@ static void _sspine_init_shared(void) { shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; shd_desc.fs.samplers[0].used = true; - shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; shd_desc.fs.image_sampler_pairs[0].used = true; shd_desc.fs.image_sampler_pairs[0].image_slot = 0; shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; @@ -4554,8 +4691,8 @@ static void _sspine_init_shared(void) { shd_desc.vs.bytecode = SG_RANGE(_sspine_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_sspine_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.bytecode = SG_RANGE(_sspine_vs_bytecode_wgpu); - shd_desc.fs.bytecode = SG_RANGE(_sspine_fs_bytecode_wgpu); + shd_desc.vs.source = _sspine_vs_source_wgsl; + shd_desc.fs.source = _sspine_fs_source_wgsl; #else shd_desc.vs.source = _sspine_vs_source_dummy; shd_desc.fs.source = _sspine_fs_source_dummy; |