From aebb1f90ee90488f4911a434e0b88c54ac0250d0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 16 Mar 2026 15:12:30 +0000 Subject: [PATCH] Fix AoS output in mesh viewer being incorrect with mesh shaders * This would generally only come up with gl_PerVertex, and was hidden before because gl_Position is the first member. It likely would also reproduce if a user had a struct output from the mesh shader. --- .../driver/shaders/spirv/spirv_reflect.cpp | 6 +++ util/test/demos/vk/vk_mesh_shader.cpp | 53 ++++++++++++++++++- util/test/tests/Vulkan/VK_Mesh_Shader.py | 26 ++++++++- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index abe47620b..0071adff2 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -1284,9 +1284,13 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st // move to the inner struct if this is an array of structs - e.g. for arrayed shader outputs const DataType *structType = &baseType; + bool arrayOfStructsBase = false; if(structType->type == DataType::ArrayType && dataTypes[structType->InnerType()].type == DataType::StructType) + { structType = &dataTypes[structType->InnerType()]; + arrayOfStructsBase = true; + } // if this is a struct variable then either all members must be builtins, or none of them, as // per the SPIR-V Decoration rules: @@ -1339,6 +1343,8 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st SPIRVInterfaceAccess patch; patch.accessChain = {i}; + if(arrayOfStructsBase) + patch.accessChain.insert(0, 0); uint32_t dummy = 0; AddSignatureParameter(isInput, stage, global.id, structType->id, dummy, patch, diff --git a/util/test/demos/vk/vk_mesh_shader.cpp b/util/test/demos/vk/vk_mesh_shader.cpp index d117db74f..27416e562 100644 --- a/util/test/demos/vk/vk_mesh_shader.cpp +++ b/util/test/demos/vk/vk_mesh_shader.cpp @@ -174,6 +174,42 @@ void main() } } +)EOSHADER"; + + std::string point_mesh = R"EOSHADER( + +#version 460 +#extension GL_EXT_mesh_shader : require + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(points, max_vertices = 2, max_primitives = 2) out; +layout(location = 0) out vec4 outColor[]; + +void main() +{ + uint primCount = 2; + uint vertexCount = 1 * primCount; + + SetMeshOutputsEXT(vertexCount, primCount); + + for (uint i = 0; i < primCount; ++i) + { + uint vertIdx = i * 1; + uint tri = i + 2 * gl_WorkGroupID.x; + vec4 org = vec4(0.21, 0.0, 0.0, 0.0) * tri; + + uint vert0 = 0 + vertIdx; + + gl_MeshVerticesEXT[vert0].gl_Position = vec4(-0.4, -0.4, 0.0, 1.0) + org; + gl_MeshVerticesEXT[vert0].gl_PointSize = 20.0f; + + outColor[vert0] = vec4(0.0, 1.0, 0.0, 1.0); + + gl_PrimitivePointIndicesEXT[i] = vert0; + } +} + )EOSHADER"; std::string pixel = R"EOSHADER( @@ -244,8 +280,8 @@ void main() pipeCreateInfo.layout = layout; pipeCreateInfo.renderPass = mainWindow->rp; - VkPipeline pipelines[2]; - int countTasks[2]; + VkPipeline pipelines[3]; + int countTasks[3]; pipeCreateInfo.stages = { CompileShaderModule(simple_mesh, ShaderLang::glsl, ShaderStage::mesh, "main", {}, @@ -275,6 +311,19 @@ void main() pipelines[1] = createGraphicsPipeline(vkPipeCreateInfo); countTasks[1] = 1; + pipeCreateInfo.stages = { + CompileShaderModule(point_mesh, ShaderLang::glsl, ShaderStage::mesh, "main", {}, + SPIRVTarget::vulkan12), + CompileShaderModule(pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + vkPipeCreateInfo = pipeCreateInfo; + vkPipeCreateInfo->pVertexInputState = NULL; + vkPipeCreateInfo->pInputAssemblyState = NULL; + + pipelines[2] = createGraphicsPipeline(vkPipeCreateInfo); + countTasks[2] = 3; + while(Running()) { VkCommandBuffer cmd = GetCommandBuffer(); diff --git a/util/test/tests/Vulkan/VK_Mesh_Shader.py b/util/test/tests/Vulkan/VK_Mesh_Shader.py index 08393fdbb..5818f4b8e 100644 --- a/util/test/tests/Vulkan/VK_Mesh_Shader.py +++ b/util/test/tests/Vulkan/VK_Mesh_Shader.py @@ -69,7 +69,7 @@ class VK_Mesh_Shader(rdtest.TestCase): y -= 100 action = action.next - name = f"Amplification Shader with Local Payload EID:{action.eventId}" + name = f"Task Shader with Local Payload EID:{action.eventId}" rdtest.log.begin_section(name) self.controller.SetFrameEvent(action.eventId, False) @@ -84,3 +84,27 @@ class VK_Mesh_Shader(rdtest.TestCase): self.check_mesh_data(postms_ref, postms_data) self.check_debug_pixel(x, y) rdtest.log.end_section(name) + + name = f"Mesh Shader with Points output" + + with rdtest.log.auto_section(name): + action = action.next + self.controller.SetFrameEvent(action.eventId, False) + x = 290 + y = 90 + + color = [0.0, 1.0, 0.0, 1.0] + postms_ref = {} + for i in range(6): + postms_ref[i] = { + 'vtx': i, + 'idx': i, + 'gl_Position': [-0.4+0.21*i, -0.4, 0.0, 1.0], + 'gl_PointSize': 20.0, + 'outColor': color, + } + postms_data = self.get_postvs(action, rd.MeshDataStage.MeshOut, 0, action.numIndices) + self.check_mesh_data(postms_ref, postms_data) + self.check_debug_pixel(x, y) + rdtest.log.end_section(name) +