Implement bindless feedback for DXBC shaders on D3D12 with patching

This commit is contained in:
baldurk
2021-07-15 16:41:23 +01:00
parent 95ea12b0e5
commit 46266a9dd6
18 changed files with 904 additions and 68 deletions
+40 -2
View File
@@ -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]
+42 -4
View File
@@ -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;
+2 -2
View File
@@ -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,
+5
View File
@@ -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))