Implement descriptor buffer binding and track & restore its state

This commit is contained in:
baldurk
2025-06-05 17:11:09 +01:00
parent dacb5aab79
commit f75a39d171
3 changed files with 404 additions and 12 deletions
+103 -12
View File
@@ -311,6 +311,52 @@ void VulkanRenderState::BindPipeline(WrappedVulkan *vk, VkCommandBuffer cmd,
if(subpass0 && dynamicRendering.active)
subpass0 = false;
if(!descBufs.empty())
{
VkDescriptorBufferBindingPushDescriptorBufferHandleEXT push;
rdcarray<VkBufferUsageFlags2CreateInfo> usage2;
rdcarray<VkDescriptorBufferBindingInfoEXT> bind;
uint32_t bufferCount = descBufs.count();
usage2.resize(bufferCount);
bind.resize(bufferCount);
for(uint32_t i = 0; i < bufferCount; i++)
{
bind[i] = {VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT};
bind[i].address = descBufs[i].address;
if(descBufs[i].flags2)
{
usage2[i] = {
VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO,
NULL,
descBufs[i].usage,
};
bind[i].pNext = &usage2[i];
}
else
{
bind[i].usage = VkBufferUsageFlags(descBufs[i].usage);
}
if(descBufs[i].pushBuffer != ResourceId())
{
push = {
VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_PUSH_DESCRIPTOR_BUFFER_HANDLE_EXT,
bind[i].pNext,
Unwrap(vk->GetResourceManager()->GetCurrentHandle<VkBuffer>(descBufs[i].pushBuffer)),
};
bind[i].pNext = &push;
}
}
ObjDisp(cmd)->CmdBindDescriptorBuffersEXT(Unwrap(cmd), bufferCount, bind.data());
}
if(binding == BindGraphics || binding == BindInitial)
{
if(graphics.pipeline != ResourceId())
@@ -798,7 +844,7 @@ void VulkanRenderState::BindDescriptorSetsForPipeline(WrappedVulkan *vk, VkComma
{
const DescSetLayout &descLayout = vk->GetDebugManager()->GetDescSetLayout(descSetLayouts[i]);
if(i < pipe.descSets.size() && pipe.descSets[i].descSet != ResourceId())
if(i < pipe.descSets.size() && pipe.descSets[i].IsBound())
{
// if we come to a descriptor set that isn't compatible, stop setting descriptor sets from
// here on.
@@ -808,7 +854,19 @@ void VulkanRenderState::BindDescriptorSetsForPipeline(WrappedVulkan *vk, VkComma
// by the next action. The remaining sets are invalid, but also unused and this is
// explicitly allowed by the spec. We just have to make sure we don't try to actively bind
// an incompatible descriptor set.
ResourceId createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet);
ResourceId createdDescSetLayoutId;
if(pipe.descSets[i].descSet != ResourceId())
{
createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet);
}
else
{
const VulkanCreationInfo::PipelineLayout &refPipeLayout =
vk->GetDebugManager()->GetPipelineLayoutInfo(pipe.descSets[i].pipeLayout);
createdDescSetLayoutId = refPipeLayout.descSetLayouts[i];
}
if(descSetLayouts[i] != createdDescSetLayoutId)
{
@@ -877,7 +935,7 @@ void VulkanRenderState::BindDescriptorSetsWithoutPipeline(WrappedVulkan *vk, VkC
for(size_t i = 0; i < pipe.descSets.size(); i++)
{
if(pipe.descSets[i].pipeLayout == ResourceId() || pipe.descSets[i].descSet == ResourceId())
if(pipe.descSets[i].pipeLayout == ResourceId() || !pipe.descSets[i].IsBound())
continue;
const VulkanCreationInfo::PipelineLayout &iPipeLayout =
@@ -953,7 +1011,7 @@ void VulkanRenderState::BindDescriptorSetsWithoutPipeline(WrappedVulkan *vk, VkC
}
}
if(pipe.descSets[i].descSet != ResourceId())
if(pipe.descSets[i].IsBound())
{
const DescSetLayout &descLayout =
vk->GetDebugManager()->GetDescSetLayout(iPipeLayout.descSetLayouts[i]);
@@ -1006,7 +1064,7 @@ void VulkanRenderState::BindDescriptorSetsForShaders(WrappedVulkan *vk, VkComman
{
const DescSetLayout &descLayout = vk->GetDebugManager()->GetDescSetLayout(descSetLayouts[i]);
if(i < pipe.descSets.size() && pipe.descSets[i].descSet != ResourceId())
if(i < pipe.descSets.size() && pipe.descSets[i].IsBound())
{
// if we come to a descriptor set that isn't compatible, stop setting descriptor sets from
// here on.
@@ -1016,7 +1074,19 @@ void VulkanRenderState::BindDescriptorSetsForShaders(WrappedVulkan *vk, VkComman
// by the next action. The remaining sets are invalid, but also unused and this is
// explicitly allowed by the spec. We just have to make sure we don't try to actively bind
// an incompatible descriptor set.
ResourceId createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet);
ResourceId createdDescSetLayoutId;
if(pipe.descSets[i].descSet != ResourceId())
{
createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet);
}
else
{
const VulkanCreationInfo::PipelineLayout &refPipeLayout =
vk->GetDebugManager()->GetPipelineLayoutInfo(pipe.descSets[i].pipeLayout);
createdDescSetLayoutId = refPipeLayout.descSetLayouts[i];
}
if(descSetLayouts[i] != createdDescSetLayoutId)
{
@@ -1069,16 +1139,30 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout
VkCommandBuffer cmd, VkPipelineBindPoint bindPoint,
uint32_t setIndex, uint32_t *dynamicOffsets)
{
ResourceId descSet = GetPipeline(bindPoint).descSets[setIndex].descSet;
ResourceId pipeLayout = GetPipeline(bindPoint).descSets[setIndex].pipeLayout;
const VulkanStatePipeline::DescriptorAndOffsets &desc = GetPipeline(bindPoint).descSets[setIndex];
ResourceId descSet = desc.descSet;
ResourceId pipeLayout = desc.pipeLayout;
VkPipelineLayout layout = vk->GetResourceManager()->GetCurrentHandle<VkPipelineLayout>(pipeLayout);
if((descLayout.flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT) == 0)
{
ObjDisp(cmd)->CmdBindDescriptorSets(
Unwrap(cmd), bindPoint, Unwrap(layout), (uint32_t)setIndex, 1,
UnwrapPtr(vk->GetResourceManager()->GetCurrentHandle<VkDescriptorSet>(descSet)),
descLayout.dynamicCount, dynamicOffsets);
if(descSet != ResourceId())
{
ObjDisp(cmd)->CmdBindDescriptorSets(
Unwrap(cmd), bindPoint, Unwrap(layout), setIndex, 1,
UnwrapPtr(vk->GetResourceManager()->GetCurrentHandle<VkDescriptorSet>(descSet)),
descLayout.dynamicCount, dynamicOffsets);
}
else if(desc.descBufferEmbeddedSamplers)
{
ObjDisp(cmd)->CmdBindDescriptorBufferEmbeddedSamplersEXT(Unwrap(cmd), bindPoint,
Unwrap(layout), setIndex);
}
else
{
ObjDisp(cmd)->CmdSetDescriptorBufferOffsetsEXT(Unwrap(cmd), bindPoint, Unwrap(layout), setIndex,
1, &desc.descBufferIdx, &desc.descBufferOffset);
}
}
else
{
@@ -1086,6 +1170,13 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout
// current state.
rdcarray<VkWriteDescriptorSet> writes;
// for descriptor buffer there may be a buffer to bind still
if(desc.descBufferIdx != ~0U)
{
ObjDisp(cmd)->CmdSetDescriptorBufferOffsetsEXT(Unwrap(cmd), bindPoint, Unwrap(layout), setIndex,
1, &desc.descBufferIdx, &desc.descBufferOffset);
}
// any allocated arrays
rdcarray<VkDescriptorImageInfo *> allocImgWrites;
rdcarray<VkDescriptorBufferInfo *> allocBufWrites;
+27
View File
@@ -45,14 +45,32 @@ struct VulkanStatePipeline
struct DescriptorAndOffsets
{
ResourceId pipeLayout;
// if descriptor set bound
ResourceId descSet;
rdcarray<uint32_t> offsets;
// if descriptor buffer bound
uint32_t descBufferIdx = ~0U;
VkDeviceSize descBufferOffset = 0;
bool descBufferEmbeddedSamplers = false;
bool IsBound() const
{
return descSet != ResourceId() || descBufferIdx != ~0U || descBufferEmbeddedSamplers;
}
};
rdcarray<DescriptorAndOffsets> descSets;
// the index of the last set bound. In the case where we are re-binding sets and don't have a
// valid pipeline to reference, this can help us resolve which descriptor sets to rebind in the
// event that they're not all compatible
uint32_t lastBoundSet = 0;
bool UsingDescBufs() const
{
// all sets must be using buffers or not
return !descSets.empty() && descSets[0].descBufferIdx != ~0U;
}
};
struct VulkanRenderState
@@ -181,6 +199,15 @@ struct VulkanRenderState
return compute;
}
struct DescriptorBuffer
{
VkDeviceAddress address;
VkBufferUsageFlags2 usage;
bool flags2;
ResourceId pushBuffer;
};
rdcarray<DescriptorBuffer> descBufs;
struct IdxBuffer
{
ResourceId buf;
@@ -3548,6 +3548,13 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorSets(
VulkanStatePipeline &pipeline = renderstate.GetPipeline(pipelineBindPoint);
rdcarray<VulkanStatePipeline::DescriptorAndOffsets> &descsets = pipeline.descSets;
// descriptor buffers and descriptor sets can't co-exist, each invalidates the other
if(pipeline.UsingDescBufs())
{
renderstate.descBufs.clear();
descsets.clear();
}
// expand as necessary
if(descsets.size() < firstSet + setCount)
descsets.resize(firstSet + setCount);
@@ -8672,12 +8679,120 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorBuffersEXT(
SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t bufferCount,
const VkDescriptorBufferBindingInfoEXT *pBindingInfos)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(bufferCount).Important();
SERIALISE_ELEMENT_ARRAY(pBindingInfos, bufferCount);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
size_t memSize = 0;
for(uint32_t b = 0; b < bufferCount; b++)
memSize += GetNextPatchSize(&pBindingInfos[b]);
byte *tempMem = GetTempMemory(memSize);
rdcarray<VkDescriptorBufferBindingInfoEXT> unwrappedInfos;
for(uint32_t b = 0; b < bufferCount; b++)
unwrappedInfos.push_back(*UnwrapStructAndChain(m_State, tempMem, &pBindingInfos[b]));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
ObjDisp(commandBuffer)
->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data());
{
VulkanRenderState &renderstate = GetCmdRenderState();
// all descriptor buffers above bufferCount are unbound
renderstate.descBufs.resize(bufferCount);
for(uint32_t i = 0; i < bufferCount; i++)
{
renderstate.descBufs[i].address = pBindingInfos[i].address;
renderstate.descBufs[i].usage = pBindingInfos[i].usage;
renderstate.descBufs[i].flags2 = false;
const VkBufferUsageFlags2CreateInfo *usage2 =
(const VkBufferUsageFlags2CreateInfo *)FindNextStruct(
&pBindingInfos[i], VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO);
if(usage2)
{
renderstate.descBufs[i].usage = usage2->usage;
renderstate.descBufs[i].flags2 = true;
}
const VkDescriptorBufferBindingPushDescriptorBufferHandleEXT *push =
(const VkDescriptorBufferBindingPushDescriptorBufferHandleEXT *)FindNextStruct(
&pBindingInfos[i],
VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_PUSH_DESCRIPTOR_BUFFER_HANDLE_EXT);
if(push)
{
renderstate.descBufs[i].pushBuffer = GetResID(push->buffer);
}
}
// any offsets that refer to these buffers are invalidated, but then also other buffers
// are unbound meaning those are invalid - we can clear all bindings that refer to
// descriptor buffers however normal descriptor sets must remain as they are *not*
// invalidated and could still be used
for(VkPipelineBindPoint bindPoint :
{VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_COMPUTE,
VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR})
{
VulkanStatePipeline &pipe = renderstate.GetPipeline(bindPoint);
for(uint32_t i = 0; i < pipe.descSets.size(); i++)
if(pipe.descSets[i].descSet == ResourceId())
pipe.descSets[i] = {};
}
}
}
}
else
{
ObjDisp(commandBuffer)
->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data());
}
}
return true;
}
void WrappedVulkan::vkCmdBindDescriptorBuffersEXT(VkCommandBuffer commandBuffer, uint32_t bufferCount,
const VkDescriptorBufferBindingInfoEXT *pBindingInfos)
{
SCOPED_DBG_SINK();
size_t memSize = 0;
for(uint32_t b = 0; b < bufferCount; b++)
memSize += GetNextPatchSize(&pBindingInfos[b]);
byte *tempMem = GetTempMemory(memSize);
rdcarray<VkDescriptorBufferBindingInfoEXT> unwrappedInfos;
for(uint32_t b = 0; b < bufferCount; b++)
unwrappedInfos.push_back(*UnwrapStructAndChain(m_State, tempMem, &pBindingInfos[b]));
SERIALISE_TIME_CALL(
ObjDisp(commandBuffer)
->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data()));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBindDescriptorBuffersEXT);
Serialise_vkCmdBindDescriptorBuffersEXT(ser, commandBuffer, bufferCount, pBindingInfos);
record->AddChunk(scope.Get(&record->cmdInfo->alloc));
// buffers are bound with addresses, they will be forced referenced
}
}
template <typename SerialiserType>
@@ -8686,6 +8801,71 @@ bool WrappedVulkan::Serialise_vkCmdSetDescriptorBufferOffsetsEXT(
VkPipelineLayout layout, uint32_t firstSet, uint32_t setCount, const uint32_t *pBufferIndices,
const VkDeviceSize *pOffsets)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(pipelineBindPoint).Important();
SERIALISE_ELEMENT(layout);
SERIALISE_ELEMENT(firstSet).Important();
SERIALISE_ELEMENT(setCount);
SERIALISE_ELEMENT_ARRAY(pBufferIndices, setCount);
SERIALISE_ELEMENT_ARRAY(pOffsets, setCount);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
ObjDisp(commandBuffer)
->CmdSetDescriptorBufferOffsetsEXT(Unwrap(commandBuffer), pipelineBindPoint,
Unwrap(layout), firstSet, setCount, pBufferIndices,
pOffsets);
{
VulkanRenderState &renderstate = GetCmdRenderState();
VulkanStatePipeline &pipeline = renderstate.GetPipeline(pipelineBindPoint);
pipeline.lastBoundSet = firstSet;
// descriptor set bindings are overwritten/cleared by descriptor buffer bindings
for(uint32_t set = 0; set < setCount; set++)
{
pipeline.descSets.resize_for_index(firstSet + set);
pipeline.descSets[firstSet + set].pipeLayout = GetResID(layout);
pipeline.descSets[firstSet + set].descBufferIdx = pBufferIndices[set];
pipeline.descSets[firstSet + set].descBufferOffset = pOffsets[set];
pipeline.descSets[firstSet + set].descBufferEmbeddedSamplers = false;
}
// any normal descriptor set bindings are invalidated
for(VkPipelineBindPoint bindPoint :
{VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_COMPUTE,
VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR})
{
VulkanStatePipeline &pipe = renderstate.GetPipeline(bindPoint);
for(uint32_t i = 0; i < pipe.descSets.size(); i++)
if(pipe.descSets[i].descSet != ResourceId())
pipe.descSets[i] = {};
}
}
}
}
else
{
ObjDisp(commandBuffer)
->CmdSetDescriptorBufferOffsetsEXT(Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout),
firstSet, setCount, pBufferIndices, pOffsets);
}
}
return true;
}
@@ -8696,6 +8876,26 @@ void WrappedVulkan::vkCmdSetDescriptorBufferOffsetsEXT(VkCommandBuffer commandBu
const uint32_t *pBufferIndices,
const VkDeviceSize *pOffsets)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
->CmdSetDescriptorBufferOffsetsEXT(
Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), firstSet,
setCount, pBufferIndices, pOffsets));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdSetDescriptorBufferOffsetsEXT);
Serialise_vkCmdSetDescriptorBufferOffsetsEXT(ser, commandBuffer, pipelineBindPoint, layout,
firstSet, setCount, pBufferIndices, pOffsets);
record->AddChunk(scope.Get(&record->cmdInfo->alloc));
record->MarkResourceFrameReferenced(GetResID(layout), eFrameRef_Read);
}
}
template <typename SerialiserType>
@@ -8703,6 +8903,61 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorBufferEmbeddedSamplersEXT(
SerialiserType &ser, VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint,
VkPipelineLayout layout, uint32_t set)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(pipelineBindPoint).Important();
SERIALISE_ELEMENT(layout);
SERIALISE_ELEMENT(set).Important();
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
ObjDisp(commandBuffer)
->CmdBindDescriptorBufferEmbeddedSamplersEXT(Unwrap(commandBuffer), pipelineBindPoint,
Unwrap(layout), set);
{
VulkanRenderState &renderstate = GetCmdRenderState();
VulkanStatePipeline &pipeline = renderstate.GetPipeline(pipelineBindPoint);
pipeline.descSets.resize_for_index(set);
pipeline.descSets[set].pipeLayout = GetResID(layout);
pipeline.descSets[set].descBufferIdx = ~0U;
pipeline.descSets[set].descBufferOffset = 0;
pipeline.descSets[set].descBufferEmbeddedSamplers = true;
// any normal descriptor set bindings are invalidated
for(VkPipelineBindPoint bindPoint :
{VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_COMPUTE,
VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR})
{
VulkanStatePipeline &pipe = renderstate.GetPipeline(bindPoint);
for(uint32_t i = 0; i < pipe.descSets.size(); i++)
if(pipe.descSets[i].descSet != ResourceId())
pipe.descSets[i] = {};
}
}
}
}
else
{
ObjDisp(commandBuffer)
->CmdBindDescriptorBufferEmbeddedSamplersEXT(Unwrap(commandBuffer), pipelineBindPoint,
Unwrap(layout), set);
}
}
return true;
}
@@ -8711,6 +8966,25 @@ void WrappedVulkan::vkCmdBindDescriptorBufferEmbeddedSamplersEXT(VkCommandBuffer
VkPipelineLayout layout,
uint32_t set)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
->CmdBindDescriptorBufferEmbeddedSamplersEXT(
Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), set));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBindDescriptorBufferEmbeddedSamplersEXT);
Serialise_vkCmdBindDescriptorBufferEmbeddedSamplersEXT(ser, commandBuffer, pipelineBindPoint,
layout, set);
record->AddChunk(scope.Get(&record->cmdInfo->alloc));
record->MarkResourceFrameReferenced(GetResID(layout), eFrameRef_Read);
}
}
INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateCommandPool, VkDevice device,