Files
renderdoc/util/test/tests/D3D12/D3D12_Descriptor_Indexing.py
T

252 lines
12 KiB
Python

from typing import Any, Dict, List, Tuple
import rdtest
import renderdoc as rd
class D3D12_Descriptor_Indexing(rdtest.TestCase):
demos_test_name = 'D3D12_Descriptor_Indexing'
def check_compute(self, eventId: int):
action = self.find_action("Dispatch", eventId)
assert action is not None
self.set_event(action.eventId, False)
pipe = self.controller.GetPipelineState()
d3d12pipe = self.controller.GetD3D12PipelineState()
rw = pipe.GetReadWriteResources(rd.ShaderStage.Compute)
self.check_eq(len(rw), 1)
self.check_eq(rw[0].access.index, 0)
self.check_eq(rw[0].access.stage, rd.ShaderStage.Compute)
self.check_eq(rw[0].access.type, rd.DescriptorType.ReadWriteBuffer)
self.check_eq(rw[0].access.staticallyUnused, False)
self.check_eq(rw[0].access.arrayElement, 15)
# we don't check currently which one is the sampler heap
assert rw[0].access.descriptorStore in d3d12pipe.descriptorHeaps
self.check_eq(len(pipe.GetReadOnlyResources(rd.ShaderStage.Compute)), 0)
rw_used = pipe.GetReadWriteResources(rd.ShaderStage.Compute, True)
# should get the same results for dynamic array indexing, the 'only used' is only for
# statically unused or used bindings
assert rw == rw_used
def check_capture(self):
for sm in ["sm_5_1", "sm_6_0", "sm_6_6"]:
base = self.find_action("Tests " + sm)
if base == None:
rdtest.log.print("Skipping test " + sm)
continue
self.check_compute(base.eventId)
action = self.find_action("Draw", base.eventId)
assert action is not None
self.set_event(action.eventId, False)
pipe = self.controller.GetPipelineState()
d3d12pipe = self.controller.GetD3D12PipelineState()
# Check bindings:
# - buffer 8 in root range 0 should be statically used (single fixed declaration) for the parameters
# image 12 in root range 0 should also be statically used
# - images 19, 20, 21 in root range 1 should be used for the non-uniform index
# images 49 & 59 in root range 1 should be used for a second array in the same range
# image 60 in root range 1 should be used for a fixed index in an array
# image 99 and 103 in root range 1 should be used
#
# Currently D3D12 only reports descriptor category
bind_info: Dict[Tuple[rd.DescriptorCategory, int], Dict[str, Any]] = {
(rd.DescriptorCategory.ReadOnlyResource, 0): {
'loc': (0, 8),
'elems': [0],
'names': {0: ''}
},
(rd.DescriptorCategory.ReadOnlyResource, 1): {
'loc': (0, 12),
'elems': [0],
'names': {0: 'smiley'}
},
(rd.DescriptorCategory.ReadOnlyResource, 2): {
'loc': (1, 0),
'elems': [19, 20, 21],
'names': {19: 'another_smiley', 20: 'more_smileys???', 21: ''}
},
(rd.DescriptorCategory.ReadOnlyResource, 3): {
'loc': (1, 40),
'elems': [9, 19, 20],
'names': {9: '', 19: '', 20: ''}
},
(rd.DescriptorCategory.ReadOnlyResource, 4): {
'loc': (1, 80),
'elems': [19, 23],
'names': {19: '', 23: ''}
},
(rd.DescriptorCategory.Sampler, 1): {
'loc': (1, 0),
'elems': [19, 20, 21],
'names': {19: '', 20: '', 21: ''}
},
}
ro = pipe.GetReadOnlyResources(rd.ShaderStage.Pixel)
samp = pipe.GetSamplers(rd.ShaderStage.Pixel)
rw = pipe.GetReadWriteResources(rd.ShaderStage.Pixel)
self.check_eq(len(ro), 2 + 8)
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 and a.access.index == 0: # static sampler
# descriptor store should not be a heap, but we don't verify exactly where it comes from
assert a.access.descriptorStore not in d3d12pipe.descriptorHeaps
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.samplers[a.access.index]
heapName = "SamplerDescriptorHeap"
if idx not in bind_info.keys():
raise rdtest.TestFailureException(
f"Accessed bind {a.access.index} of type {a.access.type!s} doesn't exist in expected list")
if a.access.arrayElement not in bind_info[idx]['elems']:
raise rdtest.TestFailureException(
f"Bind {res.name} reports array element {a.access.arrayElement} as used, which shouldn't be")
if (res.fixedBindSetOrSpace, res.fixedBindNumber) != bind_info[idx]['loc']:
raise rdtest.TestFailureException(f"Bind {res.name} expected to be {bind_info[idx]['loc']} but is {res.fixedBindSetOrSpace}, {res.fixedBindNumber}")
# On D3D12 the logical location is just an index into the heap. This test sets up all
# descriptor tables at 0 so register = heap
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(
f"Location {loc.fixedBindNumber} not expected for {a.access.type!s} at space,reg {bind_info[idx]['loc']} array element {a.access.arrayElement}")
expectedDescName = bind_info[idx]['names'][a.access.arrayElement]
if expectedDescName == '':
expectedDescName = heapName
expectedDescName = f"{expectedDescName}[{bind_info[idx]['loc'][1] + a.access.arrayElement}]"
if loc.logicalBindName != expectedDescName:
raise rdtest.TestFailureException(
f"Location {loc.logicalBindName} not the expected {expectedDescName} for space,reg {bind_info[idx]['loc']} array element {a.access.arrayElement}")
bind_info[idx]['elems'].remove(a.access.arrayElement)
rdtest.log.success(f"Dynamic usage is as expected for {sm}")
self.check_debug_pixel()
for sm in ["sm_6_6_heap"]:
base = self.find_action("Tests " + sm)
if base == None:
rdtest.log.print("Skipping test " + sm)
continue
self.check_compute(base.eventId)
action = self.find_action("Draw", base.eventId)
assert action is not None
self.set_event(action.eventId, False)
pipe = self.controller.GetPipelineState()
# Check bindings:
# - CBV
# - Samplers
# - SRV resources
# - UAV resources
heap_bind_info: Dict[rd.DescriptorCategory, List[int]] = {
rd.DescriptorCategory.ConstantBlock: [9],
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],
}
ro = pipe.GetReadOnlyResources(rd.ShaderStage.Pixel)
samp = pipe.GetSamplers(rd.ShaderStage.Pixel)
rw = pipe.GetReadWriteResources(rd.ShaderStage.Pixel)
# All accesses should come direct without a shader binding
assert all([d.access.index == rd.DescriptorAccess.NoShaderBinding for d in ro])
assert all([d.access.index == rd.DescriptorAccess.NoShaderBinding for d in samp])
assert all([d.access.index == rd.DescriptorAccess.NoShaderBinding for d in rw])
# Check accesses are in the right lists
assert all(
[
rd.CategoryForDescriptorType(d.access.type)
== rd.DescriptorCategory.ReadOnlyResource
for d in ro
]
)
assert all(
[
rd.CategoryForDescriptorType(d.access.type)
== rd.DescriptorCategory.Sampler
for d in samp
]
)
assert all(
[
rd.CategoryForDescriptorType(d.access.type)
== rd.DescriptorCategory.ReadWriteResource
for d in rw
]
)
# the "byte offsets" are descriptor indices and should match the expectation above
assert [d.access.byteOffset for d in rw] == sorted(heap_bind_info[rd.DescriptorCategory.ReadWriteResource])
assert [d.access.byteOffset for d in ro] == sorted(heap_bind_info[rd.DescriptorCategory.ReadOnlyResource])
assert [d.access.byteOffset for d in samp] == sorted(heap_bind_info[rd.DescriptorCategory.Sampler])
descriptor_names = {
12: 'smiley',
19: 'another_smiley',
20: 'more_smileys???',
10: 'outUAV',
}
for a in ro + rw:
loc = self.controller.GetDescriptorLocations(a.access.descriptorStore,
[rd.DescriptorRange(a.access)])[0]
if loc.fixedBindNumber != a.access.byteOffset:
raise rdtest.TestFailureException(f"Bind {loc.fixedBindNumber} not expected for offset/index {a.access.byteOffset}")
if a.access.byteOffset in descriptor_names.keys():
name = descriptor_names[a.access.byteOffset]
else:
name = "ResourceDescriptorHeap"
if loc.logicalBindName != f"{name}[{a.access.byteOffset}]":
raise rdtest.TestFailureException(
f"Bind '{loc.logicalBindName}' is not the expected '{name}' for descriptor {a.access.byteOffset}")
for a in samp:
loc = self.controller.GetDescriptorLocations(a.access.descriptorStore,
[rd.DescriptorRange(a.access)])[0]
if loc.fixedBindNumber != a.access.byteOffset:
raise rdtest.TestFailureException(f"Bind {loc.fixedBindNumber} not expected for offset/index {a.access.byteOffset}")
if loc.logicalBindName != f"SamplerDescriptorHeap[{a.access.byteOffset}]":
raise rdtest.TestFailureException(
f"Bind {loc.logicalBindName} not expected for descriptor access SamplerDescriptorHeap[{a.access.byteOffset}]")
rdtest.log.success(f"Dynamic usage is as expected for {sm}")
self.check_debug_pixel()