1+ import renderdoc as rd
2+ import struct
3+ import rdtest
4+
5+ # Not a real test, re-used by API-specific tests
6+ class Subgroup_Zoo (rdtest .TestCase ):
7+ internal = True
8+ demos_test_name = None
9+
10+ def check_support (self , ** kwargs ):
11+ # Only allow this if explicitly run
12+ if kwargs ['test_include' ] == self .demos_test_name :
13+ return True , ''
14+ return False , 'Disabled test'
15+
16+ def check_capture (self ):
17+ graphics_tests = [a for a in self .find_action (
18+ "Graphics Tests" ).children if a .flags & rd .ActionFlags .Drawcall ]
19+ compute_dims = [a for a in self .find_action (
20+ "Compute Tests" ).children if 'x' in a .customName ]
21+
22+ rdtest .log .begin_section ("Graphics tests" )
23+
24+ # instances to check in instanced draws
25+ inst_checks = [0 , 1 , 5 , 10 ]
26+ # pixels to check
27+ pixel_checks = [
28+ # top quad
29+ (0 , 0 ), (1 , 0 ), (0 , 1 ), (1 , 1 ),
30+ # middle quad (away from triangle border)
31+ (64 , 56 ), (65 , 56 ), (64 , 57 ), (65 , 57 ),
32+ # middle quad (on triangle border)
33+ (64 , 64 ), (65 , 64 ), (64 , 65 ), (65 , 65 ),
34+ # middle quad on other triangle
35+ (56 , 64 ), (57 , 64 ), (56 , 65 ), (57 , 65 ),
36+ ]
37+ # threads to check. largest dimension only (all small dim checked)
38+ thread_checks = [
39+ # first few
40+ 0 , 1 , 2 ,
41+ # near end of 32-subgroup and boundary
42+ 30 , 31 , 32 ,
43+ # near end of 64-subgroup and boundary
44+ 62 , 63 , 64 ,
45+ # near end of 64-subgroup and boundary
46+ 62 , 63 , 64 ,
47+ # large values spaced out with one near the end of our unaligned size
48+ 100 , 110 , 120 , 140 , 149 , 150 , 160 , 200 , 250 ,
49+ ]
50+ clear_col = (123456.0 , 789.0 , 101112.0 , 0.0 )
51+
52+ overallFailed = False
53+ for idx , action in enumerate (graphics_tests ):
54+ failed = False
55+ self .controller .SetFrameEvent (action .eventId , False )
56+
57+ pipe = self .controller .GetPipelineState ()
58+
59+ # check vertex output for every vertex
60+ for inst in [inst for inst in inst_checks if inst < action .numInstances ]:
61+ for view in range (pipe .MultiviewBroadcastCount ()):
62+
63+ postvs = self .get_postvs (
64+ action , rd .MeshDataStage .VSOut , first_index = 0 , num_indices = action .numIndices , instance = inst )
65+
66+ for vtx in range (action .numIndices ):
67+ trace = self .controller .DebugVertex (
68+ vtx , inst , vtx , view )
69+
70+ if trace .debugger is None :
71+ self .controller .FreeTrace (trace )
72+
73+ rdtest .log .error (
74+ f"Test { idx } at { action .eventId } got no debug result at { vtx } inst { inst } view { view } " )
75+ failed = True
76+ return
77+
78+ _ , variables = self .process_trace (trace )
79+
80+ for var in trace .sourceVars :
81+ if var .name == 'vertdata' :
82+ name = var .name
83+
84+ if var .name not in postvs [vtx ].keys ():
85+ rdtest .log .error (
86+ f"Don't have expected output for { var .name } " )
87+ failed = True
88+ continue
89+
90+ real = postvs [vtx ][name ]
91+ debugged = self .evaluate_source_var (
92+ var , variables )
93+
94+ if debugged .columns != 4 or len (real ) != 4 :
95+ rdtest .log .error (
96+ f"Vertex output is not the right size ({ len (real )} vs { debugged .columns } )" )
97+ failed = True
98+ continue
99+
100+ if not rdtest .value_compare (real , debugged .value .f32v [0 :4 ], eps = 5.0E-06 ):
101+ rdtest .log .error (
102+ f"Test { idx } at { action .eventId } debugged vertex value { debugged .value .f32v [0 :4 ]} at { vtx } instance { inst } view { view } does not match output { real } " )
103+ failed = True
104+
105+ self .controller .FreeTrace (trace )
106+
107+ # check some assorted pixel outputs
108+ target = pipe .GetOutputTargets ()[0 ].resource
109+
110+ for pixel in pixel_checks :
111+ for view in range (pipe .MultiviewBroadcastCount ()):
112+ x , y = pixel
113+
114+ picked = self .controller .PickPixel (
115+ target , x , y , rd .Subresource (0 , 0 , 0 ), rd .CompType .Float )
116+
117+ real = picked .floatValue
118+
119+ # silently skip pixels that weren't written to
120+ if real == clear_col :
121+ continue
122+
123+ inputs = rd .DebugPixelInputs ()
124+ inputs .sample = 0
125+ inputs .primitive = rd .ReplayController .NoPreference
126+ inputs .view = view
127+ trace = self .controller .DebugPixel (x , y , inputs )
128+
129+ if trace .debugger is None :
130+ self .controller .FreeTrace (trace )
131+ rdtest .log .error (
132+ f"Test { idx } at { action .eventId } got no debug result at { x } ,{ y } " )
133+ failed = True
134+ continue
135+
136+ _ , variables = self .process_trace (trace )
137+
138+ output_sourcevar = self .find_output_source_var (
139+ trace , rd .ShaderBuiltin .ColorOutput , 0 )
140+
141+ if output_sourcevar is None :
142+ rdtest .log .error ("No output variable found" )
143+ failed = True
144+ continue
145+
146+ debugged = self .evaluate_source_var (
147+ output_sourcevar , variables )
148+
149+ self .controller .FreeTrace (trace )
150+
151+ debuggedValue = list (debugged .value .f32v [0 :4 ])
152+
153+ if not rdtest .value_compare (real , debuggedValue , eps = 5.0E-06 ):
154+ rdtest .log .error (
155+ f"Test { idx } at { action .eventId } debugged pixel value { debuggedValue } at { x } ,{ y } in { view } does not match output { real } " )
156+ failed = True
157+
158+ overallFailed |= failed
159+ if not failed :
160+ rdtest .log .success (f"Test { idx } successful" )
161+ else :
162+ rdtest .log .error (f"Test { idx } failed" )
163+
164+ rdtest .log .end_section ("Graphics tests" )
165+
166+ for comp_dim in compute_dims :
167+ rdtest .log .begin_section (
168+ f"Compute tests with { comp_dim .customName } workgroup" )
169+
170+ compute_tests = [
171+ a for a in comp_dim .children if a .flags & rd .ActionFlags .Dispatch ]
172+
173+ for test , action in enumerate (compute_tests ):
174+ failed = False
175+ self .controller .SetFrameEvent (action .eventId , False )
176+
177+ pipe = self .controller .GetPipelineState ()
178+ csrefl = pipe .GetShaderReflection (rd .ShaderStage .Compute )
179+
180+ dim = csrefl .dispatchThreadsDimension
181+
182+ rw = pipe .GetReadWriteResources (rd .ShaderStage .Compute )
183+
184+ if len (rw ) != 1 :
185+ rdtest .log .error ("Unexpected number of RW resources" )
186+ continue
187+
188+ # each test writes up to 16k data, one vec4 per thread * up to 1024 threads
189+ bufdata = self .controller .GetBufferData (
190+ rw [0 ].descriptor .resource , test * 16 * 1024 , 16 * 1024 )
191+
192+ for t in thread_checks :
193+ xrange = 1
194+ yrange = dim [1 ]
195+ xbase = t
196+ ybase = 0
197+
198+ # vertical orientation
199+ if dim [1 ] > dim [0 ]:
200+ xrange = dim [0 ]
201+ yrange = 1
202+ xbase = 0
203+ ybase = t
204+
205+ for x in range (xbase , xbase + xrange ):
206+ for y in range (ybase , ybase + yrange ):
207+ z = 0
208+
209+ if x >= dim [0 ] or y >= dim [1 ]:
210+ continue
211+
212+ try :
213+ real = struct .unpack_from (
214+ "4f" , bufdata , 16 * y * dim [0 ] + 16 * x )
215+
216+ trace = self .controller .DebugThread (
217+ (0 , 0 , 0 ), (x , y , z ))
218+
219+ _ , variables = self .process_trace (trace )
220+
221+ if trace .debugger is None :
222+ raise rdtest .TestFailureException (f"Test { test } at { action .eventId } got no debug result at { x } ,{ y } ,{ z } " )
223+
224+ # Find the source variable 'data' at the highest instruction index
225+ debugged = None
226+ countInst = len (trace .instInfo )
227+ for inst in range (countInst ):
228+ sourceVars = trace .instInfo [countInst - 1 - inst ].sourceVars
229+ try :
230+ dataVars = [v for v in sourceVars if v .name == 'data' ]
231+ if len (dataVars ) == 0 :
232+ continue
233+ debugged = self .evaluate_source_var (dataVars [0 ], variables )
234+ except KeyError as ex :
235+ continue
236+ except rdtest .TestFailureException as ex :
237+ continue
238+ break
239+ if debugged is None :
240+ raise rdtest .TestFailureException (f"Couldn't find source variable { name } " )
241+
242+ debuggedValue = list (debugged .value .f32v [0 :4 ])
243+
244+ if not rdtest .value_compare (real , debuggedValue , eps = 5.0E-06 ):
245+ raise rdtest .TestFailureException (f"EID:{ action .eventId } TID:{ x } ,{ y } ,{ z } debugged thread value { debuggedValue } does not match output { real } " )
246+
247+ except rdtest .TestFailureException as ex :
248+ rdtest .log .error (f"Test { test } failed { ex } " )
249+ failed = True
250+ continue
251+ finally :
252+ self .controller .FreeTrace (trace )
253+
254+ overallFailed |= failed
255+ if not failed :
256+ rdtest .log .success (f"Test { test } successful" )
257+ else :
258+ rdtest .log .error (f"Test { test } failed" )
259+
260+ rdtest .log .end_section (
261+ f"Compute tests with { comp_dim .customName } workgroup" )
262+
263+ if overallFailed :
264+ raise rdtest .TestFailureException ("Some tests were not as expected" )
0 commit comments