| /* |
| * Copyright (c) 2015-2025 The Khronos Group Inc. |
| * Copyright (c) 2015-2025 Valve Corporation |
| * Copyright (c) 2015-2025 LunarG, Inc. |
| * Copyright (c) 2015-2025 Google, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| */ |
| |
| #include "../framework/layer_validation_tests.h" |
| #include "../framework/pipeline_helper.h" |
| #include "../framework/descriptor_helper.h" |
| #include "../framework/render_pass_helper.h" |
| #include "shader_helper.h" |
| |
| class PositiveShaderInterface : public VkLayerTest {}; |
| |
| TEST_F(PositiveShaderInterface, InputAndOutputComponents) { |
| TEST_DESCRIPTION("Test shader layout in and out with different components."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| |
| layout(location = 0, component = 0) out vec2 rg; |
| layout(location = 0, component = 2) out float b; |
| |
| layout(location = 1, component = 0) out float r; |
| layout(location = 1, component = 1) out vec3 gba; |
| |
| layout(location = 2) out vec4 out_color_0; |
| layout(location = 3) out vec4 out_color_1; |
| |
| layout(location = 4, component = 0) out float x; |
| layout(location = 4, component = 1) out vec2 yz; |
| layout(location = 4, component = 3) out float w; |
| |
| layout(location = 5, component = 0) out vec3 stp; |
| layout(location = 5, component = 3) out float q; |
| |
| layout(location = 6, component = 0) out vec2 cd; |
| layout(location = 6, component = 2) out float e; |
| layout(location = 6, component = 3) out float f; |
| |
| layout(location = 7, component = 0) out float ar1; |
| layout(location = 7, component = 3) out float ar3; |
| |
| void main() { |
| vec2 xy = vec2((gl_VertexIndex >> 1u) & 1u, gl_VertexIndex & 1u); |
| gl_Position = vec4(xy, 0.0f, 1.0f); |
| out_color_0 = vec4(1.0f, 0.0f, 1.0f, 0.0f); |
| out_color_1 = vec4(0.0f, 1.0f, 0.0f, 1.0f); |
| rg = vec2(0.25f, 0.75f); |
| b = 0.5f; |
| r = 0.75f; |
| gba = vec3(1.0f); |
| x = 1.0f; |
| yz = vec2(0.25f); |
| w = 0.5f; |
| stp = vec3(1.0f); |
| q = 0.1f; |
| ar1 = 1.0f; |
| ar3 = 1.0f; |
| } |
| )glsl"; |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| |
| layout(location = 0, component = 0) in float r; |
| layout(location = 0, component = 1) in vec2 gb; |
| |
| layout(location = 1, component = 0) in float r1; |
| layout(location = 1, component = 1) in float g1; |
| layout(location = 1, component = 2) in float b1; |
| layout(location = 1, component = 3) in float a1; |
| |
| layout(location = 2) in InputBlock { |
| layout(location = 3, component = 3) float one_alpha; |
| layout(location = 2, component = 3) float zero_alpha; |
| layout(location = 3, component = 2) float one_blue; |
| layout(location = 2, component = 2) float zero_blue; |
| layout(location = 3, component = 1) float one_green; |
| layout(location = 2, component = 1) float zero_green; |
| layout(location = 3, component = 0) float one_red; |
| layout(location = 2, component = 0) float zero_red; |
| } inBlock; |
| |
| layout(location = 4, component = 0) in vec2 xy; |
| layout(location = 4, component = 2) in vec2 zw; |
| |
| layout(location = 5, component = 0) in vec4 st; |
| |
| layout(location = 6, component = 0) in vec4 cdef; |
| |
| layout(location = 7, component = 0) in float ar1; |
| layout(location = 7, component = 3) in float ar4; |
| |
| layout (location = 0) out vec4 color; |
| |
| void main() { |
| color = vec4(r, gb, 1.0f) * |
| vec4(r1, g1, 1.0f, a1) * |
| vec4(inBlock.zero_red, inBlock.zero_green, inBlock.zero_blue, inBlock.zero_alpha) * |
| vec4(inBlock.one_red, inBlock.one_green, inBlock.one_blue, inBlock.one_alpha) * |
| vec4(xy, zw) * st * cdef * vec4(ar1, ar1, ar1, ar4); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, InputAndOutputStructComponents) { |
| TEST_DESCRIPTION("Test shader interface with structs."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| |
| struct R { |
| vec4 rgba; |
| }; |
| |
| layout(location = 0) out R color[3]; |
| |
| void main() { |
| color[0].rgba = vec4(1.0f); |
| color[1].rgba = vec4(0.5f); |
| color[2].rgba = vec4(0.75f); |
| } |
| )glsl"; |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| |
| struct R { |
| vec4 rgba; |
| }; |
| |
| layout(location = 0) in R inColor[3]; |
| |
| layout (location = 0) out vec4 color; |
| |
| void main() { |
| color = inColor[0].rgba * inColor[1].rgba * inColor[2].rgba; |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, RelaxedBlockLayout) { |
| // This is a positive test, no errors expected |
| // Verifies the ability to relax block layout rules with a shader that requires them to be relaxed |
| TEST_DESCRIPTION("Create a shader that requires relaxed block layout."); |
| |
| AddRequiredExtensions(VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| // Vertex shader requiring relaxed layout. |
| // Without relaxed layout, we would expect a message like: |
| // "Structure id 2 decorated as Block for variable in Uniform storage class |
| // must follow standard uniform buffer layout rules: member 1 at offset 4 is not aligned to 16" |
| |
| const char *spv_source = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| VkShaderObj vs(*m_device, spv_source, VK_SHADER_STAGE_VERTEX_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); |
| } |
| |
| TEST_F(PositiveShaderInterface, UboStd430Layout) { |
| // This is a positive test, no errors expected |
| // Verifies the ability to scalar block layout rules with a shader that requires them to be relaxed |
| TEST_DESCRIPTION("Create a shader that requires UBO std430 layout."); |
| AddRequiredExtensions(VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::uniformBufferStandardLayout); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| // Vertex shader requiring std430 in a uniform buffer. |
| // Without uniform buffer standard layout, we would expect a message like: |
| // "Structure id 3 decorated as Block for variable in Uniform storage class |
| // must follow standard uniform buffer layout rules: member 0 is an array |
| // with stride 4 not satisfying alignment to 16" |
| |
| const char *spv_source = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 460 |
| OpDecorate %_arr_float_uint_8 ArrayStride 4 |
| OpMemberDecorate %foo 0 Offset 0 |
| OpDecorate %foo Block |
| OpDecorate %b DescriptorSet 0 |
| OpDecorate %b Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %uint_8 = OpConstant %uint 8 |
| %_arr_float_uint_8 = OpTypeArray %float %uint_8 |
| %foo = OpTypeStruct %_arr_float_uint_8 |
| %_ptr_Uniform_foo = OpTypePointer Uniform %foo |
| %b = OpVariable %_ptr_Uniform_foo Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| VkShaderObj::CreateFromASM(this, spv_source, VK_SHADER_STAGE_VERTEX_BIT); |
| } |
| |
| TEST_F(PositiveShaderInterface, ScalarBlockLayout) { |
| // This is a positive test, no errors expected |
| // Verifies the ability to scalar block layout rules with a shader that requires them to be relaxed |
| TEST_DESCRIPTION("Create a shader that requires scalar block layout."); |
| AddRequiredExtensions(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::scalarBlockLayout); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| // Vertex shader requiring scalar layout. |
| // Without scalar layout, we would expect a message like: |
| // "Structure id 2 decorated as Block for variable in Uniform storage class |
| // must follow standard uniform buffer layout rules: member 1 at offset 4 is not aligned to 16" |
| |
| const char *spv_source = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource GLSL 450 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 4 |
| OpMemberDecorate %S 2 Offset 8 |
| OpDecorate %S Block |
| OpDecorate %B DescriptorSet 0 |
| OpDecorate %B Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %S = OpTypeStruct %float %float %v3float |
| %_ptr_Uniform_S = OpTypePointer Uniform %S |
| %B = OpVariable %_ptr_Uniform_S Uniform |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| VkShaderObj vs(*m_device, spv_source, VK_SHADER_STAGE_VERTEX_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); |
| } |
| |
| TEST_F(PositiveShaderInterface, FragmentOutputNotWrittenButMasked) { |
| TEST_DESCRIPTION( |
| "Test that no error is produced when the fragment shader fails to declare an output, but the corresponding attachment's " |
| "write mask is 0."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| void main() {} |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_[1] = fs.GetStageCreateInfo(); |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, RelaxedTypeMatch) { |
| TEST_DESCRIPTION( |
| "Test that pipeline validation accepts the relaxed type matching rules set out in VK_KHR_maintenance4 (default in Vulkan " |
| "1.3) device extension:" |
| "fundamental type must match, and producer side must have at least as many components"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_1); // At least 1.1 is required for maintenance4 |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::maintenance4); |
| RETURN_IF_SKIP(Init()); |
| |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location=0) out vec3 x; |
| layout(location=1) out ivec3 y; |
| layout(location=2) out vec3 z; |
| void main(){ |
| gl_Position = vec4(0); |
| x = vec3(0); y = ivec3(0); z = vec3(0); |
| } |
| )glsl"; |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) out vec4 color; |
| layout(location=0) in float x; |
| layout(location=1) flat in int y; |
| layout(location=2) in vec2 z; |
| void main(){ |
| color = vec4(1 + x + y + z.x); |
| } |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, TessPerVertex) { |
| TEST_DESCRIPTION("Test that pipeline validation accepts per-vertex variables passed between the TCS and TES stages"); |
| |
| AddRequiredFeature(vkt::Feature::tessellationShader); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *tcsSource = R"glsl( |
| #version 450 |
| layout(location=0) out int x[]; |
| layout(vertices=3) out; |
| void main(){ |
| gl_TessLevelOuter[0] = gl_TessLevelOuter[1] = gl_TessLevelOuter[2] = 1; |
| gl_TessLevelInner[0] = 1; |
| x[gl_InvocationID] = gl_InvocationID; |
| } |
| )glsl"; |
| const char *tesSource = R"glsl( |
| #version 450 |
| layout(triangles, equal_spacing, cw) in; |
| layout(location=0) in int x[]; |
| void main(){ |
| gl_Position.xyz = gl_TessCoord; |
| gl_Position.w = x[0] + x[1] + x[2]; |
| } |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, kMinimalShaderGlsl, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj tcs(*m_device, tcsSource, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT); |
| VkShaderObj tes(*m_device, tesSource, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT); |
| VkShaderObj fs(*m_device, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0, |
| VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE}; |
| |
| VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, nullptr, 0, 3}; |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.gp_ci_.pTessellationState = &tsci; |
| pipe.gp_ci_.pInputAssemblyState = &iasci; |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), tcs.GetStageCreateInfo(), tes.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, GeometryInputBlockPositive) { |
| TEST_DESCRIPTION( |
| "Test that pipeline validation accepts a user-defined interface block passed into the geometry shader. This is interesting " |
| "because the 'extra' array level is not present on the member type, but on the block instance."); |
| |
| AddRequiredFeature(vkt::Feature::geometryShader); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| |
| layout(location = 0) out VertexData { vec4 x; } gs_out; |
| |
| void main(){ |
| gs_out.x = vec4(1.0f); |
| } |
| )glsl"; |
| |
| const char *gsSource = R"glsl( |
| #version 450 |
| layout(triangles) in; |
| layout(triangle_strip, max_vertices=3) out; |
| layout(location=0) in VertexData { vec4 x; } gs_in[]; |
| void main() { |
| gl_Position = gs_in[0].x; |
| EmitVertex(); |
| } |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj gs(*m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT); |
| VkShaderObj fs(*m_device, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, InputAttachment) { |
| TEST_DESCRIPTION("Positive test for a correctly matched input attachment"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput x; |
| layout(location=0) out vec4 color; |
| void main() { |
| color = subpassLoad(x); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| VkDescriptorSetLayoutBinding dslb = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| const vkt::DescriptorSetLayout dsl(*m_device, {dslb}); |
| const vkt::PipelineLayout pl(*m_device, {&dsl}); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}); |
| rp.AddAttachmentReference({1, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddColorAttachment(0); |
| rp.AddInputAttachment(1); |
| rp.CreateRenderPass(); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_[1] = fs.GetStageCreateInfo(); |
| pipe.gp_ci_.layout = pl; |
| pipe.gp_ci_.renderPass = rp; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, InputAttachmentMissingNotRead) { |
| TEST_DESCRIPTION("Input Attachment would be missing, but it is not read from in shader"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| // layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[1]; |
| // layout(location=0) out vec4 color; |
| // void main() { |
| // // (not actually called) color = subpassLoad(xs[0]); |
| // } |
| const char *fsSource = R"( |
| OpCapability Shader |
| OpCapability InputAttachment |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %color |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %color Location 0 |
| OpDecorate %xs DescriptorSet 0 |
| OpDecorate %xs Binding 0 |
| OpDecorate %xs InputAttachmentIndex 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %color = OpVariable %_ptr_Output_v4float Output |
| %10 = OpTypeImage %float SubpassData 0 0 0 2 Unknown |
| %uint = OpTypeInt 32 0 |
| %uint_1 = OpConstant %uint 1 |
| %_arr_10_uint_1 = OpTypeArray %10 %uint_1 |
| %_ptr_UniformConstant__arr_10_uint_1 = OpTypePointer UniformConstant %_arr_10_uint_1 |
| %xs = OpVariable %_ptr_UniformConstant__arr_10_uint_1 UniformConstant |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 |
| %v2int = OpTypeVector %int 2 |
| %22 = OpConstantComposite %v2int %int_0 %int_0 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd)"; |
| |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, InputAttachmentArray) { |
| TEST_DESCRIPTION("Input Attachment array where need to follow the index into the array"); |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredFeature(vkt::Feature::fragmentStoresAndAtomics); |
| RETURN_IF_SKIP(Init()); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(m_render_target_fmt); |
| // index 0 is unused |
| rp.AddAttachmentReference({VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_GENERAL}); |
| // index 1 is is valid (for both color and input) |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| // index 2 and 3 point to same image as index 1 |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddInputAttachment(0); |
| rp.AddInputAttachment(1); |
| rp.AddInputAttachment(2); |
| rp.AddInputAttachment(3); |
| rp.AddColorAttachment(1); |
| rp.CreateRenderPass(); |
| |
| // use static array of 2 and index into element 1 to read |
| { |
| const char *fs_source = R"glsl( |
| #version 460 |
| layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[2]; |
| layout(location=0) out vec4 color; |
| void main() { |
| color = subpassLoad(xs[1]); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| helper.gp_ci_.renderPass = rp; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| // use undefined size array and index into element 1 to read |
| { |
| const char *fs_source = R"glsl( |
| #version 460 |
| layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[]; |
| layout(location=0) out vec4 color; |
| void main() { |
| color = subpassLoad(xs[1]); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| helper.gp_ci_.renderPass = rp; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| // Array of size 1 |
| // loads from index 0, but not the invalid index 0 since has offest of 3 |
| { |
| const char *fs_source = R"glsl( |
| #version 460 |
| layout(input_attachment_index=3, set=0, binding=0) uniform subpassInput xs[1]; |
| layout(location=0) out vec4 color; |
| void main() { |
| color = subpassLoad(xs[0]); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| helper.gp_ci_.renderPass = rp; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| // Index from non-zero |
| { |
| const char *fs_source = R"glsl( |
| #version 460 |
| layout(input_attachment_index=2, set=0, binding=0) uniform subpassInput xs[2]; |
| layout(location=0) out vec4 color; |
| void main() { |
| color = subpassLoad(xs[0]) + subpassLoad(xs[1]); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| helper.gp_ci_.renderPass = rp; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| } |
| |
| TEST_F(PositiveShaderInterface, InputAttachmentRuntimeArray) { |
| TEST_DESCRIPTION("Input Attachment array where need to follow the index into the array"); |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredFeature(vkt::Feature::fragmentStoresAndAtomics); |
| AddRequiredFeature(vkt::Feature::runtimeDescriptorArray); |
| AddRequiredFeature(vkt::Feature::shaderInputAttachmentArrayNonUniformIndexing); |
| RETURN_IF_SKIP(Init()); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(m_render_target_fmt); |
| // index 0 is unused |
| rp.AddAttachmentReference({VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_GENERAL}); |
| // index 1 is is valid (for both color and input) |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| // index 2 and 3 point to same image as index 1 |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddInputAttachment(0); |
| rp.AddInputAttachment(1); |
| rp.AddInputAttachment(2); |
| rp.AddInputAttachment(3); |
| rp.AddColorAttachment(1); |
| rp.CreateRenderPass(); |
| |
| // use OpTypeRuntimeArray and index into it |
| // This is something that is needed to be validated at draw time, so should not be an error |
| const char *fs_source = R"glsl( |
| #version 460 |
| #extension GL_EXT_nonuniform_qualifier : require |
| layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[]; |
| layout(set = 0, binding = 3) buffer ssbo { int rIndex; }; |
| layout(location=0) out vec4 color; |
| void main() { |
| color = subpassLoad(xs[nonuniformEXT(rIndex)]); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, |
| {3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}; |
| helper.gp_ci_.renderPass = rp; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| |
| } |
| TEST_F(PositiveShaderInterface, InputAttachmentDepthStencil) { |
| TEST_DESCRIPTION("Input Attachment sharing same variable, but different aspect"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| RETURN_IF_SKIP(Init()); |
| |
| const VkFormat ds_format = FindSupportedDepthStencilFormat(Gpu()); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(m_render_target_fmt); |
| rp.AddAttachmentDescription(ds_format); |
| // index 0 = color | index 1 = depth | index 2 = stencil |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddAttachmentReference({1, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddAttachmentReference({1, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddInputAttachment(0); |
| rp.AddInputAttachment(1); |
| rp.AddInputAttachment(2); |
| rp.AddColorAttachment(0); |
| rp.CreateRenderPass(); |
| |
| // Depth and Stencil use same index, but valid because differnet image aspect masks |
| const char *fs_source = R"glsl( |
| #version 460 |
| layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput i_color; |
| layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput i_depth; |
| layout(input_attachment_index = 1, set = 0, binding = 2) uniform usubpassInput i_stencil; |
| layout(location=0) out vec4 color; |
| |
| void main(void) |
| { |
| color = subpassLoad(i_color); |
| vec4 depth = subpassLoad(i_depth); |
| uvec4 stencil = subpassLoad(i_stencil); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, |
| {2, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}; |
| helper.gp_ci_.renderPass = rp; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, FragmentOutputNotConsumedButAlphaToCoverageEnabled) { |
| TEST_DESCRIPTION( |
| "Test that no warning is produced when writing to non-existing color attachment if alpha to coverage is enabled."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(0u); |
| |
| VkPipelineMultisampleStateCreateInfo ms_state_ci = vku::InitStructHelper(); |
| ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
| ms_state_ci.alphaToCoverageEnable = VK_TRUE; |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.ms_ci_ = ms_state_ci; |
| helper.cb_ci_.attachmentCount = 0; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, AlphaToCoverageOutputIndex0) { |
| TEST_DESCRIPTION("DualSource blend has two outputs at location zero, so Index 0 is the one that's required"); |
| AddRequiredFeature(vkt::Feature::dualSrcBlend); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(0u); |
| |
| const char *fs_src = R"glsl( |
| #version 460 |
| layout(location = 0, index = 0) out vec4 c0; |
| void main() { |
| c0 = vec4(0.0f); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fs_src, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| VkPipelineMultisampleStateCreateInfo ms_state_ci = vku::InitStructHelper(); |
| ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
| ms_state_ci.alphaToCoverageEnable = VK_TRUE; |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.ms_ci_ = ms_state_ci; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| // Spec doesn't clarify if this is valid or not |
| // https://gitlab.khronos.org/vulkan/vulkan/-/issues/3445 |
| TEST_F(PositiveShaderInterface, DISABLED_InputOutputMatch2) { |
| TEST_DESCRIPTION("Test matching vertex shader output with fragment shader input."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char vsSource[] = R"glsl(#version 450 |
| layout(location = 0) out vec2 v1; |
| layout(location = 1) out vec2 v2; |
| layout(location = 2) out vec2 v3; |
| |
| void main() { |
| v1 = vec2(0.0f); |
| v2 = vec2(1.0f); |
| v3 = vec2(0.5f); |
| } |
| )glsl"; |
| |
| const char fsSource[] = R"glsl(#version 450 |
| layout(location = 0) in mat3x2 v; |
| layout(location = 0) out vec4 color; |
| |
| void main() { |
| color = vec4(v[0][0], v[0][1], v[1][0], v[1][1]); |
| } |
| )glsl"; |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, InputOutputMatch) { |
| TEST_DESCRIPTION("Test matching vertex shader output with fragment shader input."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char vsSource[] = R"glsl(#version 450 |
| |
| layout(location = 0) in vec4 dEQP_Position; |
| layout(location = 1) in mat3 in0; |
| layout(location = 0) out vec4 v1; |
| layout(location = 1) out vec4 v2; |
| layout(location = 2) out vec4 v3; |
| layout(location = 3) out vec4 v4; |
| |
| void main() { |
| v1 = mat4(in0)[0]; |
| v2 = mat4(in0)[1]; |
| v3 = mat4(in0)[2]; |
| v4 = mat4(in0)[3]; |
| gl_Position = dEQP_Position; |
| } |
| )glsl"; |
| |
| const char fsSource[] = R"glsl(#version 450 |
| |
| bool isOk (mat4 a, mat4 b, float eps) { |
| vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); |
| return all(lessThanEqual(diff, vec4(eps))); |
| } |
| |
| layout(location = 0) in mat4 out0; |
| layout(set = 0, binding = 0) uniform block { mat4 ref_out0; }; |
| layout(location = 0) out vec4 color; |
| |
| void main() { |
| bool RES = isOk(out0, ref_out0, 0.05); |
| color = vec4(RES, RES, RES, 1.0); |
| } |
| )glsl"; |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| VkVertexInputBindingDescription vertex_input_binding_description{}; |
| vertex_input_binding_description.binding = 0; |
| vertex_input_binding_description.stride = 0; |
| vertex_input_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; |
| |
| VkVertexInputAttributeDescription vertex_input_attribute_descriptions[4]; |
| vertex_input_attribute_descriptions[0].location = 0; |
| vertex_input_attribute_descriptions[0].binding = 0; |
| vertex_input_attribute_descriptions[0].format = VK_FORMAT_R8G8B8A8_UNORM; |
| vertex_input_attribute_descriptions[0].offset = 0; |
| vertex_input_attribute_descriptions[1].location = 1; |
| vertex_input_attribute_descriptions[1].binding = 0; |
| vertex_input_attribute_descriptions[1].format = VK_FORMAT_R8G8B8A8_UNORM; |
| vertex_input_attribute_descriptions[1].offset = 32; |
| vertex_input_attribute_descriptions[2].location = 2; |
| vertex_input_attribute_descriptions[2].binding = 0; |
| vertex_input_attribute_descriptions[2].format = VK_FORMAT_R8G8B8A8_UNORM; |
| vertex_input_attribute_descriptions[2].offset = 64; |
| vertex_input_attribute_descriptions[3].location = 3; |
| vertex_input_attribute_descriptions[3].binding = 0; |
| vertex_input_attribute_descriptions[3].format = VK_FORMAT_R8G8B8A8_UNORM; |
| vertex_input_attribute_descriptions[3].offset = 96; |
| |
| OneOffDescriptorSet ds( |
| m_device, { |
| {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, |
| }); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.vi_ci_.vertexBindingDescriptionCount = 1; |
| pipe.vi_ci_.pVertexBindingDescriptions = &vertex_input_binding_description; |
| pipe.vi_ci_.vertexAttributeDescriptionCount = 4; |
| pipe.vi_ci_.pVertexAttributeDescriptions = vertex_input_attribute_descriptions; |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&ds.layout_}); |
| pipe.CreateGraphicsPipeline(); |
| |
| vkt::Buffer uniform_buffer(*m_device, 1024, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); |
| ds.WriteDescriptorBufferInfo(0, uniform_buffer, 0, 1024); |
| ds.UpdateDescriptorSets(); |
| |
| vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); |
| VkBuffer buffer_handle = buffer; |
| VkDeviceSize offset = 0; |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); |
| |
| vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &buffer_handle, &offset); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_, 0, 1, &ds.set_, 0, nullptr); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); |
| vk::CmdDraw(m_command_buffer, 3, 1, 0, 0); |
| |
| m_command_buffer.EndRenderPass(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveShaderInterface, NestedStructs) { |
| TEST_DESCRIPTION("Use nested structs between shaders."); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char vsSource[] = R"glsl( |
| #version 450 |
| struct TestStruct { |
| vec2 dummy; |
| vec4 variableInStruct; |
| }; |
| layout(location = 0) out block { |
| vec2 dummy; |
| TestStruct structInBlock; |
| } testBlock; |
| void main(void) {} |
| )glsl"; |
| |
| const char fsSource[] = R"glsl( |
| #version 450 |
| layout(location = 0) out vec4 color; |
| struct TestStruct { |
| vec2 dummy; |
| vec4 variableInStruct; |
| }; |
| layout(location = 0) in block { |
| vec2 dummy; |
| noperspective TestStruct structInBlock; |
| } testBlock; |
| void main(void) {} |
| )glsl"; |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, AlphaToCoverageOffsetToAlpha) { |
| TEST_DESCRIPTION("Only set the needed component and nothing else."); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(0u); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0, component = 3) out float x; |
| void main(){ |
| x = 1.0; |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| VkPipelineMultisampleStateCreateInfo ms_state_ci = vku::InitStructHelper(); |
| ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
| ms_state_ci.alphaToCoverageEnable = VK_TRUE; |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.ms_ci_ = ms_state_ci; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, AlphaToCoverageArray) { |
| TEST_DESCRIPTION("Have array out outputs"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(0u); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| // Just need to declare variable |
| layout(location=0) out vec4 fragData[4]; |
| void main() { |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| VkPipelineMultisampleStateCreateInfo ms_state_ci = vku::InitStructHelper(); |
| ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
| ms_state_ci.alphaToCoverageEnable = VK_TRUE; |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| helper.ms_ci_ = ms_state_ci; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, VsFsTypeMismatchBlockStructArray) { |
| TEST_DESCRIPTION("Have an struct inside a block between shaders"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| if (m_device->Physical().limits_.maxVertexOutputComponents <= 64) { |
| GTEST_SKIP() << "maxVertexOutputComponents is too low"; |
| } |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| struct S { |
| float b; |
| int[2] c; |
| }; |
| |
| out block { |
| layout(location=0) float x; |
| layout(location=6) S[3] y; // difference, but can have extra output locations |
| layout(location=16) int[4] z; |
| } outBlock; |
| |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| struct S { |
| float b; |
| int[2] c; |
| }; |
| |
| in block { |
| layout(location=0) float x; |
| layout(location=6) S[2] y; |
| layout(location=16) int[4] z; |
| } inBlock; |
| |
| layout(location=0) out vec4 color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, VsFsTypeMismatchBlockNestedStructLastElementArray) { |
| TEST_DESCRIPTION("Have nested struct inside a block between shaders"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| struct A { |
| float a0_; |
| }; |
| struct B { |
| int b0_; |
| A b1_[2]; // on last element, so just a dangling vertex output |
| }; |
| struct C { |
| vec4 c0_[2]; |
| A c1_; |
| B c2_; |
| }; |
| |
| out block { |
| layout(location=0) float x; |
| layout(location=1) C y; |
| } outBlock; |
| |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| struct A { |
| float a0_; |
| }; |
| struct B { |
| int b0_; |
| A b1_; |
| }; |
| struct C { |
| vec4 c0_[2]; |
| A c1_; |
| B c2_; |
| }; |
| |
| in block { |
| layout(location=0) float x; |
| layout(location=1) C y; |
| } inBlock; |
| |
| layout(location=0) out vec4 color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, VsFsTypeMismatchBlockNestedStructArray) { |
| TEST_DESCRIPTION("Have nested struct inside a block between shaders"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| struct A { |
| float a0_; |
| }; |
| struct B { |
| int b0_; |
| A b1_[2][3]; |
| }; |
| struct C { |
| vec4 c0_[2]; |
| A c1_; |
| B c2_; |
| }; |
| |
| out block { |
| layout(location=0) float x; |
| layout(location=1) C y; |
| } outBlock; |
| |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| struct A { |
| float a0_; |
| }; |
| struct B { |
| int b0_; |
| A b1_[3][2]; |
| }; |
| struct C { |
| vec4 c0_[2]; |
| A c1_; |
| B c2_; |
| }; |
| |
| in block { |
| layout(location=0) float x; |
| layout(location=1) C y; |
| } inBlock; |
| |
| layout(location=0) out vec4 color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, MultidimensionalArray) { |
| TEST_DESCRIPTION("Make sure multidimensional arrays are handled"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location=0) out float[4][2][2] x; |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) in float[4][2][2] x; |
| layout(location=0) out float color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, MultidimensionalArrayVertex) { |
| TEST_DESCRIPTION("multidimensional arrays but have lingering vertex output"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| if (m_device->Physical().limits_.maxVertexOutputComponents <= 64) { |
| GTEST_SKIP() << "maxVertexOutputComponents is too low"; |
| } |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location=0) out float[4][3][2] x; |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) in float[4][2][2] x; |
| layout(location=0) out float color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, MultidimensionalArrayDims) { |
| TEST_DESCRIPTION("multidimensional arrays but have lingering vertex output"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| if (m_device->Physical().limits_.maxVertexOutputComponents <= 64) { |
| GTEST_SKIP() << "maxVertexOutputComponents is too low"; |
| } |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location=0) out float[4][3][2] x; // 24 locations |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) in float[3][2][4] x; // 24 locations |
| layout(location=0) out float color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, MultidimensionalArrayDims2) { |
| TEST_DESCRIPTION("multidimensional arrays but have lingering vertex output"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| if (m_device->Physical().limits_.maxVertexOutputComponents <= 64) { |
| GTEST_SKIP() << "maxVertexOutputComponents is too low"; |
| } |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location=0) out float[4][3][2] x; // 24 locations |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) in float[24] x; |
| layout(location=0) out float color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, MultidimensionalArray64bit) { |
| TEST_DESCRIPTION("Make sure multidimensional arrays are handled for 64bits"); |
| |
| AddRequiredFeature(vkt::Feature::shaderFloat64); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| if (m_device->Physical().limits_.maxFragmentOutputAttachments < 9) { |
| GTEST_SKIP() << "maxFragmentOutputAttachments is too low"; |
| } |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| #extension GL_EXT_shader_explicit_arithmetic_types_float64 : enable |
| layout(location=0) out f64vec3[2][2] x; // take 2 locations each (total 8) |
| layout(location=8) out float y; |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| #extension GL_EXT_shader_explicit_arithmetic_types_float64 : enable |
| layout(location=0) flat in f64vec3[2][2] x; |
| layout(location=8) out float color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit); |
| } |
| |
| TEST_F(PositiveShaderInterface, MultipleFragmentAttachment) { |
| TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7923"); |
| RETURN_IF_SKIP(Init()); |
| m_errorMonitor->ExpectSuccess(kWarningBit | kErrorBit); |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) out vec4 color[2]; |
| void main() { |
| color[0] = vec4(1.0); |
| color[1] = vec4(1.0); |
| } |
| )glsl"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}); |
| rp.AddAttachmentReference({1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}); |
| rp.AddColorAttachment(0); |
| rp.AddColorAttachment(1); |
| rp.CreateRenderPass(); |
| |
| VkPipelineColorBlendAttachmentState cb_as[2]; |
| cb_as[0] = DefaultColorBlendAttachmentState(); |
| cb_as[1] = DefaultColorBlendAttachmentState(); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_[1] = fs.GetStageCreateInfo(); |
| pipe.cb_ci_.attachmentCount = 2; |
| pipe.cb_ci_.pAttachments = cb_as; |
| pipe.gp_ci_.renderPass = rp; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, MissingInputAttachmentIndex) { |
| TEST_DESCRIPTION("You don't need InputAttachmentIndex if using dynamicRenderingLocalRead"); |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::dynamicRendering); |
| AddRequiredFeature(vkt::Feature::dynamicRenderingLocalRead); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| // layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs; |
| // layout(location=0) out vec4 color; |
| // void main() { |
| // color = subpassLoad(xs); |
| // } |
| // |
| // missing OpDecorate %xs InputAttachmentIndex 0 |
| const char *fsSource = R"( |
| OpCapability Shader |
| OpCapability InputAttachment |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %color %xs |
| OpExecutionMode %main OriginUpperLeft |
| OpDecorate %color Location 0 |
| OpDecorate %xs DescriptorSet 0 |
| OpDecorate %xs Binding 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %color = OpVariable %_ptr_Output_v4float Output |
| %10 = OpTypeImage %float SubpassData 0 0 0 2 Unknown |
| %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 |
| %xs = OpVariable %_ptr_UniformConstant_10 UniformConstant |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %v2int = OpTypeVector %int 2 |
| %17 = OpConstantComposite %v2int %int_0 %int_0 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %13 = OpLoad %10 %xs |
| %18 = OpImageRead %v4float %13 %17 |
| OpStore %color %18 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddInputAttachment(0); |
| rp.CreateRenderPass(); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; |
| pipe.gp_ci_.renderPass = rp; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, FragmentOutputDynamicRenderingUnusedAttachments) { |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); |
| AddRequiredExtensions(VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::dynamicRendering); |
| AddRequiredFeature(vkt::Feature::dynamicRenderingUnusedAttachments); |
| RETURN_IF_SKIP(Init()); |
| m_errorMonitor->ExpectSuccess(kErrorBit | kWarningBit); |
| |
| VkPipelineRenderingCreateInfo pipeline_rendering_info = vku::InitStructHelper(); |
| |
| VkFormat color_formats[] = {VK_FORMAT_R8G8B8A8_UNORM}; |
| pipeline_rendering_info.colorAttachmentCount = 1; |
| pipeline_rendering_info.pColorAttachmentFormats = color_formats; |
| |
| CreatePipelineHelper pipe(*this, &pipeline_rendering_info); |
| pipe.CreateGraphicsPipeline(); |
| |
| VkFormat ds_format = FindSupportedDepthStencilFormat(Gpu()); |
| |
| vkt::Image color_image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| vkt::ImageView color_image_view = color_image.CreateView(); |
| |
| vkt::Image ds_image(*m_device, 32, 32, ds_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); |
| const vkt::ImageView depth_view(*m_device, ds_image.BasicViewCreatInfo(VK_IMAGE_ASPECT_DEPTH_BIT)); |
| |
| VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper(); |
| color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| color_attachment.imageView = color_image_view; |
| |
| VkRenderingAttachmentInfo depth_stencil_attachment = vku::InitStructHelper(); |
| depth_stencil_attachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| depth_stencil_attachment.imageView = depth_view; |
| |
| m_command_buffer.Begin(); |
| VkRenderingInfo begin_rendering_info = vku::InitStructHelper(); |
| begin_rendering_info.layerCount = 1; |
| begin_rendering_info.colorAttachmentCount = 1; |
| begin_rendering_info.pColorAttachments = &color_attachment; |
| begin_rendering_info.pDepthAttachment = &depth_stencil_attachment; |
| begin_rendering_info.renderArea = {{0, 0}, {1, 1}}; |
| m_command_buffer.BeginRendering(begin_rendering_info); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); |
| vk::CmdDraw(m_command_buffer, 1, 1, 0, 0); |
| m_command_buffer.EndRendering(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveShaderInterface, FragmentOutputTypeDynamicRenderingLocalReadTypeRemap) { |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::dynamicRendering); |
| AddRequiredFeature(vkt::Feature::dynamicRenderingLocalRead); |
| RETURN_IF_SKIP(Init()); |
| InitDynamicRenderTarget(VK_FORMAT_R8G8B8A8_SINT); |
| InitDynamicRenderTarget(VK_FORMAT_R8G8B8A8_UNORM); |
| m_errorMonitor->ExpectSuccess(kWarningBit | kErrorBit); |
| |
| const char *fs_source = R"glsl( |
| #version 450 |
| layout(location=0) out vec4 x; |
| void main(){ |
| x = vec4(1); |
| } |
| )glsl"; |
| |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| static constexpr uint32_t locations[] = {VK_ATTACHMENT_UNUSED, 0}; |
| VkRenderingAttachmentLocationInfo locations_info = vku::InitStructHelper(); |
| locations_info.colorAttachmentCount = std::size(locations); |
| locations_info.pColorAttachmentLocations = locations; |
| |
| static constexpr VkFormat color_formats[] = {VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R8G8B8A8_UNORM}; |
| VkPipelineRenderingCreateInfo pipeline_rendering_info = vku::InitStructHelper(&locations_info); |
| pipeline_rendering_info.colorAttachmentCount = std::size(color_formats); |
| pipeline_rendering_info.pColorAttachmentFormats = color_formats; |
| |
| VkPipelineColorBlendAttachmentState color_blend_attachments[2] = {}; |
| VkPipelineColorBlendStateCreateInfo cbi = vku::InitStructHelper(); |
| cbi.attachmentCount = 2u; |
| cbi.pAttachments = color_blend_attachments; |
| |
| CreatePipelineHelper pipe(*this, &pipeline_rendering_info); |
| pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.cb_attachments_.colorWriteMask = 0xf; // all components |
| pipe.gp_ci_.pColorBlendState = &cbi; |
| pipe.CreateGraphicsPipeline(); |
| |
| VkRenderingAttachmentInfo color_attachment[] = {vku::InitStructHelper(), vku::InitStructHelper()}; |
| color_attachment[0].imageView = GetDynamicRenderTarget(0); |
| color_attachment[0].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| color_attachment[1].imageView = GetDynamicRenderTarget(1); |
| color_attachment[1].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| VkRenderingInfo rendering_info = vku::InitStructHelper(); |
| rendering_info.colorAttachmentCount = std::size(color_attachment); |
| rendering_info.pColorAttachments = color_attachment; |
| rendering_info.layerCount = 1; |
| rendering_info.renderArea = GetRenderTargetArea(); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.BeginRendering(rendering_info); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); |
| vk::CmdSetRenderingAttachmentLocationsKHR(m_command_buffer, &locations_info); |
| vk::CmdDraw(m_command_buffer, 3, 1, 0, 0); |
| m_command_buffer.EndRendering(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveShaderInterface, NonZeroComponentArray) { |
| TEST_DESCRIPTION("From https://gitlab.khronos.org/vulkan/vulkan/-/issues/3558"); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location = 0, component = 1) out float[2] x; |
| void main() {} |
| )glsl"; |
| |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location = 0, component = 1) in float[2] x; |
| layout(location=0) out float color; |
| void main(){} |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, PackingInsideArray) { |
| TEST_DESCRIPTION( |
| "From https://gitlab.khronos.org/vulkan/vulkan/-/issues/3558, but then overturned in " |
| "https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/4719 and " |
| "https://gitlab.khronos.org/vulkan/vulkan/-/issues/4216"); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| // GLSL use to allow alias location of different types |
| // https://github.com/KhronosGroup/glslang/pull/3438 |
| // |
| // layout(location = 0, component = 1) out float[2] x; |
| // layout(location = 1, component = 0) out int y; |
| // layout(location = 1, component = 2) out int z; |
| const char *vs_source1 = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %x %y %z |
| OpDecorate %x Component 1 |
| OpDecorate %x Location 0 |
| OpDecorate %y Component 0 |
| OpDecorate %y Location 1 |
| OpDecorate %z Component 2 |
| OpDecorate %z Location 1 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_float_uint_2 = OpTypeArray %float %uint_2 |
| %_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 |
| %x = OpVariable %_ptr_Output__arr_float_uint_2 Output |
| %int = OpTypeInt 32 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %y = OpVariable %_ptr_Output_int Output |
| %z = OpVariable %_ptr_Output_int Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| VkShaderObj vs1(*m_device, vs_source1, VK_SHADER_STAGE_VERTEX_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); |
| |
| const char *vs_source2 = R"glsl( |
| #version 450 |
| layout(location = 0, component = 0) out float x; |
| layout(location = 0, component = 1) out float[2] y; |
| void main() {} |
| )glsl"; |
| |
| VkShaderObj vs2(*m_device, vs_source2, VK_SHADER_STAGE_VERTEX_BIT); |
| } |
| |
| TEST_F(PositiveShaderInterface, MeshFragment) { |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::meshShader); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char* ms_source = R"glsl( |
| #version 460 |
| #extension GL_EXT_mesh_shader : require |
| layout(max_vertices = 12) out; |
| layout(max_primitives = 4) out; |
| layout(triangles) out; |
| |
| layout(location = 0) out uint out_0[12]; |
| layout(location = 1) perprimitiveEXT out uint out_1[4]; |
| layout(location = 2) out float out_2[12]; |
| layout(location = 3) perprimitiveEXT out float out_3[4]; |
| |
| void main() { |
| SetMeshOutputsEXT(12,4); |
| out_0[0] = 0; |
| out_1[0] = 0; |
| out_2[0] = 0.0; |
| out_3[0] = 0.0; |
| gl_PrimitiveTriangleIndicesEXT[1] = uvec3(0,1,2); |
| } |
| )glsl"; |
| |
| const char* fs_source = R"glsl( |
| #version 460 |
| #extension GL_EXT_mesh_shader : require |
| |
| layout(location = 0) in flat uint in_0; |
| layout(location = 1) perprimitiveEXT flat in uint in_1; |
| layout(location = 2) in float in_2; |
| layout(location = 3) perprimitiveEXT in float in_3; |
| layout(location = 0) out vec4 c; |
| void main(){ |
| c = vec4(1); |
| if (in_0 == 0 && in_1 == 0 && in_2 == 1.0 && in_3 == 1.0) { |
| c = vec4(0); |
| } |
| } |
| )glsl"; |
| |
| VkShaderObj ms(*m_device, ms_source, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2); |
| VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_2); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {ms.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(PositiveShaderInterface, MeshFragmentSlang) { |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::meshShader); |
| RETURN_IF_SKIP(Init()); |
| RETURN_IF_SKIP(CheckSlangSupport()); |
| InitRenderTarget(); |
| |
| const char* slang_shader = R"slang( |
| struct MeshInOut { |
| float4 position : SV_Position; |
| float4 normal; |
| }; |
| |
| struct PerPrimitive { |
| float4 color : COLOR; |
| bool cull : SV_CullPrimitive; |
| }; |
| |
| [outputtopology("triangle")] |
| [numthreads(1, 1, 1)] |
| void mmain(out indices uint3 triangles[3], |
| out vertices MeshInOut verts[9], |
| out primitives PerPrimitive prims[3]) { |
| SetMeshOutputCounts(6, 2); |
| triangles[0] = uint3(0,0,0); |
| verts[0].position = float4(0); |
| verts[0].normal = float4(0); |
| prims[0].color = float4(0); |
| prims[0].cull = true; |
| } |
| |
| [shader("fragment")] |
| float4 fmain(MeshInOut input) : SV_Target |
| { |
| return input.position + input.normal; |
| } |
| )slang"; |
| |
| VkShaderObj ms(*m_device, slang_shader, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_SLANG, nullptr, "mmain"); |
| VkShaderObj fs(*m_device, slang_shader, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_SLANG, nullptr, "fmain"); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {ms.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.CreateGraphicsPipeline(); |
| } |