mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-08 08:40:55 +00:00
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:
@@ -744,7 +744,8 @@ QVariantList VulkanPipelineStateViewer::makeSampler(const QString &bindset, cons
|
||||
// arrange like either UVW: WRAP or UV: WRAP, W: CLAMP
|
||||
for(int a = 0; a < 3; a++)
|
||||
{
|
||||
QString prefix = QString(QLatin1Char("UVW"[a]));
|
||||
const char *uvw = "UVW";
|
||||
QString prefix = QString(QLatin1Char(uvw[a]));
|
||||
|
||||
if(a == 0 || addr[a] == addr[a - 1])
|
||||
{
|
||||
@@ -937,10 +938,12 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
|
||||
BindType bindType = BindType::Unknown;
|
||||
ShaderStageMask stageBits = ShaderStageMask::Unknown;
|
||||
bool pushDescriptor = false;
|
||||
uint32_t dynamicallyUsedCount = 1;
|
||||
|
||||
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;
|
||||
bindType = pipe.descriptorSets[bindset].bindings[bind].type;
|
||||
stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags;
|
||||
@@ -955,7 +958,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
|
||||
bindType = isrw ? BindType::ReadWriteImage : BindType::ReadOnlyImage;
|
||||
}
|
||||
|
||||
bool usedSlot = bindMap != NULL && bindMap->used;
|
||||
bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0;
|
||||
bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage));
|
||||
|
||||
// skip descriptors that aren't for this shader stage
|
||||
@@ -1025,8 +1028,13 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
|
||||
{
|
||||
const VKPipe::BindingElement *descriptorBind = NULL;
|
||||
if(slotBinds != NULL)
|
||||
{
|
||||
descriptorBind = &(*slotBinds)[idx];
|
||||
|
||||
if(!showNode(usedSlot && descriptorBind->dynamicallyUsed, filledSlot))
|
||||
continue;
|
||||
}
|
||||
|
||||
if(arrayLength > 1)
|
||||
{
|
||||
if(shaderRes && !shaderRes->name.isEmpty())
|
||||
@@ -1315,18 +1323,20 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
|
||||
const rdcarray<VKPipe::BindingElement> *slotBinds = NULL;
|
||||
BindType bindType = BindType::ConstantBuffer;
|
||||
ShaderStageMask stageBits = ShaderStageMask::Unknown;
|
||||
uint32_t dynamicallyUsedCount = 1;
|
||||
|
||||
bool pushDescriptor = false;
|
||||
|
||||
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;
|
||||
bindType = pipe.descriptorSets[bindset].bindings[bind].type;
|
||||
stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags;
|
||||
}
|
||||
|
||||
bool usedSlot = bindMap != NULL && bindMap->used;
|
||||
bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0;
|
||||
bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage));
|
||||
|
||||
// skip descriptors that aren't for this shader stage
|
||||
@@ -1385,8 +1395,13 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
|
||||
{
|
||||
const VKPipe::BindingElement *descriptorBind = NULL;
|
||||
if(slotBinds != NULL)
|
||||
{
|
||||
descriptorBind = &(*slotBinds)[idx];
|
||||
|
||||
if(!showNode(usedSlot && descriptorBind->dynamicallyUsed, filledSlot))
|
||||
continue;
|
||||
}
|
||||
|
||||
if(arrayLength > 1)
|
||||
{
|
||||
if(cblock != NULL && !cblock->name.isEmpty())
|
||||
|
||||
@@ -2095,20 +2095,24 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage,
|
||||
const Bindpoint &key = mapping[idx];
|
||||
|
||||
const rdcarray<BoundResource> *resArray = NULL;
|
||||
uint32_t dynamicallyUsedResCount = 1;
|
||||
|
||||
int residx = ResList.indexOf(key);
|
||||
if(residx >= 0)
|
||||
resArray = &ResList[residx].resources;
|
||||
|
||||
int arrayLen = resArray != NULL ? resArray->count() : 1;
|
||||
|
||||
// it's too expensive in bindless-type cases to create a thumbnail for every array element, so
|
||||
// for now we create one just for the first element and add an array size indicator to the slot
|
||||
// name
|
||||
// for(int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++)
|
||||
int arrayIdx = 0;
|
||||
|
||||
{
|
||||
resArray = &ResList[residx].resources;
|
||||
dynamicallyUsedResCount = ResList[residx].dynamicallyUsedCount;
|
||||
}
|
||||
|
||||
const bool collapseArray = dynamicallyUsedResCount > 20;
|
||||
|
||||
const int arrayLen = resArray != NULL ? resArray->count() : 1;
|
||||
|
||||
for(int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++)
|
||||
{
|
||||
if(resArray && !resArray->at(arrayIdx).dynamicallyUsed)
|
||||
continue;
|
||||
|
||||
ResourceId id = resArray != NULL ? resArray->at(arrayIdx).resourceId : ResourceId();
|
||||
CompType typeHint = resArray != NULL ? resArray->at(arrayIdx).typeHint : CompType::Typeless;
|
||||
|
||||
@@ -2137,8 +2141,10 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage,
|
||||
.arg(rw ? lit("RW ") : lit(""))
|
||||
.arg(idx);
|
||||
|
||||
if(arrayLen > 1)
|
||||
if(collapseArray)
|
||||
slotName += QFormatStr(" Arr[%1]").arg(arrayLen);
|
||||
else
|
||||
slotName += QFormatStr("[%1]").arg(arrayIdx);
|
||||
|
||||
if(copy)
|
||||
slotName = tr("SRC");
|
||||
@@ -2177,6 +2183,9 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage,
|
||||
prevIndex++;
|
||||
|
||||
InitResourcePreview(prev, show ? id : ResourceId(), typeHint, show, follow, bindName, slotName);
|
||||
|
||||
if(collapseArray)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -176,7 +176,7 @@ public:
|
||||
RDCEraseEl(ret);
|
||||
return ret;
|
||||
}
|
||||
void SavePipelineState() {}
|
||||
void SavePipelineState(uint32_t eventId) {}
|
||||
DriverInformation GetDriverInfo()
|
||||
{
|
||||
DriverInformation ret = {};
|
||||
|
||||
@@ -1524,13 +1524,15 @@ ShaderDebugTrace ReplayProxy::DebugThread(uint32_t eventId, const uint32_t group
|
||||
}
|
||||
|
||||
template <typename ParamSerialiser, typename ReturnSerialiser>
|
||||
void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSerialiser &retser)
|
||||
void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
||||
uint32_t eventId)
|
||||
{
|
||||
const ReplayProxyPacket expectedPacket = eReplayProxy_SavePipelineState;
|
||||
ReplayProxyPacket packet = eReplayProxy_SavePipelineState;
|
||||
|
||||
{
|
||||
BEGIN_PARAMS();
|
||||
SERIALISE_ELEMENT(eventId);
|
||||
END_PARAMS();
|
||||
}
|
||||
|
||||
@@ -1538,7 +1540,7 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer
|
||||
REMOTE_EXECUTION();
|
||||
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
||||
{
|
||||
m_Remote->SavePipelineState();
|
||||
m_Remote->SavePipelineState(eventId);
|
||||
|
||||
if(m_APIProps.pipelineType == GraphicsAPI::D3D11)
|
||||
m_D3D11PipelineState = *m_Remote->GetD3D11PipelineState();
|
||||
@@ -1636,9 +1638,9 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer
|
||||
CheckError(packet, expectedPacket);
|
||||
}
|
||||
|
||||
void ReplayProxy::SavePipelineState()
|
||||
void ReplayProxy::SavePipelineState(uint32_t eventId)
|
||||
{
|
||||
PROXY_FUNCTION(SavePipelineState);
|
||||
PROXY_FUNCTION(SavePipelineState, eventId);
|
||||
}
|
||||
|
||||
template <typename ParamSerialiser, typename ReturnSerialiser>
|
||||
@@ -2581,7 +2583,7 @@ bool ReplayProxy::Tick(int type)
|
||||
GetTextureData(ResourceId(), 0, 0, GetTextureDataParams(), dummy);
|
||||
break;
|
||||
}
|
||||
case eReplayProxy_SavePipelineState: SavePipelineState(); break;
|
||||
case eReplayProxy_SavePipelineState: SavePipelineState(0); break;
|
||||
case eReplayProxy_GetUsage: GetUsage(ResourceId()); break;
|
||||
case eReplayProxy_GetLiveID: GetLiveID(ResourceId()); break;
|
||||
case eReplayProxy_GetFrameRecord: GetFrameRecord(); break;
|
||||
|
||||
@@ -453,7 +453,7 @@ public:
|
||||
|
||||
IMPLEMENT_FUNCTION_PROXIED(std::vector<DebugMessage>, GetDebugMessages);
|
||||
|
||||
IMPLEMENT_FUNCTION_PROXIED(void, SavePipelineState);
|
||||
IMPLEMENT_FUNCTION_PROXIED(void, SavePipelineState, uint32_t eventId);
|
||||
IMPLEMENT_FUNCTION_PROXIED(void, ReplayLog, uint32_t endEventID, ReplayLogType replayType);
|
||||
|
||||
IMPLEMENT_FUNCTION_PROXIED(std::vector<uint32_t>, GetPassEvents, uint32_t eventId);
|
||||
|
||||
@@ -638,7 +638,7 @@ std::vector<ResourceId> D3D11Replay::GetTextures()
|
||||
return ret;
|
||||
}
|
||||
|
||||
void D3D11Replay::SavePipelineState()
|
||||
void D3D11Replay::SavePipelineState(uint32_t eventId)
|
||||
{
|
||||
D3D11RenderState *rs = m_pDevice->GetImmediateContext()->GetCurrentPipelineState();
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ public:
|
||||
|
||||
FrameRecord GetFrameRecord();
|
||||
|
||||
void SavePipelineState();
|
||||
void SavePipelineState(uint32_t eventId);
|
||||
const D3D11Pipe::State *GetD3D11PipelineState() { return &m_CurPipelineState; }
|
||||
const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; }
|
||||
const GLPipe::State *GetGLPipelineState() { return NULL; }
|
||||
|
||||
@@ -1220,7 +1220,7 @@ void D3D12Replay::FillRegisterSpaces(const D3D12RenderState::RootSignature &root
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12Replay::SavePipelineState()
|
||||
void D3D12Replay::SavePipelineState(uint32_t eventId)
|
||||
{
|
||||
const D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
|
||||
FrameRecord GetFrameRecord();
|
||||
|
||||
void SavePipelineState();
|
||||
void SavePipelineState(uint32_t eventId);
|
||||
const D3D11Pipe::State *GetD3D11PipelineState() { return NULL; }
|
||||
const D3D12Pipe::State *GetD3D12PipelineState() { return &m_PipelineState; }
|
||||
const GLPipe::State *GetGLPipelineState() { return NULL; }
|
||||
|
||||
@@ -757,7 +757,7 @@ string GLReplay::DisassembleShader(ResourceId pipeline, const ShaderReflection *
|
||||
return StringFormat::Fmt("; Invalid disassembly target %s", target.c_str());
|
||||
}
|
||||
|
||||
void GLReplay::SavePipelineState()
|
||||
void GLReplay::SavePipelineState(uint32_t eventId)
|
||||
{
|
||||
GLPipe::State &pipe = m_CurPipelineState;
|
||||
WrappedOpenGL &drv = *m_pDriver;
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
|
||||
FrameRecord GetFrameRecord();
|
||||
|
||||
void SavePipelineState();
|
||||
void SavePipelineState(uint32_t eventId);
|
||||
const D3D11Pipe::State *GetD3D11PipelineState() { return NULL; }
|
||||
const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; }
|
||||
const GLPipe::State *GetGLPipelineState() { return &m_CurPipelineState; }
|
||||
|
||||
@@ -864,13 +864,19 @@ void VulkanReplay::SetDriverInformation(const VkPhysicalDeviceProperties &props)
|
||||
memcpy(m_DriverInfo.version, versionString.c_str(), versionString.size());
|
||||
}
|
||||
|
||||
void VulkanReplay::SavePipelineState()
|
||||
void VulkanReplay::SavePipelineState(uint32_t eventId)
|
||||
{
|
||||
const VulkanRenderState &state = m_pDriver->m_RenderState;
|
||||
VulkanCreationInfo &c = m_pDriver->m_CreationInfo;
|
||||
|
||||
VulkanResourceManager *rm = m_pDriver->GetResourceManager();
|
||||
|
||||
VkMarkerRegion::Begin(StringFormat::Fmt("FetchShaderFeedback for %u", eventId));
|
||||
|
||||
FetchShaderFeedback(eventId);
|
||||
|
||||
VkMarkerRegion::End();
|
||||
|
||||
m_VulkanPipelineState = VKPipe::State();
|
||||
|
||||
m_VulkanPipelineState.pushconsts.resize(state.pushConstSize);
|
||||
@@ -1355,11 +1361,30 @@ void VulkanReplay::SavePipelineState()
|
||||
|
||||
for(size_t p = 0; p < ARRAY_COUNT(srcs); p++)
|
||||
{
|
||||
bool hasUsedBinds = false;
|
||||
const BindIdx *usedBindsData = NULL;
|
||||
size_t usedBindsSize = 0;
|
||||
|
||||
{
|
||||
const DynamicUsedBinds &usage = m_BindlessFeedback.Usage[eventId];
|
||||
bool curCompute = (p == 1);
|
||||
if(usage.valid && usage.compute == curCompute)
|
||||
{
|
||||
hasUsedBinds = true;
|
||||
usedBindsData = usage.used.data();
|
||||
usedBindsSize = usage.used.size();
|
||||
}
|
||||
}
|
||||
|
||||
BindIdx curBind;
|
||||
|
||||
for(size_t i = 0; i < srcs[p]->size(); i++)
|
||||
{
|
||||
ResourceId src = (*srcs[p])[i].descSet;
|
||||
VKPipe::DescriptorSet &dst = (*dsts[p])[i];
|
||||
|
||||
curBind.set = (uint32_t)i;
|
||||
|
||||
ResourceId layoutId = m_pDriver->m_DescriptorSetState[src].layout;
|
||||
|
||||
// push descriptors don't have a real descriptor set backing them
|
||||
@@ -1381,6 +1406,8 @@ void VulkanReplay::SavePipelineState()
|
||||
DescriptorSetSlot *info = m_pDriver->m_DescriptorSetState[src].currentBindings[b];
|
||||
const DescSetLayout::Binding &layoutBind = c.m_DescSetLayout[layoutId].bindings[b];
|
||||
|
||||
curBind.bind = (uint32_t)b;
|
||||
|
||||
bool dynamicOffset = false;
|
||||
|
||||
dst.bindings[b].descriptorCount = layoutBind.descriptorCount;
|
||||
@@ -1426,6 +1453,49 @@ void VulkanReplay::SavePipelineState()
|
||||
dst.bindings[b].binds.resize(layoutBind.descriptorCount);
|
||||
for(uint32_t a = 0; a < layoutBind.descriptorCount; a++)
|
||||
{
|
||||
VKPipe::BindingElement &dstel = dst.bindings[b].binds[a];
|
||||
|
||||
curBind.arrayidx = a;
|
||||
|
||||
// if we have a list of used binds, and this is an array descriptor (so would be
|
||||
// expected to be in the list), check it for dynamic usage.
|
||||
if(layoutBind.descriptorCount > 1 && hasUsedBinds)
|
||||
{
|
||||
// if we exhausted the list, all other elements are unused
|
||||
if(usedBindsSize == 0)
|
||||
{
|
||||
dstel.dynamicallyUsed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we never saw the current value of usedBindsData (which is odd, we should have
|
||||
// when iterating over all descriptors. This could only happen if there's some
|
||||
// layout mismatch or a feedback bug that lead to an invalid entry in the list).
|
||||
// Keep advancing until we get to one that is >= our current bind
|
||||
while(curBind > *usedBindsData && usedBindsSize)
|
||||
{
|
||||
usedBindsData++;
|
||||
usedBindsSize--;
|
||||
}
|
||||
|
||||
// the next used bind is equal to this one. Mark it as dynamically used, and consume
|
||||
if(curBind == *usedBindsData)
|
||||
{
|
||||
dstel.dynamicallyUsed = true;
|
||||
usedBindsData++;
|
||||
usedBindsSize--;
|
||||
}
|
||||
// the next used bind is after the current one, this is not used.
|
||||
else if(curBind < *usedBindsData)
|
||||
{
|
||||
dstel.dynamicallyUsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dstel.dynamicallyUsed)
|
||||
dst.bindings[b].dynamicallyUsedCount++;
|
||||
|
||||
if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
|
||||
layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
{
|
||||
|
||||
@@ -254,7 +254,7 @@ public:
|
||||
FrameRecord GetFrameRecord();
|
||||
vector<DebugMessage> GetDebugMessages();
|
||||
|
||||
void SavePipelineState();
|
||||
void SavePipelineState(uint32_t eventId);
|
||||
const D3D11Pipe::State *GetD3D11PipelineState() { return NULL; }
|
||||
const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; }
|
||||
const GLPipe::State *GetGLPipelineState() { return NULL; }
|
||||
|
||||
@@ -226,7 +226,7 @@ void ReplayController::SetFrameEvent(uint32_t eventId, bool force)
|
||||
|
||||
m_pDevice->ReplayLog(eventId, eReplay_OnlyDraw);
|
||||
|
||||
FetchPipelineState();
|
||||
FetchPipelineState(eventId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2060,8 +2060,6 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd
|
||||
if(status != ReplayStatus::Succeeded)
|
||||
return status;
|
||||
|
||||
FetchPipelineState();
|
||||
|
||||
m_APIProps = m_pDevice->GetAPIProperties();
|
||||
|
||||
// fetch GCN ISA targets
|
||||
@@ -2095,6 +2093,8 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd
|
||||
m_Drawcalls.clear();
|
||||
SetupDrawcallPointers(m_Drawcalls, m_FrameRecord.drawcallList);
|
||||
|
||||
FetchPipelineState(m_Drawcalls.back()->eventId);
|
||||
|
||||
return ReplayStatus::Succeeded;
|
||||
}
|
||||
|
||||
@@ -2112,11 +2112,11 @@ APIProperties ReplayController::GetAPIProperties()
|
||||
return m_pDevice->GetAPIProperties();
|
||||
}
|
||||
|
||||
void ReplayController::FetchPipelineState()
|
||||
void ReplayController::FetchPipelineState(uint32_t eventId)
|
||||
{
|
||||
CHECK_REPLAY_THREAD();
|
||||
|
||||
m_pDevice->SavePipelineState();
|
||||
m_pDevice->SavePipelineState(eventId);
|
||||
|
||||
m_D3D11PipelineState = m_pDevice->GetD3D11PipelineState();
|
||||
m_D3D12PipelineState = m_pDevice->GetD3D12PipelineState();
|
||||
|
||||
@@ -139,8 +139,6 @@ public:
|
||||
|
||||
void SetFrameEvent(uint32_t eventId, bool force);
|
||||
|
||||
void FetchPipelineState();
|
||||
|
||||
const D3D11Pipe::State *GetD3D11PipelineState();
|
||||
const D3D12Pipe::State *GetD3D12PipelineState();
|
||||
const GLPipe::State *GetGLPipelineState();
|
||||
@@ -215,6 +213,8 @@ public:
|
||||
private:
|
||||
ReplayStatus PostCreateInit(IReplayDriver *device, RDCFile *rdc);
|
||||
|
||||
void FetchPipelineState(uint32_t eventId);
|
||||
|
||||
DrawcallDescription *GetDrawcallByEID(uint32_t eventId);
|
||||
bool ContainsMarker(const rdcarray<DrawcallDescription> &draws);
|
||||
bool PassEquivalent(const DrawcallDescription &a, const DrawcallDescription &b);
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
|
||||
virtual vector<EventUsage> GetUsage(ResourceId id) = 0;
|
||||
|
||||
virtual void SavePipelineState() = 0;
|
||||
virtual void SavePipelineState(uint32_t eventId) = 0;
|
||||
virtual const D3D11Pipe::State *GetD3D11PipelineState() = 0;
|
||||
virtual const D3D12Pipe::State *GetD3D12PipelineState() = 0;
|
||||
virtual const GLPipe::State *GetGLPipelineState() = 0;
|
||||
|
||||
Reference in New Issue
Block a user