Implement bindless feedback for samplers on D3D12. Closes #3387

This commit is contained in:
baldurk committed 2024-07-22 16:17:10 +01:00
1 parent 97c072fa79
commit 7db141c77b
4 files changed
+125 -32

No files matched your search

@@ -112,7 +112,8 @@ static bool AnnotateDXBCShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
for(const Operand &operand : op.operands)
{
if(operand.type != TYPE_RESOURCE && operand.type != TYPE_UNORDERED_ACCESS_VIEW)
if(operand.type != TYPE_RESOURCE && operand.type != TYPE_UNORDERED_ACCESS_VIEW &&
operand.type != TYPE_SAMPLER)
continue;
const Declaration *decl =
@@ -187,9 +188,6 @@ static bool AnnotateDXBCShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
// atomic or the slot
editor.InsertOperation(
i++, oper(OPCODE_ATOMIC_OR, {uav(u), temp(t).swizzle(0), imm(magicFeedbackValue)}));
// only one resource operand per instruction
break;
}
}
@@ -249,10 +247,10 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
Attribute::NoUnwind | Attribute::ReadNone);
// while we're iterating through the metadata to add our UAV, we'll also note the shader-local
// register IDs of each SRV/UAV with slots, and record the base slot in this array for easy access
// later when annotating dx.op.createHandle calls. We also need to know the base register because
// the index dxc provides is register-relative
rdcarray<rdcpair<uint32_t, uint32_t>> srvBaseSlots, uavBaseSlots;
// register IDs of each SRV/UAV/Sampler with slots, and record the base slot in this array for
// easy access later when annotating dx.op.createHandle calls. We also need to know the base
// register because the index dxc provides is register-relative
rdcarray<rdcpair<uint32_t, uint32_t>> srvBaseSlots, uavBaseSlots, sampBaseSlots;
// declare the resource, this happens purely in metadata but we need to store the slot
uint32_t regSlot = 0;
@@ -272,6 +270,7 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
Metadata *srvs = reslist->children[0];
Metadata *uavs = reslist->children[1];
Metadata *samps = reslist->children[3];
// if there isn't a UAV list, create an empty one so we can add our own
if(!uavs)
uavs = reslist->children[1] = editor.CreateMetadata();
@@ -329,6 +328,56 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
srvBaseSlots[id] = {feedbackSlot, key.bind};
}
key.type = DXBCBytecode::TYPE_SAMPLER;
for(size_t i = 0; samps && i < samps->children.size(); i++)
{
// each Sampler child should have a fixed format
const Metadata *samp = samps->children[i];
const Constant *slot = cast<Constant>(samp->children[(size_t)ResField::ID]->value);
const Constant *sampSpace = cast<Constant>(samp->children[(size_t)ResField::Space]->value);
const Constant *reg = cast<Constant>(samp->children[(size_t)ResField::RegBase]->value);
if(!slot)
{
RDCWARN("Unexpected non-constant slot ID in sampler");
continue;
}
if(!sampSpace)
{
RDCWARN("Unexpected non-constant register space in sampler");
continue;
}
if(!reg)
{
RDCWARN("Unexpected non-constant register base in sampler");
continue;
}
uint32_t id = slot->getU32();
key.space = sampSpace->getU32();
key.bind = reg->getU32();
// ensure every valid ID has an index, even if it's 0
sampBaseSlots.resize_for_index(id);
auto it = slots.find(key);
// not annotated
if(it == slots.end())
continue;
uint32_t feedbackSlot = it->second.slot;
// we assume all feedback slots are non-zero, so that a 0 base slot can be used as an
// identifier for 'this resource isn't annotated'
RDCASSERT(feedbackSlot > 0);
sampBaseSlots[id] = {feedbackSlot, key.bind};
}
key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
for(size_t i = 0; i < uavs->children.size(); i++)
@@ -616,6 +665,8 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
slotInfo = srvBaseSlots[id];
else if(kind == HandleKind::UAV && id < uavBaseSlots.size())
slotInfo = uavBaseSlots[id];
else if(kind == HandleKind::Sampler && id < sampBaseSlots.size())
slotInfo = sampBaseSlots[id];
}
else
{
@@ -658,10 +709,14 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
}
HandleKind kind = (HandleKind)kindArg->getU32();
if(kind != HandleKind::SRV && kind != HandleKind::UAV)
if(kind == HandleKind::SRV)
key.type = DXBCBytecode::TYPE_RESOURCE;
else if(kind == HandleKind::UAV)
key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW;
else if(kind == HandleKind::Sampler)
key.type = DXBCBytecode::TYPE_SAMPLER;
else
continue;
key.type = kind == HandleKind::UAV ? DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW
: DXBCBytecode::TYPE_RESOURCE;
key.space = spaceArg->getU32();
key.bind = regArg->getU32();
}
@@ -942,6 +997,32 @@ static bool AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_
}
}
for(size_t i = 0; i < refl.samplers.size(); i++)
{
const ShaderSampler &s = refl.samplers[i];
if(s.bindArraySize > 1)
{
D3D12FeedbackKey key;
key.stage = refl.stage;
key.type = DXBCBytecode::TYPE_SAMPLER;
key.space = s.fixedBindSetOrSpace;
key.bind = s.fixedBindNumber;
DescriptorAccess access;
access.stage = refl.stage;
access.type = DescriptorType::Sampler;
access.index = i & 0xffff;
// descriptor storage side will be calculated later when finalising this with the root
// signature information.
slots[key].numDescriptors = RDCMIN(maxDescriptors, s.bindArraySize);
slots[key].access = access;
slots[key].slot = numSlots;
numSlots += slots[key].numDescriptors;
dynamicUsed = true;
}
}
for(size_t i = 0; i < refl.readWriteResources.size(); i++)
{
const ShaderResource &rw = refl.readWriteResources[i];
@@ -1504,7 +1585,7 @@ bool D3D12Replay::FetchShaderFeedback(uint32_t eventId)
D3D12_DESCRIPTOR_RANGE_TYPE rangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
if(IsConstantBlockDescriptor(access.type))
rangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
else if(IsConstantBlockDescriptor(access.type))
else if(IsSamplerDescriptor(access.type))
rangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
else if(IsReadOnlyDescriptor(access.type))
rangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
@@ -90,6 +90,8 @@ Texture2D<float4> texArray1[32] : register(t0, space1);
Texture2D<float4> texArray2[32] : register(t40, space1);
Texture2D<float4> texArray3[32] : register(t80, space1);
SamplerState sArray1[32] : register(s0, space1);
struct alias1
{
float4 Color;
@@ -126,9 +128,9 @@ float4 main(v2f IN) : SV_Target0
if(t.tex == 0)
{
ret *= texArray1[t.binding].SampleLevel(s, uv.xy, 0);
ret *= texArray1[t.binding+1].SampleLevel(s, uv.xy, 0);
ret *= texArray1[t.binding+2].SampleLevel(s, uv.xy, 0);
ret *= texArray1[t.binding].SampleLevel(sArray1[t.binding], uv.xy, 0);
ret *= texArray1[t.binding+1].SampleLevel(sArray1[t.binding+1], uv.xy, 0);
ret *= texArray1[t.binding+2].SampleLevel(sArray1[t.binding+2], uv.xy, 0);
}
else if(t.tex == 1)
{
@@ -219,9 +221,9 @@ float4 main(v2f IN) : SV_Target0
}
else if(t.tex == 1)
{
SamplerState s1 = SamplerDescriptorHeap[4];
SamplerState s2 = SamplerDescriptorHeap[5];
SamplerState s3 = SamplerDescriptorHeap[6];
SamplerState s1 = SamplerDescriptorHeap[19];
SamplerState s2 = SamplerDescriptorHeap[20];
SamplerState s3 = SamplerDescriptorHeap[21];
Texture2D<float4> tex1 = ResourceDescriptorHeap[40+t.binding];
Texture2D<float4> tex2 = ResourceDescriptorHeap[40+t.binding+10];
ConstantBuffer<CBuffer> cbv = ResourceDescriptorHeap[9];
@@ -232,7 +234,7 @@ float4 main(v2f IN) : SV_Target0
}
else if(t.tex == 2)
{
SamplerState s = SamplerDescriptorHeap[7];
SamplerState s = SamplerDescriptorHeap[25];
Texture2D<float4> tex = ResourceDescriptorHeap[80+t.binding];
ret *= tex.SampleLevel(s, uv.xy, 0);
}
@@ -302,6 +304,7 @@ float4 main(v2f IN) : SV_Target0
tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 20, 0),
tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 150, 0),
tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 15, 32, 150),
tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0, 32, 0),
},
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &staticSamp);
ID3D12PipelineStatePtr graphicspso[4];
@@ -479,7 +482,7 @@ float4 main(v2f IN) : SV_Target0
D3D12_TEXTURE_ADDRESS_MODE_WRAP;
UINT increment = dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
D3D12_CPU_DESCRIPTOR_HANDLE samplerStart = m_Sampler->GetCPUDescriptorHandleForHeapStart();
for(int i = 0; i < 8; ++i)
for(int i = 0; i < 128; ++i)
dev->CreateSampler(&samplerDesc, {samplerStart.ptr + increment * i});
while(Running())
@@ -525,6 +528,7 @@ float4 main(v2f IN) : SV_Target0
{
cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart());
cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart());
cmd->SetGraphicsRootDescriptorTable(3, m_Sampler->GetGPUDescriptorHandleForHeapStart());
}
RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});
+1 -1
View File
@@ -499,7 +499,7 @@ void D3D12GraphicsTest::PostDeviceCreate()
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
desc.NumDescriptors = 8;
desc.NumDescriptors = 128;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_Sampler));
@@ -77,6 +77,10 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase):
'loc': (1, 80),
'elems': [19, 23]
},
(rd.DescriptorCategory.Sampler, 1): {
'loc': (1, 0),
'elems': [19, 20, 21]
},
}
ro = pipe.GetReadOnlyResources(rd.ShaderStage.Pixel)
@@ -84,23 +88,26 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase):
rw = pipe.GetReadWriteResources(rd.ShaderStage.Pixel)
self.check_eq(len(ro), 2 + 8)
self.check_eq(len(samp), 1)
self.check_eq(len(samp), 1 + 3)
self.check_eq(len(rw), 0)
refl = pipe.GetShaderReflection(rd.ShaderStage.Pixel)
for a in samp + ro:
idx = (rd.CategoryForDescriptorType(a.access.type), a.access.index)
if a.access.type == rd.DescriptorType.Sampler: # static sampler
if a.access.type == rd.DescriptorType.Sampler and a.access.index == 0: # static sampler
# descriptor store should not be a heap, but we don't verify exactly where it comes from
self.check(a.access.descriptorStore not in d3d12pipe.descriptorHeaps)
self.check_eq(a.access.index, 0)
continue
heapName = "ResourceDescriptorHeap"
if rd.IsReadOnlyDescriptor(a.access.type):
res = refl.readOnlyResources[a.access.index]
elif rd.IsReadWriteDescriptor(a.access.type):
res = refl.readOnlyResources[a.access.index]
else:
res = refl.readWriteResources[a.access.index]
res = refl.samplers[a.access.index]
heapName = "SamplerDescriptorHeap"
if idx not in bind_info.keys():
raise rdtest.TestFailureException(
@@ -121,12 +128,13 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase):
loc = self.controller.GetDescriptorLocations(a.access.descriptorStore,
[rd.DescriptorRange(a.access)])[0]
if loc.fixedBindNumber != bind_info[idx]['loc'][1] + a.access.arrayElement:
raise rdtest.TestFailureException("Location {} not expected for space,reg {} array element {}".format(
loc.fixedBindNumber, bind_info[idx]['loc'], a.access.arrayElement))
if loc.logicalBindName != "ResourceDescriptorHeap[{}]".format(bind_info[idx]['loc'][1] +
a.access.arrayElement):
raise rdtest.TestFailureException("Location {} not expected for space,reg {} array element {}".format(
loc.logicalBindName, bind_info[idx]['loc'], a.access.arrayElement))
raise rdtest.TestFailureException(
"Location {} not expected for {} at space,reg {} array element {}".format(
loc.fixedBindNumber, str(a.access.type), bind_info[idx]['loc'], a.access.arrayElement))
if loc.logicalBindName != "{}[{}]".format(heapName, bind_info[idx]['loc'][1] + a.access.arrayElement):
raise rdtest.TestFailureException(
"Location {} not expected for space,reg {} array element {}".format(
loc.logicalBindName, bind_info[idx]['loc'], a.access.arrayElement))
bind_info[idx]['elems'].remove(a.access.arrayElement)
@@ -152,7 +160,7 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase):
# - UAV resources
bind_info = {
rd.DescriptorCategory.ConstantBlock: [9],
rd.DescriptorCategory.Sampler: [0, 1, 2, 4, 5, 6, 7],
rd.DescriptorCategory.Sampler: [0, 1, 2, 19, 20, 21, 25],
rd.DescriptorCategory.ReadOnlyResource: [8, 12, 19, 20, 21, 49, 59, 6, 99, 103, 156, 162],
rd.DescriptorCategory.ReadWriteResource: [10],
}