mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-03 21:30:59 +00:00
Implement bindless feedback for DXBC shaders on D3D12 with patching
This commit is contained in:
@@ -282,6 +282,15 @@ struct View
|
||||
:type: TextureSwizzle4
|
||||
)");
|
||||
TextureSwizzle4 swizzle;
|
||||
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:`D3DBufferViewFlags` set for the buffer.");
|
||||
D3DBufferViewFlags bufferFlags = D3DBufferViewFlags::NoFlags;
|
||||
DOCUMENT("If the view has a hidden counter, this stores the current value of the counter.");
|
||||
@@ -464,8 +473,9 @@ struct RootSignatureRange
|
||||
bool operator==(const RootSignatureRange &o) const
|
||||
{
|
||||
return immediate == o.immediate && rootElement == o.rootElement && visibility == o.visibility &&
|
||||
registerSpace == o.registerSpace && constantBuffers == o.constantBuffers &&
|
||||
samplers == o.samplers && views == o.views;
|
||||
registerSpace == o.registerSpace && dynamicallyUsedCount == o.dynamicallyUsedCount &&
|
||||
firstUsedIndex == o.firstUsedIndex && lastUsedIndex == o.lastUsedIndex &&
|
||||
constantBuffers == o.constantBuffers && samplers == o.samplers && views == o.views;
|
||||
}
|
||||
bool operator<(const RootSignatureRange &o) const
|
||||
{
|
||||
@@ -477,6 +487,12 @@ struct RootSignatureRange
|
||||
return visibility < o.visibility;
|
||||
if(!(registerSpace == o.registerSpace))
|
||||
return registerSpace < o.registerSpace;
|
||||
if(!(dynamicallyUsedCount == o.dynamicallyUsedCount))
|
||||
return dynamicallyUsedCount < o.dynamicallyUsedCount;
|
||||
if(!(firstUsedIndex == o.firstUsedIndex))
|
||||
return firstUsedIndex < o.firstUsedIndex;
|
||||
if(!(lastUsedIndex == o.lastUsedIndex))
|
||||
return lastUsedIndex < o.lastUsedIndex;
|
||||
if(!(constantBuffers == o.constantBuffers))
|
||||
return constantBuffers < o.constantBuffers;
|
||||
if(!(samplers == o.samplers))
|
||||
@@ -496,6 +512,28 @@ struct RootSignatureRange
|
||||
ShaderStageMask visibility = ShaderStageMask::All;
|
||||
DOCUMENT("The register space of this element.");
|
||||
uint32_t registerSpace;
|
||||
DOCUMENT(R"(Lists how many bindings in :data:`views` are dynamically used. Useful to avoid
|
||||
redundant iteration to determine whether any bindings are present.
|
||||
|
||||
For more information see :data:`D3D12View.dynamicallyUsed`.
|
||||
)");
|
||||
uint32_t dynamicallyUsedCount = ~0U;
|
||||
DOCUMENT(R"(Gives the index of the first binding in :data:`views` 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:`D3D12View.dynamicallyUsed`.
|
||||
)");
|
||||
int32_t firstUsedIndex = 0;
|
||||
DOCUMENT(R"(Gives the index of the first binding in :data:`views` 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:`D3D12View.dynamicallyUsed`.
|
||||
)");
|
||||
int32_t lastUsedIndex = 0x7fffffff;
|
||||
DOCUMENT(R"(The constant buffers in this range.
|
||||
|
||||
:type: List[D3D12ConstantBuffer]
|
||||
|
||||
@@ -1272,6 +1272,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
|
||||
|
||||
rdcarray<BoundResource> &val = ret.back().resources;
|
||||
|
||||
ret.back().dynamicallyUsedCount = 0;
|
||||
|
||||
for(size_t i = 0; i < m_D3D12->rootElements.size(); ++i)
|
||||
{
|
||||
const D3D12Pipe::RootSignatureRange &element = m_D3D12->rootElements[i];
|
||||
@@ -1281,7 +1283,19 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
|
||||
if(element.type == BindType::ReadOnlyResource &&
|
||||
element.registerSpace == (uint32_t)bind.bindset)
|
||||
{
|
||||
for(size_t j = 0; j < element.views.size(); ++j)
|
||||
size_t firstIdx = 0;
|
||||
size_t count = element.views.size();
|
||||
|
||||
if(onlyUsed && val.empty())
|
||||
{
|
||||
firstIdx = (uint32_t)element.firstUsedIndex;
|
||||
count = std::min(count - firstIdx,
|
||||
size_t(element.lastUsedIndex - element.firstUsedIndex + 1));
|
||||
|
||||
ret.back().firstIndex = (int32_t)firstIdx;
|
||||
}
|
||||
|
||||
for(size_t j = firstIdx; j < count; ++j)
|
||||
{
|
||||
const D3D12Pipe::View &view = element.views[j];
|
||||
if(view.bind >= start && view.bind < end)
|
||||
@@ -1289,9 +1303,13 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
|
||||
val.push_back(BoundResource());
|
||||
BoundResource &b = val.back();
|
||||
b.resourceId = view.resourceId;
|
||||
b.dynamicallyUsed = view.dynamicallyUsed;
|
||||
b.firstMip = (int)view.firstMip;
|
||||
b.firstSlice = (int)view.firstSlice;
|
||||
b.typeCast = view.viewFormat.compType;
|
||||
|
||||
if(view.dynamicallyUsed)
|
||||
ret.back().dynamicallyUsedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1352,7 +1370,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadOnlyResources(ShaderStage stage,
|
||||
if(onlyUsed)
|
||||
{
|
||||
firstIdx = (uint32_t)bind.firstUsedIndex;
|
||||
count = std::min(count, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
|
||||
count =
|
||||
std::min(count - firstIdx, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
|
||||
}
|
||||
|
||||
rdcarray<BoundResource> &val = ret.back().resources;
|
||||
@@ -1454,6 +1473,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
|
||||
|
||||
rdcarray<BoundResource> &val = ret.back().resources;
|
||||
|
||||
ret.back().dynamicallyUsedCount = 0;
|
||||
|
||||
for(size_t i = 0; i < m_D3D12->rootElements.size(); ++i)
|
||||
{
|
||||
const D3D12Pipe::RootSignatureRange &element = m_D3D12->rootElements[i];
|
||||
@@ -1463,7 +1484,19 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
|
||||
if(element.type == BindType::ReadWriteResource &&
|
||||
element.registerSpace == (uint32_t)bind.bindset)
|
||||
{
|
||||
for(size_t j = 0; j < element.views.size(); ++j)
|
||||
size_t firstIdx = 0;
|
||||
size_t count = element.views.size();
|
||||
|
||||
if(onlyUsed && val.empty())
|
||||
{
|
||||
firstIdx = (uint32_t)element.firstUsedIndex;
|
||||
count = std::min(count - firstIdx,
|
||||
size_t(element.lastUsedIndex - element.firstUsedIndex + 1));
|
||||
|
||||
ret.back().firstIndex = (int32_t)firstIdx;
|
||||
}
|
||||
|
||||
for(size_t j = firstIdx; j < count; ++j)
|
||||
{
|
||||
const D3D12Pipe::View &view = element.views[j];
|
||||
if(view.bind >= start && view.bind < end)
|
||||
@@ -1471,9 +1504,13 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
|
||||
val.push_back(BoundResource());
|
||||
BoundResource &b = val.back();
|
||||
b.resourceId = view.resourceId;
|
||||
b.dynamicallyUsed = view.dynamicallyUsed;
|
||||
b.firstMip = (int)view.firstMip;
|
||||
b.firstSlice = (int)view.firstSlice;
|
||||
b.typeCast = view.viewFormat.compType;
|
||||
|
||||
if(view.dynamicallyUsed)
|
||||
ret.back().dynamicallyUsedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1530,7 +1567,8 @@ rdcarray<BoundResourceArray> PipeState::GetReadWriteResources(ShaderStage stage,
|
||||
if(onlyUsed)
|
||||
{
|
||||
firstIdx = (uint32_t)bind.firstUsedIndex;
|
||||
count = std::min(count, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
|
||||
count =
|
||||
std::min(count - firstIdx, uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1));
|
||||
}
|
||||
|
||||
rdcarray<BoundResource> &val = ret.back().resources;
|
||||
|
||||
@@ -421,7 +421,7 @@ DOCUMENT(R"(A single source component for a destination texture swizzle.
|
||||
|
||||
The fixed value ``1``.
|
||||
)");
|
||||
enum class TextureSwizzle : uint32_t
|
||||
enum class TextureSwizzle : uint8_t
|
||||
{
|
||||
Red,
|
||||
Green,
|
||||
@@ -4077,7 +4077,7 @@ DOCUMENT(R"(A set of flags for D3D buffer view properties.
|
||||
|
||||
The buffer is used with a structured buffer with associated hidden counter.
|
||||
)");
|
||||
enum class D3DBufferViewFlags : uint32_t
|
||||
enum class D3DBufferViewFlags : uint8_t
|
||||
{
|
||||
NoFlags = 0x0,
|
||||
Raw = 0x1,
|
||||
|
||||
@@ -231,6 +231,7 @@ struct DescriptorBinding
|
||||
bool operator==(const DescriptorBinding &o) const
|
||||
{
|
||||
return descriptorCount == o.descriptorCount && dynamicallyUsedCount == o.dynamicallyUsedCount &&
|
||||
firstUsedIndex == o.firstUsedIndex && lastUsedIndex == o.lastUsedIndex &&
|
||||
type == o.type && stageFlags == o.stageFlags && binds == o.binds;
|
||||
}
|
||||
bool operator<(const DescriptorBinding &o) const
|
||||
@@ -239,6 +240,10 @@ struct DescriptorBinding
|
||||
return descriptorCount < o.descriptorCount;
|
||||
if(!(dynamicallyUsedCount == o.dynamicallyUsedCount))
|
||||
return dynamicallyUsedCount < o.dynamicallyUsedCount;
|
||||
if(!(firstUsedIndex == o.firstUsedIndex))
|
||||
return firstUsedIndex < o.firstUsedIndex;
|
||||
if(!(lastUsedIndex == o.lastUsedIndex))
|
||||
return lastUsedIndex < o.lastUsedIndex;
|
||||
if(!(type == o.type))
|
||||
return type < o.type;
|
||||
if(!(stageFlags == o.stageFlags))
|
||||
|
||||
@@ -185,6 +185,8 @@
|
||||
<ClInclude Include="d3d11_video.h">
|
||||
<Filter>Wrapped</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="d3d11_hooks.h" />
|
||||
<ClInclude Include="d3d11_hooks.h">
|
||||
<Filter>Hooks</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -64,6 +64,8 @@ enum CBVUAVSRVSlot
|
||||
|
||||
TMP_UAV,
|
||||
|
||||
FEEDBACK_CLEAR_UAV,
|
||||
|
||||
MSAA_SRV2x,
|
||||
MSAA_SRV4x,
|
||||
MSAA_SRV8x,
|
||||
|
||||
@@ -168,6 +168,7 @@ void D3D12Replay::CreateResources()
|
||||
void D3D12Replay::DestroyResources()
|
||||
{
|
||||
ClearPostVSCache();
|
||||
ClearFeedbackCache();
|
||||
|
||||
m_General.Release();
|
||||
m_TexRender.Release();
|
||||
@@ -176,6 +177,8 @@ void D3D12Replay::DestroyResources()
|
||||
m_PixelPick.Release();
|
||||
m_Histogram.Release();
|
||||
|
||||
SAFE_RELEASE(m_BindlessFeedback.FeedbackBuffer);
|
||||
|
||||
SAFE_RELEASE(m_SOBuffer);
|
||||
SAFE_RELEASE(m_SOStagingBuffer);
|
||||
SAFE_RELEASE(m_SOPatchedIndexBuffer);
|
||||
@@ -926,7 +929,7 @@ ShaderStageMask ToShaderStageMask(D3D12_SHADER_VISIBILITY vis)
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSig,
|
||||
void D3D12Replay::FillRootElements(uint32_t eventId, const D3D12RenderState::RootSignature &rootSig,
|
||||
const ShaderBindpointMapping *mappings[(uint32_t)ShaderStage::Count],
|
||||
rdcarray<D3D12Pipe::RootSignatureRange> &rootElements)
|
||||
{
|
||||
@@ -935,6 +938,12 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
|
||||
D3D12ResourceManager *rm = m_pDevice->GetResourceManager();
|
||||
|
||||
const D3D12DynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
|
||||
|
||||
const D3D12FeedbackBindIdentifier *curUsage = usage.used.begin();
|
||||
const D3D12FeedbackBindIdentifier *lastUsage = usage.used.end();
|
||||
D3D12FeedbackBindIdentifier curIdentifier;
|
||||
|
||||
WrappedID3D12RootSignature *sig =
|
||||
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>(rootSig.rootsig);
|
||||
|
||||
@@ -944,6 +953,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
|
||||
for(size_t rootEl = 0; rootEl < sig->sig.Parameters.size(); rootEl++)
|
||||
{
|
||||
curIdentifier.rootEl = rootEl;
|
||||
|
||||
const D3D12RootSignatureParameter &p = sig->sig.Parameters[rootEl];
|
||||
|
||||
if(p.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS)
|
||||
@@ -1027,6 +1038,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
element.views.push_back(D3D12Pipe::View(p.Descriptor.ShaderRegister));
|
||||
D3D12Pipe::View &view = element.views.back();
|
||||
|
||||
view.dynamicallyUsed = true;
|
||||
|
||||
if(rootEl < rootSig.sigelems.size())
|
||||
{
|
||||
const D3D12RenderState::SignatureElement &e = rootSig.sigelems[rootEl];
|
||||
@@ -1065,6 +1078,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
element.views.push_back(D3D12Pipe::View(p.Descriptor.ShaderRegister));
|
||||
D3D12Pipe::View &view = element.views.back();
|
||||
|
||||
view.dynamicallyUsed = true;
|
||||
|
||||
if(rootEl < rootSig.sigelems.size())
|
||||
{
|
||||
const D3D12RenderState::SignatureElement &e = rootSig.sigelems[rootEl];
|
||||
@@ -1104,6 +1119,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
{
|
||||
const D3D12_DESCRIPTOR_RANGE1 &range = p.ranges[r];
|
||||
|
||||
curIdentifier.rangeIndex = r;
|
||||
|
||||
// Here we diverge slightly from how root signatures store data. A descriptor table can
|
||||
// contain multiple ranges which can each contain different types. D3D12Pipe treats
|
||||
// each range as a separate RootElement
|
||||
@@ -1146,10 +1163,13 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
// Find shader binds that map any or all of this range to see if we can trim it
|
||||
for(ShaderStage stage = ShaderStage::Vertex; stage < ShaderStage::Count; ++stage)
|
||||
{
|
||||
const ShaderBindpointMapping *mapping = mappings[(uint32_t)stage];
|
||||
if((element.visibility & MaskForStage(stage)) != ShaderStageMask::Unknown)
|
||||
{
|
||||
// This range is visible to this shader stage, check its mappings
|
||||
const rdcarray<Bindpoint> &bps = mappings[(uint32_t)stage]->readOnlyResources;
|
||||
const rdcarray<Bindpoint> &bps = range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV
|
||||
? mapping->readWriteResources
|
||||
: mapping->readOnlyResources;
|
||||
for(size_t b = 0; b < bps.size(); ++b)
|
||||
{
|
||||
if(bps[b].bindset == (int32_t)element.registerSpace &&
|
||||
@@ -1237,9 +1257,16 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV)
|
||||
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV ||
|
||||
range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)
|
||||
{
|
||||
element.type = BindType::ReadOnlyResource;
|
||||
if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV)
|
||||
element.type = BindType::ReadOnlyResource;
|
||||
else
|
||||
element.type = BindType::ReadWriteResource;
|
||||
|
||||
if(usage.valid)
|
||||
element.dynamicallyUsedCount = 0;
|
||||
|
||||
for(UINT i = 0; i < num; i++, shaderReg++)
|
||||
{
|
||||
@@ -1247,22 +1274,59 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi
|
||||
D3D12Pipe::View &view = element.views.back();
|
||||
view.tableIndex = offset + i;
|
||||
|
||||
if(desc)
|
||||
if(usage.valid)
|
||||
{
|
||||
FillResourceView(view, desc);
|
||||
desc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)
|
||||
{
|
||||
element.type = BindType::ReadWriteResource;
|
||||
curIdentifier.descIndex = i;
|
||||
|
||||
for(UINT i = 0; i < num; i++, shaderReg++)
|
||||
{
|
||||
element.views.push_back(D3D12Pipe::View(shaderReg));
|
||||
D3D12Pipe::View &view = element.views.back();
|
||||
view.tableIndex = offset + i;
|
||||
// if the usage data is valid we expect entries for all descriptors that are used.
|
||||
// Note that because of the messed up root signature/binding split, some of these
|
||||
// entries will just be statically inserted and not actually from dynamic feedback
|
||||
//
|
||||
// usage data is only valid when at least one array exists to have feedback
|
||||
if(curUsage >= lastUsage)
|
||||
{
|
||||
// if we exhausted the list, all other elements are unused
|
||||
view.dynamicallyUsed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we never saw the current value of curUsage (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(*curUsage < curIdentifier && curUsage < lastUsage)
|
||||
{
|
||||
curUsage++;
|
||||
}
|
||||
|
||||
// the next used bind is equal to this one. Mark it as dynamically used, and consume
|
||||
if(curUsage < lastUsage && *curUsage == curIdentifier)
|
||||
{
|
||||
view.dynamicallyUsed = true;
|
||||
curUsage++;
|
||||
}
|
||||
// the next used bind is after the current one, this is not used.
|
||||
else if(curUsage < lastUsage && curIdentifier < *curUsage)
|
||||
{
|
||||
view.dynamicallyUsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
view.dynamicallyUsed = true;
|
||||
}
|
||||
|
||||
if(view.dynamicallyUsed)
|
||||
{
|
||||
element.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
|
||||
element.lastUsedIndex = i;
|
||||
|
||||
if(element.firstUsedIndex < 0)
|
||||
element.firstUsedIndex = i;
|
||||
}
|
||||
|
||||
if(desc)
|
||||
{
|
||||
@@ -1340,6 +1404,13 @@ void D3D12Replay::SavePipelineState(uint32_t eventId)
|
||||
{
|
||||
const D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
|
||||
|
||||
D3D12MarkerRegion::Begin(m_pDevice->GetQueue(),
|
||||
StringFormat::Fmt("FetchShaderFeedback for %u", eventId));
|
||||
|
||||
FetchShaderFeedback(eventId);
|
||||
|
||||
D3D12MarkerRegion::End(m_pDevice->GetQueue());
|
||||
|
||||
D3D12Pipe::State &state = m_PipelineState;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
@@ -1464,12 +1535,12 @@ void D3D12Replay::SavePipelineState(uint32_t eventId)
|
||||
|
||||
if(pipe && pipe->IsCompute())
|
||||
{
|
||||
FillRootElements(rs.compute, mappings, state.rootElements);
|
||||
FillRootElements(eventId, rs.compute, mappings, state.rootElements);
|
||||
state.rootSignatureResourceId = rm->GetOriginalID(rs.compute.rootsig);
|
||||
}
|
||||
else if(pipe)
|
||||
{
|
||||
FillRootElements(rs.graphics, mappings, state.rootElements);
|
||||
FillRootElements(eventId, rs.graphics, mappings, state.rootElements);
|
||||
state.rootSignatureResourceId = rm->GetOriginalID(rs.graphics.rootsig);
|
||||
}
|
||||
}
|
||||
@@ -2991,6 +3062,7 @@ void D3D12Replay::ReplaceResource(ResourceId from, ResourceId to)
|
||||
RefreshDerivedReplacements();
|
||||
|
||||
ClearPostVSCache();
|
||||
ClearFeedbackCache();
|
||||
}
|
||||
|
||||
void D3D12Replay::RemoveReplacement(ResourceId id)
|
||||
@@ -3002,6 +3074,7 @@ void D3D12Replay::RemoveReplacement(ResourceId id)
|
||||
RefreshDerivedReplacements();
|
||||
|
||||
ClearPostVSCache();
|
||||
ClearFeedbackCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,27 @@ enum TexDisplayFlags
|
||||
eTexDisplay_RemapSInt = 0x40,
|
||||
};
|
||||
|
||||
struct D3D12FeedbackBindIdentifier
|
||||
{
|
||||
size_t rootEl;
|
||||
size_t rangeIndex;
|
||||
UINT descIndex;
|
||||
|
||||
bool operator<(const D3D12FeedbackBindIdentifier &o) const
|
||||
{
|
||||
if(rootEl != o.rootEl)
|
||||
return rootEl < o.rootEl;
|
||||
if(rangeIndex != o.rangeIndex)
|
||||
return rangeIndex < o.rangeIndex;
|
||||
return descIndex < o.descIndex;
|
||||
}
|
||||
|
||||
bool operator==(const D3D12FeedbackBindIdentifier &o) const
|
||||
{
|
||||
return rootEl == o.rootEl && rangeIndex == o.rangeIndex && descIndex == o.descIndex;
|
||||
}
|
||||
};
|
||||
|
||||
class D3D12Replay : public IReplayDriver
|
||||
{
|
||||
public:
|
||||
@@ -214,14 +235,16 @@ public:
|
||||
void FileChanged() {}
|
||||
AMDCounters *GetAMDCounters() { return m_pAMDCounters; }
|
||||
private:
|
||||
void FillRootElements(const D3D12RenderState::RootSignature &rootSig,
|
||||
void FillRootElements(uint32_t eventId, const D3D12RenderState::RootSignature &rootSig,
|
||||
const ShaderBindpointMapping *mappings[(uint32_t)ShaderStage::Count],
|
||||
rdcarray<D3D12Pipe::RootSignatureRange> &rootElements);
|
||||
void FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor *desc);
|
||||
|
||||
bool CreateSOBuffers();
|
||||
void ClearPostVSCache();
|
||||
|
||||
bool CreateSOBuffers();
|
||||
void FetchShaderFeedback(uint32_t eventId);
|
||||
void ClearFeedbackCache();
|
||||
|
||||
void RefreshDerivedReplacements();
|
||||
|
||||
@@ -238,6 +261,19 @@ private:
|
||||
m_OutputHeight = (float)h;
|
||||
}
|
||||
|
||||
struct D3D12DynamicShaderFeedback
|
||||
{
|
||||
bool compute = false, valid = false;
|
||||
rdcarray<D3D12FeedbackBindIdentifier> used;
|
||||
};
|
||||
|
||||
struct Feedback
|
||||
{
|
||||
ID3D12Resource *FeedbackBuffer = NULL;
|
||||
|
||||
std::unordered_map<uint32_t, D3D12DynamicShaderFeedback> Usage;
|
||||
} m_BindlessFeedback;
|
||||
|
||||
struct D3D12PostVSData
|
||||
{
|
||||
struct InstData
|
||||
|
||||
@@ -0,0 +1,604 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/settings.h"
|
||||
#include "driver/shaders/dxbc/dxbc_bytecode_editor.h"
|
||||
#include "d3d12_command_queue.h"
|
||||
#include "d3d12_debug.h"
|
||||
#include "d3d12_device.h"
|
||||
#include "d3d12_replay.h"
|
||||
#include "d3d12_resources.h"
|
||||
#include "d3d12_shader_cache.h"
|
||||
|
||||
RDOC_CONFIG(rdcstr, D3D12_Debug_FeedbackDumpDirPath, "",
|
||||
"Path to dump bindless feedback annotation generated DXBC/DXIL files.");
|
||||
RDOC_CONFIG(bool, D3D12_Experimental_BindlessFeedback, true,
|
||||
"EXPERIMENTAL: Enable fetching from GPU which descriptors were dynamically used in "
|
||||
"descriptor arrays.");
|
||||
|
||||
struct D3D12FeedbackKey
|
||||
{
|
||||
DXBCBytecode::OperandType type;
|
||||
Bindpoint bind;
|
||||
|
||||
bool operator<(const D3D12FeedbackKey &key) const
|
||||
{
|
||||
if(type != key.type)
|
||||
return type < key.type;
|
||||
return bind < key.bind;
|
||||
}
|
||||
};
|
||||
|
||||
struct D3D12FeedbackSlot
|
||||
{
|
||||
public:
|
||||
D3D12FeedbackSlot()
|
||||
{
|
||||
slot = 0;
|
||||
used = 0;
|
||||
}
|
||||
void SetSlot(uint32_t s) { slot = s; }
|
||||
void SetStaticUsed() { used = 0x1; }
|
||||
bool StaticUsed() const { return used != 0x0; }
|
||||
uint32_t Slot() const { return slot; }
|
||||
private:
|
||||
uint32_t slot : 31;
|
||||
uint32_t used : 1;
|
||||
};
|
||||
|
||||
static bool AnnotateShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
const std::map<D3D12FeedbackKey, D3D12FeedbackSlot> &slots,
|
||||
bytebuf &editedBlob)
|
||||
{
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCBytecode::Edit;
|
||||
|
||||
ProgramEditor editor(dxbc, editedBlob);
|
||||
|
||||
// get ourselves a temp
|
||||
uint32_t t = editor.AddTemp();
|
||||
|
||||
// declare the output UAV
|
||||
ResourceDecl desc;
|
||||
desc.compType = CompType::UInt;
|
||||
desc.type = TextureType::Buffer;
|
||||
desc.raw = true;
|
||||
|
||||
ResourceIdentifier u = {~0U, ~0U};
|
||||
|
||||
for(size_t i = 0; i < editor.GetNumInstructions(); i++)
|
||||
{
|
||||
const Operation &op = editor.GetInstruction(i);
|
||||
|
||||
for(const Operand &operand : op.operands)
|
||||
{
|
||||
if(operand.type != TYPE_RESOURCE && operand.type != TYPE_UNORDERED_ACCESS_VIEW)
|
||||
continue;
|
||||
|
||||
const Declaration *decl =
|
||||
editor.FindDeclaration(operand.type, (uint32_t)operand.indices[0].index);
|
||||
|
||||
if(!decl)
|
||||
{
|
||||
RDCERR("Couldn't find declaration for %d operand identifier %u", operand.type,
|
||||
(uint32_t)operand.indices[0].index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore non-arrayed declarations
|
||||
if(decl->operand.indices[1].index == decl->operand.indices[2].index)
|
||||
continue;
|
||||
|
||||
// the operand should be relative addressing like r0.x + 6 for a t6 resource being indexed
|
||||
// with [r0.x]
|
||||
RDCASSERT(operand.indices[1].relative &&
|
||||
operand.indices[1].index == decl->operand.indices[1].index);
|
||||
|
||||
D3D12FeedbackKey key;
|
||||
key.type = operand.type;
|
||||
key.bind.bindset = decl->space;
|
||||
key.bind.bind = (int32_t)decl->operand.indices[1].index;
|
||||
|
||||
auto it = slots.find(key);
|
||||
|
||||
if(it == slots.end())
|
||||
{
|
||||
RDCERR("Couldn't find reserved base slot for %d at space %u and bind %u", key.type,
|
||||
key.bind.bindset, key.bind.bind);
|
||||
continue;
|
||||
}
|
||||
|
||||
// should be getting a scalar index
|
||||
if(operand.indices[1].operand.comps[1] != 0xff ||
|
||||
operand.indices[1].operand.comps[2] != 0xff || operand.indices[1].operand.comps[3] != 0xff)
|
||||
{
|
||||
RDCERR("Unexpected vector index for resource: %s",
|
||||
operand.toString(dxbc->GetReflection(), ToString::None).c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(u.first == ~0U && u.second == ~0U)
|
||||
u = editor.DeclareUAV(desc, space, 0, 0);
|
||||
|
||||
// resource base plus index
|
||||
editor.InsertOperation(i++, oper(OPCODE_IADD, {temp(t).swizzle(0), imm(it->second.Slot()),
|
||||
operand.indices[1].operand}));
|
||||
// multiply by 4 for byte index
|
||||
editor.InsertOperation(i++,
|
||||
oper(OPCODE_ISHL, {temp(t).swizzle(0), temp(t).swizzle(0), imm(2)}));
|
||||
// atomic or the slot
|
||||
editor.InsertOperation(i++, oper(OPCODE_ATOMIC_OR, {uav(u), temp(t).swizzle(0), imm(~0U)}));
|
||||
|
||||
// only one resource operand per instruction
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(u.first != ~0U || u.second != ~0U)
|
||||
{
|
||||
editor.InsertOperation(0, oper(OPCODE_MOV, {temp(t).swizzle(0), imm(0)}));
|
||||
editor.InsertOperation(1, oper(OPCODE_ATOMIC_OR, {uav(u), temp(t).swizzle(0), imm(~0U)}));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_t space,
|
||||
uint32_t maxDescriptors,
|
||||
std::map<D3D12FeedbackKey, D3D12FeedbackSlot> &slots, uint32_t &numSlots,
|
||||
bytebuf &editedBlob, D3D12_SHADER_BYTECODE &desc)
|
||||
{
|
||||
if(!shad)
|
||||
return;
|
||||
|
||||
ShaderReflection &refl = shad->GetDetails();
|
||||
const ShaderBindpointMapping &mapping = shad->GetMapping();
|
||||
|
||||
for(const ShaderResource &ro : refl.readOnlyResources)
|
||||
{
|
||||
const Bindpoint &bind = mapping.readOnlyResources[ro.bindPoint];
|
||||
if(bind.arraySize > 1)
|
||||
{
|
||||
D3D12FeedbackKey key;
|
||||
key.type = DXBCBytecode::TYPE_RESOURCE;
|
||||
key.bind = bind;
|
||||
|
||||
slots[key].SetSlot(numSlots);
|
||||
numSlots += RDCMIN(maxDescriptors, bind.arraySize);
|
||||
}
|
||||
else if(bind.arraySize <= 1 && bind.used)
|
||||
{
|
||||
// since the eventual descriptor range iteration won't know which descriptors map to arrays
|
||||
// and which to fixed slots, it can't mark fixed descriptors as dynamically used itself. So
|
||||
// instead we don't reserve a slot and set the top bit for these binds to indicate that
|
||||
// they're fixed used. This allows for overlap between an array and a fixed resource which is
|
||||
// allowed
|
||||
D3D12FeedbackKey key;
|
||||
key.type = DXBCBytecode::TYPE_RESOURCE;
|
||||
key.bind = bind;
|
||||
|
||||
slots[key].SetStaticUsed();
|
||||
}
|
||||
}
|
||||
|
||||
for(const ShaderResource &rw : refl.readWriteResources)
|
||||
{
|
||||
const Bindpoint &bind = mapping.readWriteResources[rw.bindPoint];
|
||||
if(bind.arraySize > 1)
|
||||
{
|
||||
D3D12FeedbackKey key;
|
||||
key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
|
||||
key.bind = bind;
|
||||
|
||||
slots[key].SetSlot(numSlots);
|
||||
numSlots += RDCMIN(maxDescriptors, bind.arraySize);
|
||||
}
|
||||
else if(bind.arraySize <= 1 && bind.used)
|
||||
{
|
||||
// since the eventual descriptor range iteration won't know which descriptors map to arrays
|
||||
// and which to fixed slots, it can't mark fixed descriptors as dynamically used itself. So
|
||||
// instead we don't reserve a slot and set the top bit for these binds to indicate that
|
||||
// they're fixed used. This allows for overlap between an array and a fixed resource which is
|
||||
// allowed
|
||||
D3D12FeedbackKey key;
|
||||
key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
|
||||
key.bind = bind;
|
||||
|
||||
slots[key].SetStaticUsed();
|
||||
}
|
||||
}
|
||||
|
||||
if(shad->GetDXBC()->m_Version.Major > 6)
|
||||
{
|
||||
RDCERR("DXIL shaders are not supported for bindless feedback currently");
|
||||
}
|
||||
else
|
||||
{
|
||||
// only SM5.1 can have dynamic array indexing
|
||||
if(shad->GetDXBC()->m_Version.Major == 5 && shad->GetDXBC()->m_Version.Minor == 1)
|
||||
{
|
||||
if(AnnotateShader(shad->GetDXBC(), space, slots, editedBlob))
|
||||
{
|
||||
if(!D3D12_Debug_FeedbackDumpDirPath().empty())
|
||||
FileIO::WriteAll(D3D12_Debug_FeedbackDumpDirPath() + "/before_dxbc_" +
|
||||
ToStr(shad->GetDetails().stage).c_str() + ".dxbc",
|
||||
shad->GetDXBC()->GetShaderBlob());
|
||||
|
||||
if(!D3D12_Debug_FeedbackDumpDirPath().empty())
|
||||
FileIO::WriteAll(D3D12_Debug_FeedbackDumpDirPath() + "/after_dxbc_" +
|
||||
ToStr(shad->GetDetails().stage).c_str() + ".dxbc",
|
||||
editedBlob);
|
||||
|
||||
desc.pShaderBytecode = editedBlob.data();
|
||||
desc.BytecodeLength = editedBlob.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12Replay::FetchShaderFeedback(uint32_t eventId)
|
||||
{
|
||||
if(m_BindlessFeedback.Usage.find(eventId) != m_BindlessFeedback.Usage.end())
|
||||
return;
|
||||
|
||||
if(!D3D12_Experimental_BindlessFeedback())
|
||||
return;
|
||||
|
||||
// create it here so we won't re-run any code if the event is re-selected. We'll mark it as valid
|
||||
// if it actually has any data in it later.
|
||||
D3D12DynamicShaderFeedback &result = m_BindlessFeedback.Usage[eventId];
|
||||
|
||||
const ActionDescription *action = m_pDevice->GetAction(eventId);
|
||||
|
||||
if(action == NULL || !(action->flags & (ActionFlags::Dispatch | ActionFlags::Drawcall)))
|
||||
return;
|
||||
|
||||
result.compute = bool(action->flags & ActionFlags::Dispatch);
|
||||
|
||||
D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
|
||||
|
||||
D3D12ResourceManager *rm = m_pDevice->GetResourceManager();
|
||||
|
||||
WrappedID3D12PipelineState *pipe =
|
||||
(WrappedID3D12PipelineState *)rm->GetCurrentAs<ID3D12PipelineState>(rs.pipe);
|
||||
D3D12RootSignature modsig;
|
||||
|
||||
bytebuf editedBlob[5];
|
||||
|
||||
D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc;
|
||||
pipe->Fill(pipeDesc);
|
||||
|
||||
uint32_t space = 1;
|
||||
|
||||
uint32_t maxDescriptors = 0;
|
||||
for(ResourceId id : rs.heaps)
|
||||
{
|
||||
D3D12_DESCRIPTOR_HEAP_DESC desc = rm->GetCurrentAs<ID3D12DescriptorHeap>(id)->GetDesc();
|
||||
|
||||
if(desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
|
||||
{
|
||||
maxDescriptors = desc.NumDescriptors;
|
||||
RDCDEBUG("Clamping any unbounded ranges to %u descriptors", maxDescriptors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<D3D12FeedbackKey, D3D12FeedbackSlot> slots[6];
|
||||
|
||||
// reserve the first 4 dwords for debug info and a validity flag
|
||||
uint32_t numSlots = 4;
|
||||
|
||||
if(result.compute)
|
||||
{
|
||||
ID3D12RootSignature *sig = rm->GetCurrentAs<ID3D12RootSignature>(rs.compute.rootsig);
|
||||
modsig = ((WrappedID3D12RootSignature *)sig)->sig;
|
||||
|
||||
space = modsig.maxSpaceIndex;
|
||||
|
||||
AddArraySlots(pipe->CS(), space, maxDescriptors, slots[0], numSlots, editedBlob[0], pipeDesc.CS);
|
||||
}
|
||||
else
|
||||
{
|
||||
ID3D12RootSignature *sig = rm->GetCurrentAs<ID3D12RootSignature>(rs.graphics.rootsig);
|
||||
modsig = ((WrappedID3D12RootSignature *)sig)->sig;
|
||||
|
||||
space = modsig.maxSpaceIndex;
|
||||
|
||||
AddArraySlots(pipe->VS(), space, maxDescriptors, slots[0], numSlots, editedBlob[0], pipeDesc.VS);
|
||||
AddArraySlots(pipe->HS(), space, maxDescriptors, slots[1], numSlots, editedBlob[1], pipeDesc.HS);
|
||||
AddArraySlots(pipe->DS(), space, maxDescriptors, slots[2], numSlots, editedBlob[2], pipeDesc.DS);
|
||||
AddArraySlots(pipe->GS(), space, maxDescriptors, slots[3], numSlots, editedBlob[3], pipeDesc.GS);
|
||||
AddArraySlots(pipe->PS(), space, maxDescriptors, slots[4], numSlots, editedBlob[4], pipeDesc.PS);
|
||||
}
|
||||
|
||||
// if numSlots was 0, none of the resources were arrayed so we have nothing to do. Silently return
|
||||
if(numSlots == 0)
|
||||
return;
|
||||
|
||||
// need to be able to add a descriptor of our UAV without hitting the 64 DWORD limit
|
||||
if(modsig.dwordLength > 62)
|
||||
{
|
||||
RDCWARN("Root signature is 64 DWORDS, adding feedback buffer might fail");
|
||||
}
|
||||
|
||||
// add root UAV element
|
||||
modsig.Parameters.push_back(D3D12RootSignatureParameter());
|
||||
{
|
||||
D3D12RootSignatureParameter ¶m = modsig.Parameters.back();
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
|
||||
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE;
|
||||
param.Descriptor.RegisterSpace = space;
|
||||
param.Descriptor.ShaderRegister = 0;
|
||||
}
|
||||
|
||||
if(m_BindlessFeedback.FeedbackBuffer == NULL ||
|
||||
m_BindlessFeedback.FeedbackBuffer->GetDesc().Width < numSlots * sizeof(uint32_t))
|
||||
{
|
||||
SAFE_RELEASE(m_BindlessFeedback.FeedbackBuffer);
|
||||
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Alignment = 0;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.Height = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.MipLevels = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Width = numSlots * sizeof(uint32_t);
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps;
|
||||
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProps.CreationNodeMask = 1;
|
||||
heapProps.VisibleNodeMask = 1;
|
||||
|
||||
HRESULT hr = m_pDevice->CreateCommittedResource(
|
||||
&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL,
|
||||
__uuidof(ID3D12Resource), (void **)&m_BindlessFeedback.FeedbackBuffer);
|
||||
|
||||
if(m_BindlessFeedback.FeedbackBuffer == NULL || FAILED(hr))
|
||||
{
|
||||
RDCERR("Couldn't create feedback buffer with %u slots: %s", numSlots, ToStr(hr).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
m_BindlessFeedback.FeedbackBuffer->SetName(L"m_BindlessFeedback.FeedbackBuffer");
|
||||
}
|
||||
|
||||
{
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
|
||||
uavDesc.Format = DXGI_FORMAT_R32_UINT;
|
||||
uavDesc.Buffer.CounterOffsetInBytes = 0;
|
||||
// start with elements after the counter
|
||||
uavDesc.Buffer.FirstElement = 0;
|
||||
uavDesc.Buffer.NumElements = numSlots;
|
||||
uavDesc.Buffer.StructureByteStride = 0;
|
||||
|
||||
m_pDevice->CreateUnorderedAccessView(m_BindlessFeedback.FeedbackBuffer, NULL, &uavDesc,
|
||||
GetDebugManager()->GetCPUHandle(FEEDBACK_CLEAR_UAV));
|
||||
m_pDevice->CreateUnorderedAccessView(m_BindlessFeedback.FeedbackBuffer, NULL, &uavDesc,
|
||||
GetDebugManager()->GetUAVClearHandle(FEEDBACK_CLEAR_UAV));
|
||||
|
||||
ID3D12GraphicsCommandList *list = m_pDevice->GetNewList();
|
||||
|
||||
UINT zeroes[4] = {0, 0, 0, 0};
|
||||
list->ClearUnorderedAccessViewUint(GetDebugManager()->GetGPUHandle(FEEDBACK_CLEAR_UAV),
|
||||
GetDebugManager()->GetUAVClearHandle(FEEDBACK_CLEAR_UAV),
|
||||
m_BindlessFeedback.FeedbackBuffer, zeroes, 0, NULL);
|
||||
|
||||
list->Close();
|
||||
}
|
||||
|
||||
ID3D12RootSignature *annotatedSig = NULL;
|
||||
|
||||
{
|
||||
ID3DBlob *root = m_pDevice->GetShaderCache()->MakeRootSig(modsig);
|
||||
HRESULT hr =
|
||||
m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(),
|
||||
__uuidof(ID3D12RootSignature), (void **)&annotatedSig);
|
||||
|
||||
if(annotatedSig == NULL || FAILED(hr))
|
||||
{
|
||||
RDCERR("Couldn't create feedback modified root signature: %s", ToStr(hr).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ID3D12PipelineState *annotatedPipe = NULL;
|
||||
|
||||
{
|
||||
pipeDesc.pRootSignature = annotatedSig;
|
||||
|
||||
HRESULT hr = m_pDevice->CreatePipeState(pipeDesc, &annotatedPipe);
|
||||
if(annotatedPipe == NULL || FAILED(hr))
|
||||
{
|
||||
SAFE_RELEASE(annotatedSig);
|
||||
RDCERR("Couldn't create feedback modified pipeline: %s", ToStr(hr).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12RenderState prev = rs;
|
||||
|
||||
rs.pipe = GetResID(annotatedPipe);
|
||||
|
||||
if(result.compute)
|
||||
{
|
||||
rs.compute.rootsig = GetResID(annotatedSig);
|
||||
size_t idx = modsig.Parameters.size() - 1;
|
||||
rs.compute.sigelems.resize_for_index(idx);
|
||||
rs.compute.sigelems[idx] =
|
||||
D3D12RenderState::SignatureElement(eRootUAV, GetResID(m_BindlessFeedback.FeedbackBuffer), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.graphics.rootsig = GetResID(annotatedSig);
|
||||
size_t idx = modsig.Parameters.size() - 1;
|
||||
rs.graphics.sigelems.resize_for_index(idx);
|
||||
rs.graphics.sigelems[idx] =
|
||||
D3D12RenderState::SignatureElement(eRootUAV, GetResID(m_BindlessFeedback.FeedbackBuffer), 0);
|
||||
}
|
||||
|
||||
m_pDevice->ReplayLog(0, eventId, eReplay_OnlyDraw);
|
||||
|
||||
m_pDevice->ExecuteLists();
|
||||
m_pDevice->FlushLists();
|
||||
|
||||
SAFE_RELEASE(annotatedPipe);
|
||||
SAFE_RELEASE(annotatedSig);
|
||||
|
||||
rs = prev;
|
||||
|
||||
bytebuf results;
|
||||
GetDebugManager()->GetBufferData(m_BindlessFeedback.FeedbackBuffer, 0, 0, results);
|
||||
|
||||
if(results.size() < numSlots * sizeof(uint32_t))
|
||||
{
|
||||
RDCERR("Results buffer not the right size!");
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t *slotsData = (uint32_t *)results.data();
|
||||
|
||||
result.valid = true;
|
||||
|
||||
// now we iterate over descriptor ranges and find which (of potentially multiple) registers each
|
||||
// descriptor maps to and store the index if it's dynamically or statically used. We do this
|
||||
// here so it only happens once instead of doing it when looking up the data.
|
||||
|
||||
D3D12FeedbackKey curKey;
|
||||
D3D12FeedbackBindIdentifier curIdentifier = {};
|
||||
// don't iterate the last signature element because that's ours!
|
||||
for(size_t rootEl = 0; rootEl < modsig.Parameters.size() - 1; rootEl++)
|
||||
{
|
||||
curIdentifier.rootEl = rootEl;
|
||||
|
||||
const D3D12RootSignatureParameter &p = modsig.Parameters[rootEl];
|
||||
|
||||
// only tables need feedback data, others all are treated as dynamically used
|
||||
if(p.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
|
||||
{
|
||||
for(size_t r = 0; r < p.ranges.size(); r++)
|
||||
{
|
||||
const D3D12_DESCRIPTOR_RANGE1 &range = p.ranges[r];
|
||||
|
||||
curIdentifier.rangeIndex = r;
|
||||
|
||||
curKey.bind.bindset = range.RegisterSpace;
|
||||
curKey.bind.bind = range.BaseShaderRegister;
|
||||
|
||||
UINT num = range.NumDescriptors;
|
||||
uint32_t visMask = 0;
|
||||
// see which shader's binds we should look up for this range
|
||||
switch(p.ShaderVisibility)
|
||||
{
|
||||
case D3D12_SHADER_VISIBILITY_ALL: visMask = result.compute ? 0x1 : 0xff; break;
|
||||
case D3D12_SHADER_VISIBILITY_VERTEX: visMask = 1 << 0; break;
|
||||
case D3D12_SHADER_VISIBILITY_HULL: visMask = 1 << 1; break;
|
||||
case D3D12_SHADER_VISIBILITY_DOMAIN: visMask = 1 << 2; break;
|
||||
case D3D12_SHADER_VISIBILITY_GEOMETRY: visMask = 1 << 3; break;
|
||||
case D3D12_SHADER_VISIBILITY_PIXEL: visMask = 1 << 4; break;
|
||||
default: RDCERR("Unexpected shader visibility %d", p.ShaderVisibility); return;
|
||||
}
|
||||
|
||||
// set the key type
|
||||
if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV)
|
||||
curKey.type = DXBCBytecode::TYPE_RESOURCE;
|
||||
else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)
|
||||
curKey.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
|
||||
|
||||
for(uint32_t st = 0; st < 5; st++)
|
||||
{
|
||||
if(visMask & (1 << st))
|
||||
{
|
||||
// the feedback entries start here
|
||||
auto slotIt = slots[st].lower_bound(curKey);
|
||||
|
||||
curIdentifier.descIndex = 0;
|
||||
|
||||
// iterate over the declared range. This could be unbounded, so we might exit
|
||||
// another way
|
||||
for(uint32_t i = 0; i < num; i++)
|
||||
{
|
||||
// stop when we've run out of recorded used slots
|
||||
if(slotIt == slots[st].end())
|
||||
break;
|
||||
|
||||
Bindpoint bind = slotIt->first.bind;
|
||||
|
||||
// stop if the next used slot is in another space or is another type
|
||||
if(bind.bindset > curKey.bind.bindset || slotIt->first.type != curKey.type)
|
||||
break;
|
||||
|
||||
// if the next bind is definitely outside this range, early out now instead of
|
||||
// iterating fruitlessly
|
||||
if((uint32_t)bind.bind > range.BaseShaderRegister + num)
|
||||
break;
|
||||
|
||||
int32_t lastBind = bind.bind + (int32_t)RDCCLAMP(bind.arraySize, 1U, maxDescriptors);
|
||||
|
||||
// if this slot's array covers the current bind, check the result
|
||||
if(bind.bind <= curKey.bind.bind && curKey.bind.bind < lastBind)
|
||||
{
|
||||
// if it's static used by having a fixed result declared, it's used
|
||||
const bool staticUsed = slotIt->second.StaticUsed();
|
||||
|
||||
// otherwise check the feedback we got
|
||||
const uint32_t baseSlot = slotIt->second.Slot();
|
||||
const uint32_t arrayIndex = curKey.bind.bind - bind.bind;
|
||||
|
||||
if(staticUsed || slotsData[baseSlot + arrayIndex])
|
||||
result.used.push_back(curIdentifier);
|
||||
}
|
||||
|
||||
curKey.bind.bind++;
|
||||
curIdentifier.descIndex++;
|
||||
|
||||
// if we've passed this slot, move to the next one. Because we're iterating a
|
||||
// contiguous range of binds the next slot will be enough for the next iteration
|
||||
if(curKey.bind.bind >= lastBind)
|
||||
slotIt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replay from the start as we may have corrupted state while fetching the above feedback.
|
||||
m_pDevice->ReplayLog(0, eventId, eReplay_Full);
|
||||
}
|
||||
|
||||
void D3D12Replay::ClearFeedbackCache()
|
||||
{
|
||||
m_BindlessFeedback.Usage.clear();
|
||||
}
|
||||
@@ -140,6 +140,7 @@
|
||||
<ClCompile Include="d3d12_serialise.cpp" />
|
||||
<ClCompile Include="d3d12_shaderdebug.cpp" />
|
||||
<ClCompile Include="d3d12_shader_cache.cpp" />
|
||||
<ClCompile Include="d3d12_shader_feedback.cpp" />
|
||||
<ClCompile Include="d3d12_state.cpp" />
|
||||
<ClCompile Include="d3d12_stringise.cpp" />
|
||||
<ClCompile Include="precompiled.cpp">
|
||||
|
||||
@@ -75,7 +75,9 @@
|
||||
<ClInclude Include="..\dx\official\D3D11On12On7.h">
|
||||
<Filter>official</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="d3d12_hooks.h" />
|
||||
<ClInclude Include="d3d12_hooks.h">
|
||||
<Filter>Hooks</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="d3d12_common.cpp">
|
||||
@@ -204,5 +206,8 @@
|
||||
<ClCompile Include="d3d12_sdk_select.cpp">
|
||||
<Filter>Util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="d3d12_shader_feedback.cpp">
|
||||
<Filter>Replay</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -128,13 +128,13 @@ Operation oper(OpcodeType o, const rdcarray<Operand> &operands);
|
||||
|
||||
struct ResourceDecl
|
||||
{
|
||||
TextureType type;
|
||||
CompType compType;
|
||||
uint32_t sampleCount;
|
||||
TextureType type = TextureType::Buffer;
|
||||
CompType compType = CompType::Float;
|
||||
uint32_t sampleCount = 0;
|
||||
|
||||
bool structured = false;
|
||||
uint32_t stride;
|
||||
bool hasCounter, globallyCoherant, rov;
|
||||
uint32_t stride = 0;
|
||||
bool hasCounter = false, globallyCoherant = false, rov = false;
|
||||
|
||||
bool raw = false;
|
||||
};
|
||||
|
||||
@@ -1623,7 +1623,7 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
|
||||
&state.graphics.descSets, &state.compute.descSets,
|
||||
};
|
||||
|
||||
const DynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
|
||||
const VKDynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
|
||||
|
||||
m_VulkanPipelineState.shaderMessages = usage.messages;
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ struct VulkanPostVSData
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicShaderFeedback
|
||||
struct VKDynamicShaderFeedback
|
||||
{
|
||||
bool compute = false, valid = false;
|
||||
rdcarray<BindpointIndex> used;
|
||||
@@ -744,7 +744,7 @@ private:
|
||||
|
||||
GPUBuffer FeedbackBuffer;
|
||||
|
||||
std::map<uint32_t, DynamicShaderFeedback> Usage;
|
||||
std::map<uint32_t, VKDynamicShaderFeedback> Usage;
|
||||
} m_BindlessFeedback;
|
||||
|
||||
ShaderDebugData m_ShaderDebugData;
|
||||
|
||||
@@ -1310,7 +1310,7 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId)
|
||||
|
||||
// create it here so we won't re-run any code if the event is re-selected. We'll mark it as valid
|
||||
// if it actually has any data in it later.
|
||||
DynamicShaderFeedback &result = m_BindlessFeedback.Usage[eventId];
|
||||
VKDynamicShaderFeedback &result = m_BindlessFeedback.Usage[eventId];
|
||||
|
||||
bool useBufferAddress = (m_pDriver->GetExtensions(NULL).ext_KHR_buffer_device_address ||
|
||||
m_pDriver->GetExtensions(NULL).ext_EXT_buffer_device_address) &&
|
||||
|
||||
@@ -441,7 +441,7 @@ void DoSerialise(SerialiserType &ser, TextureSwizzle4 &el)
|
||||
SERIALISE_MEMBER(blue);
|
||||
SERIALISE_MEMBER(alpha);
|
||||
|
||||
SIZE_CHECK(16);
|
||||
SIZE_CHECK(4);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1374,11 +1374,14 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::RootSignatureRange &el)
|
||||
SERIALISE_MEMBER(type);
|
||||
SERIALISE_MEMBER(visibility);
|
||||
SERIALISE_MEMBER(registerSpace);
|
||||
SERIALISE_MEMBER(dynamicallyUsedCount);
|
||||
SERIALISE_MEMBER(firstUsedIndex);
|
||||
SERIALISE_MEMBER(lastUsedIndex);
|
||||
SERIALISE_MEMBER(constantBuffers);
|
||||
SERIALISE_MEMBER(samplers);
|
||||
SERIALISE_MEMBER(views);
|
||||
|
||||
SIZE_CHECK(96);
|
||||
SIZE_CHECK(104);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1391,6 +1394,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::View &el)
|
||||
SERIALISE_MEMBER(viewFormat);
|
||||
|
||||
SERIALISE_MEMBER(swizzle);
|
||||
SERIALISE_MEMBER(dynamicallyUsed);
|
||||
SERIALISE_MEMBER(bufferFlags);
|
||||
SERIALISE_MEMBER(bufferStructCount);
|
||||
SERIALISE_MEMBER(elementByteSize);
|
||||
@@ -1407,7 +1411,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::View &el)
|
||||
|
||||
SERIALISE_MEMBER(minLODClamp);
|
||||
|
||||
SIZE_CHECK(112);
|
||||
SIZE_CHECK(96);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1547,7 +1551,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::OM &el)
|
||||
SERIALISE_MEMBER(multiSampleCount);
|
||||
SERIALISE_MEMBER(multiSampleQuality);
|
||||
|
||||
SIZE_CHECK(280);
|
||||
SIZE_CHECK(264);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1591,7 +1595,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el)
|
||||
|
||||
SERIALISE_MEMBER(resourceStates);
|
||||
|
||||
SIZE_CHECK(1432);
|
||||
SIZE_CHECK(1416);
|
||||
}
|
||||
|
||||
#pragma endregion D3D12 pipeline state
|
||||
@@ -1678,7 +1682,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::Texture &el)
|
||||
SERIALISE_MEMBER(depthReadChannel);
|
||||
SERIALISE_MEMBER(completeStatus);
|
||||
|
||||
SIZE_CHECK(64);
|
||||
SIZE_CHECK(56);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1811,7 +1815,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::Attachment &el)
|
||||
SERIALISE_MEMBER(mipLevel);
|
||||
SERIALISE_MEMBER(swizzle);
|
||||
|
||||
SIZE_CHECK(40);
|
||||
SIZE_CHECK(24);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1824,7 +1828,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::FBO &el)
|
||||
SERIALISE_MEMBER(drawBuffers);
|
||||
SERIALISE_MEMBER(readBuffer);
|
||||
|
||||
SIZE_CHECK(144);
|
||||
SIZE_CHECK(112);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1845,7 +1849,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::FrameBuffer &el)
|
||||
SERIALISE_MEMBER(readFBO);
|
||||
SERIALISE_MEMBER(blendState);
|
||||
|
||||
SIZE_CHECK(336);
|
||||
SIZE_CHECK(272);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -1894,7 +1898,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::State &el)
|
||||
|
||||
SERIALISE_MEMBER(hints);
|
||||
|
||||
SIZE_CHECK(2024);
|
||||
SIZE_CHECK(1960);
|
||||
}
|
||||
|
||||
#pragma endregion OpenGL pipeline state
|
||||
@@ -1942,7 +1946,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::BindingElement &el)
|
||||
SERIALISE_MEMBER(chromaFilter);
|
||||
SERIALISE_MEMBER(forceExplicitReconstruction);
|
||||
|
||||
SIZE_CHECK(200);
|
||||
SIZE_CHECK(184);
|
||||
};
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -2223,7 +2227,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::Attachment &el)
|
||||
SERIALISE_MEMBER(numMips);
|
||||
SERIALISE_MEMBER(numSlices);
|
||||
|
||||
SIZE_CHECK(56);
|
||||
SIZE_CHECK(48);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
|
||||
Reference in New Issue
Block a user