113 lines
5 KiB
Text
113 lines
5 KiB
Text
|
|
/*
|
||
|
|
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<UICustomBinding> 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<const UICustomBinding> bindings = {},
|
||
|
|
bool rayQuery = false) {
|
||
|
|
pushUniformSize = pushUniformSize_;
|
||
|
|
rayQueryCapable = rayQuery;
|
||
|
|
customBindings.assign(bindings.begin(), bindings.end());
|
||
|
|
pipelineHandle = WebGPU::wgpuLoadComputePipeline(
|
||
|
|
wgsl.data(), static_cast<std::int32_t>(wgsl.size()),
|
||
|
|
static_cast<std::int32_t>(pushUniformSize),
|
||
|
|
customBindings.empty() ? nullptr : customBindings.data(),
|
||
|
|
static_cast<std::int32_t>(customBindings.size()),
|
||
|
|
rayQuery ? 1 : 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Load(const std::filesystem::path& wgslPath,
|
||
|
|
std::uint32_t pushUniformSize_,
|
||
|
|
std::span<const UICustomBinding> 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<char>(f)),
|
||
|
|
std::istreambuf_iterator<char>());
|
||
|
|
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<const std::uint32_t> 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<std::int32_t>(pushBytes),
|
||
|
|
handles.empty() ? nullptr : handles.data(),
|
||
|
|
static_cast<std::int32_t>(handles.size()),
|
||
|
|
static_cast<std::int32_t>(gx),
|
||
|
|
static_cast<std::int32_t>(gy),
|
||
|
|
static_cast<std::int32_t>(gz));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
#endif // CRAFTER_GRAPHICS_WINDOW_DOM
|