/* Crafter®.Graphics Copyright (C) 2026 Catcrafts® catcrafts.net */ // User-authored compute shader for DOM mode. // // Contract: // - WGSL source authored by the user. // - Group 0 binding 0 is reserved for the UIDispatchHeader uniform // (with dynamic offset). The library writes it from the first 48 // bytes of the push data each dispatch. // - Group 1 is reserved for the ping-pong textures: binding 0 is the // storage `out` (texture_storage_2d), binding 1 // is the sampled `prev` (texture_2d). The library auto-binds // the right textures depending on the current ping-pong state. // - Groups 2+ are user-defined. The user declares each binding via a // UICustomBinding descriptor at Load time, naming: // - the @group(N) and @binding(N) numbers, // - the resource KIND (buffer / sampled texture / sampler), // - the BYTE OFFSET in the per-dispatch push data where a // uint32 heap slot index lives. // At Dispatch time the renderer reads each declared slot out of // push data, looks the GPU handle up in the heap (bufferTable / // imageTable / samplerTable), and assembles the bind group. export module Crafter.Graphics:WebGPUComputeShader; #ifdef CRAFTER_GRAPHICS_WINDOW_DOM import std; import :WebGPU; export namespace Crafter { enum class UICustomBindingKind : std::uint8_t { Buffer = 0, // read-only-storage SSBO, handle is a slot into heap.bufferTable SampledTexture = 1, // sampled texture_2d, handle is a slot into heap.imageTable Sampler = 2, // filtering sampler, handle is a slot into heap.samplerTable }; struct UICustomBinding { std::uint8_t group; // @group(N), must be >= 2 (0 and 1 are reserved) std::uint8_t binding; // @binding(N) UICustomBindingKind kind; std::uint8_t _pad; std::uint32_t pushOffset; // offset in push data where the slot uint32 lives }; static_assert(sizeof(UICustomBinding) == 8); class WebGPUComputeShader { public: std::uint32_t pipelineHandle = 0; bool rayQueryCapable = false; std::vector customBindings; WebGPUComputeShader() = default; WebGPUComputeShader(const WebGPUComputeShader&) = delete; WebGPUComputeShader& operator=(const WebGPUComputeShader&) = delete; WebGPUComputeShader(WebGPUComputeShader&& o) noexcept : pipelineHandle(o.pipelineHandle), rayQueryCapable(o.rayQueryCapable), customBindings(std::move(o.customBindings)) { o.pipelineHandle = 0; } // Compile + link a custom compute shader. `wgsl` is the source // string; the library does NOT add anything to it (except when // `rayQuery` is true — then a RT prelude exposing the rayQuery* // API is prepended). The user's shader must declare // @group(0)/@group(1) bindings matching the contract above // (rayQuery shaders MUST NOT redeclare group(1)). // `bindings` lists every additional resource (groups 2+) that the // renderer should bind at dispatch time. void Load(std::string_view wgsl, std::span bindings = {}, bool rayQuery = false); // Path-based overload for symmetry with the Vulkan ComputeShader. // Reads the file from disk (browser VFS) and forwards to Load(wgsl). void Load(const std::filesystem::path& wgslPath, std::span bindings = {}, bool rayQuery = false); }; } #endif // CRAFTER_GRAPHICS_WINDOW_DOM