webgpu support

This commit is contained in:
Jorijn van der Graaf 2026-05-18 04:58:52 +02:00
commit dedf6b0467
22 changed files with 1656 additions and 324 deletions

View file

@ -28,13 +28,14 @@ import :ImageVulkan;
import :VulkanBuffer;
import :FontAtlas;
import :Font;
import :GraphicsTypes;
import std;
using namespace Crafter;
// ─── Initialize ─────────────────────────────────────────────────────────
void UIRenderer::Initialize(Window& window, DescriptorHeapVulkan& heap, VkCommandBuffer initCmd,
void UIRenderer::Initialize(Window& window, GraphicsDescriptorHeap& heap, GraphicsCommandBuffer initCmd,
std::filesystem::path quadsSpv,
std::filesystem::path circlesSpv,
std::filesystem::path imagesSpv,
@ -81,7 +82,7 @@ void UIRenderer::Initialize(Window& window, DescriptorHeapVulkan& heap, VkComman
// ─── per-frame Record ───────────────────────────────────────────────────
void UIRenderer::Record(VkCommandBuffer cmd, std::uint32_t frameIdx, Window& window) {
void UIRenderer::Record(GraphicsCommandBuffer cmd, std::uint32_t frameIdx, Window& window) {
// Reset per-frame state.
firstDispatchThisFrame_ = true;
@ -102,28 +103,6 @@ void UIRenderer::Record(VkCommandBuffer cmd, std::uint32_t frameIdx, Window& win
(void)window;
}
// ─── header builder ─────────────────────────────────────────────────────
UIDispatchHeader UIRenderer::FillHeader(std::uint32_t itemBufferSlot,
std::uint32_t itemCount,
std::array<float,4> clipRectPx,
std::uint32_t flags) const noexcept {
UIDispatchHeader h{};
h.outImage = outImageSlot_;
h.itemBuffer = itemBufferSlot;
h.surfaceWidth = window_->width;
h.surfaceHeight = window_->height;
h.clipX = clipRectPx[0];
h.clipY = clipRectPx[1];
h.clipW = clipRectPx[2];
h.clipH = clipRectPx[3];
h.itemCount = itemCount;
h.frameIdx = window_->currentBuffer;
h.flags = flags;
h._pad = 0;
return h;
}
// ─── group-count helper ────────────────────────────────────────────────
namespace {
@ -142,7 +121,7 @@ namespace {
// without inter-workgroup races on imageLoad/imageStore — the bug that the
// per-item dispatch model had.
void UIRenderer::DispatchQuads(VkCommandBuffer cmd, std::uint32_t bufferSlot,
void UIRenderer::DispatchQuads(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot,
std::uint32_t itemCount,
std::array<float,4> clipRectPx) {
if (itemCount == 0) return;
@ -151,7 +130,7 @@ void UIRenderer::DispatchQuads(VkCommandBuffer cmd, std::uint32_t bufferSlot,
TilesFor(window_->width), TilesFor(window_->height), 1u);
}
void UIRenderer::DispatchCircles(VkCommandBuffer cmd, std::uint32_t bufferSlot,
void UIRenderer::DispatchCircles(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot,
std::uint32_t itemCount,
std::array<float,4> clipRectPx) {
if (itemCount == 0) return;
@ -160,7 +139,7 @@ void UIRenderer::DispatchCircles(VkCommandBuffer cmd, std::uint32_t bufferSlot,
TilesFor(window_->width), TilesFor(window_->height), 1u);
}
void UIRenderer::DispatchImages(VkCommandBuffer cmd, std::uint32_t bufferSlot,
void UIRenderer::DispatchImages(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot,
std::uint32_t itemCount,
std::array<float,4> clipRectPx) {
if (itemCount == 0) return;
@ -169,7 +148,7 @@ void UIRenderer::DispatchImages(VkCommandBuffer cmd, std::uint32_t bufferSlot,
TilesFor(window_->width), TilesFor(window_->height), 1u);
}
void UIRenderer::DispatchText(VkCommandBuffer cmd, std::uint32_t bufferSlot,
void UIRenderer::DispatchText(GraphicsCommandBuffer cmd, std::uint32_t bufferSlot,
std::uint32_t itemCount,
std::array<float,4> clipRectPx) {
if (itemCount == 0) return;
@ -199,7 +178,7 @@ void UIRenderer::DispatchText(VkCommandBuffer cmd, std::uint32_t bufferSlot,
// ─── generic Dispatch (with barrier) ────────────────────────────────────
void UIRenderer::Dispatch(VkCommandBuffer cmd, const ComputeShader& shader,
void UIRenderer::Dispatch(GraphicsCommandBuffer cmd, const ComputeShader& shader,
const void* push, std::uint32_t pushBytes,
std::uint32_t gx, std::uint32_t gy, std::uint32_t gz) {
if (!firstDispatchThisFrame_) {
@ -356,46 +335,3 @@ SamplerSlot UIRenderer::RegisterLinearClampSampler() {
return RegisterSampler(s);
}
// ─── ShapeText ─────────────────────────────────────────────────────────
std::uint32_t UIRenderer::ShapeText(Font& font, float pxSize,
float x, float baselineY,
std::string_view utf8,
std::array<float,4> color,
GlyphItem* out, std::uint32_t outCapacity,
float* outAdvance) {
if (fontAtlas == nullptr) {
throw std::runtime_error("UIRenderer::ShapeText: no FontAtlas (set fontAtlas before Initialize)");
}
const float scale = pxSize / FontAtlas::kBaseSize;
float cursor = x;
std::uint32_t written = 0;
std::size_t i = 0;
while (i < utf8.size() && written < outCapacity) {
std::uint32_t cp = DecodeUtf8(utf8, i);
if (cp == 0) break;
if (cp == '\n') { /* single-line shaper — ignore */ continue; }
fontAtlas->Ensure(font, cp);
const Glyph* g = fontAtlas->Lookup(font, cp);
if (g == nullptr) continue;
// Empty glyph (whitespace) — advance only.
if (g->w > 0 && g->h > 0) {
GlyphItem& gi = out[written++];
gi.x = cursor + g->xoff * scale;
gi.y = baselineY + g->yoff * scale;
gi.w = g->w * scale;
gi.h = g->h * scale;
gi.u0 = g->u0; gi.v0 = g->v0;
gi.u1 = g->u1; gi.v1 = g->v1;
gi.r = color[0]; gi.g = color[1]; gi.b = color[2]; gi.a = color[3];
}
cursor += g->advance * scale;
}
if (outAdvance) *outAdvance = cursor - x;
return written;
}