mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-17 04:57:09 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user