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
+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.");