Update multiview test to check shader exported viewport

* This also has code to support the qcom implicit viewport, but we disable that
  path by default since it would break all the single-viewport tests.
This commit is contained in:
baldurk
2026-01-30 15:32:04 +00:00
parent a5d5e4d7fe
commit bd78fcc473
2 changed files with 183 additions and 20 deletions
+132 -18
View File
@@ -65,6 +65,32 @@ void main()
vertOut.col = vec4(0, 1, 0, 1);
}
)EOSHADER";
const std::string multiviewViewportVertex = common + R"EOSHADER(
#extension GL_ARB_shader_viewport_layer_array : require
layout(location = 0) in vec3 Position;
layout(location = 1) in vec4 Color;
layout(location = 2) in vec2 UV;
layout(location = 0) out v2f vertOut;
void main()
{
vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1);
gl_Position = vertOut.pos;
vertOut.col = Color;
vertOut.uv = vec4(UV.xy, 0, 1);
gl_ViewportIndex = gl_ViewIndex;
if (gl_ViewIndex == 0)
vertOut.col = vec4(1, 0, 0, 1);
if (gl_ViewIndex == 1)
vertOut.col = vec4(0, 1, 0, 1);
}
)EOSHADER";
const std::string multiViewGeom = common + R"EOSHADER(
@@ -128,7 +154,13 @@ void main()
void Prepare(int argc, char **argv)
{
features.geometryShader = VK_TRUE;
features.multiViewport = VK_TRUE;
devExts.push_back(VK_KHR_MULTIVIEW_EXTENSION_NAME);
optDevExts.push_back(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME);
// this extension is all-or-nothing and prevents us doing any other tests normally with single
// viewports
// optDevExts.push_back(VK_QCOM_MULTIVIEW_PER_VIEW_VIEWPORTS_EXTENSION_NAME);
VulkanGraphicsTest::Prepare(argc, argv);
@@ -143,6 +175,17 @@ void main()
geometryTest = multiview.multiviewGeometryShader == VK_TRUE;
devInfoNext = &multiview;
static VkPhysicalDeviceMultiviewPerViewViewportsFeaturesQCOM perviewQC = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_VIEWPORTS_FEATURES_QCOM,
};
if(hasExt(VK_QCOM_MULTIVIEW_PER_VIEW_VIEWPORTS_EXTENSION_NAME))
{
perviewQC.multiviewPerViewViewports = VK_TRUE;
perviewQC.pNext = (void *)devInfoNext;
devInfoNext = &perviewQC;
}
}
int main()
@@ -228,33 +271,72 @@ void main()
std::vector<std::string> testNames;
std::vector<VkPipeline> testPipes;
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("Vertex: viewIndex");
pipeCreateInfo.stages = {
CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(multiViewPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("Fragment: viewIndex");
size_t viewportChoosePipe = ~0U;
size_t viewportAutoPipe = ~0U;
if(geometryTest)
if(hasExt(VK_QCOM_MULTIVIEW_PER_VIEW_VIEWPORTS_EXTENSION_NAME))
{
pipeCreateInfo.stages = {
CompileShaderModule(multiviewVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(VKDefaultPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
pipeCreateInfo.viewportState.viewportCount = 2;
pipeCreateInfo.viewportState.scissorCount = 2;
viewportAutoPipe = testPipes.size();
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("viewportIndex auto");
}
else
{
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("Vertex: viewIndex");
pipeCreateInfo.stages = {
CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(multiViewPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("Fragment: viewIndex");
if(geometryTest)
{
pipeCreateInfo.stages = {
CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(VKDefaultPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
CompileShaderModule(multiViewGeom, ShaderLang::glsl, ShaderStage::geom, "main"),
};
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("Geometry: viewIndex");
}
pipeCreateInfo.stages = {
CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(VKDefaultPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
CompileShaderModule(multiViewGeom, ShaderLang::glsl, ShaderStage::geom, "main"),
};
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("Geometry: viewIndex");
}
testNames.push_back("No viewIndex");
pipeCreateInfo.stages = {
CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
CompileShaderModule(VKDefaultPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("No viewIndex");
if(hasExt(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME))
{
pipeCreateInfo.stages = {
CompileShaderModule(multiviewViewportVertex, ShaderLang::glsl, ShaderStage::vert,
"main"),
CompileShaderModule(VKDefaultPixel, ShaderLang::glsl, ShaderStage::frag, "main"),
};
pipeCreateInfo.viewportState.viewportCount = 2;
pipeCreateInfo.viewportState.scissorCount = 2;
viewportChoosePipe = testPipes.size();
testPipes.push_back(createGraphicsPipeline(pipeCreateInfo));
testNames.push_back("viewportIndex choice");
}
}
AllocatedBuffer vb(
this,
@@ -288,6 +370,38 @@ void main()
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, testPipes[i]);
vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport);
vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor);
if(i == viewportChoosePipe || i == viewportAutoPipe)
{
VkViewport v = mainWindow->viewport;
VkRect2D s = mainWindow->scissor;
VkViewport vs[2];
v.width /= 2.0f;
vs[0] = v;
// vkCmdSetViewport(cmd, 0, 1, &v);
v.x += v.width;
vs[1] = v;
// vkCmdSetViewport(cmd, 1, 1, &v);
vkCmdSetViewport(cmd, 0, 2, vs);
s.extent.width /= 2;
s.extent.width -= 150;
s.offset.x += 75;
s.extent.height -= 200;
s.offset.y += 100;
VkRect2D ss[2];
ss[0] = s;
// vkCmdSetScissor(cmd, 0, 1, &s);
s.offset.x += mainWindow->scissor.extent.width / 2;
ss[1] = s;
// vkCmdSetScissor(cmd, 1, 1, &s);
vkCmdSetScissor(cmd, 0, 2, ss);
}
vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});
setMarker(cmd, testNames[i]);
vkCmdDraw(cmd, 3, 1, 0, 0);
+51 -2
View File
@@ -14,7 +14,10 @@ class VK_Multi_View(rdtest.TestCase):
for test_name in ["Vertex: viewIndex", "Geometry: viewIndex", "Fragment: viewIndex", "No viewIndex"]:
rdtest.log.print("Test {}".format(test_name))
action: rd.ActionDescription = self.find_action(test_name).next
label = self.find_action(test_name)
if label is None:
continue
action = label.next
self.controller.SetFrameEvent(action.eventId, True)
pipe: rd.PipeState = self.controller.GetPipelineState()
@@ -45,6 +48,47 @@ class VK_Multi_View(rdtest.TestCase):
self.check_debug(vtx, idx, inst, view, postvs)
rdtest.log.print(f"View {view} Slice {slice} passed")
for test_name in ["viewportIndex choice"]:
rdtest.log.print("Test {}".format(test_name))
label = self.find_action(test_name)
if label is None:
continue
action = label.next
self.controller.SetFrameEvent(action.eventId, True)
pipe: rd.PipeState = self.controller.GetPipelineState()
if not pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo.debuggable:
raise rdtest.TestFailureException("Test {} shader can not be debugged".format(test_name))
for view in range(2):
if view == 0:
x, y = 100, 140
else:
x, y = 300, 140
# Debug the pixel shader
inputs = rd.DebugPixelInputs()
inputs.view = view
trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, inputs)
if trace.debugger is None:
self.controller.FreeTrace(trace)
raise rdtest.TestFailureException("Test {} view {} did not debug at all".format(test_name, view))
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)
slice = view + 1
sub = rd.Subresource(0, slice, 0)
self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4], sub=sub)
self.controller.FreeTrace(trace)
inst = 0
postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, instance=inst, view=view)
for vtx in range(action.numIndices):
idx = vtx
self.check_debug(vtx, idx, inst, view, postvs)
rdtest.log.print(f"View {view} Slice {slice} passed")
rdtest.log.success("All tests matched")
@@ -74,7 +118,12 @@ class VK_Multi_View(rdtest.TestCase):
"Output {} at vert {} (idx {}) instance {} view {} has different size ({} values) to expectation ({} values)"
.format(name, vtx, idx, inst, view, value.columns, len(expect)))
debugged = value.value.f32v[0:value.columns]
if value.type == rd.VarType.SInt:
debugged = value.value.s32v[0:value.columns]
elif value.type == rd.VarType.UInt:
debugged = value.value.u32v[0:value.columns]
else:
debugged = value.value.f32v[0:value.columns]
if not rdtest.value_compare(expect, debugged):
raise rdtest.TestFailureException(