Skip to content

Commit 086bf5d

Browse files
committed
[REMIX-4656] Fixed Acceleration Structure resource tracking to prevent resource getting destroyed while still being bound by dxvk
1 parent f7a180f commit 086bf5d

File tree

3 files changed

+59
-26
lines changed

3 files changed

+59
-26
lines changed

src/dxvk/dxvk_bind_mask.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ namespace dxvk {
152152
Rc<DxvkImageView> imageView;
153153
Rc<DxvkBufferView> bufferView;
154154
DxvkBufferSlice bufferSlice;
155+
Rc<DxvkAccelStructure> accelStructure;
155156
VkAccelerationStructureKHR tlas = nullptr;
156157
};
157158

src/dxvk/dxvk_context.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,14 @@ namespace dxvk {
266266
uint32_t slot,
267267
const Rc<DxvkAccelStructure> accelStructure) {
268268
ScopedCpuProfileZone();
269-
m_rc[slot].tlas = accelStructure->getAccelStructure();
269+
m_rc[slot].accelStructure = accelStructure;
270+
m_rc[slot].tlas = accelStructure != nullptr
271+
? accelStructure->getAccelStructure()
272+
: VK_NULL_HANDLE;
270273
m_rcTracked.clr(slot);
271274

272-
m_cmd->trackResource<DxvkAccess::Read>(accelStructure);
275+
if (accelStructure != nullptr)
276+
m_cmd->trackResource<DxvkAccess::Read>(accelStructure);
273277

274278
m_flags.set(
275279
DxvkContextFlag::CpDirtyResources,
@@ -4889,14 +4893,29 @@ namespace dxvk {
48894893
bindMask.clr(i);
48904894
descriptors[i].buffer = m_common->dummyResources().bufferDescriptor();
48914895
} break;
4892-
4896+
4897+
// NV-DXVK start: adding support for AS
48934898
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
4894-
if (res.tlas != VK_NULL_HANDLE) {
4899+
if (res.accelStructure != nullptr && res.tlas != VK_NULL_HANDLE) {
48954900
descriptors[i].accelerationStructure = res.tlas;
4901+
4902+
if (m_rcTracked.set(binding.slot))
4903+
// Keep the TLAS alive until this command list retires.
4904+
// When beginRecording starts a new command list, DXVK marks every descriptor slot "dirty"
4905+
// but leaves the cached resources (m_rc[...]) untouched. The next call to updateShaderResources
4906+
// therefore re emits whatever was previously stored in the slot including the old TLAS handle and
4907+
// Vulkan requires that handle to remain valid until the new command list finishes execution.
4908+
// Without tracking the accelStructure here the slot only held a raw VkAccelerationStructureKHR,
4909+
// so the resource managers Rc<DxvkAccelStructure> was the last owner.
4910+
// However, when that resource gets reallocated and overwrote that Rc, the object was destroyed immediately,
4911+
// yet the command list still queued descriptor updates pointing at it, triggering the validation error
4912+
m_cmd->trackResource<DxvkAccess::Read>(res.accelStructure);
4913+
48964914
} else {
48974915
bindMask.clr(i);
48984916
descriptors[i].accelerationStructure = VK_NULL_HANDLE;
48994917
} break;
4918+
// NV-DXVK end
49004919

49014920
default:
49024921
Logger::err(str::format("DxvkContext: Unhandled descriptor type: ", binding.type));

src/dxvk/rtx_render/rtx_accel_manager.cpp

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,21 @@ namespace dxvk {
130130
const uint32_t geometryInstanceShaderBindingTableRecordOffset = instance->getVkInstance().instanceShaderBindingTableRecordOffset;
131131

132132
if (!geometries.empty()) {
133-
if (instanceMask != geometryInstanceMask)
133+
if (instanceMask != geometryInstanceMask) {
134134
return false;
135-
if (instanceShaderBindingTableRecordOffset != geometryInstanceShaderBindingTableRecordOffset)
135+
}
136+
if (instanceShaderBindingTableRecordOffset != geometryInstanceShaderBindingTableRecordOffset) {
136137
return false;
137-
if (customIndexFlags != geometryCustomIndexFlags)
138+
}
139+
if (customIndexFlags != geometryCustomIndexFlags) {
138140
return false;
139-
if (instanceFlags != geometryInstanceFlags)
141+
}
142+
if (instanceFlags != geometryInstanceFlags) {
140143
return false;
141-
if (usesUnorderedApproximations != geometryUsesUnorderedApproximations)
144+
}
145+
if (usesUnorderedApproximations != geometryUsesUnorderedApproximations) {
142146
return false;
147+
}
143148
}
144149

145150
BlasEntry* blasEntry = instance->getBlas();
@@ -205,8 +210,7 @@ namespace dxvk {
205210
instance.billboardIndices.push_back(billboardIndex);
206211
instance.indexOffsets.push_back(billboardIndex * kNumIndicesPerBillboardQuad);
207212
}
208-
}
209-
else {
213+
} else {
210214
VkAccelerationStructureGeometryKHR geometry = {};
211215

212216
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
@@ -251,8 +255,9 @@ namespace dxvk {
251255
}
252256

253257
void AccelManager::createAndBuildIntersectionBlas(Rc<DxvkContext> ctx, DxvkBarrierSet& execBarriers) {
254-
if (m_intersectionBlas.ptr())
258+
if (m_intersectionBlas.ptr()) {
255259
return;
260+
}
256261

257262
VkAccelerationStructureGeometryKHR geometry {};
258263
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
@@ -428,8 +433,9 @@ namespace dxvk {
428433
ONCE(Logger::err("DxvkRaytrace: instances size is greater than max supported custom index value"));
429434
}
430435

431-
if (opacityMicromapManager)
436+
if (opacityMicromapManager) {
432437
opacityMicromapManager->onFrameStart(ctx);
438+
}
433439

434440
std::vector<std::unique_ptr<BlasBucket>> blasBuckets;
435441
blasBuckets.reserve(instances.size());
@@ -676,8 +682,9 @@ namespace dxvk {
676682
}
677683

678684
// Copy the instance transform data to the device
679-
if(instanceTransforms.size() > 0)
685+
if (instanceTransforms.size() > 0) {
680686
ctx->writeToBuffer(m_transformBuffer, 0, instanceTransforms.size() * sizeof(VkTransformMatrixKHR), instanceTransforms.data());
687+
}
681688

682689
ctx->getCommandList()->trackResource<DxvkAccess::Write>(m_transformBuffer);
683690
ctx->getCommandList()->trackResource<DxvkAccess::Read>(m_transformBuffer);
@@ -793,8 +800,7 @@ namespace dxvk {
793800
uint32_t paddedLastTouched = blas->frameLastTouched + 1 + (RtxOptions::enablePreviousTLAS() ? 1u : 0u); /* note: +2 because frameLastTouched is unsigned and init'd with UINT32_MAX, and keep the BLAS'es for one extra frame for previous TLAS access */
794801
if (bufferSize >= sizeInfo.accelerationStructureSize &&
795802
(!selectedBlas || bufferSize < selectedBlas->accelStructure->info().size) &&
796-
paddedLastTouched <= currentFrame)
797-
{
803+
paddedLastTouched <= currentFrame) {
798804
selectedBlas = blas.ptr();
799805
}
800806
}
@@ -818,7 +824,7 @@ namespace dxvk {
818824

819825
// Use the selected BLAS for the build
820826
buildInfo.dstAccelerationStructure = selectedBlas->accelStructure->getAccelStructure();
821-
827+
822828
if (buildInfo.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
823829
// Set the src to the dst if we're updating
824830
buildInfo.srcAccelerationStructure = buildInfo.dstAccelerationStructure;
@@ -853,15 +859,16 @@ namespace dxvk {
853859
instance.flags = bucket->instanceFlags;
854860
instance.instanceShaderBindingTableRecordOffset = bucket->instanceShaderBindingTableRecordOffset;
855861
instance.mask = bucket->instanceMask;
856-
instance.instanceCustomIndex =
862+
instance.instanceCustomIndex =
857863
(bucket->customIndexFlags & ~uint32_t(CUSTOM_INDEX_SURFACE_MASK)) |
858864
(bucket->reorderedSurfacesOffset & uint32_t(CUSTOM_INDEX_SURFACE_MASK));
859865
memcpy(static_cast<void*>(&instance.transform.matrix[0][0]), &identityTransform[0][0], sizeof(VkTransformMatrixKHR));
860866

861-
if (bucket->usesUnorderedApproximations && RtxOptions::enableSeparateUnorderedApproximations())
867+
if (bucket->usesUnorderedApproximations && RtxOptions::enableSeparateUnorderedApproximations()) {
862868
m_mergedInstances[Tlas::Unordered].push_back(instance);
863-
else
869+
} else {
864870
m_mergedInstances[Tlas::Opaque].push_back(instance);
871+
}
865872
}
866873
}
867874

@@ -875,8 +882,9 @@ namespace dxvk {
875882
}
876883
}
877884

878-
if (!haveInstances && instanceManager.getBillboards().empty())
885+
if (!haveInstances && instanceManager.getBillboards().empty()) {
879886
return;
887+
}
880888

881889
createAndBuildIntersectionBlas(ctx, execBarriers);
882890

@@ -890,8 +898,9 @@ namespace dxvk {
890898
uint32_t index = 0;
891899

892900
for (const auto& billboard : instanceManager.getBillboards()) {
893-
if (billboard.instanceMask == 0 || !billboard.allowAsIntersectionPrimitive)
901+
if (billboard.instanceMask == 0 || !billboard.allowAsIntersectionPrimitive) {
894902
continue;
903+
}
895904

896905
// Shader data
897906
MemoryBillboard& memory = memoryBillboards[index];
@@ -907,10 +916,12 @@ namespace dxvk {
907916
memory.centerUV = billboard.centerUV;
908917
memory.vertexColor = billboard.vertexColor;
909918
memory.flags = 0;
910-
if (billboard.isBeam)
919+
if (billboard.isBeam) {
911920
memory.flags |= billboardFlagIsBeam;
912-
if (billboard.isCameraFacing)
921+
}
922+
if (billboard.isCameraFacing) {
913923
memory.flags |= billboardFlagIsCameraFacing;
924+
}
914925

915926
// TLAS instance
916927
VkAccelerationStructureInstanceKHR instance {};
@@ -1262,8 +1273,9 @@ namespace dxvk {
12621273
}
12631274

12641275
void AccelManager::buildTlas(Rc<DxvkContext> ctx) {
1265-
if (m_vkInstanceBuffer == nullptr)
1276+
if (m_vkInstanceBuffer == nullptr) {
12661277
return;
1278+
}
12671279

12681280
ScopedGpuProfileZone(ctx, "buildTLAS");
12691281

@@ -1339,8 +1351,9 @@ namespace dxvk {
13391351
// Create TLAS
13401352
Tlas& tlas = m_device->getCommon()->getResources().getTLAS(type);
13411353

1342-
if (type == Tlas::Opaque)
1354+
if (type == Tlas::Opaque) {
13431355
std::swap(tlas.accelStructure, tlas.previousAccelStructure);
1356+
}
13441357

13451358
if (tlas.accelStructure == nullptr || sizeInfo.accelerationStructureSize > tlas.accelStructure->info().size) {
13461359
ScopedGpuProfileZone(ctx, "buildTLAS_createAccelStructure");

0 commit comments

Comments
 (0)