def check_modifs_consistent(self, modifs): # postmod of each should match premod of the next for i in range(len(modifs) - 1): a = value_selector(modifs[i].postMod.col) b = value_selector(modifs[i + 1].preMod.col) if self.is_depth: a = (modifs[i].postMod.depth, modifs[i].postMod.stencil) b = (modifs[i + 1].preMod.depth, modifs[i + 1].preMod.stencil) if a != b: raise rdtest.TestFailureException( "postmod at {} primitive {}: {} doesn't match premod at {} primitive {}: {}" .format(modifs[i].eventId, modifs[i].primitiveID, a, modifs[i + 1].eventId, modifs[i + 1].primitiveID, b)) # Check that if the test failed, its postmod is the same as premod for i in range(len(modifs)): if not modifs[i].Passed(): a = value_selector(modifs[i].preMod.col) b = value_selector(modifs[i].postMod.col) if self.is_depth: a = (modifs[i].preMod.depth, modifs[i].preMod.stencil) b = (modifs[i].postMod.depth, modifs[i].postMod.stencil) if not rdtest.value_compare(a, b): raise rdtest.TestFailureException( "postmod at {} primitive {}: {} doesn't match premod: {}" .format(modifs[i].eventId, modifs[i].primitiveID, b, a))
def check_debug(self, vtx, idx, inst, postvs): trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, 0) if trace.debugger is None: self.controller.FreeTrace(trace) raise rdtest.TestFailureException("Couldn't debug vertex {} in instance {}".format(vtx, inst)) cycles, variables = self.process_trace(trace) for var in trace.sourceVars: var: rd.SourceVariableMapping if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: name = var.name if name not in postvs[vtx].keys(): raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) expect = postvs[vtx][name] value = self.evaluate_source_var(var, variables) if len(expect) != value.columns: raise rdtest.TestFailureException( "Output {} at vert {} (idx {}) instance {} has different size ({} values) to expectation ({} values)" .format(name, vtx, idx, inst, value.columns, len(expect))) debugged = value.value.f32v[0:value.columns] if not rdtest.value_compare(expect, debugged): raise rdtest.TestFailureException( "Debugged value {} at vert {} (idx {}) instance {}: {} doesn't exactly match postvs output {}".format( name, vtx, idx, inst, debugged, expect)) rdtest.log.success('Successfully debugged vertex {} in instance {}' .format(vtx, inst))
def check_compute(self, eventId): action = self.find_action("Dispatch", eventId) self.check(action is not None) self.controller.SetFrameEvent(action.eventId, False) pipe = self.controller.GetD3D12PipelineState() if len(pipe.rootElements) != 2: raise rdtest.TestFailureException( "Wrong number of root elements is bound: {}, not 2".format( len(pipe.rootElements))) root = pipe.rootElements[0] # second root element is the cbuffer const, we don't care if root.dynamicallyUsedCount != 1: raise rdtest.TestFailureException( "Compute root range 0 has {} dynamically used, not 1".format( root.dynamicallyUsedCount)) if not root.views[15].dynamicallyUsed: raise rdtest.TestFailureException( "Compute root range 0[15] isn't dynamically used") for i in range(len(root.views)): if i == 15: continue if root.views[i].dynamicallyUsed: raise rdtest.TestFailureException( "Compute root range 0[{}] i dynamically used".format(i))
def check_capture(self): self.check_final_backbuffer() # Open the capture and grab the thumbnail, check that it is all green too (dirty way of verifying we didn't # break in-app updates but somehow end up with the right data) cap = rd.OpenCaptureFile() # Open a particular file status = cap.OpenFile(self.capture_filename, '', None) # Make sure the file opened successfully if status != rd.ReplayStatus.Succeeded: cap.Shutdown() raise rdtest.TestFailureException("Couldn't open '{}': {}".format( self.capture_filename, str(status))) thumb: rd.Thumbnail = cap.GetThumbnail(rd.FileType.PNG, 0) tmp_path = rdtest.get_tmp_path('thumbnail.png') with open(tmp_path, 'wb') as f: f.write(thumb.data) # The original thumbnail should also be identical, since we have the uncompressed extended thumbnail. ref_path = self.get_ref_path('backbuffer.png') if not rdtest.png_compare(tmp_path, ref_path): raise rdtest.TestFailureException( "Reference backbuffer and thumbnail image differ", tmp_path, ref_path) rdtest.log.success("Thumbnail is identical to reference")
def check_capture(self): sdf = self.controller.GetStructuredFile() # The marker name, and the calls that we expect to follow it expected = { 'First Test': ['glUniform1ui'], 'Second Test': ['glVertexAttribBinding', 'glProgramUniform4f'], 'Third Test': ['glVertexArrayAttribBinding', 'glUniform4f'], } for test in expected.keys(): marker: rd.DrawcallDescription = self.find_draw(test) if marker is None: raise rdtest.TestFailureException( 'Failed to find draw {}'.format(test)) draw: rd.DrawcallDescription = marker.next calls = [] ev: rd.APIEvent for ev in draw.events: # skip any events up to and including the marker itself if ev.eventId <= marker.eventId: continue calls.append(sdf.chunks[ev.chunkIndex].name) for i in range(len(expected[test])): if expected[test][i] != calls[i]: raise rdtest.TestFailureException( 'After marker {} got call {} but expected {}'.format( test, calls[i], expected[test][i])) rdtest.log.success("API calls are as expected")
def run(self): self.capture_filename = self.get_capture() self.check(os.path.exists(self.capture_filename), "Didn't generate capture in make_capture") rdtest.log.print("Loading capture") memory_before: int = rd.GetCurrentProcessMemoryUsage() start_time = time.time() self.controller = rdtest.open_capture(self.capture_filename, opts=self.get_replay_options()) duration = time.time() - start_time memory_after: int = rd.GetCurrentProcessMemoryUsage() memory_increase = memory_after - memory_before rdtest.log.print("Loaded capture in {:02} seconds, consuming {} bytes of memory".format(duration, memory_increase)) if memory_increase > 1200*1000*1000: raise rdtest.TestFailureException("Memory usage is too high".format(duration)) else: rdtest.log.success("Memory usage is OK") if rd.IsReleaseBuild(): if duration >= 2.5: raise rdtest.TestFailureException("Time to load is too high") rdtest.log.success("Time to load is OK") else: rdtest.log.print("Not checking time to load in non-release build") if self.controller is not None: self.controller.Shutdown()
def check_capture(self): self.check_final_backbuffer() draw = self.find_draw("Primary") resources = self.controller.GetResources() self.check(draw is not None and draw.next is not None) self.controller.SetFrameEvent(draw.next.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() self.check(pipe.GetVBuffers()[0].byteOffset == 0) rdtest.log.success("Primary draw has correct byte offset") pipeline: rd.ResourceId = self.controller.GetVulkanPipelineState( ).graphics.pipelineResourceId checked = False res: rd.ResourceDescription for res in resources: if res.resourceId == pipeline: self.check(res.name == "Pipeline 0") checked = True if not checked: raise rdtest.TestFailureException( "Couldn't find resource description for pipeline {}".format( pipeline)) rdtest.log.success("Primary draw has correct pipeline bound") draw = self.find_draw("Secondary") self.check(draw is not None and draw.next is not None) self.controller.SetFrameEvent(draw.next.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() self.check(pipe.GetVBuffers()[0].byteOffset == 108) rdtest.log.success("Secondary draw has correct byte offset") pipeline: rd.ResourceId = self.controller.GetVulkanPipelineState( ).graphics.pipelineResourceId checked = False res: rd.ResourceDescription for res in resources: if res.resourceId == pipeline: self.check(res.name == "Pipeline 1") checked = True if not checked: raise rdtest.TestFailureException( "Couldn't find resource description for pipeline {}".format( pipeline)) rdtest.log.success("Secondary draw has correct pipeline bound")
def check_capture(self): tex = rd.TextureDisplay() # At each action, the centre pixel of the viewport should be green action = self.get_first_action() while action is not None: self.controller.SetFrameEvent(action.eventId, False) if action.flags & rd.ActionFlags.Drawcall: pipe = self.controller.GetPipelineState() tex = self.controller.GetPipelineState().GetOutputTargets()[0].resourceId view: rd.Viewport = self.controller.GetPipelineState().GetViewport(0) x,y = int(view.x + view.width / 2), int(view.y + view.height / 2) # convert to top-left co-ordinates for use with PickPixel y = self.get_texture(tex).height - y self.check_pixel_value(tex, x, y, [0.0, 1.0, 0.0, 1.0]) action = action.next rdtest.log.success("Draws are all green") # Now save the backbuffer to disk ref_path = rdtest.get_tmp_path('backbuffer.png') save_data = rd.TextureSave() save_data.resourceId = tex save_data.destType = rd.FileType.PNG self.controller.SaveTexture(save_data, ref_path) # Open the capture and grab the thumbnail, check that it is all green too (dirty way of verifying we didn't # break in-app updates but somehow end up with the right data) cap = rd.OpenCaptureFile() # Open a particular file status = cap.OpenFile(self.capture_filename, '', None) # Make sure the file opened successfully if status != rd.ReplayStatus.Succeeded: cap.Shutdown() raise rdtest.TestFailureException("Couldn't open '{}': {}".format(self.capture_filename, str(status))) thumb: rd.Thumbnail = cap.GetThumbnail(rd.FileType.PNG, 0) tmp_path = rdtest.get_tmp_path('thumbnail.png') with open(tmp_path, 'wb') as f: f.write(thumb.data) # The original thumbnail should also be identical, since we have the uncompressed extended thumbnail. if not rdtest.png_compare(tmp_path, ref_path): raise rdtest.TestFailureException("Reference backbuffer and thumbnail image differ", tmp_path, ref_path) rdtest.log.success("Thumbnail is identical to reference")
def check_capture(self): last_action: rd.ActionDescription = self.get_last_action() self.controller.SetFrameEvent(last_action.eventId, True) action: rd.ActionDescription = self.find_action('Duration') min_duration = float(action.customName.split(' = ')[1]) if rd.IsReleaseBuild(): if min_duration >= 15.0: raise rdtest.TestFailureException( "Minimum duration noted {} ms is too high".format( min_duration)) rdtest.log.success( "Minimum duration ({}) is OK".format(min_duration)) else: rdtest.log.print( "Not checking duration ({}) in non-release build".format( min_duration)) resources = self.controller.GetResources() for i in range(8): res: rd.ResourceDescription = [ r for r in resources if r.name == 'Offscreen{}'.format(i) ][0] tex: rd.TextureDescription = self.get_texture(res.resourceId) data = self.controller.GetTextureData(res.resourceId, rd.Subresource(0, 0, 0)) pixels = [ struct.unpack_from("4f", data, 16 * p) for p in range(tex.width * tex.height) ] unique_pixels = list(set(pixels)) if len(unique_pixels) > 2: raise rdtest.TestFailureException( "Too many pixel values found ({})".format( len(unique_pixels))) if (0.0, 0.0, 0.0, 1.0) not in unique_pixels: raise rdtest.TestFailureException( "Didn't find background colour in unique pixels list") unique_pixels.remove((0.0, 0.0, 0.0, 1.0)) if not rdtest.value_compare( (0.8, 0.8, 0.8, 0.4), unique_pixels[0]): raise rdtest.TestFailureException( "Didn't find foreground colour in unique pixels list") rdtest.log.success("{} has correct contents".format(res.name))
def check_capture(self): # Make an output so we can pick pixels out: rd.ReplayOutput = self.controller.CreateOutput( rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture) self.check(out is not None) # find the first draw draw = self.find_draw("Draw") # check the centre pixel of the viewport is white self.controller.SetFrameEvent(draw.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() tex = rd.TextureDisplay() tex.resourceId = pipe.GetOutputTargets()[0].resourceId out.SetTextureDisplay(tex) view: rd.Viewport = pipe.GetViewport(0) picked: rd.PixelValue = out.PickPixel(tex.resourceId, False, int(view.width / 2), int(view.height / 2), 0, 0, 0) if not rdtest.value_compare(picked.floatValue, [1.0, 1.0, 1.0, 1.0]): raise rdtest.TestFailureException( "Picked value {} doesn't match expectation".format( picked.floatValue)) rdtest.log.success("Picked value for first draw is as expected") # find the second draw draw = self.find_draw("Draw", draw.eventId + 1) pipe: rd.PipeState = self.controller.GetPipelineState() tex.resourceId = pipe.GetOutputTargets()[0].resourceId out.SetTextureDisplay(tex) view: rd.Viewport = pipe.GetViewport(0) picked: rd.PixelValue = out.PickPixel(tex.resourceId, False, int(view.width / 2), int(view.height / 2), 0, 0, 0) if not rdtest.value_compare(picked.floatValue, [1.0, 1.0, 1.0, 1.0]): raise rdtest.TestFailureException( "Picked value {} doesn't match expectation".format( picked.floatValue)) rdtest.log.success("Picked value for second draw is as expected")
def check_capture(self): if not self.controller.GetAPIProperties().shaderDebugging: rdtest.log.success("Shader debugging not enabled, skipping test") return failed = False test_marker: rd.ActionDescription = self.find_action("action") while test_marker is not None: action = test_marker.next event_name = test_marker.customName test_marker: rd.ActionDescription = self.find_action( "action", action.eventId) self.controller.SetFrameEvent(action.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() if not pipe.GetShaderReflection( rd.ShaderStage.Pixel).debugInfo.debuggable: rdtest.log.print( "Skipping undebuggable shader at {}.".format(event_name)) continue # Debug the shader trace: rd.ShaderDebugTrace = self.controller.DebugPixel( 200, 150, rd.ReplayController.NoPreference, rd.ReplayController.NoPreference) if trace.debugger is None: failed = True rdtest.log.error( "Test {} could not be debugged.".format(event_name)) continue cycles, variables = self.process_trace(trace) output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) debugged = self.evaluate_source_var(output, variables) try: self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 200, 150, debugged.value.f32v[0:4]) except rdtest.TestFailureException as ex: failed = True rdtest.log.error("Test {} did not match. {}".format( event_name, str(ex))) continue finally: self.controller.FreeTrace(trace) rdtest.log.success( "Test {} matched as expected".format(event_name)) if failed: raise rdtest.TestFailureException( "Some tests were not as expected") rdtest.log.success("All tests matched")
def check_clearbeforedraw_depth(self, out, depthId): # Test ClearBeforeDraw with a depth target tex = rd.TextureDisplay() tex.overlay = rd.DebugOverlay.ClearBeforeDraw tex.resourceId = depthId out.SetTextureDisplay(tex) out.GetDebugOverlayTexID() # Called to refresh the overlay overlay = rd.DebugOverlay.ClearBeforeDraw test_name = str(overlay) + '.Depth' overlay_path = rdtest.get_tmp_path(test_name + '.png') ref_path = self.get_ref_path(test_name + '.png') save_data = rd.TextureSave() save_data.resourceId = depthId save_data.destType = rd.FileType.PNG save_data.channelExtract = 0 tolerance = 2 self.controller.SaveTexture(save_data, overlay_path) if not rdtest.png_compare(overlay_path, ref_path, tolerance): raise rdtest.TestFailureException("Reference and output image differ for overlay {}".format(test_name), overlay_path, ref_path) rdtest.log.success("Reference and output image are identical for {}".format(test_name))
def check_capture(self): if not self.controller.GetAPIProperties().shaderDebugging: rdtest.log.success("Shader debugging not enabled, skipping test") return success = True # Jump to the draw test_marker: rd.DrawcallDescription = self.find_draw("Test") # Draw 1: No GS, PS without prim draw = test_marker.next success &= self.test_draw(draw, 100, 80, rd.ReplayController.NoPreference, [0], [0, 1, 0, 1]) # Draw 2: No GS, PS with prim draw = draw.next success &= self.test_draw(draw, 300, 80, rd.ReplayController.NoPreference, [0], [0, 1, 0, 1]) # Draw 3: GS, PS without prim draw = draw.next success &= self.test_draw(draw, 125, 250, rd.ReplayController.NoPreference, [0], [0, 1, 0, 1]) # Draw 4: GS, PS with prim draw = draw.next success &= self.test_draw(draw, 325, 250, 2, [2], [0.5, 1, 0, 1]) success &= self.test_draw(draw, 325, 250, 3, [3], [0.75, 1, 0, 1]) # No expected output here, since it's nondeterministic which primitive gets selected success &= self.test_draw(draw, 325, 250, rd.ReplayController.NoPreference, [2, 3], None) if not success: raise rdtest.TestFailureException("Some tests were not as expected") rdtest.log.success("All tests matched")
def check_capture(self): draw = self.find_draw("Draw") self.check(draw is not None) self.controller.SetFrameEvent(draw.eventId, False) # Make an output so we can pick pixels out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture) pipe: rd.PipeState = self.controller.GetPipelineState() tex = rd.TextureDisplay() tex.resourceId = pipe.GetOutputTargets()[0].resourceId out.SetTextureDisplay(tex) texdetails = self.get_texture(tex.resourceId) picked: rd.PixelValue = out.PickPixel(tex.resourceId, False, int(texdetails.width / 2), int(texdetails.height / 2), 0, 0, 0) if not rdtest.value_compare(picked.floatValue, [1.0, 0.0, 0.0, 1.0]): raise rdtest.TestFailureException("Picked value {} doesn't match expectation".format(picked.floatValue)) rdtest.log.success("picked value is as expected") out.Shutdown()
def check_region(self, region, test): colors = self.get_region_cols(region) if not test(colors): tmp_path = rdtest.get_tmp_path('output.png') rdtest.png_save(tmp_path, self.rows, self.out.GetDimensions(), False) raise rdtest.TestFailureException("Expected line segment wrong, colors: {}".format(colors), tmp_path)
def check_capture(self): draw = self.find_draw("Draw") self.check(draw is not None) self.controller.SetFrameEvent(draw.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() stage = rd.ShaderStage.Pixel cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) variables = self.controller.GetCBufferVariableContents( pipe.GetGraphicsPipelineObject(), pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset, cbuf.byteSize) outcol: rd.ShaderVariable = variables[1] self.check(outcol.name == "outcol") if not rdtest.value_compare(outcol.value.fv[0:4], [0.0, 0.0, 0.0, 0.0]): raise rdtest.TestFailureException( "expected outcol to be 0s, but got {}".format( outcol.value.fv[0:4])) rdtest.log.success("CBuffer value was truncated as expected")
def check_vertex(self, x, y, result): pick = self.out.PickVertex(x, y) if not rdtest.value_compare(result, pick): raise rdtest.TestFailureException("When picking ({},{}) expected vertex {} in instance {}, but found {} in {}".format(x, y, result[0], result[1], pick[0], pick[1])) rdtest.log.success("Picking {},{} returns vertex {} in instance {} as expected".format(x, y, result[0], result[1]))
def check_capture_with_controller(self, proxy_api: str): any_failed = False if proxy_api != '': rdtest.log.print('Running with {} local proxy'.format(proxy_api)) self.proxied = True else: rdtest.log.print('Running on direct replay') self.proxied = False for d in self.controller.GetDrawcalls(): # Check each region for the tests within if d.flags & rd.DrawFlags.PushMarker: name = '' tests_run = 0 failed = False # Iterate over drawcalls in this region for sub in d.children: sub: rd.DrawcallDescription if sub.flags & rd.DrawFlags.SetMarker: name = sub.name # Check this draw if sub.flags & rd.DrawFlags.Drawcall: tests_run = tests_run + 1 try: # Set this event as current self.controller.SetFrameEvent(sub.eventId, True) self.filename = (d.name + '@' + name).replace( '->', '_') self.check_test(d.name, name, Texture_Zoo.TEST_CAPTURE) except rdtest.TestFailureException as ex: failed = any_failed = True rdtest.log.error(str(ex)) if not failed: rdtest.log.success( "All {} texture tests for {} are OK".format( tests_run, d.name)) if not any_failed: if proxy_api != '': rdtest.log.success( 'All textures are OK with {} as local proxy'.format( proxy_api)) else: rdtest.log.success("All textures are OK on direct replay") else: raise rdtest.TestFailureException( "Some tests were not as expected")
def check_capture(self): draw = self.find_draw("Draw") self.check(draw is not None) self.controller.SetFrameEvent(draw.eventId, False) pipe: rd.VKState = self.controller.GetVulkanPipelineState() # Check bindings: # - buffer 15 in bind 0 should be used # - images 19, 20, 21 in bind 1 should be used for the non-uniform index # images 49 & 59 in bind 1 should be used for the first fixed index # - images 381 & 386 in bind 2 should be used for the second fixed index bind_info = { 0: { 'dynamicallyUsedCount': 1, 'used': [15] }, 1: { 'dynamicallyUsedCount': 5, 'used': [19, 20, 21, 49, 59] }, 2: { 'dynamicallyUsedCount': 2, 'used': [381, 386] }, } if len(pipe.graphics.descriptorSets) != 1: raise rdtest.TestFailureException("Wrong number of sets is bound: {}, not 1".format(len(pipe.graphics.descriptorSets))) desc_set: rd.VKDescriptorSet = pipe.graphics.descriptorSets[0] binding: rd.VKDescriptorBinding for bind, binding in enumerate(desc_set.bindings): if binding.dynamicallyUsedCount != bind_info[bind]['dynamicallyUsedCount']: raise rdtest.TestFailureException("Bind {} doesn't have the right used count. {} is not the expected count of {}" .format(bind, binding.dynamicallyUsedCount, bind_info[bind]['dynamicallyUsedCount'])) el: rd.VKBindingElement for idx, el in enumerate(binding.binds): expected_used = idx in bind_info[bind]['used'] actually_used = el.dynamicallyUsed if expected_used and not actually_used: raise rdtest.TestFailureException("Bind {} element {} expected to be used, but isn't.".format(bind, idx)) if not expected_used and actually_used: raise rdtest.TestFailureException("Bind {} element {} expected to be unused, but is.".format(bind, idx)) rdtest.log.success("Dynamic usage is as expected")
def check_capture(self): memory: int = rd.GetCurrentProcessMemoryUsage() if memory > 500 * 1000 * 1000: raise rdtest.TestFailureException( "Memory usage of {} is too high".format(memory)) rdtest.log.success( "Capture {} opened with reasonable memory ({})".format( self.demos_frame_cap, memory))
def check_capture(self): if not self.controller.GetAPIProperties().shaderDebugging: rdtest.log.success("Shader debugging not enabled, skipping test") return failed = False for test_name in ["GLSL1 tests", "GLSL2 tests", "ASM tests"]: rdtest.log.begin_section(test_name) action = self.find_action(test_name) for child in range(len(action.children)): section = action.children[child] self.controller.SetFrameEvent(section.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() if not pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo.debuggable: rdtest.log.print("Skipping undebuggable shader at {} in {}.".format(child, test_name)) return for test in range(section.numInstances): x = 4 * test + 1 y = 4 * child + 1 # Debug the shader trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, rd.ReplayController.NoPreference, rd.ReplayController.NoPreference) if trace.debugger is None: failed = True rdtest.log.error("Test {} in sub-section {} did not debug at all".format(test, child)) self.controller.FreeTrace(trace) continue cycles, variables = self.process_trace(trace) output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) debugged = self.evaluate_source_var(output, variables) try: self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, x, y, debugged.value.f32v[0:4]) except rdtest.TestFailureException as ex: failed = True rdtest.log.error("Test {} in sub-section {} did not match. {}".format(test, child, str(ex))) continue finally: self.controller.FreeTrace(trace) rdtest.log.success("Test {} in sub-section {} matched as expected".format(test, child)) rdtest.log.end_section(test_name) if failed: raise rdtest.TestFailureException("Some tests were not as expected") rdtest.log.success("All tests matched")
def check_capture(self): # Need capture access. Rather than trying to keep the original around, we just open a new one cap = rd.OpenCaptureFile() # Open a particular file status = cap.OpenFile(self.capture_filename, '', None) # Make sure the file opened successfully if status != rd.ReplayStatus.Succeeded: cap.Shutdown() raise rdtest.TestFailureException( "Couldn't open capture for access: {}".format( self.capture_filename, str(status))) if not cap.HasCallstacks(): raise rdtest.TestFailureException( "Capture does not report having callstacks") if not cap.InitResolver(False, None): raise rdtest.TestFailureException( "Failed to initialise callstack resolver") draw = self.find_draw("Draw") event: rd.APIEvent = draw.events[-1] expected_funcs = [ "GL_Callstacks::testFunction", "GL_Callstacks::main", ] expected_lines = [7001, 8002] callstack = cap.GetResolve(list(event.callstack)) if len(callstack) < len(expected_funcs): raise rdtest.TestFailureException( "Resolved callstack isn't long enough ({} stack frames), expected at least {}" .format(len(event.callstack), len(expected_funcs))) for i in range(len(expected_funcs)): stack: str = callstack[i] if expected_funcs[i] not in stack: raise rdtest.TestFailureException( "Expected '{}' in '{}'".format(expected_funcs[i], stack)) idx = callstack[i].find("line") if idx < 0: raise rdtest.TestFailureException( "Expected a line number in '{}'".format(stack)) if int(stack[idx + 5:]) != expected_lines[i]: raise rdtest.TestFailureException( "Expected line number {} in '{}'".format( expected_lines[i], stack)) rdtest.log.success("Callstacks are as expected")
def find_draw(self, name): draw = None for d in self.controller.GetDrawcalls(): if name in d.name: draw = d break if draw is None: raise rdtest.TestFailureException("Couldn't find '{}' draw".format(name)) return draw
def check_capture(self): last_draw: rd.DrawcallDescription = self.get_last_draw() self.controller.SetFrameEvent(last_draw.eventId, True) tex = last_draw.copyDestination # green background around first triangle, blue around second self.check_pixel_value(tex, 10, 10, [0.0, 1.0, 0.0, 1.0]) self.check_pixel_value(tex, 118, 10, [0.0, 1.0, 0.0, 1.0]) self.check_pixel_value(tex, 118, 118, [0.0, 1.0, 0.0, 1.0]) self.check_pixel_value(tex, 10, 118, [0.0, 1.0, 0.0, 1.0]) self.check_pixel_value(tex, 138, 10, [0.0, 0.0, 1.0, 1.0]) self.check_pixel_value(tex, 246, 10, [0.0, 0.0, 1.0, 1.0]) self.check_pixel_value(tex, 246, 118, [0.0, 0.0, 1.0, 1.0]) self.check_pixel_value(tex, 138, 118, [0.0, 0.0, 1.0, 1.0]) # Sample across each triangle, we expect things to be identical for xoffs in [0, 128]: # Check black checkerboard squares self.check_pixel_value(tex, xoffs + 42, 92, [0.0, 0.0, 0.0, 1.0]) self.check_pixel_value(tex, xoffs + 40, 85, [0.0, 0.0, 0.0, 1.0]) self.check_pixel_value(tex, xoffs + 68, 66, [0.0, 0.0, 0.0, 1.0]) self.check_pixel_value(tex, xoffs + 59, 47, [0.0, 0.0, 0.0, 1.0]) self.check_pixel_value(tex, xoffs + 81, 92, [0.0, 0.0, 0.0, 1.0]) # Check the red and green eyes of the smiley self.check_pixel_value(tex, xoffs + 49, 83, [1.0, 0.0, 0.09, 1.0]) self.check_pixel_value(tex, xoffs + 60, 83, [0.0, 1.0, 0.09, 1.0]) # Check the blue smile self.check_pixel_value(tex, xoffs + 64, 72, [0.09, 0.0, 1.0, 1.0]) # Check the orange face self.check_pixel_value(tex, xoffs + 46, 86, [1.0, 0.545, 0.36, 1.0]) # Check the empty space where we clamped and didn't repeat self.check_pixel_value(tex, xoffs + 82, 79, [0.72, 1.0, 1.0, 1.0]) self.check_pixel_value(tex, xoffs + 84, 86, [0.72, 1.0, 1.0, 1.0]) self.check_pixel_value(tex, xoffs + 88, 92, [0.72, 1.0, 1.0, 1.0]) # Check that the repeated smiley above is there self.check_pixel_value(tex, xoffs + 67, 53, [0.905, 0.635, 0.36, 1.0]) self.check_pixel_value(tex, xoffs + 65, 50, [1.0, 0.0, 0.09, 1.0]) # Check for resource leaks if len(self.controller.GetResources()) > 75: raise rdtest.TestFailureException( "Too many resources found: {}".format( len(self.controller.GetResources())))
def check_capture(self): apiprops: rd.APIProperties = self.controller.GetAPIProperties() if not apiprops.rgpCapture: rdtest.log.print("RGP capture not tested") return path = self.controller.CreateRGPProfile(rd.CreateHeadlessWindowingData(100, 100)) if os.path.exists(path) and os.path.getsize(path) > 100: rdtest.log.success("RGP capture created successfully") else: raise rdtest.TestFailureException("RGP capture failed")
def check_events(self, events, modifs): self.check(len(modifs) == len(events)) # Check for consistency first self.check_modifs_consistent(modifs) for i in range(len(modifs)): for c in range(len(events[i])): expected = events[i][c][1] actual = events[i][c][0](modifs[i]) if not rdtest.value_compare(actual, expected): raise rdtest.TestFailureException( "eventId {}, testing {} expected {}, got {}".format( modifs[i].eventId, events[i][c][0].__name__, expected, actual))
def find_action(self, name): action = None for d in self.controller.GetRootActions(): if name in d.customName: action = d break if action is None: raise rdtest.TestFailureException( "Couldn't find '{}' action".format(name)) return action
def check_modifs_consistent(self, modifs): # postmod of each should match premod of the next for i in range(len(modifs) - 1): if value_selector(modifs[i].postMod.col) != value_selector( modifs[i + 1].preMod.col): raise rdtest.TestFailureException( "postmod at {}: {} doesn't match premod at {}: {}".format( modifs[i].eventId, value_selector(modifs[i].postMod.col), modifs[i + 1].eventId, value_selector(modifs[i].preMod.col))) # Check that if the test failed, its postmod is the same as premod for i in range(len(modifs)): if not modifs[i].Passed(): if not rdtest.value_compare( value_selector(modifs[i].preMod.col), value_selector(modifs[i].postMod.col)): raise rdtest.TestFailureException( "postmod at {}: {} doesn't match premod: {}".format( modifs[i].eventId, value_selector(modifs[i].postMod.col), value_selector(modifs[i].preMod.col)))
def check_capture(self): failed = False test_marker: rd.DrawcallDescription = self.find_draw("draw") while test_marker is not None: drawcall = test_marker.next event_name = test_marker.name test_marker: rd.DrawcallDescription = self.find_draw( "draw", drawcall.eventId) self.controller.SetFrameEvent(drawcall.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() # Debug the shader trace: rd.ShaderDebugTrace = self.controller.DebugPixel( 200, 150, rd.ReplayController.NoPreference, rd.ReplayController.NoPreference) if trace.debugger is None: failed = True rdtest.log.error( "Test {} could not be debugged.".format(event_name)) continue cycles, variables = self.process_trace(trace) output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) debugged = self.evaluate_source_var(output, variables) try: self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 200, 150, debugged.value.f32v[0:4]) except rdtest.TestFailureException as ex: failed = True rdtest.log.error("Test {} did not match. {}".format( event_name, str(ex))) continue finally: self.controller.FreeTrace(trace) rdtest.log.success( "Test {} matched as expected".format(event_name)) if failed: raise rdtest.TestFailureException( "Some tests were not as expected") rdtest.log.success("All tests matched")
def check_events(self, events, modifs, hasSecondary): self.check(len(modifs) == len(events), "Expected {} events, got {}".format(len(events), len(modifs))) # Check for consistency first. For secondary command buffers, # might not have all information, so don't check for consistency if not hasSecondary: self.check_modifs_consistent(modifs) for i in range(len(modifs)): for c in range(len(events[i])): expected = events[i][c][1] actual = events[i][c][0](modifs[i]) if not rdtest.value_compare(actual, expected, eps=1.0/256.0): raise rdtest.TestFailureException( "eventId {}, primitiveID {}: testing {} expected {}, got {}".format(modifs[i].eventId, modifs[i].primitiveID, events[i][c][0].__name__, expected, actual))