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
|
|
@ -39,51 +39,107 @@ namespace {
|
|||
constexpr VkBufferUsageFlags2 kIndexUsageBase =
|
||||
kVertexUsageBase | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
|
||||
// Translate the portable RTBuildOptions to Vulkan build flags. The two
|
||||
// preferences are mutually exclusive; ALLOW_UPDATE is layered on top so
|
||||
// a later in-place refit (UPDATE mode) is permitted.
|
||||
VkBuildAccelerationStructureFlagsKHR BlasFlags(const RTBuildOptions& options) {
|
||||
VkBuildAccelerationStructureFlagsKHR flags =
|
||||
options.preference == RTBuildPreference::FastBuild
|
||||
? VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR
|
||||
: VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
|
||||
if (options.allowUpdate)
|
||||
flags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// their single geometry through here. `update` selects an in-place
|
||||
// refit (VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, src == dst)
|
||||
// over a fresh build; the caller guarantees the geometry topology and
|
||||
// primitiveCount match the existing AS and that `flags` carried
|
||||
// ALLOW_UPDATE on the original build.
|
||||
void RecordBLASBuildFromGeometry(Mesh& self, const VkAccelerationStructureGeometryKHR& blasGeometry, std::uint32_t primitiveCount, VkBuildAccelerationStructureFlagsKHR flags, bool update, VkCommandBuffer cmd) {
|
||||
VkAccelerationStructureBuildGeometryInfoKHR blasBuildGeometryInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR,
|
||||
.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,
|
||||
.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR,
|
||||
.flags = flags,
|
||||
.mode = update
|
||||
? VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR
|
||||
: VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR,
|
||||
.geometryCount = 1,
|
||||
.pGeometries = &blasGeometry,
|
||||
};
|
||||
|
||||
VkAccelerationStructureBuildSizesInfoKHR blasBuildSizes = {
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR
|
||||
};
|
||||
Device::vkGetAccelerationStructureBuildSizesKHR(
|
||||
Device::device,
|
||||
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
|
||||
&blasBuildGeometryInfo,
|
||||
&primitiveCount,
|
||||
&blasBuildSizes
|
||||
);
|
||||
if (!update) {
|
||||
// Fresh build: query sizes, (re)allocate scratch + AS storage,
|
||||
// create a new AS handle. The scratch buffer is sized for the
|
||||
// build (always >= the update scratch requirement), so it also
|
||||
// covers later in-place refits without resizing.
|
||||
VkAccelerationStructureBuildSizesInfoKHR blasBuildSizes = {
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR
|
||||
};
|
||||
Device::vkGetAccelerationStructureBuildSizesKHR(
|
||||
Device::device,
|
||||
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
|
||||
&blasBuildGeometryInfo,
|
||||
&primitiveCount,
|
||||
&blasBuildSizes
|
||||
);
|
||||
|
||||
self.scratchBuffer.Resize(
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
blasBuildSizes.buildScratchSize);
|
||||
|
||||
self.blasBuffer.Resize(
|
||||
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR
|
||||
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
|
||||
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
blasBuildSizes.accelerationStructureSize);
|
||||
|
||||
// Re-Build of an existing Mesh: destroy the stale handle first
|
||||
// so the previous AS storage isn't leaked.
|
||||
if (self.accelerationStructure != VK_NULL_HANDLE) {
|
||||
Device::vkDestroyAccelerationStructureKHR(Device::device, self.accelerationStructure, nullptr);
|
||||
self.accelerationStructure = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkAccelerationStructureCreateInfoKHR blasCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR,
|
||||
.buffer = self.blasBuffer.buffer,
|
||||
.offset = 0,
|
||||
.size = blasBuildSizes.accelerationStructureSize,
|
||||
.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,
|
||||
};
|
||||
Device::CheckVkResult(Device::vkCreateAccelerationStructureKHR(Device::device, &blasCreateInfo, nullptr, &self.accelerationStructure));
|
||||
|
||||
VkAccelerationStructureDeviceAddressInfoKHR addrInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR,
|
||||
.accelerationStructure = self.accelerationStructure
|
||||
};
|
||||
self.blasAddr = Device::vkGetAccelerationStructureDeviceAddressKHR(Device::device, &addrInfo);
|
||||
} else {
|
||||
// In-place refit: the AS may have been read by a prior TLAS
|
||||
// build / trace, so order that read before the build overwrites
|
||||
// it. Scratch reuse needs the same write-after-read guard.
|
||||
VkMemoryBarrier asBarrier {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR,
|
||||
.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR
|
||||
};
|
||||
vkCmdPipelineBarrier(cmd,
|
||||
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR,
|
||||
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
|
||||
0, 1, &asBarrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
self.scratchBuffer.Resize(
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
blasBuildSizes.buildScratchSize);
|
||||
blasBuildGeometryInfo.scratchData.deviceAddress = self.scratchBuffer.address;
|
||||
|
||||
self.blasBuffer.Resize(
|
||||
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR
|
||||
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
|
||||
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
blasBuildSizes.accelerationStructureSize);
|
||||
|
||||
VkAccelerationStructureCreateInfoKHR blasCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR,
|
||||
.buffer = self.blasBuffer.buffer,
|
||||
.offset = 0,
|
||||
.size = blasBuildSizes.accelerationStructureSize,
|
||||
.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,
|
||||
};
|
||||
Device::CheckVkResult(Device::vkCreateAccelerationStructureKHR(Device::device, &blasCreateInfo, nullptr, &self.accelerationStructure));
|
||||
blasBuildGeometryInfo.dstAccelerationStructure = self.accelerationStructure;
|
||||
blasBuildGeometryInfo.dstAccelerationStructure = self.accelerationStructure;
|
||||
// UPDATE refits in place (src == dst); a fresh build has no source.
|
||||
blasBuildGeometryInfo.srcAccelerationStructure =
|
||||
update ? self.accelerationStructure : VK_NULL_HANDLE;
|
||||
|
||||
VkAccelerationStructureBuildRangeInfoKHR blasRangeInfo {
|
||||
.primitiveCount = primitiveCount,
|
||||
|
|
@ -94,14 +150,11 @@ namespace {
|
|||
VkAccelerationStructureBuildRangeInfoKHR* blasRangeInfoPP = &blasRangeInfo;
|
||||
Device::vkCmdBuildAccelerationStructuresKHR(cmd, 1, &blasBuildGeometryInfo, &blasRangeInfoPP);
|
||||
|
||||
VkAccelerationStructureDeviceAddressInfoKHR addrInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR,
|
||||
.accelerationStructure = self.accelerationStructure
|
||||
};
|
||||
self.blasAddr = Device::vkGetAccelerationStructureDeviceAddressKHR(Device::device, &addrInfo);
|
||||
self.buildFlags = flags;
|
||||
self.builtPrimitiveCount = primitiveCount;
|
||||
}
|
||||
|
||||
void RecordBLASBuild(Mesh& self, std::uint32_t vertexCount, std::uint32_t indexCount, VkCommandBuffer cmd) {
|
||||
void RecordBLASBuild(Mesh& self, std::uint32_t vertexCount, std::uint32_t indexCount, VkBuildAccelerationStructureFlagsKHR flags, bool update, VkCommandBuffer cmd) {
|
||||
VkDeviceOrHostAddressConstKHR vertexAddr;
|
||||
vertexAddr.deviceAddress = self.vertexBuffer.address;
|
||||
|
||||
|
|
@ -126,11 +179,11 @@ namespace {
|
|||
.flags = VK_GEOMETRY_OPAQUE_BIT_KHR
|
||||
};
|
||||
|
||||
RecordBLASBuildFromGeometry(self, blasGeometry, indexCount / 3, cmd);
|
||||
RecordBLASBuildFromGeometry(self, blasGeometry, indexCount / 3, flags, update, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::Build(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32_t> indicies, VkCommandBuffer cmd) {
|
||||
void Mesh::Build(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32_t> indicies, VkCommandBuffer cmd, RTBuildOptions options) {
|
||||
vertexBuffer.Resize(kVertexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, verticies.size());
|
||||
indexBuffer.Resize(kIndexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indicies.size());
|
||||
|
||||
|
|
@ -140,10 +193,12 @@ void Mesh::Build(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32
|
|||
vertexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
|
||||
indexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
|
||||
|
||||
RecordBLASBuild(*this, static_cast<std::uint32_t>(verticies.size()), static_cast<std::uint32_t>(indicies.size()), cmd);
|
||||
allowUpdate = options.allowUpdate;
|
||||
builtInputCount = static_cast<std::uint32_t>(verticies.size());
|
||||
RecordBLASBuild(*this, static_cast<std::uint32_t>(verticies.size()), static_cast<std::uint32_t>(indicies.size()), BlasFlags(options), /*update*/ false, cmd);
|
||||
}
|
||||
|
||||
void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd) {
|
||||
void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd, RTBuildOptions options) {
|
||||
if (!Device::memoryDecompressionSupported) {
|
||||
// CPU fallback: decompress into temporary host vectors, then take
|
||||
// the existing uncompressed path. The data region is decompressed
|
||||
|
|
@ -159,7 +214,7 @@ void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd) {
|
|||
std::span<std::byte>(dataDiscard),
|
||||
};
|
||||
Compression::DecompressCPU(asset.blob, std::span(outputs).first(asset.blob.regions.size()));
|
||||
Build(vertices, indices, cmd);
|
||||
Build(vertices, indices, cmd, options);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -206,35 +261,98 @@ void Mesh::Build(const CompressedMeshAsset& asset, VkCommandBuffer cmd) {
|
|||
VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
|
||||
VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR);
|
||||
|
||||
RecordBLASBuild(*this, asset.vertexCount, asset.indexCount, cmd);
|
||||
allowUpdate = options.allowUpdate;
|
||||
builtInputCount = asset.vertexCount;
|
||||
RecordBLASBuild(*this, asset.vertexCount, asset.indexCount, BlasFlags(options), /*update*/ false, cmd);
|
||||
}
|
||||
|
||||
void Mesh::BuildProcedural(std::span<const RTAabb> aabbs, bool opaque, VkCommandBuffer cmd) {
|
||||
this->opaque = opaque;
|
||||
namespace {
|
||||
// Re-upload AABB build input and record an AABB BLAS build (fresh or
|
||||
// in-place refit). Shared by BuildProcedural and RefitProcedural; the
|
||||
// geometry's opaque bit is read from self.opaque so an UPDATE keeps the
|
||||
// exact geometry description of the original build.
|
||||
void RecordProceduralBuild(Mesh& self, std::span<const RTAabb> aabbs, VkBuildAccelerationStructureFlagsKHR flags, bool update, VkCommandBuffer cmd) {
|
||||
// 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. A refit reuses the
|
||||
// existing same-sized buffer (count is unchanged), so the device
|
||||
// address — and the geometry it feeds — stays stable.
|
||||
if (!update) {
|
||||
self.aabbBuffer.Resize(kVertexUsageBase, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, static_cast<std::uint32_t>(aabbs.size()));
|
||||
}
|
||||
std::memcpy(self.aabbBuffer.value, aabbs.data(), aabbs.size() * sizeof(RTAabb));
|
||||
self.aabbBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
|
||||
|
||||
// 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<std::uint32_t>(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 = self.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 = self.opaque ? static_cast<VkGeometryFlagsKHR>(VK_GEOMETRY_OPAQUE_BIT_KHR) : VkGeometryFlagsKHR{}
|
||||
};
|
||||
|
||||
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<VkGeometryFlagsKHR>(VK_GEOMETRY_OPAQUE_BIT_KHR) : VkGeometryFlagsKHR{}
|
||||
};
|
||||
|
||||
RecordBLASBuildFromGeometry(*this, blasGeometry, static_cast<std::uint32_t>(aabbs.size()), cmd);
|
||||
RecordBLASBuildFromGeometry(self, blasGeometry, static_cast<std::uint32_t>(aabbs.size()), flags, update, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::BuildProcedural(std::span<const RTAabb> aabbs, bool opaque, VkCommandBuffer cmd, RTBuildOptions options) {
|
||||
this->opaque = opaque;
|
||||
allowUpdate = options.allowUpdate;
|
||||
builtInputCount = static_cast<std::uint32_t>(aabbs.size());
|
||||
|
||||
RecordProceduralBuild(*this, aabbs, BlasFlags(options), /*update*/ false, cmd);
|
||||
}
|
||||
|
||||
void Mesh::Refit(std::span<Vector<float, 3, 3>> verticies, std::span<std::uint32_t> indicies, VkCommandBuffer cmd) {
|
||||
// A hardware in-place UPDATE is only valid when the original build asked
|
||||
// for it, an AS already exists, and the topology is unchanged. Otherwise
|
||||
// fall back to a full rebuild (mode BUILD) of the same buffers — still
|
||||
// correct, just not the cheap path.
|
||||
const bool sameTopology =
|
||||
accelerationStructure != VK_NULL_HANDLE
|
||||
&& verticies.size() == builtInputCount
|
||||
&& indicies.size() == static_cast<std::size_t>(builtPrimitiveCount) * 3;
|
||||
const bool update = allowUpdate && sameTopology;
|
||||
|
||||
if (!update) {
|
||||
// Counts may have changed (or update wasn't permitted): take the
|
||||
// resizing build path, preserving the caller's original flags.
|
||||
Build(verticies, indicies, cmd, RTBuildOptions{
|
||||
.preference = (buildFlags & VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR)
|
||||
? RTBuildPreference::FastBuild : RTBuildPreference::FastTrace,
|
||||
.allowUpdate = allowUpdate,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Same-sized buffers: overwrite in place so the device addresses (and
|
||||
// thus the geometry the UPDATE reads) stay stable.
|
||||
std::memcpy(vertexBuffer.value, verticies.data(), verticies.size() * sizeof(Vector<float, 3, 3>));
|
||||
std::memcpy(indexBuffer.value, indicies.data(), indicies.size() * sizeof(std::uint32_t));
|
||||
vertexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
|
||||
indexBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
|
||||
|
||||
RecordBLASBuild(*this, static_cast<std::uint32_t>(verticies.size()), static_cast<std::uint32_t>(indicies.size()), buildFlags, /*update*/ true, cmd);
|
||||
}
|
||||
|
||||
void Mesh::RefitProcedural(std::span<const RTAabb> aabbs, VkCommandBuffer cmd) {
|
||||
const bool sameTopology =
|
||||
accelerationStructure != VK_NULL_HANDLE
|
||||
&& aabbs.size() == builtInputCount;
|
||||
const bool update = allowUpdate && sameTopology;
|
||||
|
||||
if (!update && aabbs.size() != builtInputCount) {
|
||||
// Topology changed — a full rebuild needs the new count.
|
||||
builtInputCount = static_cast<std::uint32_t>(aabbs.size());
|
||||
}
|
||||
RecordProceduralBuild(*this, aabbs, buildFlags, update, cmd);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue