custom shader webgpu
This commit is contained in:
parent
dedf6b0467
commit
64116cd980
12 changed files with 445 additions and 36 deletions
|
|
@ -168,14 +168,17 @@ export namespace Crafter {
|
|||
void DispatchText(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot, std::uint32_t itemCount,
|
||||
std::array<float,4> clipRectPx = {0.0f, 0.0f, 1e9f, 1e9f});
|
||||
|
||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
// Generic dispatch — user-authored shaders. Vulkan-only in v1; on DOM
|
||||
// the WebGPU side has no bindless and would need per-shader bind-group
|
||||
// declaration. See plan section 3b for the design path.
|
||||
void Dispatch(GraphicsCommandBuffer cmd, const ComputeShader& shader,
|
||||
// Generic dispatch for user-authored shaders. On Vulkan, `shader` is
|
||||
// a SPIR-V compute pipeline (bindless via VK_EXT_descriptor_heap, so
|
||||
// any resource indices baked into push data resolve through the
|
||||
// global heap). On DOM, `shader` carries a UICustomBinding list
|
||||
// declared at Load time; the renderer reads the listed slot uints
|
||||
// out of `push`, resolves them against heap.bufferTable /
|
||||
// imageTable / samplerTable, and builds the bind groups before
|
||||
// dispatching.
|
||||
void Dispatch(GraphicsCommandBuffer cmd, const GraphicsComputeShader& shader,
|
||||
const void* push, std::uint32_t pushBytes,
|
||||
std::uint32_t gx, std::uint32_t gy = 1, std::uint32_t gz = 1);
|
||||
#endif
|
||||
|
||||
// Allocates a heap slot for the buffer and registers the GPU handle.
|
||||
// Returns a move-only BufferSlot RAII handle.
|
||||
|
|
|
|||
|
|
@ -71,5 +71,15 @@ namespace Crafter::WebGPU {
|
|||
extern "C" void wgpuDispatchText(std::uint32_t itemsHandle, const void* headerPtr,
|
||||
std::int32_t gx, std::int32_t gy,
|
||||
std::uint32_t atlasHandle, std::uint32_t sampHandle);
|
||||
|
||||
// ─── custom user-authored compute shaders ───────────────────────────
|
||||
__attribute__((import_module("env"), import_name("wgpuLoadCustomShader")))
|
||||
extern "C" std::uint32_t wgpuLoadCustomShader(const void* wgslPtr, std::int32_t wgslLen,
|
||||
const void* bindingsPtr, std::int32_t bindingsCount);
|
||||
__attribute__((import_module("env"), import_name("wgpuDispatchCustom")))
|
||||
extern "C" void wgpuDispatchCustom(std::uint32_t pipelineHandle,
|
||||
const void* pushPtr, std::int32_t pushBytes,
|
||||
const void* handlesPtr, std::int32_t handlesCount,
|
||||
std::int32_t gx, std::int32_t gy, std::int32_t gz);
|
||||
}
|
||||
#endif // CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
|
|||
|
|
@ -4,22 +4,74 @@ Copyright (C) 2026 Catcrafts®
|
|||
catcrafts.net
|
||||
*/
|
||||
|
||||
// Placeholder ComputeShader for DOM mode. The four standard UI pipelines
|
||||
// are compiled JS-side at startup (see additional/dom-webgpu.js); the C++
|
||||
// side never sees a WGSL handle. This type exists so UIRenderer can
|
||||
// declare `WebGPUComputeShader drawQuads;` members for symmetry with the
|
||||
// Vulkan side, but `Load()` and `Dispatch()` are intentionally absent —
|
||||
// the DispatchQuads / DispatchCircles / etc convenience methods on
|
||||
// UIRenderer route directly to the JS bridge.
|
||||
// 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<rgba8unorm, write>), binding 1
|
||||
// is the sampled `prev` (texture_2d<f32>). 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 {
|
||||
struct WebGPUComputeShader {
|
||||
// Marker only; pipelines live JS-side per dispatchStandard in
|
||||
// dom-webgpu.js. No state required.
|
||||
enum class UICustomBindingKind : std::uint8_t {
|
||||
Buffer = 0, // read-only-storage SSBO, handle is a slot into heap.bufferTable
|
||||
SampledTexture = 1, // sampled texture_2d<f32>, 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;
|
||||
std::vector<UICustomBinding> customBindings;
|
||||
|
||||
WebGPUComputeShader() = default;
|
||||
WebGPUComputeShader(const WebGPUComputeShader&) = delete;
|
||||
WebGPUComputeShader& operator=(const WebGPUComputeShader&) = delete;
|
||||
WebGPUComputeShader(WebGPUComputeShader&& o) noexcept
|
||||
: pipelineHandle(o.pipelineHandle),
|
||||
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 — the user's
|
||||
// shader must declare @group(0)/@group(1) bindings matching the
|
||||
// contract above. `bindings` lists every additional resource
|
||||
// (groups 2+) that the renderer should bind at dispatch time.
|
||||
void Load(std::string_view wgsl,
|
||||
std::span<const UICustomBinding> bindings = {});
|
||||
|
||||
// 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<const UICustomBinding> bindings = {});
|
||||
};
|
||||
}
|
||||
#endif // CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue