Fix handling of variable descriptor counts to not allocate maximum size

* We previously ignored the variable descriptor size allowed by descriptor
  indexing,
This commit is contained in:
baldurk
2020-08-28 18:50:06 +01:00
parent 75f2dc119c
commit 30ecf66cca
15 changed files with 545 additions and 45 deletions
+2
View File
@@ -458,12 +458,14 @@ struct BindingStorage
~BindingStorage() { clear(); }
bytebuf inlineBytes;
rdcarray<DescriptorSetSlot *> binds;
uint32_t variableDescriptorCount;
void clear()
{
inlineBytes.clear();
binds.clear();
elems.clear();
variableDescriptorCount = 0;
}
void reset()
+2 -6
View File
@@ -1040,13 +1040,9 @@ public:
{
return m_DescriptorSetState[descSet].layout;
}
const rdcarray<DescriptorSetSlot *> &GetCurrentDescSetBindings(ResourceId descSet)
const BindingStorage &GetCurrentDescSetBindingStorage(ResourceId descSet)
{
return m_DescriptorSetState[descSet].data.binds;
}
const bytebuf &GetCurrentDescSetInlineData(ResourceId descSet)
{
return m_DescriptorSetState[descSet].data.inlineBytes;
return m_DescriptorSetState[descSet].data;
}
uint32_t GetReadbackMemoryIndex(uint32_t resourceCompatibleBitmask);
+20 -9
View File
@@ -2461,6 +2461,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
creationInfo.m_DescSetLayout[creationInfo.m_PipelineLayout[pipe.descSets[i].pipeLayout]
.descSetLayouts[i]];
WrappedVulkan::DescriptorSetInfo &setInfo =
m_pDriver->m_DescriptorSetState[pipe.descSets[i].descSet];
for(size_t b = 0; !error && b < origLayout.bindings.size(); b++)
{
const DescSetLayout::Binding &bind = origLayout.bindings[b];
@@ -2469,22 +2472,27 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM)
continue;
uint32_t descriptorCount = bind.descriptorCount;
if(bind.variableSize)
descriptorCount = setInfo.data.variableDescriptorCount;
// make room in the pool
if(bind.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
{
poolSizes[InlinePoolIndex].descriptorCount += bind.descriptorCount;
poolSizes[InlinePoolIndex].descriptorCount += descriptorCount;
inlineCreateInfo.maxInlineUniformBlockBindings++;
}
else
{
poolSizes[bind.descriptorType].descriptorCount += bind.descriptorCount;
poolSizes[bind.descriptorType].descriptorCount += descriptorCount;
}
VkDescriptorSetLayoutBinding newBind;
// offset the binding. We offset all sets to make it easier for patching - don't need to
// conditionally patch shader bindings depending on which set they're in.
newBind.binding = uint32_t(b + newBindingsCount);
newBind.descriptorCount = bind.descriptorCount;
newBind.descriptorCount = descriptorCount;
newBind.descriptorType = bind.descriptorType;
// we only need it available for compute, just make all bindings visible otherwise dynamic
@@ -2500,8 +2508,6 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
else
newBind.stageFlags = bind.stageFlags;
uint32_t descriptorCount = bind.descriptorCount;
switch(bind.descriptorType)
{
case VK_DESCRIPTOR_TYPE_SAMPLER:
@@ -2665,11 +2671,16 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM)
continue;
uint32_t descriptorCount = bind.descriptorCount;
if(bind.variableSize)
descriptorCount = setInfo.data.variableDescriptorCount;
DescriptorSetSlot *slot = setInfo.data.binds[b];
write.dstBinding = uint32_t(b + newBindingsCount);
write.dstArrayElement = 0;
write.descriptorCount = bind.descriptorCount;
write.descriptorCount = descriptorCount;
write.descriptorType = bind.descriptorType;
switch(write.descriptorType)
@@ -2728,7 +2739,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
VkWriteDescriptorSetInlineUniformBlockEXT *inlineWrite = allocInlineWrites.back();
inlineWrite->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT;
inlineWrite->pNext = NULL;
inlineWrite->dataSize = bind.descriptorCount;
inlineWrite->dataSize = descriptorCount;
inlineWrite->pData = setInfo.data.inlineBytes.data() + slot[0].inlineOffset;
write.pNext = inlineWrite;
break;
@@ -2740,7 +2751,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
// different
if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
{
write.descriptorCount = bind.descriptorCount;
write.descriptorCount = descriptorCount;
descWrites.push_back(write);
continue;
}
@@ -2748,7 +2759,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
// start with no descriptors
write.descriptorCount = 0;
for(uint32_t w = 0; w < bind.descriptorCount; w++)
for(uint32_t w = 0; w < descriptorCount; w++)
{
// if this write is valid, we increment the descriptor count and continue
if(IsValid(m_pDriver->NULLDescriptorsAllowed(), write, w - write.dstArrayElement))
+19 -2
View File
@@ -119,6 +119,10 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo
flags = pCreateInfo->flags;
VkDescriptorSetLayoutBindingFlagsCreateInfo *bindingFlags =
(VkDescriptorSetLayoutBindingFlagsCreateInfo *)FindNextStruct(
pCreateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO);
// descriptor set layouts can be sparse, such that only three bindings exist
// but they are at 0, 5 and 10.
// We assume here that while the layouts may be sparse that's mostly to allow
@@ -157,6 +161,12 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo
for(uint32_t s = 0; s < bindings[b].descriptorCount; s++)
bindings[b].immutableSampler[s] = GetResID(pCreateInfo->pBindings[i].pImmutableSamplers[s]);
}
if(bindingFlags &&
(bindingFlags->pBindingFlags[i] & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT))
bindings[b].variableSize = 1;
else
bindings[b].variableSize = 0;
}
// assign offsets in sorted bindings order, as the bindings we were provided by the application
@@ -167,6 +177,11 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo
{
bindings[b].elemOffset = elemOffset;
// don't count the descriptors in the variable size array. We'll add on the allocated size after
// this
if(bindings[b].variableSize)
break;
if(bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
{
elemOffset++;
@@ -180,11 +195,13 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo
totalElems = elemOffset;
}
void DescSetLayout::CreateBindingsArray(BindingStorage &bindingStorage) const
void DescSetLayout::CreateBindingsArray(BindingStorage &bindingStorage, uint32_t variableAllocSize) const
{
bindingStorage.variableDescriptorCount = variableAllocSize;
if(!bindings.empty())
{
bindingStorage.elems.resize(totalElems);
bindingStorage.elems.resize(totalElems + variableAllocSize);
bindingStorage.binds.resize(bindings.size());
if(inlineByteSize == 0)
+17 -2
View File
@@ -72,7 +72,7 @@ struct DescSetLayout
void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info,
const VkDescriptorSetLayoutCreateInfo *pCreateInfo);
void CreateBindingsArray(BindingStorage &bindingStorage) const;
void CreateBindingsArray(BindingStorage &bindingStorage, uint32_t variableAllocSize) const;
void UpdateBindingsArray(const DescSetLayout &prevLayout, BindingStorage &bindingStorage) const;
struct Binding
@@ -85,15 +85,28 @@ struct DescSetLayout
elemOffset(0),
descriptorCount(0),
stageFlags(0),
variableSize(0),
immutableSampler(NULL)
{
}
// move the immutable sampler
Binding(Binding &&b)
: descriptorType(b.descriptorType),
elemOffset(b.elemOffset),
descriptorCount(b.descriptorCount),
stageFlags(b.stageFlags),
variableSize(b.variableSize),
immutableSampler(b.immutableSampler)
{
b.immutableSampler = NULL;
}
// Copy the immutable sampler
Binding(const Binding &b)
: descriptorType(b.descriptorType),
elemOffset(b.elemOffset),
descriptorCount(b.descriptorCount),
stageFlags(b.stageFlags),
variableSize(b.variableSize),
immutableSampler(NULL)
{
if(b.immutableSampler)
@@ -111,6 +124,7 @@ struct DescSetLayout
elemOffset = b.elemOffset;
descriptorCount = b.descriptorCount;
stageFlags = b.stageFlags;
variableSize = b.variableSize;
SAFE_DELETE_ARRAY(immutableSampler);
if(b.immutableSampler)
{
@@ -123,7 +137,8 @@ struct DescSetLayout
VkDescriptorType descriptorType;
uint32_t elemOffset;
uint32_t descriptorCount;
VkShaderStageFlags stageFlags;
VkShaderStageFlags stageFlags : 31;
uint32_t variableSize : 1;
ResourceId *immutableSampler;
};
rdcarray<Binding> bindings;
+3
View File
@@ -659,6 +659,9 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V
{
uint32_t descriptorCount = layout.bindings[j].descriptorCount;
if(layout.bindings[j].variableSize)
descriptorCount = m_DescriptorSetState[liveid].data.variableDescriptorCount;
if(descriptorCount == 0)
continue;
+10 -3
View File
@@ -1614,7 +1614,13 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
bool dynamicOffset = false;
dst.bindings[b].descriptorCount = layoutBind.descriptorCount;
uint32_t descriptorCount = layoutBind.descriptorCount;
if(layoutBind.variableSize)
descriptorCount = m_pDriver->m_DescriptorSetState[src].data.variableDescriptorCount;
dst.bindings[b].descriptorCount = descriptorCount;
dst.bindings[b].stageFlags = (ShaderStageMask)layoutBind.stageFlags;
switch(layoutBind.descriptorType)
{
@@ -1851,7 +1857,7 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
dst.bindings[b].binds[a].resourceResourceId = ResourceId();
dst.bindings[b].binds[a].inlineBlock = true;
dst.bindings[b].binds[a].byteOffset = 0;
dst.bindings[b].binds[a].byteSize = layoutBind.descriptorCount;
dst.bindings[b].binds[a].byteSize = descriptorCount;
}
else if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ||
@@ -1998,7 +2004,8 @@ void VulkanReplay::FillCBufferVariables(ResourceId pipeline, ResourceId shader,
bytebuf inlineData;
inlineData.assign(
setData.data.inlineBytes.data() + setData.data.binds[bind.bind]->inlineOffset,
layoutBind.descriptorCount);
layoutBind.variableSize ? setData.data.variableDescriptorCount
: layoutBind.descriptorCount);
StandardFillCBufferVariables(refl.resourceId, c.variables, outvars, inlineData);
return;
}
+20 -17
View File
@@ -189,9 +189,10 @@ public:
DescSetSnapshot &dstSet = m_DescSets[set];
const rdcarray<DescriptorSetSlot *> &curBinds =
m_pDriver->GetCurrentDescSetBindings(descSets[set].descSet);
const bytebuf &curInline = m_pDriver->GetCurrentDescSetInlineData(descSets[set].descSet);
const BindingStorage &bindStorage =
m_pDriver->GetCurrentDescSetBindingStorage(descSets[set].descSet);
const rdcarray<DescriptorSetSlot *> &curBinds = bindStorage.binds;
const bytebuf &curInline = bindStorage.inlineBytes;
// use the descriptor set layout from when it was bound. If the pipeline layout declared a
// descriptor set layout for this set, but it's statically unused, it may be complete
@@ -207,7 +208,12 @@ public:
{
const DescSetLayout::Binding &bindLayout = setLayout.bindings[bind];
if(bindLayout.descriptorCount == 0)
uint32_t descriptorCount = bindLayout.descriptorCount;
if(bindLayout.variableSize)
descriptorCount = bindStorage.variableDescriptorCount;
if(descriptorCount == 0)
continue;
if(bindLayout.stageFlags & stage)
@@ -226,8 +232,8 @@ public:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
{
dstBind.imageInfos.resize(bindLayout.descriptorCount);
for(uint32_t i = 0; i < bindLayout.descriptorCount; i++)
dstBind.imageInfos.resize(descriptorCount);
for(uint32_t i = 0; i < descriptorCount; i++)
{
dstBind.imageInfos[i].imageLayout = curSlots[i].imageInfo.imageLayout;
dstBind.imageInfos[i].imageView =
@@ -243,8 +249,8 @@ public:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
{
dstBind.texelBuffers.resize(bindLayout.descriptorCount);
for(uint32_t i = 0; i < bindLayout.descriptorCount; i++)
dstBind.texelBuffers.resize(descriptorCount);
for(uint32_t i = 0; i < descriptorCount; i++)
{
dstBind.texelBuffers[i] =
m_pDriver->GetResourceManager()->GetCurrentHandle<VkBufferView>(
@@ -255,8 +261,8 @@ public:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
{
dstBind.buffers.resize(bindLayout.descriptorCount);
for(uint32_t i = 0; i < bindLayout.descriptorCount; i++)
dstBind.buffers.resize(descriptorCount);
for(uint32_t i = 0; i < descriptorCount; i++)
{
dstBind.buffers[i].offset = curSlots[i].bufferInfo.offset;
dstBind.buffers[i].range = curSlots[i].bufferInfo.range;
@@ -273,15 +279,14 @@ public:
idx.bindset = (int32_t)set;
idx.bind = (int32_t)bind;
idx.arrayIndex = 0;
bufferCache[idx].assign(curInline.data() + curSlots->inlineOffset,
bindLayout.descriptorCount);
bufferCache[idx].assign(curInline.data() + curSlots->inlineOffset, descriptorCount);
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
{
dstBind.buffers.resize(bindLayout.descriptorCount);
for(uint32_t i = 0; i < bindLayout.descriptorCount; i++)
dstBind.buffers.resize(descriptorCount);
for(uint32_t i = 0; i < descriptorCount; i++)
{
dstBind.buffers[i].offset = curSlots[i].bufferInfo.offset;
dstBind.buffers[i].range = curSlots[i].bufferInfo.range;
@@ -303,9 +308,7 @@ public:
switch(bindLayout.descriptorType)
{
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
dynamicOffset += bindLayout.descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: dynamicOffset += descriptorCount; break;
default: break;
}
}
@@ -4406,7 +4406,8 @@ void WrappedVulkan::ApplyPushDescriptorWrites(VkPipelineBindPoint pipelineBindPo
if(prevLayout == ResourceId())
{
desclayout.CreateBindingsArray(m_DescriptorSetState[setId].data);
// push descriptors can't have variable count, so just pass 0
desclayout.CreateBindingsArray(m_DescriptorSetState[setId].data, 0);
}
else if(prevLayout != descSetLayouts[set])
{
@@ -457,7 +457,29 @@ bool WrappedVulkan::Serialise_vkAllocateDescriptorSets(SerialiserType &ser, VkDe
// this is stored in the resource record on capture, we need to be able to look to up
m_DescriptorSetState[live].layout = layoutId;
m_CreationInfo.m_DescSetLayout[layoutId].CreateBindingsArray(m_DescriptorSetState[live].data);
// If descriptorSetCount is zero or this structure is not included in the pNext chain,
// then the variable lengths are considered to be zero.
uint32_t variableDescriptorAlloc = 0;
if(!m_CreationInfo.m_DescSetLayout[layoutId].bindings.empty() &&
m_CreationInfo.m_DescSetLayout[layoutId].bindings.back().variableSize)
{
VkDescriptorSetVariableDescriptorCountAllocateInfo *variableAlloc =
(VkDescriptorSetVariableDescriptorCountAllocateInfo *)FindNextStruct(
&AllocateInfo,
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO);
if(variableAlloc)
{
// this struct will have been patched similar to VkDescriptorSetAllocateInfo so we look up
// the [0]th element
variableDescriptorAlloc = variableAlloc->pDescriptorCounts[0];
}
}
m_CreationInfo.m_DescSetLayout[layoutId].CreateBindingsArray(m_DescriptorSetState[live].data,
variableDescriptorAlloc);
}
AddResource(DescriptorSet, ResourceType::ShaderBinding, "Descriptor Set");
@@ -481,6 +503,22 @@ VkResult WrappedVulkan::vkAllocateDescriptorSets(VkDevice device,
if(ret != VK_SUCCESS)
return ret;
VkDescriptorSetVariableDescriptorCountAllocateInfo *variableAlloc =
(VkDescriptorSetVariableDescriptorCountAllocateInfo *)FindNextStruct(
pAllocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO);
VkDescriptorSetAllocateInfo mutableInfo = *pAllocateInfo;
{
byte *tempMem = GetTempMemory(GetNextPatchSize(mutableInfo.pNext));
CopyNextChainForPatching("VkDescriptorSetAllocateInfo", tempMem,
(VkBaseInStructure *)&mutableInfo);
}
VkDescriptorSetVariableDescriptorCountAllocateInfo *mutableVariableInfo =
(VkDescriptorSetVariableDescriptorCountAllocateInfo *)FindNextStruct(
&mutableInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO);
for(uint32_t i = 0; i < pAllocateInfo->descriptorSetCount; i++)
{
VkResourceRecord *poolrecord = NULL;
@@ -489,12 +527,19 @@ VkResult WrappedVulkan::vkAllocateDescriptorSets(VkDevice device,
ResourceId id;
VkResourceRecord *record = NULL;
bool exactReuse = false;
uint32_t variableDescriptorAlloc = 0;
if(IsCaptureMode(m_State))
{
layoutRecord = GetRecord(pAllocateInfo->pSetLayouts[i]);
poolrecord = GetRecord(pAllocateInfo->descriptorPool);
if(!layoutRecord->descInfo->layout->bindings.empty() &&
layoutRecord->descInfo->layout->bindings.back().variableSize && variableAlloc)
{
variableDescriptorAlloc = variableAlloc->pDescriptorCounts[i];
}
if(Atomic::CmpExch32(&m_ReuseEnabled, 1, 1) == 1)
{
rdcarray<VkResourceRecord *> &freelist = poolrecord->descPoolInfo->freelist;
@@ -510,7 +555,8 @@ VkResult WrappedVulkan::vkAllocateDescriptorSets(VkDevice device,
return a->descInfo->layout < search;
});
if(it != freelist.end() && (*it)->descInfo->layout == layoutRecord->descInfo->layout)
if(it != freelist.end() && (*it)->descInfo->layout == layoutRecord->descInfo->layout &&
(*it)->descInfo->data.variableDescriptorCount == variableDescriptorAlloc)
{
record = freelist.takeAt(it - freelist.begin());
exactReuse = true;
@@ -561,9 +607,15 @@ VkResult WrappedVulkan::vkAllocateDescriptorSets(VkDevice device,
{
CACHE_THREAD_SERIALISER();
VkDescriptorSetAllocateInfo info = *pAllocateInfo;
VkDescriptorSetAllocateInfo info = mutableInfo;
info.descriptorSetCount = 1;
info.pSetLayouts += i;
info.pSetLayouts = mutableInfo.pSetLayouts + i;
if(mutableVariableInfo)
{
mutableVariableInfo->descriptorSetCount = 1;
mutableVariableInfo->pDescriptorCounts = variableAlloc->pDescriptorCounts + i;
}
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkAllocateDescriptorSets);
Serialise_vkAllocateDescriptorSets(ser, device, &info, &pDescriptorSets[i]);
@@ -577,7 +629,8 @@ VkResult WrappedVulkan::vkAllocateDescriptorSets(VkDevice device,
record->AddParent(layoutRecord);
record->descInfo->layout = layoutRecord->descInfo->layout;
record->descInfo->layout->CreateBindingsArray(record->descInfo->data);
record->descInfo->layout->CreateBindingsArray(record->descInfo->data,
variableDescriptorAlloc);
}
else
{
@@ -1209,6 +1262,9 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount,
// descriptors. All consecutive bindings updated via a single VkWriteDescriptorSet structure
// must have identical descriptorType and stageFlags, and must all either use immutable
// samplers or must all not use immutable samplers.
//
// Note we don't have to worry about this interacting with variable descriptor counts
// because the variable descriptor must be the last one, so there's no more overlap.
if(curIdx >= layoutBinding->descriptorCount)
{
@@ -1655,6 +1711,9 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate(
// descriptors. All consecutive bindings updated via a single VkWriteDescriptorSet structure
// must have identical descriptorType and stageFlags, and must all either use immutable
// samplers or must all not use immutable samplers.
//
// Note we don't have to worry about this interacting with variable descriptor counts
// because the variable descriptor must be the last one, so there's no more overlap.
if(curIdx >= layoutBinding->descriptorCount)
{
+1
View File
@@ -13,6 +13,7 @@ set(VULKAN_SRC
vk/vk_custom_border_color.cpp
vk/vk_descriptor_index.cpp
vk/vk_descriptor_reuse.cpp
vk/vk_descriptor_variable_count.cpp
vk/vk_discard_rects.cpp
vk/vk_discard_zoo.cpp
vk/vk_draw_zoo.cpp
+1
View File
@@ -264,6 +264,7 @@
<ClCompile Include="texture_zoo.cpp" />
<ClCompile Include="vk\vk_custom_border_color.cpp" />
<ClCompile Include="vk\vk_descriptor_reuse.cpp" />
<ClCompile Include="vk\vk_descriptor_variable_count.cpp" />
<ClCompile Include="vk\vk_discard_zoo.cpp" />
<ClCompile Include="vk\vk_empty_capture.cpp" />
<ClCompile Include="vk\vk_extended_dyn_state.cpp" />
+3
View File
@@ -541,6 +541,9 @@
<ClCompile Include="vk\vk_descriptor_reuse.cpp">
<Filter>Vulkan\demos</Filter>
</ClCompile>
<ClCompile Include="vk\vk_descriptor_variable_count.cpp">
<Filter>Vulkan\demos</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="D3D11">
@@ -0,0 +1,368 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "vk_test.h"
RD_TEST(VK_Descriptor_Variable_Count, VulkanGraphicsTest)
{
static constexpr const char *Description =
"Allocates descriptor sets using a variable count to ensure we don't pessimistically "
"allocate and don't do anything with un-allocated descriptors.";
std::string common = R"EOSHADER(
#version 450 core
#extension GL_EXT_nonuniform_qualifier : require
#extension GL_EXT_samplerless_texture_functions : require
struct v2f
{
vec4 pos;
vec4 col;
vec4 uv;
};
)EOSHADER";
const std::string vertex = R"EOSHADER(
layout(location = 0) in vec3 Position;
layout(location = 1) in vec4 Color;
layout(location = 2) in vec2 UV;
layout(location = 0) out v2f vertOut;
void main()
{
vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1);
gl_Position = vertOut.pos;
vertOut.col = Color;
vertOut.uv = vec4(UV.xy, 0, 1);
}
)EOSHADER";
const std::string pixel = R"EOSHADER(
layout(location = 0) in v2f vertIn;
layout(location = 0, index = 0) out vec4 Color;
layout(push_constant) uniform PushData
{
uint bufidx;
} push;
layout(binding = 0) uniform texture2D tex[];
void main()
{
Color = texelFetch(tex[push.bufidx], ivec2(vertIn.uv.xy * vec2(4,4)), 0);
}
)EOSHADER";
const static uint32_t numDescriptorSetsInLayout = 100 * 1024;
void Prepare(int argc, char **argv)
{
devExts.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
// dependencies of VK_EXT_descriptor_indexing
devExts.push_back(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
// enable robustness2 if possible for NULL descriptors
optDevExts.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
VulkanGraphicsTest::Prepare(argc, argv);
if(!Avail.empty())
return;
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(phys, &props);
// require at least a million descriptors - we won't use them but this gives us enough headroom
// to check for overallocation
if(props.limits.maxDescriptorSetSamplers < numDescriptorSetsInLayout)
Avail = "maxDescriptorSetSamplers " + std::to_string(props.limits.maxDescriptorSetSamplers) +
" is insufficient";
else if(props.limits.maxDescriptorSetSampledImages < numDescriptorSetsInLayout)
Avail = "maxDescriptorSetSampledImages " +
std::to_string(props.limits.maxDescriptorSetSampledImages) + " is insufficient";
if(!Avail.empty())
return;
static VkPhysicalDeviceDescriptorIndexingFeaturesEXT descIndexing = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
};
VkPhysicalDeviceDescriptorIndexingFeaturesEXT indexingAvail = descIndexing;
getPhysFeatures2(&indexingAvail);
if(!indexingAvail.descriptorBindingPartiallyBound)
Avail = "Descriptor indexing feature 'descriptorBindingPartiallyBound' not available";
else if(!indexingAvail.descriptorBindingVariableDescriptorCount)
Avail =
"Descriptor indexing feature 'descriptorBindingVariableDescriptorCount' not available";
else if(!indexingAvail.runtimeDescriptorArray)
Avail = "Descriptor indexing feature 'runtimeDescriptorArray' not available";
descIndexing.descriptorBindingPartiallyBound = VK_TRUE;
descIndexing.descriptorBindingVariableDescriptorCount = VK_TRUE;
descIndexing.runtimeDescriptorArray = VK_TRUE;
devInfoNext = &descIndexing;
static VkPhysicalDeviceRobustness2FeaturesEXT robust2Feats = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
};
// enable NULL descriptors if they're supported and the extension was enabled
if(std::find(devExts.begin(), devExts.end(), VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) != devExts.end())
{
VkPhysicalDeviceRobustness2FeaturesEXT robust2avail = robust2Feats;
getPhysFeatures2(&robust2avail);
if(robust2avail.nullDescriptor)
robust2Feats.nullDescriptor = VK_TRUE;
robust2Feats.pNext = (void *)devInfoNext;
devInfoNext = &robust2Feats;
}
}
int main()
{
// initialise, create window, create context, etc
if(!Init())
return 3;
const static uint32_t numDescriptorSets = 10 * 1024;
const static uint32_t numDescriptorsPerSet = 2;
VkDescriptorSetLayoutBindingFlagsCreateInfoEXT descFlags = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT,
};
VkDescriptorBindingFlagsEXT bindFlags[1] = {
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT |
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT,
};
descFlags.bindingCount = 1;
descFlags.pBindingFlags = bindFlags;
VkDescriptorSetLayout setlayout =
createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo(
{
{
0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
numDescriptorSetsInLayout, VK_SHADER_STAGE_FRAGMENT_BIT,
},
})
.next(&descFlags));
VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo(
{
setlayout,
},
{
vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Vec4i)),
}));
vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
pipeCreateInfo.layout = layout;
pipeCreateInfo.renderPass = mainWindow->rp;
pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)};
pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {
vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 0, DefaultA2V, col),
vkh::vertexAttr(2, 0, DefaultA2V, uv),
};
pipeCreateInfo.stages = {
CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo);
DefaultA2V tri[3] = {
{Vec3f(-0.5f, -0.5f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
{Vec3f(0.0f, 0.5f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
{Vec3f(0.5f, -0.5f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)},
};
AllocatedBuffer vb(this, vkh::BufferCreateInfo(sizeof(tri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
vb.upload(tri);
AllocatedImage img(
this, vkh::ImageCreateInfo(4, 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
setName(img.image, "Colour Tex");
VkImageView imgview = createImageView(
vkh::ImageViewCreateInfo(img.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT));
Vec4f pixels[4 * 4];
for(int i = 0; i < 4 * 4; i++)
pixels[i] = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
AllocatedBuffer uploadBuf(
this, vkh::BufferCreateInfo(sizeof(pixels), VK_BUFFER_USAGE_TRANSFER_SRC_BIT),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
uploadBuf.upload(pixels);
uploadBufferToImage(img.image, {4, 4, 1}, uploadBuf.buffer,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
std::vector<VkDescriptorSet> descsets;
VkDescriptorPool descpool = VK_NULL_HANDLE;
descsets.resize(numDescriptorSets);
{
CHECK_VKR(vkCreateDescriptorPool(
device, vkh::DescriptorPoolCreateInfo(numDescriptorSets,
{
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
numDescriptorSets * numDescriptorsPerSet},
}),
NULL, &descpool));
std::vector<VkDescriptorSetLayout> setLayouts(numDescriptorSets, setlayout);
std::vector<uint32_t> counts(numDescriptorSets, numDescriptorsPerSet);
// make the last one large-ish, to ensure that we still pass the right count through for each
// set
counts.back() = std::min(100U, numDescriptorSetsInLayout);
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT countInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT, NULL,
numDescriptorSets, counts.data(),
};
VkDescriptorSetAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
&countInfo,
descpool,
numDescriptorSets,
setLayouts.data(),
};
CHECK_VKR(vkAllocateDescriptorSets(device, &allocInfo, descsets.data()));
}
vkh::DescriptorImageInfo iminfo(imgview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_NULL_HANDLE);
vkh::WriteDescriptorSet up(VK_NULL_HANDLE, 0, 0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, {iminfo});
up.pImageInfo = &iminfo;
up.dstArrayElement = 0;
up.descriptorCount = 1;
std::vector<VkWriteDescriptorSet> ups;
// fill the descriptor sets
for(uint32_t i = 0; i < numDescriptorSets; i++)
{
up.dstSet = descsets[i];
if(i == numDescriptorSets - 1)
up.dstArrayElement = std::min(100U, numDescriptorSetsInLayout) - 1;
ups.push_back(up);
}
vkh::updateDescriptorSets(device, ups);
while(Running())
{
VkCommandBuffer cmd = GetCommandBuffer();
vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
VkImage swapimg =
StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL,
vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1,
vkh::ImageSubresourceRange());
Vec4i idx = {};
vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Vec4i), &idx);
vkCmdBeginRenderPass(
cmd, vkh::RenderPassBeginInfo(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor),
VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport);
vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor);
vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});
// force all descriptor sets to be referenced
for(uint32_t i = 0; i < numDescriptorSets; i++)
{
// for the last set, use the last descriptor
if(i == numDescriptorSets - 1)
idx.x = std::min(100U, numDescriptorSetsInLayout) - 1;
vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Vec4i), &idx);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, &descsets[i], 0,
NULL);
vkCmdDraw(cmd, 3, 1, 0, 0);
}
vkCmdEndRenderPass(cmd);
FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
vkEndCommandBuffer(cmd);
Submit(0, 1, {cmd});
Present();
}
vkDeviceWaitIdle(device);
vkDestroyDescriptorPool(device, descpool, NULL);
return 0;
}
};
REGISTER_TEST();
@@ -0,0 +1,13 @@
import renderdoc as rd
import rdtest
class VK_Descriptor_Variable_Count(rdtest.TestCase):
demos_test_name = 'VK_Descriptor_Variable_Count'
def check_capture(self):
last_draw: rd.DrawcallDescription = self.get_last_draw()
self.controller.SetFrameEvent(last_draw.eventId, True)
self.check_triangle(out=last_draw.copyDestination)