Don't require an extra descriptor set for vulkan postvs fetch

This commit is contained in:
baldurk
2018-10-04 16:07:35 +01:00
parent b87792f721
commit 92feb1013b
4 changed files with 274 additions and 118 deletions
-22
View File
@@ -1424,25 +1424,6 @@ void VulkanReplay::CreateResources()
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 1.0f);
WrappedVulkan *driver = m_pDriver;
CREATE_OBJECT(
m_MeshFetchDescSetLayout,
{
// output buffer
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// index buffer (if needed)
{1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// vertex buffers (float type)
{2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 16, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// vertex buffers (uint32_t type)
{3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 16, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// vertex buffers (int32_t type)
{4, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 16, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
});
CREATE_OBJECT(m_MeshFetchDescSet, m_General.DescriptorPool, m_MeshFetchDescSetLayout);
GPA_vkContextOpenInfo context = {Unwrap(m_pDriver->GetInstance()),
Unwrap(m_pDriver->GetPhysDev()), Unwrap(m_pDriver->GetDev())};
@@ -1475,9 +1456,6 @@ void VulkanReplay::DestroyResources()
{
ClearPostVSCache();
if(m_MeshFetchDescSetLayout != VK_NULL_HANDLE)
m_pDriver->vkDestroyDescriptorSetLayout(m_pDriver->GetDev(), m_MeshFetchDescSetLayout, NULL);
m_General.Destroy(m_pDriver);
m_TexRender.Destroy(m_pDriver);
m_Overlay.Destroy(m_pDriver);
+274 -78
View File
@@ -35,11 +35,18 @@ static const char *PatchedMeshOutputEntryPoint = "rdc";
static const uint32_t MeshOutputDispatchWidth = 128;
static const uint32_t MeshOutputTBufferArraySize = 16;
// 0 = output
// 1 = indices
// 2 = float vbuffers
// 3 = uint vbuffers
// 4 = sint vbuffers
static const uint32_t MeshOutputReservedBindings = 5;
static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRVPatchData &patchData,
const char *entryName, std::vector<uint32_t> instDivisor,
uint32_t &descSet, const DrawcallDescription *draw,
uint32_t numVerts, uint32_t numViews,
std::vector<uint32_t> &modSpirv, uint32_t &bufStride)
const DrawcallDescription *draw, uint32_t numVerts,
uint32_t numViews, std::vector<uint32_t> &modSpirv,
uint32_t &bufStride)
{
SPIRVEditor editor(modSpirv);
@@ -48,18 +55,19 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
uint32_t numOutputs = (uint32_t)refl.outputSignature.size();
RDCASSERT(numOutputs > 0);
descSet = 0;
for(SPIRVIterator it = editor.BeginDecorations(), end = editor.EndDecorations(); it < end; ++it)
{
// we will use the descriptor set immediately after the last set statically used by the shader.
// This means we don't have to worry about if the descriptor set layout declares more sets which
// might be invalid and un-bindable, we just trample over the next set that's unused.
// This is much easier than trying to add a new bind to an existing descriptor set (which would
// cascade into a new descriptor set layout, new pipeline layout, etc etc!). However, this might
// push us over the limit on number of descriptor sets.
if(it.opcode() == spv::OpDecorate && it.word(2) == spv::DecorationDescriptorSet)
descSet = RDCMAX(descSet, it.word(3) + 1);
// we will use descriptor set 0 bindings 0..N for our own purposes.
//
// Since bindings are arbitrary, we just increase all user bindings to make room, and we'll
// redeclare the descriptor set layouts and pipeline layout. This is inevitable in the case
// where all descriptor sets are already used. In theory we only have to do this with set 0, but
// that requires knowing which variables are in set 0 and it's simpler to increase all bindings.
if(it.opcode() == spv::OpDecorate && it.word(2) == spv::DecorationBinding)
{
RDCASSERT(it.word(2) < (0xffffffff - MeshOutputReservedBindings));
it.word(3) += MeshOutputReservedBindings;
}
}
// tbuffer types, the values are the descriptor bindings
@@ -471,7 +479,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
editor.SetName(tbuffers[tb].variableID, name);
editor.AddDecoration(SPIRVOperation(
spv::OpDecorate, {tbuffers[tb].variableID, (uint32_t)spv::DecorationDescriptorSet, descSet}));
spv::OpDecorate, {tbuffers[tb].variableID, (uint32_t)spv::DecorationDescriptorSet, 0}));
editor.AddDecoration(SPIRVOperation(
spv::OpDecorate, {tbuffers[tb].variableID, (uint32_t)spv::DecorationBinding, (uint32_t)tb}));
}
@@ -498,8 +506,8 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
editor.SetName(idxImagePtr, "ibuffer");
editor.AddDecoration(SPIRVOperation(
spv::OpDecorate, {idxImagePtr, (uint32_t)spv::DecorationDescriptorSet, descSet}));
editor.AddDecoration(
SPIRVOperation(spv::OpDecorate, {idxImagePtr, (uint32_t)spv::DecorationDescriptorSet, 0}));
editor.AddDecoration(
SPIRVOperation(spv::OpDecorate, {idxImagePtr, (uint32_t)spv::DecorationBinding, 1}));
}
@@ -596,7 +604,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
// set binding
editor.AddDecoration(
SPIRVOperation(spv::OpDecorate, {outBufferVarID, spv::DecorationDescriptorSet, descSet}));
SPIRVOperation(spv::OpDecorate, {outBufferVarID, spv::DecorationDescriptorSet, 0}));
editor.AddDecoration(SPIRVOperation(spv::OpDecorate, {outBufferVarID, spv::DecorationBinding, 0}));
}
@@ -1133,20 +1141,15 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
if(drawcall == NULL || drawcall->numIndices == 0 || drawcall->numInstances == 0)
return;
// the SPIR-V patching will determine the next descriptor set to use, after all sets statically
// used by the shader. This gets around the problem where the shader only uses 0 and 1, but the
// layout declares 0-4, and 2,3,4 are invalid at bind time and we are unable to bind our new set
// 5. Instead we'll notice that only 0 and 1 are used and just use 2 ourselves (although it was
// in
// the original set layout, we know it's statically unused by the shader so we can safely steal
// it).
uint32_t descSet = 0;
// we go through the driver for all these creations since they need to be properly
// registered in order to be put in the partial replay state
VkResult vkr = VK_SUCCESS;
VkDevice dev = m_Device;
VkDescriptorPool descpool;
std::vector<VkDescriptorSetLayout> setLayouts;
std::vector<VkDescriptorSet> descSets;
VkPipelineLayout pipeLayout;
VkGraphicsPipelineCreateInfo pipeCreateInfo;
@@ -1154,6 +1157,224 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
// get pipeline create info
m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo, state.graphics.pipeline);
// create a duplicate set of descriptor sets, with all bindings shifted, and copy the bindings
// into them
{
std::vector<VkCopyDescriptorSet> descCopies;
// one for each descriptor type. 1 of each to start with plus enough for our internal resources,
// we then increment for each descriptor we need to allocate
VkDescriptorPoolSize poolSizes[11] = {
{VK_DESCRIPTOR_TYPE_SAMPLER, 1},
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1},
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1},
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 50},
{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1},
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1},
};
const std::vector<ResourceId> &descSetLayoutIds =
creationInfo.m_PipelineLayout[pipeInfo.layout].descSetLayouts;
std::vector<VkDescriptorSetLayoutBinding> newBindings;
// need to add our own bindings to the first descriptor set
{
// output buffer
newBindings.push_back({
0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
});
// index buffer (if needed)
newBindings.push_back({
1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
});
// vertex buffers (float type)
newBindings.push_back({
2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, MeshOutputTBufferArraySize,
VK_SHADER_STAGE_COMPUTE_BIT, NULL,
});
// vertex buffers (uint32_t type)
newBindings.push_back({
3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, MeshOutputTBufferArraySize,
VK_SHADER_STAGE_COMPUTE_BIT, NULL,
});
// vertex buffers (int32_t type)
newBindings.push_back({
4, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, MeshOutputTBufferArraySize,
VK_SHADER_STAGE_COMPUTE_BIT, NULL,
});
}
// if there are fewer sets bound than were declared in the pipeline layout, only process the
// bound sets (as otherwise we'd fail to copy from them). Assume the application knew what it
// was doing and the other sets are statically unused.
setLayouts.resize(RDCMIN(state.graphics.descSets.size(), descSetLayoutIds.size()));
// need at least one set, if the shader isn't using any we'll just make our own
if(setLayouts.empty())
setLayouts.resize(1);
for(size_t i = 0; i < setLayouts.size(); i++)
{
bool hasImmutableSamplers = false;
// except for the first layout we need to start from scratch
if(i > 0)
newBindings.clear();
// if the shader had no descriptor sets at all, i will be invalid, so just skip and add a set
// with only our own bindings.
if(i < descSetLayoutIds.size())
{
const DescSetLayout &origLayout = creationInfo.m_DescSetLayout[descSetLayoutIds[i]];
for(size_t b = 0; b < origLayout.bindings.size(); b++)
{
const DescSetLayout::Binding &bind = origLayout.bindings[b];
// skip empty bindings
if(bind.descriptorCount == 0)
continue;
// make room in the pool
poolSizes[bind.descriptorType].descriptorCount += bind.descriptorCount;
VkDescriptorSetLayoutBinding newBind;
// offset the binding
newBind.binding = (uint32_t)b + MeshOutputReservedBindings;
newBind.descriptorCount = bind.descriptorCount;
newBind.descriptorType = bind.descriptorType;
// we only need it available for compute, just make all bindings visible otherwise dynamic
// buffer offsets could be indexed wrongly. Consider the case where we have binding 0 as a
// fragment UBO, and binding 1 as a vertex UBO. Then there are two dynamic offsets, and
// the second is the one we want to use with ours. If we only add the compute visibility
// bit to the second UBO, then suddenly it's the *first* offset that we must provide.
// Instead of trying to remap offsets to match, we simply make every binding compute
// visible so the ordering is still the same. Since compute and graphics are disjoint this
// is safe.
newBind.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
if(bind.immutableSampler)
{
hasImmutableSamplers = true;
VkSampler *samplers = new VkSampler[bind.descriptorCount];
newBind.pImmutableSamplers = samplers;
for(uint32_t s = 0; s < bind.descriptorCount; s++)
samplers[s] =
GetResourceManager()->GetCurrentHandle<VkSampler>(bind.immutableSampler[s]);
}
else
{
newBind.pImmutableSamplers = NULL;
}
newBindings.push_back(newBind);
}
}
VkDescriptorSetLayoutCreateInfo descsetLayoutInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
NULL,
0,
(uint32_t)newBindings.size(),
newBindings.data(),
};
// create new offseted descriptor layout
vkr = m_pDriver->vkCreateDescriptorSetLayout(dev, &descsetLayoutInfo, NULL, &setLayouts[i]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
if(hasImmutableSamplers)
{
for(const VkDescriptorSetLayoutBinding &bind : newBindings)
delete[] bind.pImmutableSamplers;
}
}
VkDescriptorPoolCreateInfo poolCreateInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
// 1 set for each layout
poolCreateInfo.maxSets = (uint32_t)setLayouts.size();
poolCreateInfo.poolSizeCount = ARRAY_COUNT(poolSizes);
poolCreateInfo.pPoolSizes = poolSizes;
// create descriptor pool with enough space for our descriptors
vkr = m_pDriver->vkCreateDescriptorPool(dev, &poolCreateInfo, NULL, &descpool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// allocate all the descriptors
VkDescriptorSetAllocateInfo descSetAllocInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
NULL,
descpool,
(uint32_t)setLayouts.size(),
setLayouts.data(),
};
descSets.resize(setLayouts.size());
m_pDriver->vkAllocateDescriptorSets(dev, &descSetAllocInfo, descSets.data());
// copy the data across from the real descriptors into our adjusted bindings
for(size_t i = 0; i < descSetLayoutIds.size(); i++)
{
const DescSetLayout &origLayout = creationInfo.m_DescSetLayout[descSetLayoutIds[i]];
if(i >= state.graphics.descSets.size())
continue;
if(state.graphics.descSets[i].descSet == ResourceId())
continue;
VkCopyDescriptorSet copy = {VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET};
copy.srcSet =
GetResourceManager()->GetCurrentHandle<VkDescriptorSet>(state.graphics.descSets[i].descSet);
copy.dstSet = descSets[i];
for(size_t b = 0; b < origLayout.bindings.size(); b++)
{
const DescSetLayout::Binding &bind = origLayout.bindings[b];
// skip empty bindings
if(bind.descriptorCount == 0)
continue;
copy.srcBinding = (uint32_t)b;
copy.dstBinding = (uint32_t)b + MeshOutputReservedBindings;
copy.descriptorCount = bind.descriptorCount;
descCopies.push_back(copy);
}
}
m_pDriver->vkUpdateDescriptorSets(dev, 0, NULL, (uint32_t)descCopies.size(), descCopies.data());
}
// create pipeline layout with new descriptor set layouts
{
std::vector<VkPushConstantRange> push = creationInfo.m_PipelineLayout[pipeInfo.layout].pushRanges;
// ensure the push range is visible to the compute shader
for(VkPushConstantRange &range : push)
range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkPipelineLayoutCreateInfo pipeLayoutInfo = {
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
NULL,
0,
(uint32_t)setLayouts.size(),
setLayouts.data(),
(uint32_t)push.size(),
push.data(),
};
vkr = m_pDriver->vkCreatePipelineLayout(dev, &pipeLayoutInfo, NULL, &pipeLayout);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
VkBuffer meshBuffer = VK_NULL_HANDLE, readbackBuffer = VK_NULL_HANDLE;
VkDeviceMemory meshMem = VK_NULL_HANDLE, readbackMem = VK_NULL_HANDLE;
@@ -1444,7 +1665,8 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
// we fetch the vertex buffer data up front here since there's a very high chance of either
// overlap due to interleaved attributes, or no overlap and no wastage due to separate compact
// attributes.
bytebuf origVBs[16];
std::vector<bytebuf> origVBs;
origVBs.reserve(16);
for(uint32_t vb = 0; vb < vi->vertexBindingDescriptionCount; vb++)
{
@@ -1465,7 +1687,10 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
}
if(state.vbuffers[vb].buf != ResourceId())
GetBufferData(state.vbuffers[vb].buf, offs, len, origVBs[vb]);
{
origVBs.push_back(bytebuf());
GetBufferData(state.vbuffers[vb].buf, offs, len, origVBs.back());
}
}
for(uint32_t i = 0; i < vi->vertexAttributeDescriptionCount; i++)
@@ -1738,7 +1963,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
attrInstDivisor[attr] = instDivisor;
descWrites[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrites[numWrites].dstSet = m_MeshFetchDescSet;
descWrites[numWrites].dstSet = descSets[0];
if(IsSIntFormat(attrDesc.format))
descWrites[numWrites].dstBinding = 4;
else if(IsUIntFormat(attrDesc.format) || IsDoubleFormat(attrDesc.format))
@@ -1756,7 +1981,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
if(uniqIdxBufView != VK_NULL_HANDLE)
{
descWrites[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrites[numWrites].dstSet = m_MeshFetchDescSet;
descWrites[numWrites].dstSet = descSets[0];
descWrites[numWrites].dstBinding = 1;
descWrites[numWrites].dstArrayElement = 0;
descWrites[numWrites].descriptorCount = 1;
@@ -1769,49 +1994,13 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
}
ConvertToMeshOutputCompute(*refl, *pipeInfo.shaders[0].patchData,
pipeInfo.shaders[0].entryPoint.c_str(), attrInstDivisor, descSet,
drawcall, numVerts, numViews, modSpirv, bufStride);
pipeInfo.shaders[0].entryPoint.c_str(), attrInstDivisor, drawcall,
numVerts, numViews, modSpirv, bufStride);
VkComputePipelineCreateInfo compPipeInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
{
VkDescriptorSetLayout *descSetLayouts;
// descSet will be the index of our new descriptor set
descSetLayouts = new VkDescriptorSetLayout[descSet + 1];
for(uint32_t i = 0; i < descSet; i++)
descSetLayouts[i] = m_pDriver->GetResourceManager()->GetCurrentHandle<VkDescriptorSetLayout>(
creationInfo.m_PipelineLayout[pipeInfo.layout].descSetLayouts[i]);
// this layout just says it has one storage buffer
descSetLayouts[descSet] = m_MeshFetchDescSetLayout;
std::vector<VkPushConstantRange> push = creationInfo.m_PipelineLayout[pipeInfo.layout].pushRanges;
// ensure the push range is visible to the compute shader
for(VkPushConstantRange &range : push)
range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkPipelineLayoutCreateInfo pipeLayoutInfo = {
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
NULL,
0,
descSet + 1,
descSetLayouts,
(uint32_t)push.size(),
push.empty() ? NULL : &push[0],
};
// create pipeline layout with same descriptor set layouts, plus our mesh output set
vkr = m_pDriver->vkCreatePipelineLayout(dev, &pipeLayoutInfo, NULL, &pipeLayout);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
SAFE_DELETE_ARRAY(descSetLayouts);
// repoint pipeline layout
compPipeInfo.layout = pipeLayout;
}
// repoint pipeline layout
compPipeInfo.layout = pipeLayout;
// create vertex shader with modified code
VkShaderModuleCreateInfo moduleCreateInfo = {
@@ -1852,12 +2041,13 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
// move graphics descriptor sets onto the compute pipe.
modifiedstate.compute.descSets = modifiedstate.graphics.descSets;
// push back extra descriptor set to partial replay state
// note that we examined the used pipeline layout above and inserted our descriptor set
// after any the application used. So there might be more bound, but we want to ensure to
// bind to the slot we're using
modifiedstate.compute.descSets.resize(descSet + 1);
modifiedstate.compute.descSets[descSet].descSet = GetResID(m_MeshFetchDescSet);
// replace descriptor set IDs with our temporary sets. The offsets we keep the same. If the
// original draw had no sets, we ensure there's room (with no offsets needed)
if(modifiedstate.compute.descSets.empty())
modifiedstate.compute.descSets.resize(1);
for(size_t i = 0; i < descSets.size(); i++)
modifiedstate.compute.descSets[i].descSet = GetResID(descSets[i]);
{
// create buffer of sufficient size
@@ -1947,8 +2137,8 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
fetchdesc.range = bufInfo.size;
VkWriteDescriptorSet write = {
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, m_MeshFetchDescSet, 0, 0, 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &fetchdesc, NULL};
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, descSets[0], 0, 0, 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &fetchdesc, NULL};
m_pDriver->vkUpdateDescriptorSets(dev, 1, &write, 0, NULL);
// do single draw
@@ -2121,6 +2311,12 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
m_PostVSData[eventId].vsout.hasPosOut =
refl->outputSignature[0].systemValue == ShaderBuiltin::Position;
// delete descriptors
m_pDriver->vkDestroyDescriptorPool(dev, descpool, NULL);
for(VkDescriptorSetLayout layout : setLayouts)
m_pDriver->vkDestroyDescriptorSetLayout(dev, layout, NULL);
// delete pipeline layout
m_pDriver->vkDestroyPipelineLayout(dev, pipeLayout, NULL);
-3
View File
@@ -576,9 +576,6 @@ private:
std::map<uint32_t, VulkanPostVSData> m_PostVSData;
std::map<uint32_t, uint32_t> m_PostVSAlias;
VkDescriptorSetLayout m_MeshFetchDescSetLayout = VK_NULL_HANDLE;
VkDescriptorSet m_MeshFetchDescSet = VK_NULL_HANDLE;
std::vector<ResourceDescription> m_Resources;
std::map<ResourceId, size_t> m_ResourceIdx;
@@ -260,21 +260,6 @@ bool WrappedVulkan::Serialise_vkCreateDescriptorSetLayout(
{
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkDescriptorSetLayoutBinding *bindings = (VkDescriptorSetLayoutBinding *)CreateInfo.pBindings;
// ensure any bindings available are also available to compute. This is valid and changes
// nothing, but means we don't have to create a duplicate parallel 'compute friendly' descriptor
// set layout and pipeline layout.
// Note that we apply this to all bindings, even ones that are fragment only, because otherwise
// dynamic buffer offsets could be indexed wrongly. Consider the case where we have binding 0 as
// a fragment UBO, and binding 1 as a vertex UBO. Then there are two dynamic offsets, and the
// second is the one we want to use with ours. If we only add the compute visibility bit to the
// second UBO, then suddenly it's the *first* offset that we must provide. Instead of trying to
// remap offsets to match, we simply make every binding compute visible so the ordering is still
// the same. Since compute and graphics are disjoint this is safe.
for(uint32_t i = 0; i < CreateInfo.bindingCount; i++)
bindings[i].stageFlags |= VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorSetLayoutCreateInfo unwrapped = UnwrapInfo(&CreateInfo);
VkResult ret =
ObjDisp(device)->CreateDescriptorSetLayout(Unwrap(device), &unwrapped, NULL, &layout);