/* Crafter®.Graphics Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; */ // Standalone compute pipeline. Dispatches at any point in the frame // (inside or outside the UI render pass) via the JS bridge's // wgpuDispatchCompute, which mirrors the wgpuBuildTLAS pattern of // attaching to the active encoder when one exists or creating an // ephemeral encoder+submit when not. // // This is the WebGPU counterpart to the Vulkan `:ComputeShader` partition. // They expose the same conceptual API — Load + Dispatch — but with // backend-specific binding plumbing. See `:GraphicsTypes` for the // `GraphicsComputeShader` alias picking the right one per target. // // WGSL contract: // @group(0) @binding(0) uniform PushData // optional; only if pushUniformSize>0 // @group(1+) @binding(N) // user bindings via UICustomBinding // When rayQuery is on, @group(1) is reserved for the RT heap; user // bindings start at @group(2). module; export module Crafter.Graphics:PlainComputeShader; #ifdef CRAFTER_GRAPHICS_WINDOW_DOM import std; import :WebGPU; import :WebGPUComputeShader; // for UICustomBinding + UICustomBindingKind export namespace Crafter { class PlainComputeShader { public: std::uint32_t pipelineHandle = 0; std::uint32_t pushUniformSize = 0; bool rayQueryCapable = false; std::vector customBindings; PlainComputeShader() = default; PlainComputeShader(const PlainComputeShader&) = delete; PlainComputeShader& operator=(const PlainComputeShader&) = delete; PlainComputeShader(PlainComputeShader&& o) noexcept : pipelineHandle(o.pipelineHandle), pushUniformSize(o.pushUniformSize), rayQueryCapable(o.rayQueryCapable), customBindings(std::move(o.customBindings)) { o.pipelineHandle = 0; } // Compile + link a standalone compute shader. // wgsl — source. // pushUniformSize — byte size of the @group(0)@binding(0) uniform // struct, or 0 if the shader doesn't declare one. // bindings — every user-declared resource the dispatch // should bind (groups 1+ if no rayQuery, 2+ if // rayQuery). Order MUST match `handles` at // Dispatch time. // rayQuery — prepend the RT prelude + rayQuery library // so the shader can call `rayQuery*` helpers. void Load(std::string_view wgsl, std::uint32_t pushUniformSize_, std::span bindings = {}, bool rayQuery = false) { pushUniformSize = pushUniformSize_; rayQueryCapable = rayQuery; customBindings.assign(bindings.begin(), bindings.end()); pipelineHandle = WebGPU::wgpuLoadComputePipeline( wgsl.data(), static_cast(wgsl.size()), static_cast(pushUniformSize), customBindings.empty() ? nullptr : customBindings.data(), static_cast(customBindings.size()), rayQuery ? 1 : 0); } void Load(const std::filesystem::path& wgslPath, std::uint32_t pushUniformSize_, std::span bindings = {}, bool rayQuery = false) { std::ifstream f(wgslPath, std::ios::binary); if (!f) { std::println(std::cerr, "PlainComputeShader::Load: cannot open {}", wgslPath.string()); std::abort(); } std::string wgsl((std::istreambuf_iterator(f)), std::istreambuf_iterator()); Load(std::string_view{wgsl}, pushUniformSize_, bindings, rayQuery); } // Bind, push, dispatch. `handles` is parallel to the // UICustomBinding[] passed at Load — order matches. void Dispatch(const void* push, std::uint32_t pushBytes, std::span handles, std::uint32_t gx, std::uint32_t gy = 1, std::uint32_t gz = 1) const { if (pipelineHandle == 0) return; WebGPU::wgpuDispatchCompute( pipelineHandle, push, static_cast(pushBytes), handles.empty() ? nullptr : handles.data(), static_cast(handles.size()), static_cast(gx), static_cast(gy), static_cast(gz)); } }; } #endif // CRAFTER_GRAPHICS_WINDOW_DOM