Optimise UI for large descriptor arrays with few dynamically used binds

* We tune the pipeline state view and texture viewer to only iterate over a
  small list of dynamically used binds in the (vastly more common) case where
  unused binds are not being shown.
This commit is contained in:
baldurk
2020-09-02 13:50:22 +01:00
parent afe3bee92d
commit 56f82f6bf1
11 changed files with 235 additions and 104 deletions
@@ -613,19 +613,16 @@ bool VulkanPipelineStateViewer::setViewDetails(RDTreeWidgetItem *node, const bin
bool VulkanPipelineStateViewer::showNode(bool usedSlot, bool filledSlot)
{
const bool showUnused = ui->showUnused->isChecked();
const bool showEmpty = ui->showEmpty->isChecked();
// show if it's referenced by the shader - regardless of empty or not
if(usedSlot)
return true;
// it's not referenced, but if it's bound and we have "show unused" then show it
if(showUnused && filledSlot)
if(m_ShowUnused && filledSlot)
return true;
// it's empty, and we have "show empty"
if(showEmpty && !filledSlot)
if(m_ShowEmpty && !filledSlot)
return true;
return false;
@@ -1019,16 +1016,20 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
}
const rdcarray<VKPipe::BindingElement> *slotBinds = NULL;
int32_t firstUsedBind = 0;
int32_t lastUsedBind = 0;
BindType bindType = BindType::Unknown;
ShaderStageMask stageBits = ShaderStageMask::Unknown;
bool pushDescriptor = false;
uint32_t dynamicallyUsedCount = 1;
uint32_t dynamicallyUsedCount = ~0U;
if(bindset < pipe.descriptorSets.count() && bind < pipe.descriptorSets[bindset].bindings.count())
{
pushDescriptor = pipe.descriptorSets[bindset].pushDescriptor;
dynamicallyUsedCount = pipe.descriptorSets[bindset].bindings[bind].dynamicallyUsedCount;
slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds;
firstUsedBind = pipe.descriptorSets[bindset].bindings[bind].firstUsedIndex;
lastUsedBind = pipe.descriptorSets[bindset].bindings[bind].lastUsedIndex;
bindType = pipe.descriptorSets[bindset].bindings[bind].type;
stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags;
}
@@ -1042,6 +1043,12 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
bindType = isrw ? BindType::ReadWriteImage : BindType::ReadOnlyImage;
}
if(m_ShowUnused)
{
firstUsedBind = 0;
lastUsedBind = INT_MAX;
}
bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0;
bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage));
@@ -1056,7 +1063,8 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
// consider it filled if any array element is filled
bool filledSlot = false;
for(int idx = 0; slotBinds != NULL && idx < slotBinds->count(); idx++)
for(int32_t idx = firstUsedBind;
slotBinds != NULL && !filledSlot && idx <= lastUsedBind && idx < slotBinds->count(); idx++)
{
filledSlot |= (*slotBinds)[idx].resourceResourceId != ResourceId();
if(bindType == BindType::Sampler || bindType == BindType::ImageSampler)
@@ -1114,7 +1122,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
parentNode = node;
}
for(int idx = 0; idx < arrayLength; idx++)
for(int idx = firstUsedBind; idx <= lastUsedBind && idx < arrayLength; idx++)
{
const VKPipe::BindingElement *descriptorBind = NULL;
if(slotBinds != NULL)
@@ -1441,7 +1449,9 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
const rdcarray<VKPipe::BindingElement> *slotBinds = NULL;
BindType bindType = BindType::ConstantBuffer;
ShaderStageMask stageBits = ShaderStageMask::Unknown;
uint32_t dynamicallyUsedCount = 1;
uint32_t dynamicallyUsedCount = ~0U;
int32_t firstUsedBind = 0;
int32_t lastUsedBind = 0;
bool pushDescriptor = false;
@@ -1450,10 +1460,18 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
pushDescriptor = pipe.descriptorSets[bindset].pushDescriptor;
dynamicallyUsedCount = pipe.descriptorSets[bindset].bindings[bind].dynamicallyUsedCount;
slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds;
firstUsedBind = pipe.descriptorSets[bindset].bindings[bind].firstUsedIndex;
lastUsedBind = pipe.descriptorSets[bindset].bindings[bind].lastUsedIndex;
bindType = pipe.descriptorSets[bindset].bindings[bind].type;
stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags;
}
if(m_ShowUnused)
{
firstUsedBind = 0;
lastUsedBind = INT_MAX;
}
bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0;
bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage));
@@ -1466,9 +1484,12 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
// consider it filled if any array element is filled (or it's push constants)
bool filledSlot = cblock != NULL && !cblock->bufferBacked;
for(int idx = 0; slotBinds != NULL && idx < slotBinds->count(); idx++)
for(int32_t idx = firstUsedBind;
slotBinds != NULL && !filledSlot && idx <= lastUsedBind && idx < slotBinds->count(); idx++)
{
filledSlot |=
(*slotBinds)[idx].resourceResourceId != ResourceId() || (*slotBinds)[idx].inlineBlock;
}
bool containsResource = filledSlot;
@@ -1517,7 +1538,7 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
ubos->showColumn(0);
}
for(int idx = 0; idx < arrayLength; idx++)
for(int32_t idx = firstUsedBind; idx <= lastUsedBind && idx < arrayLength; idx++)
{
const VKPipe::BindingElement *descriptorBind = NULL;
if(slotBinds != NULL)
@@ -1816,6 +1837,10 @@ void VulkanPipelineStateViewer::setState()
return;
}
// cache latest state of these checkboxes
m_ShowUnused = ui->showUnused->isChecked();
m_ShowEmpty = ui->showEmpty->isChecked();
m_CombinedImageSamplers.clear();
const VKPipe::State &state = *m_Ctx.CurVulkanPipelineState();
@@ -122,6 +122,9 @@ private:
bool showNode(bool usedSlot, bool filledSlot);
bool m_ShowUnused = false;
bool m_ShowEmpty = false;
void exportHTML(QXmlStreamWriter &xml, const VKPipe::VertexInput &vi);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::InputAssembly &ia);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::Shader &sh);
+2 -2
View File
@@ -350,8 +350,8 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e
void ShaderViewer::cacheResources()
{
m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage);
m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage);
m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage, false);
m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage, false);
}
void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader,
+63 -57
View File
@@ -166,7 +166,12 @@ BoundResource Following::GetBoundResource(ICaptureContext &ctx, int arrayIdx)
int residx = rw.indexOf(key);
if(residx >= 0)
ret = rw[residx].resources[arrayIdx];
{
const BoundResourceArray &resArray = rw[residx];
if(arrayIdx >= resArray.firstIndex &&
arrayIdx - resArray.firstIndex < resArray.resources.count())
ret = resArray.resources[arrayIdx - resArray.firstIndex];
}
}
}
else if(Type == FollowType::ReadOnly)
@@ -181,7 +186,12 @@ BoundResource Following::GetBoundResource(ICaptureContext &ctx, int arrayIdx)
int residx = ro.indexOf(key);
if(residx >= 0)
ret = ro[residx].resources[arrayIdx];
{
const BoundResourceArray &resArray = ro[residx];
if(arrayIdx >= resArray.firstIndex &&
arrayIdx - resArray.firstIndex < resArray.resources.count())
ret = resArray.resources[arrayIdx - resArray.firstIndex];
}
}
}
@@ -233,7 +243,8 @@ BoundResource Following::GetDepthTarget(ICaptureContext &ctx)
return ctx.CurPipelineState().GetDepthTarget();
}
rdcarray<BoundResourceArray> Following::GetReadWriteResources(ICaptureContext &ctx, ShaderStage stage)
rdcarray<BoundResourceArray> Following::GetReadWriteResources(ICaptureContext &ctx,
ShaderStage stage, bool onlyUsed)
{
bool copy = false, clear = false, compute = false;
GetDrawContext(ctx, copy, clear, compute);
@@ -246,22 +257,18 @@ rdcarray<BoundResourceArray> Following::GetReadWriteResources(ICaptureContext &c
{
// only return compute resources for one stage
if(stage == ShaderStage::Pixel || stage == ShaderStage::Compute)
return ctx.CurPipelineState().GetReadWriteResources(ShaderStage::Compute);
return ctx.CurPipelineState().GetReadWriteResources(ShaderStage::Compute, onlyUsed);
else
return rdcarray<BoundResourceArray>();
}
else
{
return ctx.CurPipelineState().GetReadWriteResources(stage);
return ctx.CurPipelineState().GetReadWriteResources(stage, onlyUsed);
}
}
rdcarray<BoundResourceArray> Following::GetReadWriteResources(ICaptureContext &ctx)
{
return GetReadWriteResources(ctx, Stage);
}
rdcarray<BoundResourceArray> Following::GetReadOnlyResources(ICaptureContext &ctx, ShaderStage stage)
rdcarray<BoundResourceArray> Following::GetReadOnlyResources(ICaptureContext &ctx,
ShaderStage stage, bool onlyUsed)
{
const DrawcallDescription *curDraw = ctx.CurDrawcall();
bool copy = false, clear = false, compute = false;
@@ -282,21 +289,16 @@ rdcarray<BoundResourceArray> Following::GetReadOnlyResources(ICaptureContext &ct
{
// only return compute resources for one stage
if(stage == ShaderStage::Pixel || stage == ShaderStage::Compute)
return ctx.CurPipelineState().GetReadOnlyResources(ShaderStage::Compute);
return ctx.CurPipelineState().GetReadOnlyResources(ShaderStage::Compute, onlyUsed);
else
return rdcarray<BoundResourceArray>();
}
else
{
return ctx.CurPipelineState().GetReadOnlyResources(stage);
return ctx.CurPipelineState().GetReadOnlyResources(stage, onlyUsed);
}
}
rdcarray<BoundResourceArray> Following::GetReadOnlyResources(ICaptureContext &ctx)
{
return GetReadOnlyResources(ctx, Stage);
}
const ShaderReflection *Following::GetReflection(ICaptureContext &ctx, ShaderStage stage)
{
bool copy = false, clear = false, compute = false;
@@ -2342,65 +2344,40 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage,
const rdcarray<BoundResource> *resArray = NULL;
uint32_t dynamicallyUsedResCount = 1;
int32_t firstIndex = 0;
int residx = ResList.indexOf(key);
if(residx >= 0)
{
resArray = &ResList[residx].resources;
dynamicallyUsedResCount = ResList[residx].dynamicallyUsedCount;
firstIndex = ResList[residx].firstIndex;
}
int arrayLen = resArray != NULL ? resArray->count() : 1;
const bool collapseArray =
(dynamicallyUsedResCount > 20) || (dynamicallyUsedResCount == 0 && arrayLen > 8);
const bool collapseArray = arrayLen > 8 && (dynamicallyUsedResCount > 20 || m_ShowUnused);
for(int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++)
for(int i = 0; i < arrayLen; i++)
{
if(resArray && !resArray->at(arrayIdx).dynamicallyUsed)
int arrayIdx = firstIndex + i;
if(resArray && i >= resArray->count())
break;
if(resArray && !resArray->at(i).dynamicallyUsed)
continue;
BoundResource res = {};
if(resArray)
res = resArray->at(arrayIdx);
bool used = key.used;
QString bindName;
for(const ShaderResource &bind : resourceDetails)
{
if(bind.bindPoint == idx)
{
bindName = bind.name;
break;
}
}
if(copy)
{
used = true;
bindName = tr("Source");
}
res = resArray->at(i);
Following follow(*this, rw ? FollowType::ReadWrite : FollowType::ReadOnly, stage, idx,
arrayIdx);
QString slotName = QFormatStr("%1 %2%3")
.arg(m_Ctx.CurPipelineState().Abbrev(stage))
.arg(rw ? lit("RW ") : lit(""))
.arg(idx);
if(collapseArray)
slotName += QFormatStr(" Arr[%1]").arg(arrayLen);
else
slotName += QFormatStr("[%1]").arg(arrayIdx);
if(copy)
slotName = tr("SRC");
// show if it's referenced by the shader - regardless of empty or not
bool show = used;
bool show = key.used || copy;
// it's bound, but not referenced, and we have "show disabled"
show = show || (m_ShowUnused && res.resourceId != ResourceId());
@@ -2432,6 +2409,33 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage,
prevIndex++;
QString slotName = QFormatStr("%1 %2%3")
.arg(m_Ctx.CurPipelineState().Abbrev(stage))
.arg(rw ? lit("RW ") : lit(""))
.arg(idx);
if(collapseArray)
slotName += QFormatStr(" Arr[%1]").arg(arrayLen);
else
slotName += QFormatStr("[%1]").arg(arrayIdx);
if(copy)
slotName = tr("SRC");
QString bindName;
for(const ShaderResource &bind : resourceDetails)
{
if(bind.bindPoint == idx)
{
bindName = bind.name;
break;
}
}
if(copy)
bindName = tr("Source");
InitResourcePreview(prev, show ? res : BoundResource(), show, follow, bindName, slotName);
if(collapseArray)
@@ -3073,8 +3077,10 @@ void TextureViewer::OnEventChanged(uint32_t eventId)
{
ShaderStage stage = stages[i];
m_ReadWriteResources[(uint32_t)stage] = Following::GetReadWriteResources(m_Ctx, stage);
m_ReadOnlyResources[(uint32_t)stage] = Following::GetReadOnlyResources(m_Ctx, stage);
m_ReadWriteResources[(uint32_t)stage] =
Following::GetReadWriteResources(m_Ctx, stage, !m_ShowUnused);
m_ReadOnlyResources[(uint32_t)stage] =
Following::GetReadOnlyResources(m_Ctx, stage, !m_ShowUnused);
const ShaderReflection *details = Following::GetReflection(m_Ctx, stage);
const ShaderBindpointMapping &mapping = Following::GetMapping(m_Ctx, stage);
+4 -8
View File
@@ -79,14 +79,10 @@ struct Following
static rdcarray<BoundResource> GetOutputTargets(ICaptureContext &ctx);
static BoundResource GetDepthTarget(ICaptureContext &ctx);
rdcarray<BoundResourceArray> GetReadWriteResources(ICaptureContext &ctx);
static rdcarray<BoundResourceArray> GetReadWriteResources(ICaptureContext &ctx, ShaderStage stage);
rdcarray<BoundResourceArray> GetReadOnlyResources(ICaptureContext &ctx);
static rdcarray<BoundResourceArray> GetReadOnlyResources(ICaptureContext &ctx, ShaderStage stage);
static rdcarray<BoundResourceArray> GetReadWriteResources(ICaptureContext &ctx, ShaderStage stage,
bool onlyUsed);
static rdcarray<BoundResourceArray> GetReadOnlyResources(ICaptureContext &ctx, ShaderStage stage,
bool onlyUsed);
const ShaderReflection *GetReflection(ICaptureContext &ctx);
static const ShaderReflection *GetReflection(ICaptureContext &ctx, ShaderStage stage);
+11 -1
View File
@@ -315,6 +315,7 @@ struct BoundResourceArray
BoundResourceArray(Bindpoint b, const rdcarray<BoundResource> &r) : bindPoint(b), resources(r)
{
dynamicallyUsedCount = (uint32_t)r.size();
firstIndex = 0;
}
// for convenience for searching the array, we compare only using the BindPoint
bool operator==(const BoundResourceArray &o) const { return bindPoint == o.bindPoint; }
@@ -330,8 +331,17 @@ struct BoundResourceArray
Some APIs provide fine-grained usage based on dynamic shader feedback, to support 'bindless'
scenarios where only a small sparse subset of bound resources are actually used.
If this information isn't present this will be set to a large number.
)");
uint32_t dynamicallyUsedCount = 0;
uint32_t dynamicallyUsedCount = ~0U;
DOCUMENT(R"(Gives the array index of the first binding in :data:`resource`. If only a small subset
of the resources are used by the shader then the array may be rebased such that the first element is
not array index 0.
For more information see :data:`VKBindingElement.dynamicallyUsed`.
)");
int32_t firstIndex = 0;
};
DECLARE_REFLECTION_STRUCT(BoundResourceArray);
+6 -2
View File
@@ -305,10 +305,12 @@ For some APIs that don't distinguish by entry point, this may be empty.
DOCUMENT(R"(Retrieves the read-only resources bound to a particular shader stage.
:param ShaderStage stage: The shader stage to fetch from.
:param bool onlyUsed: Return only a subset of resources containing those actually used by the
shader.
:return: The currently bound read-only resources.
:rtype: ``list`` of :class:`BoundResourceArray` entries
)");
rdcarray<BoundResourceArray> GetReadOnlyResources(ShaderStage stage) const;
rdcarray<BoundResourceArray> GetReadOnlyResources(ShaderStage stage, bool onlyUsed = false) const;
DOCUMENT(R"(Retrieves the samplers bound to a particular shader stage.
@@ -321,10 +323,12 @@ For some APIs that don't distinguish by entry point, this may be empty.
DOCUMENT(R"(Retrieves the read/write resources bound to a particular shader stage.
:param ShaderStage stage: The shader stage to fetch from.
:param bool onlyUsed: Return only a subset of resources containing those actually used by the
shader.
:return: The currently bound read/write resources.
:rtype: ``list`` of :class:`BoundResourceArray` entries
)");
rdcarray<BoundResourceArray> GetReadWriteResources(ShaderStage stage) const;
rdcarray<BoundResourceArray> GetReadWriteResources(ShaderStage stage, bool onlyUsed = false) const;
DOCUMENT(R"(Retrieves the read/write resources bound to the depth-stencil output.
+48 -18
View File
@@ -1132,7 +1132,7 @@ rdcarray<BoundResourceArray> PipeState::GetSamplers(ShaderStage stage) const
return ret;
}
rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage) const
rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage, bool onlyUsed) const
{
rdcarray<BoundResourceArray> ret;
@@ -1251,18 +1251,33 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage)
ret.push_back(BoundResourceArray());
ret.back().bindPoint = Bindpoint(set, slot);
rdcarray<BoundResource> &val = ret.back().resources;
val.resize(bind.descriptorCount);
uint32_t count = bind.descriptorCount;
uint32_t firstIdx = 0;
if(onlyUsed)
{
firstIdx = (uint32_t)bind.firstUsedIndex;
if(bind.dynamicallyUsedCount < count)
count = bind.dynamicallyUsedCount;
if((uint32_t)bind.lastUsedIndex < count)
count = uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1);
}
rdcarray<BoundResource> &val = ret.back().resources;
val.reserve(count);
ret.back().firstIndex = (int32_t)firstIdx;
ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount;
for(uint32_t i = 0; i < bind.descriptorCount; i++)
BoundResource res;
for(uint32_t i = firstIdx; i < firstIdx + count; i++)
{
val[i].resourceId = bind.binds[i].resourceResourceId;
val[i].dynamicallyUsed = bind.binds[i].dynamicallyUsed;
val[i].firstMip = (int)bind.binds[i].firstMip;
val[i].firstSlice = (int)bind.binds[i].firstSlice;
val[i].typeCast = bind.binds[i].viewFormat.compType;
res.resourceId = bind.binds[i].resourceResourceId;
res.dynamicallyUsed = bind.binds[i].dynamicallyUsed;
res.firstMip = (int)bind.binds[i].firstMip;
res.firstSlice = (int)bind.binds[i].firstSlice;
res.typeCast = bind.binds[i].viewFormat.compType;
val.push_back(res);
}
}
}
@@ -1275,7 +1290,7 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage)
return ret;
}
rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage) const
rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage, bool onlyUsed) const
{
rdcarray<BoundResourceArray> ret;
@@ -1417,18 +1432,33 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage)
ret.push_back(BoundResourceArray());
ret.back().bindPoint = Bindpoint(set, slot);
rdcarray<BoundResource> &val = ret.back().resources;
val.resize(bind.descriptorCount);
uint32_t count = bind.descriptorCount;
uint32_t firstIdx = 0;
if(onlyUsed)
{
firstIdx = (uint32_t)bind.firstUsedIndex;
if(bind.dynamicallyUsedCount < count)
count = bind.dynamicallyUsedCount;
if((uint32_t)bind.lastUsedIndex < count)
count = uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1);
}
rdcarray<BoundResource> &val = ret.back().resources;
val.reserve(count);
ret.back().firstIndex = (int32_t)firstIdx;
ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount;
for(uint32_t i = 0; i < bind.descriptorCount; i++)
BoundResource res;
for(uint32_t i = firstIdx; i < firstIdx + count; i++)
{
val[i].resourceId = bind.binds[i].resourceResourceId;
val[i].dynamicallyUsed = bind.binds[i].dynamicallyUsed;
val[i].firstMip = (int)bind.binds[i].firstMip;
val[i].firstSlice = (int)bind.binds[i].firstSlice;
val[i].typeCast = bind.binds[i].viewFormat.compType;
res.resourceId = bind.binds[i].resourceResourceId;
res.dynamicallyUsed = bind.binds[i].dynamicallyUsed;
res.firstMip = (int)bind.binds[i].firstMip;
res.firstSlice = (int)bind.binds[i].firstSlice;
res.typeCast = bind.binds[i].viewFormat.compType;
val.push_back(res);
}
}
}
+17 -1
View File
@@ -246,7 +246,23 @@ redundant iteration to determine whether any bindings are present.
For more information see :data:`VKBindingElement.dynamicallyUsed`.
)");
uint32_t dynamicallyUsedCount = 0;
uint32_t dynamicallyUsedCount = ~0U;
DOCUMENT(R"(Gives the index of the first binding in :data:`binds` that is dynamically used. Useful
to avoid redundant iteration in very large descriptor arrays with a small subset that are used.
For more information see :data:`VKBindingElement.dynamicallyUsed`.
)");
int32_t firstUsedIndex = 0;
DOCUMENT(R"(Gives the index of the first binding in :data:`binds` that is dynamically used. Useful
to avoid redundant iteration in very large descriptor arrays with a small subset that are used.
.. note::
This may be set to a higher value than the number of bindings, if no dynamic use information is
available. Ensure that this is an additional check on the bind and the count is still respected.
For more information see :data:`VKBindingElement.dynamicallyUsed`.
)");
int32_t lastUsedIndex = 0x7fffffff;
DOCUMENT("The :class:`BindType` of this binding.");
BindType type = BindType::Unknown;
DOCUMENT("The :class:`ShaderStageMask` where this binding is visible.");
+42 -3
View File
@@ -1036,7 +1036,20 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
VkMarkerRegion::End();
m_VulkanPipelineState = VKPipe::State();
{
// reset the pipeline state, but keep the descriptor set arrays. This prevents needless
// reallocations, we'll ensure that descriptors are fully overwritten below.
rdcarray<VKPipe::DescriptorSet> graphicsDescriptors;
rdcarray<VKPipe::DescriptorSet> computeDescriptors;
m_VulkanPipelineState.graphics.descriptorSets.swap(graphicsDescriptors);
m_VulkanPipelineState.compute.descriptorSets.swap(computeDescriptors);
m_VulkanPipelineState = VKPipe::State();
m_VulkanPipelineState.graphics.descriptorSets.swap(graphicsDescriptors);
m_VulkanPipelineState.compute.descriptorSets.swap(computeDescriptors);
}
m_VulkanPipelineState.pushconsts.resize(state.pushConstSize);
memcpy(m_VulkanPipelineState.pushconsts.data(), state.pushconsts, state.pushConstSize);
@@ -1668,11 +1681,18 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
default: dst.bindings[b].type = BindType::Unknown; RDCERR("Unexpected descriptor type");
}
dst.bindings[b].firstUsedIndex = -1;
dst.bindings[b].lastUsedIndex = -1;
dst.bindings[b].dynamicallyUsedCount = 0;
dst.bindings[b].binds.resize(dst.bindings[b].descriptorCount);
for(uint32_t a = 0; a < dst.bindings[b].descriptorCount; a++)
{
VKPipe::BindingElement &dstel = dst.bindings[b].binds[a];
// clear it so we don't have to manually reset all elements back to normal
memset(&dstel, 0, sizeof(dstel));
curBind.arrayIndex = a;
// if we have a list of used binds, and this is an array descriptor (so would be
@@ -1697,22 +1717,34 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
}
// the next used bind is equal to this one. Mark it as dynamically used, and consume
if(curBind == *usedBindsData)
if(usedBindsSize > 0 && curBind == *usedBindsData)
{
dstel.dynamicallyUsed = true;
usedBindsData++;
usedBindsSize--;
}
// the next used bind is after the current one, this is not used.
else if(curBind < *usedBindsData)
else if(usedBindsSize > 0 && curBind < *usedBindsData)
{
dstel.dynamicallyUsed = false;
}
}
}
else
{
dstel.dynamicallyUsed = true;
}
if(dstel.dynamicallyUsed)
{
dst.bindings[b].dynamicallyUsedCount++;
// we iterate in forward order, so we can unconditinoally set the last bind to the
// current one, and only set the first bind if we haven't encountered one before
dst.bindings[b].lastUsedIndex = a;
if(dst.bindings[b].firstUsedIndex < 0)
dst.bindings[b].firstUsedIndex = a;
}
// first handle the sampler separately because it might be in a combined descriptor
if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
@@ -1893,6 +1925,13 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
dst.bindings[b].binds[a].byteSize = info[a].bufferInfo.range;
}
}
// if no bindings were set these will still be negative. Set them to something sensible.
if(dst.bindings[b].firstUsedIndex < 0)
{
dst.bindings[b].firstUsedIndex = 0;
dst.bindings[b].lastUsedIndex = 0x7fffffff;
}
}
}
}
+3 -1
View File
@@ -1896,12 +1896,14 @@ void DoSerialise(SerialiserType &ser, VKPipe::DescriptorBinding &el)
{
SERIALISE_MEMBER(descriptorCount);
SERIALISE_MEMBER(dynamicallyUsedCount);
SERIALISE_MEMBER(firstUsedIndex);
SERIALISE_MEMBER(lastUsedIndex);
SERIALISE_MEMBER(type);
SERIALISE_MEMBER(stageFlags);
SERIALISE_MEMBER(binds);
SIZE_CHECK(40);
SIZE_CHECK(48);
}
template <typename SerialiserType>