mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-29 13:20:54 +00:00
06570ea7ad
* It's not particularly scalable and can be brittle to driver changes, and we can use targeted specific pixel tests to check what we really want - to see if the output has rendered correctly. * Overlay tests still check files directly - this is a future refactor to remove.
360 lines
16 KiB
Python
360 lines
16 KiB
Python
import rdtest
|
|
import struct
|
|
import renderdoc as rd
|
|
|
|
|
|
class VK_Indirect(rdtest.TestCase):
|
|
demos_test_name = 'VK_Indirect'
|
|
|
|
def check_overlay(self, eventId: int, out: rd.ReplayOutput, tex: rd.TextureDisplay, save_data: rd.TextureSave):
|
|
pipe: rd.PipeState = self.controller.GetPipelineState()
|
|
|
|
# Check that the highlight draw overlay is empty
|
|
tex.resourceId = pipe.GetOutputTargets()[0].resourceId
|
|
|
|
out.SetTextureDisplay(tex)
|
|
|
|
overlay_path = rdtest.get_tmp_path(str(self.overlay_idx) + '_draw.png')
|
|
ref_path = self.get_ref_path(str(self.overlay_idx) + '_draw.png')
|
|
|
|
save_data.resourceId = out.GetDebugOverlayTexID()
|
|
|
|
self.controller.SaveTexture(save_data, overlay_path)
|
|
|
|
if not rdtest.png_compare(overlay_path, ref_path):
|
|
raise rdtest.TestFailureException("Reference and output image differ @ EID {}".format(str(eventId)),
|
|
ref_path, overlay_path)
|
|
|
|
self.overlay_idx = self.overlay_idx + 1
|
|
|
|
def check_capture(self):
|
|
self.overlay_idx = 1
|
|
|
|
fill = self.find_draw("vkCmdFillBuffer")
|
|
|
|
self.check(fill is not None)
|
|
|
|
buffer_usage = {}
|
|
|
|
for usage in self.controller.GetUsage(fill.copyDestination):
|
|
usage: rd.EventUsage
|
|
if usage.eventId not in buffer_usage:
|
|
buffer_usage[usage.eventId] = []
|
|
buffer_usage[usage.eventId].append(usage.usage)
|
|
|
|
for level in ["Primary", "Secondary"]:
|
|
rdtest.log.print("Checking {} indirect calls".format(level))
|
|
|
|
final = self.find_draw("{}: Final".format(level))
|
|
|
|
indirect_count_root = self.find_draw("{}: KHR_draw_indirect_count".format(level))
|
|
|
|
self.controller.SetFrameEvent(final.eventId, False)
|
|
|
|
tex = self.controller.GetPipelineState().GetOutputTargets()[0].resourceId
|
|
|
|
# Check the top row, non indirect count and always present
|
|
self.check_pixel_value(tex, 60, 60, [1.0, 0.0, 0.0, 1.0])
|
|
self.check_pixel_value(tex, 100, 60, [0.0, 0.0, 1.0, 1.0])
|
|
self.check_pixel_value(tex, 145, 35, [1.0, 1.0, 0.0, 1.0])
|
|
self.check_pixel_value(tex, 205, 35, [0.0, 1.0, 1.0, 1.0])
|
|
|
|
# if present, check bottom row of indirect count
|
|
if indirect_count_root is not None:
|
|
self.check_pixel_value(tex, 60, 220, [0.0, 1.0, 0.0, 1.0])
|
|
self.check_pixel_value(tex, 100, 220, [1.0, 0.0, 1.0, 1.0])
|
|
self.check_pixel_value(tex, 145, 185, [0.5, 1.0, 0.0, 1.0])
|
|
self.check_pixel_value(tex, 205, 185, [0.5, 0.0, 1.0, 1.0])
|
|
|
|
dispatches = self.find_draw("{}: Dispatches".format(level))
|
|
|
|
# Set up a ReplayOutput and TextureSave for quickly testing the drawcall highlight overlay
|
|
out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100),
|
|
rd.ReplayOutputType.Texture)
|
|
|
|
self.check(out is not None)
|
|
|
|
tex = rd.TextureDisplay()
|
|
tex.overlay = rd.DebugOverlay.Drawcall
|
|
|
|
save_data = rd.TextureSave()
|
|
save_data.destType = rd.FileType.PNG
|
|
|
|
# Rewind to the start of the capture
|
|
draw: rd.DrawcallDescription = dispatches.children[0]
|
|
while draw.previous is not None:
|
|
draw = draw.previous
|
|
|
|
# Ensure we can select all draws
|
|
while draw is not None:
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
draw = draw.next
|
|
|
|
rdtest.log.success("Selected all {} draws".format(level))
|
|
|
|
self.check(dispatches and len(dispatches.children) == 3)
|
|
|
|
self.check(dispatches.children[0].dispatchDimension == [0,0,0])
|
|
self.check(dispatches.children[1].dispatchDimension == [1,1,1])
|
|
self.check(dispatches.children[2].dispatchDimension == [3,4,5])
|
|
|
|
rdtest.log.success("{} Indirect dispatches are the correct dimensions".format(level))
|
|
|
|
self.controller.SetFrameEvent(dispatches.children[2].eventId, False)
|
|
|
|
pipe: rd.PipeState = self.controller.GetPipelineState()
|
|
|
|
ssbo: rd.BoundResource = pipe.GetReadWriteResources(rd.ShaderStage.Compute)[0].resources[0]
|
|
data: bytes = self.controller.GetBufferData(ssbo.resourceId, 0, 0)
|
|
|
|
rdtest.log.print("Got {} bytes of uints".format(len(data)))
|
|
|
|
uints = [struct.unpack_from('=4L', data, offs) for offs in range(0, len(data), 16)]
|
|
|
|
for x in range(0, 6): # 3 groups of 2 threads each
|
|
for y in range(0, 8): # 3 groups of 2 threads each
|
|
for z in range(0, 5): # 5 groups of 1 thread each
|
|
idx = 100 + z*8*6 + y*6 + x
|
|
if not rdtest.value_compare(uints[idx], [x, y, z, 12345]):
|
|
raise rdtest.TestFailureException(
|
|
'expected thread index data @ {},{},{}: {} is not as expected: {}'
|
|
.format(x, y, z, uints[idx], [x, y, z, 12345]))
|
|
|
|
rdtest.log.success("Dispatched buffer contents are as expected for {}".format(level))
|
|
|
|
empties = self.find_draw("{}: Empty draws".format(level))
|
|
|
|
self.check(empties and len(empties.children) == 2)
|
|
|
|
draw: rd.DrawcallDescription
|
|
for draw in empties.children:
|
|
self.check(draw.numIndices == 0)
|
|
self.check(draw.numInstances == 0)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have empty PostVS
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut, 0, 1)
|
|
self.check(len(postvs_data) == 0)
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} empty draws are empty".format(level))
|
|
|
|
indirects = self.find_draw("{}: Indirect draws".format(level))
|
|
|
|
self.check('vkCmdDrawIndirect' in indirects.children[0].name)
|
|
self.check('vkCmdDrawIndexedIndirect' in indirects.children[1].name)
|
|
self.check(len(indirects.children[1].children) == 2)
|
|
|
|
rdtest.log.success("Correct number of {} indirect draws".format(level))
|
|
|
|
# vkCmdDrawIndirect(...)
|
|
draw = indirects.children[0]
|
|
self.check(draw.numIndices == 3)
|
|
self.check(draw.numInstances == 2)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
self.check(rd.ResourceUsage.Indirect in buffer_usage[draw.eventId])
|
|
|
|
# Check that we have PostVS as expected
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
postvs_ref = {
|
|
0: {'vtx': 0, 'idx': 0, 'gl_PerVertex.gl_Position': [-0.8, -0.5, 0.0, 1.0]},
|
|
1: {'vtx': 1, 'idx': 1, 'gl_PerVertex.gl_Position': [-0.7, -0.8, 0.0, 1.0]},
|
|
2: {'vtx': 2, 'idx': 2, 'gl_PerVertex.gl_Position': [-0.6, -0.5, 0.0, 1.0]},
|
|
}
|
|
|
|
self.check_mesh_data(postvs_ref, postvs_data)
|
|
self.check(len(postvs_data) == len(postvs_ref)) # We shouldn't have any extra vertices
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
|
|
self.check(rd.ResourceUsage.Indirect in buffer_usage[indirects.children[1].eventId])
|
|
|
|
# vkCmdDrawIndexedIndirect[0](...)
|
|
draw = indirects.children[1].children[0]
|
|
self.check(draw.numIndices == 3)
|
|
self.check(draw.numInstances == 3)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have PostVS as expected
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
# These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
|
|
# indices
|
|
postvs_ref = {
|
|
0: {'vtx': 0, 'idx': 0, 'gl_PerVertex.gl_Position': [-0.6, -0.5, 0.0, 1.0]},
|
|
1: {'vtx': 1, 'idx': 1, 'gl_PerVertex.gl_Position': [-0.5, -0.8, 0.0, 1.0]},
|
|
2: {'vtx': 2, 'idx': 2, 'gl_PerVertex.gl_Position': [-0.4, -0.5, 0.0, 1.0]},
|
|
}
|
|
|
|
self.check_mesh_data(postvs_ref, postvs_data)
|
|
self.check(len(postvs_data) == len(postvs_ref)) # We shouldn't have any extra vertices
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
|
|
# vkCmdDrawIndexedIndirect[1](...)
|
|
draw = indirects.children[1].children[1]
|
|
self.check(draw.numIndices == 6)
|
|
self.check(draw.numInstances == 2)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have PostVS as expected
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
postvs_ref = {
|
|
0: {'vtx': 0, 'idx': 0, 'gl_PerVertex.gl_Position': [-0.4, -0.5, 0.0, 1.0]},
|
|
1: {'vtx': 1, 'idx': 1, 'gl_PerVertex.gl_Position': [-0.3, -0.8, 0.0, 1.0]},
|
|
2: {'vtx': 2, 'idx': 2, 'gl_PerVertex.gl_Position': [-0.2, -0.8, 0.0, 1.0]},
|
|
|
|
3: {'vtx': 3, 'idx': 3, 'gl_PerVertex.gl_Position': [-0.1, -0.5, 0.0, 1.0]},
|
|
4: {'vtx': 4, 'idx': 4, 'gl_PerVertex.gl_Position': [ 0.0, -0.8, 0.0, 1.0]},
|
|
5: {'vtx': 5, 'idx': 5, 'gl_PerVertex.gl_Position': [ 0.1, -0.8, 0.0, 1.0]},
|
|
}
|
|
|
|
self.check_mesh_data(postvs_ref, postvs_data)
|
|
self.check(len(postvs_data) == len(postvs_ref)) # We shouldn't have any extra vertices
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
|
|
if indirect_count_root is not None:
|
|
self.check(indirect_count_root.children[0].name == '{}: Empty count draws'.format(level))
|
|
self.check(indirect_count_root.children[1].name == '{}: Indirect count draws'.format(level))
|
|
|
|
empties = indirect_count_root.children[0]
|
|
|
|
self.check(empties and len(empties.children) == 2)
|
|
|
|
draw: rd.DrawcallDescription
|
|
for draw in empties.children:
|
|
self.check(draw.numIndices == 0)
|
|
self.check(draw.numInstances == 0)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have empty PostVS
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut, 0, 1)
|
|
self.check(len(postvs_data) == 0)
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
# vkCmdDrawIndirectCountKHR
|
|
draw_indirect = indirect_count_root.children[1].children[0]
|
|
|
|
self.check(rd.ResourceUsage.Indirect in buffer_usage[draw_indirect.eventId])
|
|
|
|
self.check(draw_indirect and len(draw_indirect.children) == 1)
|
|
|
|
# vkCmdDrawIndirectCountKHR[0]
|
|
draw = draw_indirect.children[0]
|
|
|
|
self.check(draw.numIndices == 3)
|
|
self.check(draw.numInstances == 4)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have PostVS as expected
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
# These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
|
|
# indices
|
|
postvs_ref = {
|
|
0: {'vtx': 0, 'idx': 0, 'gl_PerVertex.gl_Position': [-0.8, 0.5, 0.0, 1.0]},
|
|
1: {'vtx': 1, 'idx': 1, 'gl_PerVertex.gl_Position': [-0.7, 0.2, 0.0, 1.0]},
|
|
2: {'vtx': 2, 'idx': 2, 'gl_PerVertex.gl_Position': [-0.6, 0.5, 0.0, 1.0]},
|
|
}
|
|
|
|
self.check_mesh_data(postvs_ref, postvs_data)
|
|
self.check(len(postvs_data) == len(postvs_ref)) # We shouldn't have any extra vertices
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
|
|
# vkCmdDrawIndexedIndirectCountKHR
|
|
draw_indirect = indirect_count_root.children[1].children[1]
|
|
|
|
self.check(draw_indirect and len(draw_indirect.children) == 3)
|
|
|
|
# vkCmdDrawIndirectCountKHR[0]
|
|
draw = draw_indirect.children[0]
|
|
self.check(draw.numIndices == 3)
|
|
self.check(draw.numInstances == 1)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have PostVS as expected
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
# These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
|
|
# indices
|
|
postvs_ref = {
|
|
0: {'vtx': 0, 'idx': 0, 'gl_PerVertex.gl_Position': [-0.6, 0.5, 0.0, 1.0]},
|
|
1: {'vtx': 1, 'idx': 1, 'gl_PerVertex.gl_Position': [-0.5, 0.2, 0.0, 1.0]},
|
|
2: {'vtx': 2, 'idx': 2, 'gl_PerVertex.gl_Position': [-0.4, 0.5, 0.0, 1.0]},
|
|
}
|
|
|
|
self.check_mesh_data(postvs_ref, postvs_data)
|
|
self.check(len(postvs_data) == len(postvs_ref)) # We shouldn't have any extra vertices
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
|
|
# vkCmdDrawIndirectCountKHR[1]
|
|
draw = draw_indirect.children[1]
|
|
self.check(draw.numIndices == 0)
|
|
self.check(draw.numInstances == 0)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
self.check(len(postvs_data) == 0)
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
|
|
# vkCmdDrawIndirectCountKHR[2]
|
|
draw = draw_indirect.children[2]
|
|
self.check(draw.numIndices == 6)
|
|
self.check(draw.numInstances == 2)
|
|
|
|
self.controller.SetFrameEvent(draw.eventId, False)
|
|
|
|
# Check that we have PostVS as expected
|
|
postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)
|
|
|
|
# These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
|
|
# indices
|
|
postvs_ref = {
|
|
0: {'vtx': 0, 'idx': 0, 'gl_PerVertex.gl_Position': [-0.4, 0.5, 0.0, 1.0]},
|
|
1: {'vtx': 1, 'idx': 1, 'gl_PerVertex.gl_Position': [-0.3, 0.2, 0.0, 1.0]},
|
|
2: {'vtx': 2, 'idx': 2, 'gl_PerVertex.gl_Position': [-0.2, 0.2, 0.0, 1.0]},
|
|
|
|
3: {'vtx': 3, 'idx': 3, 'gl_PerVertex.gl_Position': [-0.1, 0.5, 0.0, 1.0]},
|
|
4: {'vtx': 4, 'idx': 4, 'gl_PerVertex.gl_Position': [ 0.0, 0.2, 0.0, 1.0]},
|
|
5: {'vtx': 5, 'idx': 5, 'gl_PerVertex.gl_Position': [ 0.1, 0.2, 0.0, 1.0]},
|
|
}
|
|
|
|
self.check_mesh_data(postvs_ref, postvs_data)
|
|
self.check(len(postvs_data) == len(postvs_ref)) # We shouldn't have any extra vertices
|
|
|
|
self.check_overlay(draw.eventId, out, tex, save_data)
|
|
|
|
rdtest.log.success("{} {} is as expected".format(level, draw.name))
|
|
else:
|
|
rdtest.log.print("KHR_draw_indirect_count not tested")
|