From a9e42f9c90e7bdadcfa5924e1c71b3e78505fd6f Mon Sep 17 00:00:00 2001 From: catbot Date: Sat, 13 Jun 2026 00:17:29 +0000 Subject: [PATCH] =?UTF-8?q?feat(vulkan-rt):=20Mesh::BuildProcedural=20?= =?UTF-8?q?=E2=80=94=20AABB=20(procedural)=20BLAS=20build=20path=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the native counterpart of the WebGPU Mesh::BuildProcedural: uploads the boxes as a 24-byte-stride VkAabbPositionsKHR-compatible device buffer (AS-build-input read-only + device address) and builds the BLAS from one VK_GEOMETRY_TYPE_AABBS_KHR geometry. opaque maps to VK_GEOMETRY_OPAQUE_BIT_KHR, default-false semantics matching the WebGPU path so any-hit shaders run. The BLAS sizing/create/build tail is factored into a geometry-type-agnostic helper shared with the triangle path; no TLAS/instance changes needed. Co-Authored-By: Claude Opus 4.8 --- implementations/Crafter.Graphics-Mesh.cpp | 87 ++++++++++++++++------- interfaces/Crafter.Graphics-Mesh.cppm | 27 +++++++ 2 files changed, 88 insertions(+), 26 deletions(-) diff --git a/implementations/Crafter.Graphics-Mesh.cpp b/implementations/Crafter.Graphics-Mesh.cpp index 6f87c52..0e37756 100644 --- a/implementations/Crafter.Graphics-Mesh.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -39,31 +39,10 @@ namespace { constexpr VkBufferUsageFlags2 kIndexUsageBase = kVertexUsageBase | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - void RecordBLASBuild(Mesh& self, std::uint32_t vertexCount, std::uint32_t indexCount, VkCommandBuffer cmd) { - VkDeviceOrHostAddressConstKHR vertexAddr; - vertexAddr.deviceAddress = self.vertexBuffer.address; - - VkDeviceOrHostAddressConstKHR indexAddr; - indexAddr.deviceAddress = self.indexBuffer.address; - - auto trianglesData = VkAccelerationStructureGeometryTrianglesDataKHR { - .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR, - .vertexFormat = VK_FORMAT_R32G32B32_SFLOAT, - .vertexData = vertexAddr, - .vertexStride = sizeof(Vector), - .maxVertex = vertexCount - 1, - .indexType = VK_INDEX_TYPE_UINT32, - .indexData = indexAddr, - .transformData = {.deviceAddress = 0} - }; - VkAccelerationStructureGeometryDataKHR geometryData(trianglesData); - VkAccelerationStructureGeometryKHR blasGeometry { - .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, - .geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR, - .geometry = geometryData, - .flags = VK_GEOMETRY_OPAQUE_BIT_KHR - }; - + // Shared BLAS sizing / creation / build-record tail — geometry-type + // agnostic; both the triangle and the AABB (procedural) paths feed + // their single geometry through here. + void RecordBLASBuildFromGeometry(Mesh& self, const VkAccelerationStructureGeometryKHR& blasGeometry, std::uint32_t primitiveCount, VkCommandBuffer cmd) { VkAccelerationStructureBuildGeometryInfoKHR blasBuildGeometryInfo{ .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR, .type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, @@ -72,7 +51,6 @@ namespace { .pGeometries = &blasGeometry, }; - auto primitiveCount = indexCount / 3; VkAccelerationStructureBuildSizesInfoKHR blasBuildSizes = { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; @@ -122,6 +100,34 @@ namespace { }; self.blasAddr = Device::vkGetAccelerationStructureDeviceAddressKHR(Device::device, &addrInfo); } + + void RecordBLASBuild(Mesh& self, std::uint32_t vertexCount, std::uint32_t indexCount, VkCommandBuffer cmd) { + VkDeviceOrHostAddressConstKHR vertexAddr; + vertexAddr.deviceAddress = self.vertexBuffer.address; + + VkDeviceOrHostAddressConstKHR indexAddr; + indexAddr.deviceAddress = self.indexBuffer.address; + + auto trianglesData = VkAccelerationStructureGeometryTrianglesDataKHR { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR, + .vertexFormat = VK_FORMAT_R32G32B32_SFLOAT, + .vertexData = vertexAddr, + .vertexStride = sizeof(Vector), + .maxVertex = vertexCount - 1, + .indexType = VK_INDEX_TYPE_UINT32, + .indexData = indexAddr, + .transformData = {.deviceAddress = 0} + }; + VkAccelerationStructureGeometryDataKHR geometryData(trianglesData); + VkAccelerationStructureGeometryKHR blasGeometry { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, + .geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR, + .geometry = geometryData, + .flags = VK_GEOMETRY_OPAQUE_BIT_KHR + }; + + RecordBLASBuildFromGeometry(self, blasGeometry, indexCount / 3, cmd); + } } void Mesh::Build(std::span> verticies, std::span indicies, VkCommandBuffer cmd) { @@ -203,3 +209,32 @@ void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd) { RecordBLASBuild(*this, asset.vertexCount, asset.indexCount, cmd); } +void Mesh::BuildProcedural(std::span aabbs, bool opaque, VkCommandBuffer cmd) { + this->opaque = opaque; + + // 24-byte-stride VkAabbPositionsKHR-compatible build input + // (static_assert'd in the interface). Same usage set as the triangle + // inputs: AS-build read-only + device address. + aabbBuffer.Resize(kVertexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, static_cast(aabbs.size())); + std::memcpy(aabbBuffer.value, aabbs.data(), aabbs.size() * sizeof(RTAabb)); + aabbBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + + VkAccelerationStructureGeometryAabbsDataKHR aabbsData { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR, + .data = { .deviceAddress = aabbBuffer.address }, + .stride = sizeof(RTAabb) + }; + VkAccelerationStructureGeometryDataKHR geometryData; + geometryData.aabbs = aabbsData; + VkAccelerationStructureGeometryKHR blasGeometry { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, + .geometryType = VK_GEOMETRY_TYPE_AABBS_KHR, + .geometry = geometryData, + // Non-opaque by default (mirrors the WebGPU path) so any-hit + // shaders run for procedural geometry unless the caller opts out. + .flags = opaque ? static_cast(VK_GEOMETRY_OPAQUE_BIT_KHR) : VkGeometryFlagsKHR{} + }; + + RecordBLASBuildFromGeometry(*this, blasGeometry, static_cast(aabbs.size()), cmd); +} + diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index 4bb32b0..b71a359 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -31,12 +31,27 @@ import Crafter.Asset; import :VulkanBuffer; export namespace Crafter { + // One procedural primitive's axis-aligned bounding box, in object + // space. Layout-compatible with VkAabbPositionsKHR (24-byte stride), + // so a span of these uploads directly as a + // VK_GEOMETRY_TYPE_AABBS_KHR build input. Mirrors the DOM-side + // definition below so portable user code compiles unchanged. + struct RTAabb { + float min[3]; + float max[3]; + }; + static_assert(sizeof(RTAabb) == sizeof(VkAabbPositionsKHR)); + class Mesh { public: VulkanBuffer scratchBuffer; VulkanBuffer blasBuffer; VulkanBuffer, true> vertexBuffer; VulkanBuffer indexBuffer; + // AABB build input for the procedural path (BuildProcedural). + // Lifetime contract matches vertexBuffer/indexBuffer: must stay + // alive until the build submitted on `cmd` completes. + VulkanBuffer aabbBuffer; // Lives until the cmd buffer issued by the compressed Build path // completes execution. Kept as a member so the recorded // vkCmdDecompressMemoryEXT references valid memory until the queue @@ -57,6 +72,18 @@ export namespace Crafter { // 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); + // 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 + // VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR group + // carrying an intersection shader; that shader is invoked for + // each box the ray enters and reports the surface hit via + // reportIntersectionEXT. `opaque` maps to + // VK_GEOMETRY_OPAQUE_BIT_KHR: pass false (the WebGPU-path + // 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 aabbs, bool opaque, VkCommandBuffer cmd); }; } #endif // !CRAFTER_GRAPHICS_WINDOW_DOM