feat(vulkan-rt): BLAS build options — fast-build/fast-trace + in-place refit (#36)
Mesh::Build / BuildProcedural now take an RTBuildOptions { preference,
allowUpdate }. `preference` maps to PREFER_FAST_TRACE (default) or
PREFER_FAST_BUILD; `allowUpdate` sets ALLOW_UPDATE so the BLAS can be
refit later.
Adds Mesh::Refit / RefitProcedural: when the original build opted into
allowUpdate and the topology is unchanged, they record an in-place
UPDATE-mode build (src == dst) — much cheaper than a rebuild, and the
AS handle + blasAddr are preserved so TLAS instances stay valid. They
fall back to a full rebuild otherwise. The shared build tail also now
destroys a stale AS handle on re-Build (previously leaked) and guards
the in-place update with an AS read→write barrier.
The portable RTBuildPreference/RTBuildOptions types and Refit methods
also exist on the WebGPU backend for API symmetry; the software BVH has
no hardware AS, so the preference is a no-op and a "refit" rebuilds the
BVH (re-registering the handle).
Also adds Device::validationErrorCount, bumped by the debug-messenger
callback on ERROR-severity messages, so tests can assert a Vulkan
operation produced no validation errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
27bfb36e60
commit
1a81f115c3
5 changed files with 315 additions and 82 deletions
|
|
@ -42,6 +42,31 @@ export namespace Crafter {
|
|||
};
|
||||
static_assert(sizeof(RTAabb) == sizeof(VkAabbPositionsKHR));
|
||||
|
||||
// Build-time tuning for a BLAS, mapped onto the preference bits of
|
||||
// VkBuildAccelerationStructureFlagBitsKHR.
|
||||
enum class RTBuildPreference {
|
||||
// VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR — spend
|
||||
// more time building for faster traversal. The right default for
|
||||
// static geometry that is traced many times.
|
||||
FastTrace,
|
||||
// VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR — build
|
||||
// quickly at the cost of traversal speed. For geometry rebuilt
|
||||
// (or first-frame streamed) often enough that build time dominates.
|
||||
FastBuild,
|
||||
};
|
||||
|
||||
// Per-build options for Mesh::Build / BuildProcedural.
|
||||
struct RTBuildOptions {
|
||||
RTBuildPreference preference = RTBuildPreference::FastTrace;
|
||||
// Set VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR so the
|
||||
// BLAS can later be refit in place via Refit()/RefitProcedural().
|
||||
// Required for any subsequent refit; refitting a BLAS built without
|
||||
// it falls back to a full rebuild. PREFER_FAST_TRACE and
|
||||
// PREFER_FAST_BUILD are mutually exclusive, so `preference` selects
|
||||
// exactly one — allowUpdate is layered on top of whichever is set.
|
||||
bool allowUpdate = false;
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
VulkanBuffer<char, false> scratchBuffer;
|
||||
|
|
@ -61,17 +86,32 @@ export namespace Crafter {
|
|||
VulkanBuffer<std::byte, true> compressedStaging;
|
||||
VkAccelerationStructureGeometryTrianglesDataKHR blasData;
|
||||
VkAccelerationStructureGeometryKHR blas;
|
||||
VkAccelerationStructureKHR accelerationStructure;
|
||||
VkAccelerationStructureKHR accelerationStructure = VK_NULL_HANDLE;
|
||||
VkDeviceAddress blasAddr;
|
||||
bool opaque;
|
||||
void Build(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32_t> indicies, VkCommandBuffer cmd);
|
||||
// ─── Build-option / refit state ──────────────────────────────────
|
||||
// Flags the BLAS was last (re)built with — carried so Refit can
|
||||
// re-issue an identical-flags UPDATE build (the FAST_TRACE/FAST_BUILD
|
||||
// preference plus, when opted in, ALLOW_UPDATE).
|
||||
VkBuildAccelerationStructureFlagsKHR buildFlags = 0;
|
||||
// Primitive count of the current build (triangles = indexCount/3,
|
||||
// procedural = aabb count). An in-place UPDATE must preserve it.
|
||||
std::uint32_t builtPrimitiveCount = 0;
|
||||
// Element count the vertex / aabb input buffer was sized for. Refit
|
||||
// re-uploads in place only when the new data matches this; a change
|
||||
// forces a full rebuild (topology changed).
|
||||
std::uint32_t builtInputCount = 0;
|
||||
// Whether ALLOW_UPDATE was set on the last build, i.e. whether an
|
||||
// in-place refit is possible at all.
|
||||
bool allowUpdate = false;
|
||||
void Build(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32_t> indicies, VkCommandBuffer cmd, RTBuildOptions options = {});
|
||||
// GPU path: decompresses vertex (region 0) and index (region 1) streams
|
||||
// from asset.blob into vertexBuffer / indexBuffer using
|
||||
// VK_EXT_memory_decompression. Falls back to CPU decode + the
|
||||
// uncompressed Build if Device::memoryDecompressionSupported is false.
|
||||
// Region 2 (data) is not consumed here — the caller decompresses it
|
||||
// into their own buffer if needed (or uses Compression::DecompressCPU).
|
||||
void Build(const ::Crafter::CompressedMeshAsset& asset, VkCommandBuffer cmd);
|
||||
void Build(const ::Crafter::CompressedMeshAsset& asset, VkCommandBuffer cmd, RTBuildOptions options = {});
|
||||
// Build an AABB (procedural) BLAS from a list of object-space
|
||||
// boxes (VK_GEOMETRY_TYPE_AABBS_KHR). The hit group bound to
|
||||
// instances of this mesh must be a
|
||||
|
|
@ -83,7 +123,22 @@ export namespace Crafter {
|
|||
// default) to let any-hit shaders run. Same scratch/lifetime
|
||||
// handling as the triangle Build; instances reference blasAddr
|
||||
// exactly like a triangle BLAS.
|
||||
void BuildProcedural(std::span<const RTAabb> aabbs, bool opaque, VkCommandBuffer cmd);
|
||||
void BuildProcedural(std::span<const RTAabb> aabbs, bool opaque, VkCommandBuffer cmd, RTBuildOptions options = {});
|
||||
|
||||
// Refit the triangle BLAS against new geometry of the *same
|
||||
// topology* (same vertex count and index count as the original
|
||||
// Build). When that Build set RTBuildOptions::allowUpdate this issues
|
||||
// a hardware UPDATE-mode build — much cheaper than a rebuild, and the
|
||||
// BLAS handle / blasAddr are preserved, so TLAS instances referencing
|
||||
// it stay valid. A hardware update may only move vertex positions,
|
||||
// not change connectivity; the indices are re-uploaded for symmetry
|
||||
// with the rebuild path but must describe the same topology. Without
|
||||
// allowUpdate (or if the counts changed) it falls back to a full
|
||||
// rebuild. Lifetime contract matches Build: the spans need only
|
||||
// outlive this call. Call this per frame to track a deforming mesh.
|
||||
void Refit(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32_t> indicies, VkCommandBuffer cmd);
|
||||
// Procedural analog of Refit: new object-space boxes, same count.
|
||||
void RefitProcedural(std::span<const RTAabb> aabbs, VkCommandBuffer cmd);
|
||||
};
|
||||
}
|
||||
#endif // !CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
@ -125,6 +180,20 @@ export namespace Crafter {
|
|||
};
|
||||
static_assert(sizeof(RTAabb) == 24);
|
||||
|
||||
// Mirror of the native build-option types so portable code compiles
|
||||
// unchanged. The software-RT path has no hardware acceleration
|
||||
// structure, so the FastTrace/FastBuild preference has no effect (the
|
||||
// SAH BVH2 is always built the same way) and a "refit" simply rebuilds
|
||||
// the BVH on the host. The types exist purely for API symmetry.
|
||||
enum class RTBuildPreference {
|
||||
FastTrace,
|
||||
FastBuild,
|
||||
};
|
||||
struct RTBuildOptions {
|
||||
RTBuildPreference preference = RTBuildPreference::FastTrace;
|
||||
bool allowUpdate = false;
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
// BLAS "handle": opaque identity that goes into
|
||||
|
|
@ -147,7 +216,8 @@ export namespace Crafter {
|
|||
// kept for API symmetry with the Vulkan signature.
|
||||
void Build(std::span<Crafter::Vector<float, 3, 3>> vertices,
|
||||
std::span<std::uint32_t> indices,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
WebGPUCommandEncoderRef cmd = 0,
|
||||
RTBuildOptions options = {});
|
||||
|
||||
// CPU-decompress the .cmesh blob (no VK_EXT_memory_decompression
|
||||
// equivalent in WebGPU) and forward to the positions+indices path,
|
||||
|
|
@ -156,7 +226,8 @@ export namespace Crafter {
|
|||
// The data layout is example-defined — the heap is exposed in WGSL
|
||||
// as `vertexAttribs : array<u32>` with a per-mesh u32-word offset.
|
||||
void Build(const ::Crafter::CompressedMeshAsset& asset,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
WebGPUCommandEncoderRef cmd = 0,
|
||||
RTBuildOptions options = {});
|
||||
|
||||
// Build an AABB (procedural) BLAS from a list of object-space boxes
|
||||
// — the WebGPU analog of a VK_GEOMETRY_TYPE_AABBS_KHR geometry. The
|
||||
|
|
@ -168,8 +239,20 @@ export namespace Crafter {
|
|||
// transparent / volumetric). The `cmd` parameter is unused on
|
||||
// WebGPU — kept for API symmetry with the triangle path.
|
||||
void BuildProcedural(std::span<const RTAabb> aabbs,
|
||||
bool opaque = false,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
bool opaque = false,
|
||||
WebGPUCommandEncoderRef cmd = 0,
|
||||
RTBuildOptions options = {});
|
||||
|
||||
// Refit analogs of the native API. With no hardware AS to update,
|
||||
// these simply re-run the host BVH build over the new data, so they
|
||||
// accept the full geometry (not just moved positions). Provided so
|
||||
// portable code that calls Refit/RefitProcedural compiles and
|
||||
// behaves correctly on the WebGPU backend.
|
||||
void Refit(std::span<Crafter::Vector<float, 3, 3>> vertices,
|
||||
std::span<std::uint32_t> indices,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
void RefitProcedural(std::span<const RTAabb> aabbs,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
};
|
||||
}
|
||||
#endif // CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue