webgpu support
This commit is contained in:
parent
5352ef69a2
commit
dedf6b0467
22 changed files with 1656 additions and 324 deletions
140
implementations/Crafter.Graphics-UI-WebGPU.cpp
Normal file
140
implementations/Crafter.Graphics-UI-WebGPU.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
Crafter®.Graphics
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
catcrafts.net
|
||||
*/
|
||||
|
||||
// WebGPU UIRenderer implementation — DOM mode parallel to
|
||||
// Crafter.Graphics-UI.cpp. Compute pipelines and bind groups live JS-side
|
||||
// in additional/dom-webgpu.js; this file just translates UIRenderer's
|
||||
// public method calls into wgpu* import calls.
|
||||
|
||||
module Crafter.Graphics:UI_webgpu_impl;
|
||||
import :UI;
|
||||
import :Window;
|
||||
import :Font;
|
||||
import :FontAtlas;
|
||||
import :WebGPU;
|
||||
import :WebGPUBuffer;
|
||||
import :DescriptorHeapWebGPU;
|
||||
import :GraphicsTypes;
|
||||
import std;
|
||||
|
||||
using namespace Crafter;
|
||||
|
||||
void UIRenderer::Initialize(Window& window, GraphicsDescriptorHeap& heap, GraphicsCommandBuffer initCmd,
|
||||
std::filesystem::path /*quadsSpv*/,
|
||||
std::filesystem::path /*circlesSpv*/,
|
||||
std::filesystem::path /*imagesSpv*/,
|
||||
std::filesystem::path /*textSpv*/) {
|
||||
(void)initCmd;
|
||||
window_ = &window;
|
||||
heap_ = &heap;
|
||||
|
||||
// The JS bridge owns the compute pipelines (4 of them, precompiled at
|
||||
// page load). The C++ side has nothing to load. We do reserve one
|
||||
// image slot for the swapchain output (kept symmetrical with Vulkan)
|
||||
// and register the font atlas if one was set.
|
||||
auto outRange = heap_->AllocateImageSlots(1);
|
||||
outImageSlot_ = ImageSlot{heap_, outRange.firstElement};
|
||||
// No JS texture handle stored at this slot — the canvas's
|
||||
// getCurrentTexture() is per-frame and the JS bridge resolves it
|
||||
// internally. The slot value is only meaningful through
|
||||
// UIDispatchHeader.outImage, which the WGSL shaders ignore.
|
||||
|
||||
if (fontAtlas != nullptr) {
|
||||
auto atlasImg = heap_->AllocateImageSlots(1);
|
||||
fontAtlasImageSlot_ = ImageSlot{heap_, atlasImg.firstElement};
|
||||
heap_->imageTable[atlasImg.firstElement] = fontAtlas->textureHandle;
|
||||
fontAtlasSamplerSlot_ = RegisterLinearClampSampler();
|
||||
}
|
||||
|
||||
resizeSub_.SetEvent(&window.onResize, [this]() {
|
||||
// Storage textures + bind groups are recreated JS-side at next
|
||||
// wgpuFrameBegin (via the ensureSized path). Nothing to do here.
|
||||
});
|
||||
}
|
||||
|
||||
void UIRenderer::Record(GraphicsCommandBuffer cmd, std::uint32_t frameIdx, Window& window) {
|
||||
if (fontAtlas != nullptr && fontAtlas->dirty) {
|
||||
fontAtlas->Update(cmd);
|
||||
}
|
||||
onBuild.Invoke({cmd, frameIdx});
|
||||
if (fontAtlas != nullptr && fontAtlas->dirty) {
|
||||
fontAtlas->Update(cmd);
|
||||
}
|
||||
(void)window;
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline std::uint32_t TilesFor(std::uint32_t dim) { return (dim + 7u) / 8u; }
|
||||
}
|
||||
|
||||
void UIRenderer::DispatchQuads(GraphicsCommandBuffer /*cmd*/, std::uint32_t bufferSlot,
|
||||
std::uint32_t itemCount,
|
||||
std::array<float,4> clipRectPx) {
|
||||
if (itemCount == 0) return;
|
||||
UIDispatchHeader hdr = FillHeader(bufferSlot, itemCount, clipRectPx);
|
||||
auto handle = heap_->bufferTable[bufferSlot];
|
||||
WebGPU::wgpuDispatchQuads(handle, &hdr,
|
||||
static_cast<std::int32_t>(TilesFor(window_->width)),
|
||||
static_cast<std::int32_t>(TilesFor(window_->height)));
|
||||
}
|
||||
|
||||
void UIRenderer::DispatchCircles(GraphicsCommandBuffer /*cmd*/, std::uint32_t bufferSlot,
|
||||
std::uint32_t itemCount,
|
||||
std::array<float,4> clipRectPx) {
|
||||
if (itemCount == 0) return;
|
||||
UIDispatchHeader hdr = FillHeader(bufferSlot, itemCount, clipRectPx);
|
||||
auto handle = heap_->bufferTable[bufferSlot];
|
||||
WebGPU::wgpuDispatchCircles(handle, &hdr,
|
||||
static_cast<std::int32_t>(TilesFor(window_->width)),
|
||||
static_cast<std::int32_t>(TilesFor(window_->height)));
|
||||
}
|
||||
|
||||
void UIRenderer::DispatchImages(GraphicsCommandBuffer /*cmd*/, std::uint32_t bufferSlot,
|
||||
std::uint32_t itemCount,
|
||||
std::array<float,4> clipRectPx) {
|
||||
if (itemCount == 0) return;
|
||||
UIDispatchHeader hdr = FillHeader(bufferSlot, itemCount, clipRectPx);
|
||||
auto handle = heap_->bufferTable[bufferSlot];
|
||||
// For DispatchImages, the WGSL expects a texture + sampler in group 3.
|
||||
// The library v1 doesn't expose user-image registration on DOM (out of
|
||||
// scope per plan). If the user calls DispatchImages without a registered
|
||||
// image, fall back to using the font atlas binding — the user's items
|
||||
// should reference texSlot/sampSlot but on DOM those are ignored. For
|
||||
// now, route through the font atlas texture if available; otherwise
|
||||
// skip the dispatch.
|
||||
if (fontAtlasImageSlot_) {
|
||||
auto texHandle = heap_->imageTable[fontAtlasImageSlot_];
|
||||
auto sampHandle = heap_->samplerTable[fontAtlasSamplerSlot_];
|
||||
WebGPU::wgpuDispatchImages(handle, &hdr,
|
||||
static_cast<std::int32_t>(TilesFor(window_->width)),
|
||||
static_cast<std::int32_t>(TilesFor(window_->height)),
|
||||
texHandle, sampHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void UIRenderer::DispatchText(GraphicsCommandBuffer /*cmd*/, std::uint32_t bufferSlot,
|
||||
std::uint32_t itemCount,
|
||||
std::array<float,4> clipRectPx) {
|
||||
if (itemCount == 0) return;
|
||||
if (!fontAtlasImageSlot_) {
|
||||
std::println("UIRenderer::DispatchText: no FontAtlas registered (set fontAtlas before Initialize)");
|
||||
std::abort();
|
||||
}
|
||||
UIDispatchHeader hdr = FillHeader(bufferSlot, itemCount, clipRectPx);
|
||||
auto bufHandle = heap_->bufferTable[bufferSlot];
|
||||
auto texHandle = heap_->imageTable[fontAtlasImageSlot_];
|
||||
auto sampHandle = heap_->samplerTable[fontAtlasSamplerSlot_];
|
||||
WebGPU::wgpuDispatchText(bufHandle, &hdr,
|
||||
static_cast<std::int32_t>(TilesFor(window_->width)),
|
||||
static_cast<std::int32_t>(TilesFor(window_->height)),
|
||||
texHandle, sampHandle);
|
||||
}
|
||||
|
||||
SamplerSlot UIRenderer::RegisterLinearClampSampler() {
|
||||
auto range = heap_->AllocateSamplerSlots(1);
|
||||
heap_->samplerTable[range.firstElement] = WebGPU::wgpuCreateLinearClampSampler();
|
||||
return SamplerSlot{heap_, range.firstElement};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue