webgpu triangle

This commit is contained in:
Jorijn van der Graaf 2026-05-18 18:43:30 +02:00
commit 5553ded476
22 changed files with 2107 additions and 42 deletions

View file

@ -22,6 +22,7 @@ module;
#include "vulkan/vulkan.h"
#endif // !CRAFTER_GRAPHICS_WINDOW_DOM
export module Crafter.Graphics:RenderingElement3D;
import :RT;
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
import std;
import :Mesh;
@ -55,7 +56,7 @@ export namespace Crafter {
class RenderingElement3D {
public:
VkAccelerationStructureInstanceKHR instance;
RTInstance instance;
// Position in `elements`, maintained by Add/Remove for O(1) swap-and-pop.
// Sentinel value = not currently registered.
std::uint32_t indexInElements = std::numeric_limits<std::uint32_t>::max();
@ -87,3 +88,63 @@ export namespace Crafter {
};
}
#endif // !CRAFTER_GRAPHICS_WINDOW_DOM
#ifdef CRAFTER_GRAPHICS_WINDOW_DOM
import std;
import :Mesh;
import :WebGPU;
import :WebGPUBuffer;
import :Window;
export namespace Crafter {
// Per-frame TLAS storage. WebGPU has no real swapchain frame count
// (Window::numFrames = 1 on DOM), so this is effectively a singleton —
// the array form is kept for API symmetry with the Vulkan side so user
// code that indexes `tlases[frameIdx]` ports unchanged.
struct TlasWithBuffer {
// Host-visible instance buffer holding RTInstance entries — same
// layout as Vulkan's VkAccelerationStructureInstanceKHR, so user
// code touching .instance.mask / .flags / .transform.matrix is
// identical across backends. Also bound as a storage SSBO so
// application compute shaders (e.g. physics-tlas-transform.comp.wgsl)
// can write the .transform field directly when
// RenderingElement3D::transformOwnedByGpu is set.
WebGPUBuffer<RTInstance, true> instanceBuffer;
// Per-instance application metadata; parallel to instanceBuffer,
// identical semantics to the Vulkan-side counterpart.
WebGPUBuffer<std::uint32_t, true> metadataBuffer;
// GPU-built TLAS data: one TLASEntry per instance, written each
// BuildTLAS by a compute pass on the JS bridge. Read by traceRay /
// rayQuery as `@group(1) @binding(0) tlas: array<TLASEntry>`.
// TLASEntry layout: 96 bytes — aabbMin (12) + maskHGoffset (4) +
// aabbMax (12) + blasHandle (4) + invTransform 3x4 mat (48) +
// customIndex (4) + _pad (12). Defined in the WGSL traversal
// library; never directly read by C++.
WebGPUBuffer<char, false> buffer;
std::uint32_t builtInstanceCount = 0;
};
class RenderingElement3D {
public:
RTInstance instance{};
std::uint32_t indexInElements = std::numeric_limits<std::uint32_t>::max();
std::uint32_t userMetadata = 0;
// Application compute shader writes the transform field of this
// element's instanceBuffer slot directly — BuildTLAS preserves it.
bool transformOwnedByGpu = false;
static std::vector<RenderingElement3D*> elements;
inline static TlasWithBuffer tlases[Window::numFrames];
// Repopulate the TLAS for frame `index`. WebGPU path always does
// a fresh build (no refit) — the GPU build pass is cheap at the
// ~10100 instance counts the design targets; LBVH-for-TLAS is a
// future optimization for larger scenes.
static void BuildTLAS(WebGPUCommandEncoderRef cmd, std::uint32_t index);
static void Add(RenderingElement3D* e);
static void Remove(RenderingElement3D* e);
};
}
#endif // CRAFTER_GRAPHICS_WINDOW_DOM