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-03 18:09:47 +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);