Pass bindless feedback data to UI through vulkan pipeline state

* Each binding element within an arrayed descriptor has a bool indicating if
  it's dynamically used or not (which will be set to true if the feedback isn't
  available). Each descriptor has a uint32_t indicating how many elements are
  dynamically used - which is useful for the UI to hide the root of an array
  that has no used elements, or to heuristically decide whether to expand or
  elide the contents.
This commit is contained in:
baldurk
2019-04-05 09:19:22 +01:00
parent 7f56704a92
commit f256218e17
19 changed files with 191 additions and 52 deletions
+19 -1
View File
@@ -232,6 +232,7 @@ struct BoundResource
BoundResource()
{
resourceId = ResourceId();
dynamicallyUsed = true;
firstMip = -1;
firstSlice = -1;
typeHint = CompType::Typeless;
@@ -239,6 +240,7 @@ struct BoundResource
BoundResource(ResourceId id)
{
resourceId = id;
dynamicallyUsed = true;
firstMip = -1;
firstSlice = -1;
typeHint = CompType::Typeless;
@@ -264,6 +266,12 @@ struct BoundResource
}
DOCUMENT("A :class:`~renderdoc.ResourceId` identifying the bound resource.");
ResourceId resourceId;
DOCUMENT(R"(``True`` if this binding element is dynamically used.
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.
)");
bool dynamicallyUsed = true;
DOCUMENT("For textures, the highest mip level available on this binding, or -1 for all mips");
int firstMip;
DOCUMENT("For textures, the first array slice available on this binding. or -1 for all slices.");
@@ -285,7 +293,10 @@ struct BoundResourceArray
BoundResourceArray() = default;
BoundResourceArray(const BoundResourceArray &) = default;
BoundResourceArray(Bindpoint b) : bindPoint(b) {}
BoundResourceArray(Bindpoint b, const rdcarray<BoundResource> &r) : bindPoint(b), resources(r) {}
BoundResourceArray(Bindpoint b, const rdcarray<BoundResource> &r) : bindPoint(b), resources(r)
{
dynamicallyUsedCount = (uint32_t)r.size();
}
// for convenience for searching the array, we compare only using the BindPoint
bool operator==(const BoundResourceArray &o) const { return bindPoint == o.bindPoint; }
bool operator!=(const BoundResourceArray &o) const { return !(bindPoint == o.bindPoint); }
@@ -295,6 +306,13 @@ struct BoundResourceArray
DOCUMENT("The resources at this bind point");
rdcarray<BoundResource> resources;
DOCUMENT(R"(Lists how many bindings in :data:`resources` are dynamically used.
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.
)");
uint32_t dynamicallyUsedCount = 0;
};
DECLARE_REFLECTION_STRUCT(BoundResourceArray);
+6
View File
@@ -1115,9 +1115,12 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage)
rdcarray<BoundResource> &val = ret.back().resources;
val.resize(bind.descriptorCount);
ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount;
for(uint32_t i = 0; i < bind.descriptorCount; 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].typeHint = bind.binds[i].viewFormat.compType;
@@ -1266,9 +1269,12 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage)
rdcarray<BoundResource> &val = ret.back().resources;
val.resize(bind.descriptorCount);
ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount;
for(uint32_t i = 0; i < bind.descriptorCount; 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].typeHint = bind.binds[i].viewFormat.compType;
+33 -14
View File
@@ -37,21 +37,23 @@ struct BindingElement
bool operator==(const BindingElement &o) const
{
return viewResourceId == o.viewResourceId && resourceResourceId == o.resourceResourceId &&
samplerResourceId == o.samplerResourceId && immutableSampler == o.immutableSampler &&
viewFormat == o.viewFormat && swizzle[0] == o.swizzle[0] && swizzle[1] == o.swizzle[1] &&
swizzle[2] == o.swizzle[2] && swizzle[3] == o.swizzle[3] && firstMip == o.firstMip &&
firstSlice == o.firstSlice && numMips == o.numMips && numSlices == o.numSlices &&
byteOffset == o.byteOffset && byteSize == o.byteSize && filter == o.filter &&
addressU == o.addressU && addressV == o.addressV && addressW == o.addressW &&
mipBias == o.mipBias && maxAnisotropy == o.maxAnisotropy &&
compareFunction == o.compareFunction && minLOD == o.minLOD && maxLOD == o.maxLOD &&
borderColor[0] == o.borderColor[0] && borderColor[1] == o.borderColor[1] &&
borderColor[2] == o.borderColor[2] && borderColor[3] == o.borderColor[3] &&
unnormalized == o.unnormalized;
return dynamicallyUsed == o.dynamicallyUsed && viewResourceId == o.viewResourceId &&
resourceResourceId == o.resourceResourceId && samplerResourceId == o.samplerResourceId &&
immutableSampler == o.immutableSampler && viewFormat == o.viewFormat &&
swizzle[0] == o.swizzle[0] && swizzle[1] == o.swizzle[1] && swizzle[2] == o.swizzle[2] &&
swizzle[3] == o.swizzle[3] && firstMip == o.firstMip && firstSlice == o.firstSlice &&
numMips == o.numMips && numSlices == o.numSlices && byteOffset == o.byteOffset &&
byteSize == o.byteSize && filter == o.filter && addressU == o.addressU &&
addressV == o.addressV && addressW == o.addressW && mipBias == o.mipBias &&
maxAnisotropy == o.maxAnisotropy && compareFunction == o.compareFunction &&
minLOD == o.minLOD && maxLOD == o.maxLOD && borderColor[0] == o.borderColor[0] &&
borderColor[1] == o.borderColor[1] && borderColor[2] == o.borderColor[2] &&
borderColor[3] == o.borderColor[3] && unnormalized == o.unnormalized;
}
bool operator<(const BindingElement &o) const
{
if(!(dynamicallyUsed == o.dynamicallyUsed))
return dynamicallyUsed < o.dynamicallyUsed;
if(!(viewResourceId == o.viewResourceId))
return viewResourceId < o.viewResourceId;
if(!(resourceResourceId == o.resourceResourceId))
@@ -121,6 +123,15 @@ struct BindingElement
ResourceId samplerResourceId;
DOCUMENT("``True`` if this is an immutable sampler binding.");
bool immutableSampler = false;
DOCUMENT(R"(``True`` if this binding element is dynamically used.
If set to ``False`` this means that the binding was available to the shader but during execution it
was not referenced. The data gathered for setting this variable is conservative, meaning that only
accesses through arrays will have this calculated to reduce the required feedback bandwidth - single
non-arrayed descriptors may have this value set to ``True`` even if the shader did not use them,
since single descriptors may only be dynamically skipped by control flow.
)");
bool dynamicallyUsed = true;
DOCUMENT("The :class:`ResourceFormat` that the view uses.");
ResourceFormat viewFormat;
@@ -209,13 +220,15 @@ struct DescriptorBinding
bool operator==(const DescriptorBinding &o) const
{
return descriptorCount == o.descriptorCount && type == o.type && stageFlags == o.stageFlags &&
binds == o.binds;
return descriptorCount == o.descriptorCount && dynamicallyUsedCount == o.dynamicallyUsedCount &&
type == o.type && stageFlags == o.stageFlags && binds == o.binds;
}
bool operator<(const DescriptorBinding &o) const
{
if(!(descriptorCount == o.descriptorCount))
return descriptorCount < o.descriptorCount;
if(!(dynamicallyUsedCount == o.dynamicallyUsedCount))
return dynamicallyUsedCount < o.dynamicallyUsedCount;
if(!(type == o.type))
return type < o.type;
if(!(stageFlags == o.stageFlags))
@@ -228,6 +241,12 @@ struct DescriptorBinding
If this binding is empty/non-existant this value will be ``0``.
)");
uint32_t descriptorCount = 0;
DOCUMENT(R"(Lists how many bindings in :data:`binds` are dynamically used. Useful to avoid
redundant iteration to determine whether any bindings are present.
For more information see :data:`VKBindingElement.dynamicallyUsed`.
)");
uint32_t dynamicallyUsedCount = 0;
DOCUMENT("The :class:`BindType` of this binding.");
BindType type = BindType::Unknown;
DOCUMENT("The :class:`ShaderStageMask` where this binding is visible.");