Skip to content

Commit 74da858

Browse files
committed
Fix issue with descriptor rebinding
Descriptor binding affected in-flight frames
1 parent 3c77c3f commit 74da858

3 files changed

Lines changed: 60 additions & 7 deletions

File tree

Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.cpp

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ namespace Renderer
2828
};
2929
constexpr u32 maxDescriptorSets = 128;
3030

31+
// [Frame-safe descriptor rebind] A buffer descriptor write that must wait until its target frame
32+
// slot is no longer being read by an in-flight frame before it can safely be applied.
33+
struct PendingBufferWrite
34+
{
35+
BufferID bufferID;
36+
DescriptorType type;
37+
};
38+
3139
struct DescriptorSet
3240
{
3341
DescriptorSetDesc desc;
@@ -50,6 +58,11 @@ namespace Renderer
5058

5159
// Reverse map: buffer -> binding (for fast lookup during validation)
5260
std::unordered_map<BufferID::type, u32> bufferToBinding;
61+
62+
// [Frame-safe descriptor rebind] Buffer-binding writes recorded per frame-copy and applied in
63+
// FlushPendingBufferWrites once that slot's fence has been waited (its previous frame is done),
64+
// so we never rewrite a descriptor copy an in-flight frame is still reading.
65+
std::unordered_map<u32, PendingBufferWrite> pendingBufferWritesPerSlot[RenderDeviceVK::FRAME_INDEX_COUNT];
5366
};
5467

5568
struct DescriptorHandlerData : public IDescriptorHandlerData
@@ -339,7 +352,7 @@ namespace Renderer
339352
}
340353
}
341354

342-
void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, BufferID bufferID, VkBuffer buffer, DescriptorType type, u32 frameIndex)
355+
void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, BufferID bufferID, DescriptorType type, u32 frameIndex)
343356
{
344357
ZoneScoped;
345358
DescriptorHandlerData& data = *static_cast<DescriptorHandlerData*>(_data);
@@ -385,15 +398,26 @@ namespace Renderer
385398

386399
// Mark binding as bound
387400
descriptorSet.unboundBindings.Unset(binding);
388-
389-
// Vulkan descriptor update
401+
402+
// [Frame-safe descriptor rebind] A GPUVector resize swaps in a NEW VkBuffer, BufferIDs are
403+
// recycled, and two frames are in flight sharing this set's per-frame descriptor copies. The only
404+
// moment at which writing a slot's copy is both safe (the slot's previous frame is fully done, so
405+
// no in-flight read races the write under UPDATE_AFTER_BIND) and current is right after that slot's
406+
// fence has been waited. So record the desired buffer per (slot, binding) here and apply it in
407+
// FlushPendingBufferWrites, which runs in FlipFrame immediately after that fence wait. The actual
408+
// VkBuffer is resolved from bufferID at flush time, so it always reflects the latest generation.
409+
descriptorSet.pendingBufferWritesPerSlot[frameIndex][binding] = PendingBufferWrite{ bufferID, type };
410+
}
411+
412+
void DescriptorHandlerVK::WriteBufferDescriptor(DescriptorSet& descriptorSet, u32 binding, VkBuffer buffer, DescriptorType type, u32 frameIndex)
413+
{
390414
VkDescriptorBufferInfo bufferInfo{};
391415
bufferInfo.buffer = buffer;
392416
bufferInfo.offset = 0;
393417
bufferInfo.range = VK_WHOLE_SIZE;
394418

395419
VkWriteDescriptorSet descriptorWrite{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
396-
descriptorWrite.dstSet = GetVkDescriptorSet(setID, frameIndex);
420+
descriptorWrite.dstSet = descriptorSet.sets[frameIndex];
397421
descriptorWrite.dstBinding = binding;
398422
descriptorWrite.descriptorCount = 1;
399423
descriptorWrite.descriptorType = FormatConverterVK::ToVkDescriptorType(type);
@@ -402,6 +426,23 @@ namespace Renderer
402426
vkUpdateDescriptorSets(_device->_device, 1, &descriptorWrite, 0, nullptr);
403427
}
404428

429+
void DescriptorHandlerVK::FlushPendingBufferWrites(u32 frameIndex)
430+
{
431+
ZoneScoped;
432+
DescriptorHandlerData& data = *static_cast<DescriptorHandlerData*>(_data);
433+
434+
for (DescriptorSet& descriptorSet : data.descriptorSets)
435+
{
436+
auto& pending = descriptorSet.pendingBufferWritesPerSlot[frameIndex];
437+
for (auto& [binding, write] : pending)
438+
{
439+
VkBuffer buffer = _bufferHandler->GetBuffer(write.bufferID);
440+
WriteBufferDescriptor(descriptorSet, binding, buffer, write.type, frameIndex);
441+
}
442+
pending.clear();
443+
}
444+
}
445+
405446
void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, VkImageView image, DescriptorType type, bool isRT, u32 frameIndex)
406447
{
407448
ZoneScoped;

Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ namespace Renderer
3333

3434
DescriptorSetID CreateDescriptorSet(const DescriptorSetDesc& desc);
3535

36-
void BindDescriptor(DescriptorSetID setID, u32 binding, BufferID bufferID, VkBuffer buffer, DescriptorType type, u32 frameIndex);
36+
void BindDescriptor(DescriptorSetID setID, u32 binding, BufferID bufferID, DescriptorType type, u32 frameIndex);
37+
38+
// [Frame-safe descriptor rebind] Apply buffer descriptor writes that were deferred while their
39+
// frame slot was in flight. Call once per frame, right after that slot's fence has been waited.
40+
void FlushPendingBufferWrites(u32 frameIndex);
3741
void BindDescriptor(DescriptorSetID setID, u32 binding, VkImageView image, DescriptorType type, bool isRT, u32 frameIndex);
3842
void BindDescriptorArray(DescriptorSetID setID, u32 binding, VkImageView image, u32 arrayOffset, DescriptorType type, bool isRT, u32 frameIndex);
3943
void BindDescriptorArray(DescriptorSetID setID, u32 binding, std::vector<VkImageView>& images, u32 arrayOffset, DescriptorType type, bool isRT, u32 frameIndex);
@@ -51,6 +55,7 @@ namespace Renderer
5155
private:
5256
void CreateDescriptorPool();
5357
void CreateDescriptorSet(DescriptorSet& descriptorSet);
58+
void WriteBufferDescriptor(DescriptorSet& descriptorSet, u32 binding, VkBuffer buffer, DescriptorType type, u32 frameIndex);
5459
bool ValidatePermissionViolations(u32 slot, const DescriptorSet& descriptorSet, const PersistentBitSet& accesses, const BitSet& permissions, const char* permissionName, const PersistentBitSet* usedBindings = nullptr);
5560

5661
private:

Source/Renderer/Renderer/Renderers/Vulkan/RendererVK.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,10 @@ namespace Renderer
253253

254254
void RendererVK::BindDescriptor(DescriptorSetID descriptorSetID, u32 bindingIndex, BufferID bufferID, DescriptorType type, u32 frameIndex)
255255
{
256-
VkBuffer buffer = _bufferHandler->GetBuffer(bufferID);
257-
_descriptorHandler->BindDescriptor(descriptorSetID, bindingIndex, bufferID, buffer, type, frameIndex);
256+
// [Frame-safe descriptor rebind] The handler records this write per frame-copy and applies it in
257+
// FlushPendingBufferWrites (FlipFrame), after that slot's fence has been waited. The VkBuffer is
258+
// resolved from bufferID at that point, so it always reflects the latest GPUVector generation.
259+
_descriptorHandler->BindDescriptor(descriptorSetID, bindingIndex, bufferID, type, frameIndex);
258260
}
259261

260262
void RendererVK::BindDescriptor(DescriptorSetID descriptorSetID, u32 bindingIndex, ImageID imageID, u32 mipLevel, DescriptorType type, u32 frameIndex)
@@ -409,6 +411,11 @@ namespace Renderer
409411
timeWaited = timer.GetLifeTime();
410412
}
411413

414+
// [Frame-safe descriptor rebind] This slot's previous frame has now completed (fence waited above),
415+
// so it is finally safe to apply any buffer descriptor writes that were deferred while it was in
416+
// flight. Done before any of this frame's command lists record/use the descriptor sets.
417+
_descriptorHandler->FlushPendingBufferWrites(_frameIndex);
418+
412419
_imageHandler->FlipFrame(frameIndex);
413420
if (_renderSizeChanged)
414421
{

0 commit comments

Comments
 (0)