Skip to content

Swapchain ownership per backend

sokol_gfx has no Vulkan backend; midi-sink’s backends are Metal, D3D11, OpenGL/GLES 3 and WebGPU. Each has one swapchain translation unit in the core (swapchain_metal.mm, swapchain_d3d11.cpp, swapchain_gl.cpp, swapchain_webgpu.cpp), and that unit hosts the sokol implementation for its backend — the renderer includes sokol declarations only, and nothing above those files sees a sokol header.

BackendHost passesCore ownsNotes
Metal (macOS, iPadOS)a CAMetalLayer*the MTLDevice, the queue, nextDrawable each frame, the layer’s drawable size on resizeThe host must not touch the layer after sumi_create except to destroy it after sumi_destroy. Objective-C++ with ARC lives only here.
D3D11 (Windows)an HWNDthe device and the DXGI swapchain
OpenGL 4.1 / GLES 3 (Linux, Android)NULL—The exception: the host creates the context (GLFW or EGL), makes it current on the render thread, and the core renders into the currently bound default framebuffer; the host presents (swap buffers). Inherent to GL and documented in the header.
WebGPU (browser)the host’s WGPUDevice and a canvas selectorthe surface from the selector, its configuration in the canvas’s preferred format, the per-frame surface texture, resize reconfigurationThe device is created in JavaScript and handed to the wasm module; readback textures are created with copy-source usage and read through an asynchronous map, so the field regression works without blocking.

The core’s rule is “own the device for the surface you are given”. GL cannot express that: a context is a thread-affine host object. Rather than hide the difference behind a leaky abstraction, the contract states it — backend = GL means the host owns the context — and the Android and Linux shells are written to it (Android’s render thread owns the EGL context and blocks surfaceDestroyed until the surface is unbound).

Every backend implements the same small seam for the print target and the field dump: prepare/release hooks around readback-bound images (no-ops on Metal, D3D11 and GL; CopySrc texture injection on WebGPU) and a non-blocking begin/poll pair for the field read that the synchronous desktop path wraps with a bounded yield.

Authored once in sokol-shdc’s GLSL dialect; compiled at build time to MSL, HLSL 5, GLSL 4.10, GLES 3 and WGSL by a pinned prebuilt sokol-shdc binary that CMake downloads (or takes from SOKOL_SHDC_PATH). The deformation shader and the composite shader are the only two.

midi-sink 2.0.0-rc.5 · AGPL-3.0 · source