Report vulkan dynamic offsets separately for manual application

* Baking these into descriptors when we get arbitrary 'GetDescriptor' queries
  independent of the bound descriptor sets is not possible - a descriptor set
  could be in theory bound twice to two places with different dynamic offsets.
* Instead we report these as part of the pipeline states and the abstraction &
  replay API consumer will need to manually apply them to get the true buffer
  offset.
* The offsets are indexed by descriptor storage byte offset for easier
  processing (the dynamic offset struct can be turned into a pair and used to
  initialise a dict)
This commit is contained in:
baldurk
2024-03-11 12:20:44 +00:00
parent d03c379f3d
commit fa22c7c7fc
4 changed files with 116 additions and 1 deletions
+1
View File
@@ -419,6 +419,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DescriptorAccess)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, Attachment)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, BindingElement)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DescriptorBinding)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DynamicOffset)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DescriptorSet)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, ImageData)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, ImageLayout)
+44
View File
@@ -314,6 +314,39 @@ If :data:`descriptorCount` is 1 then this list has only one element and the bind
rdcarray<BindingElement> binds;
};
DOCUMENT("A dynamic offset applied to a single descriptor access.");
struct DynamicOffset
{
DOCUMENT("");
DynamicOffset() = default;
DynamicOffset(const DynamicOffset &) = default;
DynamicOffset &operator=(const DynamicOffset &) = default;
bool operator==(const DynamicOffset &o) const
{
return descriptorByteOffset == o.descriptorByteOffset &&
dynamicBufferByteOffset == o.dynamicBufferByteOffset;
}
bool operator<(const DynamicOffset &o) const
{
if(!(descriptorByteOffset == o.descriptorByteOffset))
return descriptorByteOffset < o.descriptorByteOffset;
if(!(dynamicBufferByteOffset == o.dynamicBufferByteOffset))
return dynamicBufferByteOffset < o.dynamicBufferByteOffset;
return false;
}
DOCUMENT(R"(The offset in bytes to the descriptor in the storage.
:type: int
)");
uint64_t descriptorByteOffset = 0;
DOCUMENT(R"(The dynamic offset to apply to the buffer in bytes.
:type: int
)");
uint64_t dynamicBufferByteOffset = 0;
};
DOCUMENT("The contents of a descriptor set.");
struct DescriptorSet
{
@@ -363,6 +396,16 @@ offset and size into this buffer.
:type: bytes
)");
bytebuf inlineData;
DOCUMENT(R"(A list of dynamic offsets to be applied to specific bindings, on top of the contents
of their descriptors.
.. note::
The returned values from :meth:`PipeState.GetConstantBuffer` already have these offsets applied.
:type: List[VKDynamicOffset]
)");
rdcarray<DynamicOffset> dynamicOffsets;
};
DOCUMENT("Describes the object and descriptor set bindings of a Vulkan pipeline object.");
@@ -1477,6 +1520,7 @@ struct State
DECLARE_REFLECTION_STRUCT(VKPipe::BindingElement);
DECLARE_REFLECTION_STRUCT(VKPipe::DescriptorBinding);
DECLARE_REFLECTION_STRUCT(VKPipe::DynamicOffset);
DECLARE_REFLECTION_STRUCT(VKPipe::DescriptorSet);
DECLARE_REFLECTION_STRUCT(VKPipe::Pipeline);
DECLARE_REFLECTION_STRUCT(VKPipe::IndexBuffer);
+59
View File
@@ -2006,6 +2006,65 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
ret.graphics.descriptorSets.resize(state.graphics.descSets.size());
ret.compute.descriptorSets.resize(state.compute.descSets.size());
// store dynamic offsets
{
rdcarray<VKPipe::DescriptorSet> *dsts[] = {
&ret.graphics.descriptorSets,
&ret.compute.descriptorSets,
};
const rdcarray<VulkanStatePipeline::DescriptorAndOffsets> *srcs[] = {
&state.graphics.descSets,
&state.compute.descSets,
};
for(size_t p = 0; p < ARRAY_COUNT(srcs); p++)
{
for(size_t i = 0; i < srcs[p]->size(); i++)
{
const VulkanStatePipeline::DescriptorAndOffsets &srcData = srcs[p]->at(i);
ResourceId sourceSet = srcData.descSet;
const uint32_t *srcOffset = srcData.offsets.begin();
VKPipe::DescriptorSet &destSet = dsts[p]->at(i);
destSet.dynamicOffsets.clear();
if(sourceSet == ResourceId())
continue;
destSet.dynamicOffsets.reserve(srcData.offsets.size());
VKPipe::DynamicOffset dynOffset;
const WrappedVulkan::DescriptorSetInfo &descSetState =
m_pDriver->m_DescriptorSetState[sourceSet];
const DescriptorSetSlot *first = descSetState.data.binds[0];
for(size_t b = 0; b < descSetState.data.binds.size(); b++)
{
const DescSetLayout::Binding &layoutBind =
c.m_DescSetLayout[descSetState.layout].bindings[b];
if(layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
continue;
uint64_t descriptorByteOffset = descSetState.data.binds[b] - first;
// inline UBOs aren't dynamic and variable size can't be used with dynamic buffers, so the
// count is what it is at definition time
for(uint32_t a = 0; a < layoutBind.descriptorCount; a++)
{
dynOffset.descriptorByteOffset = descriptorByteOffset + a;
dynOffset.dynamicBufferByteOffset = *srcOffset;
srcOffset++;
destSet.dynamicOffsets.push_back(dynOffset);
}
}
}
}
}
{
rdcarray<VKPipe::DescriptorSet> *dsts[] = {
&ret.graphics.descriptorSets,
+12 -1
View File
@@ -2138,6 +2138,15 @@ void DoSerialise(SerialiserType &ser, VKPipe::DescriptorBinding &el)
SIZE_CHECK(48);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VKPipe::DynamicOffset &el)
{
SERIALISE_MEMBER(descriptorByteOffset);
SERIALISE_MEMBER(dynamicBufferByteOffset);
SIZE_CHECK(16);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VKPipe::DescriptorSet &el)
{
@@ -2149,7 +2158,9 @@ void DoSerialise(SerialiserType &ser, VKPipe::DescriptorSet &el)
SERIALISE_MEMBER(inlineData);
SIZE_CHECK(72);
SERIALISE_MEMBER(dynamicOffsets);
SIZE_CHECK(96);
}
template <typename SerialiserType>