blob: f8c494cc5ac8ced671eddea678a0bf878d0fc348 [file] [log] [blame]
/*
* Copyright (c) 2015-2023 The Khronos Group Inc.
* Copyright (c) 2015-2023 Valve Corporation
* Copyright (c) 2015-2023 LunarG, Inc.
* Copyright (c) 2015-2023 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"
TEST_F(VkPositiveLayerTest, TestShaderInputAndOutputComponents) {
TEST_DESCRIPTION("Test shader layout in and out with different components.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
char const *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 = 1) out float ar2[2];
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;
ar2[0] = 0.5f;
ar2[1] = 0.75f;
ar3 = 1.0f;
}
)glsl";
VkShaderObj vs(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
char const *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 = 1) in float ar2;
layout(location = 8, component = 1) in float ar3;
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, ar2, ar3, ar4);
}
)glsl";
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kPerformanceWarningBit | kErrorBit);
}
TEST_F(VkPositiveLayerTest, TestShaderInputAndOutputStructComponents) {
TEST_DESCRIPTION("Test shader interface with structs.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
char const *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(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kPerformanceWarningBit | 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(InitFramework())
RETURN_IF_SKIP(InitState())
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(this, 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_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR uniform_buffer_standard_layout_features = vku::InitStructHelper(NULL);
uniform_buffer_standard_layout_features.uniformBufferStandardLayout = VK_TRUE;
GetPhysicalDeviceFeatures2(uniform_buffer_standard_layout_features);
VkPhysicalDeviceFeatures2 set_features2 = vku::InitStructHelper(&uniform_buffer_standard_layout_features);
RETURN_IF_SKIP(InitState(nullptr, &set_features2));
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_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceScalarBlockLayoutFeaturesEXT scalar_block_features = vku::InitStructHelper(NULL);
GetPhysicalDeviceFeatures2(scalar_block_features);
VkPhysicalDeviceFeatures2 set_features2 = vku::InitStructHelper(&scalar_block_features);
RETURN_IF_SKIP(InitState(nullptr, &set_features2));
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(this, 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();
char const *fsSource = R"glsl(
#version 450
void main() {}
)glsl";
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(m_commandBuffer);
CreatePipelineHelper pipe(*this);
pipe.InitState();
pipe.shader_stages_[1] = fs.GetStageCreateInfo();
pipe.gp_ci_.layout = descriptorSet.GetPipelineLayout();
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);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4FeaturesKHR maintenance_4_features = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(maintenance_4_features);
RETURN_IF_SKIP(InitState(nullptr, &maintenance_4_features));
InitRenderTarget();
char const *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";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
TEST_F(PositiveShaderInterface, TessPerVertex) {
TEST_DESCRIPTION("Test that pipeline validation accepts per-vertex variables passed between the TCS and TES stages");
RETURN_IF_SKIP(Init())
InitRenderTarget();
if (!m_device->phy().features().tessellationShader) {
GTEST_SKIP() << "Device does not support tessellation shaders";
}
char const *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";
char const *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(this, kMinimalShaderGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj tcs(this, tcsSource, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
VkShaderObj tes(this, tesSource, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
VkShaderObj fs(this, 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.InitState();
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.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
if (!m_device->phy().features().geometryShader) {
GTEST_SKIP() << "Device does not support geometry shaders";
}
char const *vsSource = R"glsl(
#version 450
layout(location = 0) out VertexData { vec4 x; } gs_out;
void main(){
gs_out.x = vec4(1.0f);
}
)glsl";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj gs(this, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
TEST_F(PositiveShaderInterface, InputAttachment) {
TEST_DESCRIPTION("Positive test for a correctly matched input attachment");
RETURN_IF_SKIP(Init())
InitRenderTarget();
char const *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(this, 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});
VkAttachmentDescription descs[2] = {
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL},
};
VkAttachmentReference color = {
0,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkAttachmentReference input = {
1,
VK_IMAGE_LAYOUT_GENERAL,
};
VkSubpassDescription sd = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 1, &input, 1, &color, nullptr, nullptr, 0, nullptr};
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 2, descs, 1, &sd, 0, nullptr};
vkt::RenderPass rp(*m_device, rpci);
CreatePipelineHelper pipe(*this);
pipe.InitState();
pipe.shader_stages_[1] = fs.GetStageCreateInfo();
pipe.gp_ci_.layout = pl.handle();
pipe.gp_ci_.renderPass = rp.handle();
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(this, 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, 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);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan12Features features12 = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(features12);
RETURN_IF_SKIP(InitState(nullptr, &features12));
const VkAttachmentDescription inputAttachmentDescription = {0,
m_render_target_fmt,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_GENERAL};
// index 0 is unused
// index 1 is is valid (for both color and input)
// index 2 and 3 point to same image as index 1
const VkAttachmentReference inputAttachmentReferences[4] = {{VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_GENERAL},
{0, VK_IMAGE_LAYOUT_GENERAL},
{0, VK_IMAGE_LAYOUT_GENERAL},
{0, VK_IMAGE_LAYOUT_GENERAL}};
const VkSubpassDescription subpassDescription = {(VkSubpassDescriptionFlags)0,
VK_PIPELINE_BIND_POINT_GRAPHICS,
4,
inputAttachmentReferences,
1,
&inputAttachmentReferences[1],
nullptr,
nullptr,
0,
nullptr};
VkRenderPassCreateInfo renderPassInfo = vku::InitStructHelper();
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &inputAttachmentDescription;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDescription;
vkt::RenderPass renderPass(*m_device, renderPassInfo);
// use static array of 2 and index into element 1 to read
{
const char *fs_source = R"(
#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]);
}
)";
VkShaderObj fs(this, 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}};
helper.gp_ci_.renderPass = renderPass.handle();
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}
// use undefined size array and index into element 1 to read
{
const char *fs_source = R"(
#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]);
}
)";
VkShaderObj fs(this, 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}};
helper.gp_ci_.renderPass = renderPass.handle();
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}
// use OpTypeRuntimeArray and index into it
// This is something that is needed to be validated at draw time, so should not be an error
if (features12.runtimeDescriptorArray && features12.shaderInputAttachmentArrayNonUniformIndexing) {
const char *fs_source = R"(
#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)]);
}
)";
VkShaderObj fs(this, 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 = renderPass.handle();
};
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"(
#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]);
}
)";
VkShaderObj fs(this, 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}};
helper.gp_ci_.renderPass = renderPass.handle();
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}
// Index from non-zero
{
const char *fs_source = R"(
#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]);
}
)";
VkShaderObj fs(this, 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}};
helper.gp_ci_.renderPass = renderPass.handle();
};
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(InitFramework())
VkPhysicalDeviceVulkan12Features features12 = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(features12);
RETURN_IF_SKIP(InitState(nullptr, &features12));
const VkFormat ds_format = FindSupportedDepthStencilFormat(gpu());
const VkAttachmentDescription inputAttachmentDescriptions[2] = {
{0, m_render_target_fmt, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL},
{0, ds_format, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL}};
// index 0 = color | index 1 = depth | index 2 = stencil
const VkAttachmentReference inputAttachmentReferences[3] = {
{0, VK_IMAGE_LAYOUT_GENERAL}, {1, VK_IMAGE_LAYOUT_GENERAL}, {1, VK_IMAGE_LAYOUT_GENERAL}};
const VkSubpassDescription subpassDescription = {(VkSubpassDescriptionFlags)0,
VK_PIPELINE_BIND_POINT_GRAPHICS,
3,
inputAttachmentReferences,
1,
&inputAttachmentReferences[0],
nullptr,
nullptr,
0,
nullptr};
VkRenderPassCreateInfo renderPassInfo = vku::InitStructHelper();
renderPassInfo.attachmentCount = 2;
renderPassInfo.pAttachments = inputAttachmentDescriptions;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDescription;
vkt::RenderPass renderPass(*m_device, renderPassInfo);
// Depth and Stencil use same index, but valid because differnet image aspect masks
const char *fs_source = R"(
#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);
}
)";
VkShaderObj fs(this, 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 = renderPass.handle();
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}
TEST_F(VkPositiveLayerTest, 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.pipe_ms_state_ci_ = ms_state_ci;
helper.cb_ci_.attachmentCount = 0;
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit | kWarningBit);
}
// 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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
TEST_F(VkPositiveLayerTest, TestShaderInputOutputMatch) {
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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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.InitState();
pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&ds.layout_});
pipe.CreateGraphicsPipeline();
VkBufferCreateInfo ub_ci = vku::InitStructHelper();
ub_ci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
ub_ci.size = 1024;
vkt::Buffer uniform_buffer(*m_device, ub_ci);
ds.WriteDescriptorBufferInfo(0, uniform_buffer.handle(), 0, 1024);
ds.UpdateDescriptorSets();
VkBufferCreateInfo vb_ci = vku::InitStructHelper();
vb_ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
vb_ci.size = 1024;
vkt::Buffer buffer(*m_device, vb_ci);
VkBuffer buffer_handle = buffer.handle();
VkDeviceSize offset = 0;
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindVertexBuffers(m_commandBuffer->handle(), 0, 1, &buffer_handle, &offset);
vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_.handle(), 0, 1,
&ds.set_, 0, nullptr);
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
m_commandBuffer->EndRenderPass();
m_commandBuffer->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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
TEST_F(PositiveShaderInterface, AlphaToCoverageOffsetToAlpha) {
TEST_DESCRIPTION("Only set the needed component and nothing else.");
RETURN_IF_SKIP(Init())
InitRenderTarget(0u);
char const *fsSource = R"glsl(
#version 450
layout(location=0, component = 3) out float x;
void main(){
x = 1.0;
}
)glsl";
VkShaderObj fs(this, 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.pipe_ms_state_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);
char const *fsSource = R"glsl(
#version 450
// Just need to declare variable
layout(location=0) out vec4 fragData[4];
void main() {
}
)glsl";
VkShaderObj fs(this, 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.pipe_ms_state_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->phy().limits_.maxVertexOutputComponents <= 64) {
GTEST_SKIP() << "maxVertexOutputComponents is too low";
}
char const *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";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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();
char const *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";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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();
char const *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";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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();
char const *vsSource = R"glsl(
#version 450
layout(location=0) out float[4][2][2] x;
void main() {}
)glsl";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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->phy().limits_.maxVertexOutputComponents <= 64) {
GTEST_SKIP() << "maxVertexOutputComponents is too low";
}
char const *vsSource = R"glsl(
#version 450
layout(location=0) out float[4][3][2] x;
void main() {}
)glsl";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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->phy().limits_.maxVertexOutputComponents <= 64) {
GTEST_SKIP() << "maxVertexOutputComponents is too low";
}
char const *vsSource = R"glsl(
#version 450
layout(location=0) out float[4][3][2] x; // 24 locations
void main() {}
)glsl";
char const *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(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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->phy().limits_.maxVertexOutputComponents <= 64) {
GTEST_SKIP() << "maxVertexOutputComponents is too low";
}
char const *vsSource = R"glsl(
#version 450
layout(location=0) out float[4][3][2] x; // 24 locations
void main() {}
)glsl";
char const *fsSource = R"glsl(
#version 450
layout(location=0) in float[24] x;
layout(location=0) out float color;
void main(){}
)glsl";
VkShaderObj vs(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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");
RETURN_IF_SKIP(Init())
InitRenderTarget();
if (!m_device->phy().features().shaderFloat64) {
GTEST_SKIP() << "Device does not support 64bit floats";
}
char const *vsSource = R"glsl(
#version 450
#extension GL_EXT_shader_explicit_arithmetic_types_float64 : enable
layout(location=0) out f64vec3[2][2][3] x; // take 2 locations each (total 24)
layout(location=24) out float y;
void main() {}
)glsl";
char const *fsSource = R"glsl(
#version 450
#extension GL_EXT_shader_explicit_arithmetic_types_float64 : enable
layout(location=0) flat in f64vec3[2][3][2] x;
layout(location=24) out float color;
void main(){}
)glsl";
VkShaderObj vs(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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, PackingInsideArray) {
TEST_DESCRIPTION("From https://gitlab.khronos.org/vulkan/vulkan/-/issues/3558");
RETURN_IF_SKIP(Init())
InitRenderTarget();
char const *vsSource = R"glsl(
#version 450
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;
void main() {}
)glsl";
char const *fsSource = R"glsl(
#version 450
layout(location = 0, component = 1) in float x1;
layout(location = 1, component = 0) flat in int y;
layout(location = 1, component = 1) in float x2;
layout(location = 1, component = 2) flat in int z;
layout(location=0) out float color;
void main(){}
)glsl";
VkShaderObj vs(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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, PhysicalStorageBufferGlslang3) {
TEST_DESCRIPTION("Taken from glslang spv.bufferhandle3.frag test");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan12Features features12 = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(features12);
if (VK_TRUE != features12.bufferDeviceAddress) {
GTEST_SKIP() << "bufferDeviceAddress not supported and is required";
}
RETURN_IF_SKIP(InitState(nullptr, &features12));
char const *fsSource = R"glsl(
#version 450
#extension GL_EXT_buffer_reference : enable
layout(buffer_reference, std430) buffer t3 {
int h;
};
layout(set = 1, binding = 2, buffer_reference, std430) buffer t4 {
layout(offset = 0) int j;
t3 k;
} x;
layout(set = 0, binding = 0, std430) buffer t5 {
t4 m;
} s5;
layout(location = 0) flat in t4 k;
t4 foo(t4 y) { return y; }
void main() {}
)glsl";
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
}
TEST_F(PositiveShaderInterface, PhysicalStorageBufferGlslang6) {
TEST_DESCRIPTION("Taken from glslang spv.bufferhandle6.frag test");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan12Features features12 = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(features12);
if (VK_TRUE != features12.bufferDeviceAddress) {
GTEST_SKIP() << "bufferDeviceAddress not supported and is required";
}
RETURN_IF_SKIP(InitState(nullptr, &features12));
char const *fsSource = R"glsl(
#version 450 core
#extension GL_EXT_buffer_reference : enable
layout (push_constant, std430) uniform Block { int identity[32]; } pc;
layout(r32ui, set = 3, binding = 0) uniform uimage2D image0_0;
layout(buffer_reference) buffer T1;
layout(set = 3, binding = 1, buffer_reference) buffer T1 {
layout(offset = 0) int a[2]; // stride = 4 for std430, 16 for std140
layout(offset = 32) int b;
layout(offset = 48) T1 c[2]; // stride = 8 for std430, 16 for std140
layout(offset = 80) T1 d;
} x;
void main() {}
)glsl";
VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
}
TEST_F(PositiveShaderInterface, PhysicalStorageBuffer) {
TEST_DESCRIPTION("Regression shaders from https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/5349");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan12Features features12 = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(features12);
if (VK_TRUE != features12.bufferDeviceAddress) {
GTEST_SKIP() << "bufferDeviceAddress not supported and is required";
}
RETURN_IF_SKIP(InitState(nullptr, &features12));
InitRenderTarget();
char const *vsSource = R"glsl(
#version 450
#extension GL_EXT_buffer_reference_uvec2 : enable
layout(set=0, binding=0) layout(buffer_reference, std430) buffer dataBuffer {
highp int value1;
highp int value2;
};
layout(location=0) out dataBuffer outgoingPtr;
void main() {
outgoingPtr = dataBuffer(uvec2(2.0));
}
)glsl";
char const *fsSource = R"glsl(
#version 450
#extension GL_EXT_buffer_reference_uvec2 : enable
layout(set=0, binding=0) layout(buffer_reference, std430) buffer dataBuffer {
highp int value1;
highp int value2;
};
layout(location=0) in dataBuffer incomingPtr;
layout(location=0) out highp vec4 fragColor;
void main() {
highp ivec2 v = ivec2(incomingPtr.value1, incomingPtr.value2);
fragColor = vec4(float(v.x)/255.0,float(v.y)/255.0, float(v.x+v.y)/255.0,1.0);
}
)glsl";
VkShaderObj vs(this, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj fs(this, 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);
}