blob: 3396e15a3e42283395cb81017777cd62183aa478 [file] [log] [blame]
/*
* Copyright (c) 2015-2019 The Khronos Group Inc.
* Copyright (c) 2015-2019 Valve Corporation
* Copyright (c) 2015-2019 LunarG, Inc.
* Copyright (c) 2015-2019 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
*
* Author: Chia-I Wu <olvaffe@gmail.com>
* Author: Chris Forbes <chrisf@ijw.co.nz>
* Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
* Author: Mark Lobodzinski <mark@lunarg.com>
* Author: Mike Stroyan <mike@LunarG.com>
* Author: Tobin Ehlis <tobine@google.com>
* Author: Tony Barbour <tony@LunarG.com>
* Author: Cody Northrop <cnorthrop@google.com>
* Author: Dave Houlton <daveh@lunarg.com>
* Author: Jeremy Kniager <jeremyk@lunarg.com>
* Author: Shannon McPherson <shannon@lunarg.com>
* Author: John Zulauf <jzulauf@lunarg.com>
*/
#include "cast_utils.h"
#include "layer_validation_tests.h"
TEST_F(VkLayerTest, PSOPolygonModeInvalid) {
TEST_DESCRIPTION("Attempt to use invalid polygon fill modes.");
VkPhysicalDeviceFeatures device_features = {};
device_features.fillModeNonSolid = VK_FALSE;
// The sacrificial device object
ASSERT_NO_FATAL_FAILURE(Init(&device_features));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkPipelineRasterizationStateCreateInfo rs_ci = {};
rs_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rs_ci.pNext = nullptr;
rs_ci.lineWidth = 1.0f;
rs_ci.rasterizerDiscardEnable = VK_TRUE;
auto set_polygonMode = [&](CreatePipelineHelper &helper) { helper.rs_state_ci_ = rs_ci; };
// Set polygonMode to POINT while the non-solid fill mode feature is disabled.
// Introduce failure by setting unsupported polygon mode
rs_ci.polygonMode = VK_POLYGON_MODE_POINT;
CreatePipelineHelper::OneshotTest(*this, set_polygonMode, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"polygonMode cannot be VK_POLYGON_MODE_POINT or VK_POLYGON_MODE_LINE");
// Set polygonMode to LINE while the non-solid fill mode feature is disabled.
// Introduce failure by setting unsupported polygon mode
rs_ci.polygonMode = VK_POLYGON_MODE_LINE;
CreatePipelineHelper::OneshotTest(*this, set_polygonMode, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"polygonMode cannot be VK_POLYGON_MODE_POINT or VK_POLYGON_MODE_LINE");
// Set polygonMode to FILL_RECTANGLE_NV while the extension is not enabled.
// Introduce failure by setting unsupported polygon mode
rs_ci.polygonMode = VK_POLYGON_MODE_FILL_RECTANGLE_NV;
CreatePipelineHelper::OneshotTest(*this, set_polygonMode, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineRasterizationStateCreateInfo-polygonMode-01414");
}
TEST_F(VkLayerTest, PipelineNotBound) {
TEST_DESCRIPTION("Pass in an invalid pipeline object handle into a Vulkan API call.");
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdBindPipeline-pipeline-parameter");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkPipeline badPipeline = CastToHandle<VkPipeline, uintptr_t>(0xbaadb1be);
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, badPipeline);
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, PipelineWrongBindPointGraphics) {
TEST_DESCRIPTION("Bind a compute pipeline in the graphics bind point");
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdBindPipeline-pipelineBindPoint-00779");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
CreateComputePipelineHelper pipe(*this);
pipe.InitInfo();
pipe.InitState();
pipe.CreateComputePipeline();
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, PipelineWrongBindPointCompute) {
TEST_DESCRIPTION("Bind a graphics pipeline in the compute bind point");
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdBindPipeline-pipelineBindPoint-00780");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
CreatePipelineHelper pipe(*this);
pipe.InitInfo();
pipe.InitState();
pipe.CreateGraphicsPipeline();
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_);
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, PipelineWrongBindPointRayTracing) {
TEST_DESCRIPTION("Bind a graphics pipeline in the ray-tracing bind point");
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
} else {
printf("%s Extension %s is not supported.\n", kSkipPrefix, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
return;
}
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
if (DeviceExtensionSupported(gpu(), nullptr, VK_NV_RAY_TRACING_EXTENSION_NAME)) {
m_device_extension_names.push_back(VK_NV_RAY_TRACING_EXTENSION_NAME);
m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
} else {
printf("%s Extension %s is not supported.\n", kSkipPrefix, VK_NV_RAY_TRACING_EXTENSION_NAME);
return;
}
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdBindPipeline-pipelineBindPoint-02392");
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
if (!EnableDeviceProfileLayer()) {
printf("%s Failed to enable device profile layer.\n", kSkipPrefix);
return;
}
CreatePipelineHelper pipe(*this);
pipe.InitInfo();
pipe.InitState();
pipe.CreateGraphicsPipeline();
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, pipe.pipeline_);
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, CreatePipelineBadVertexAttributeFormat) {
TEST_DESCRIPTION("Test that pipeline validation catches invalid vertex attribute formats");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkVertexInputBindingDescription input_binding;
memset(&input_binding, 0, sizeof(input_binding));
VkVertexInputAttributeDescription input_attribs;
memset(&input_attribs, 0, sizeof(input_attribs));
// Pick a really bad format for this purpose and make sure it should fail
input_attribs.format = VK_FORMAT_BC2_UNORM_BLOCK;
VkFormatProperties format_props = m_device->format_properties(input_attribs.format);
if ((format_props.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) != 0) {
printf("%s Format unsuitable for test; skipped.\n", kSkipPrefix);
return;
}
input_attribs.location = 0;
auto set_info = [&](CreatePipelineHelper &helper) {
helper.vi_ci_.pVertexBindingDescriptions = &input_binding;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
helper.vi_ci_.pVertexAttributeDescriptions = &input_attribs;
helper.vi_ci_.vertexAttributeDescriptionCount = 1;
};
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkVertexInputAttributeDescription-format-00623");
}
TEST_F(VkLayerTest, DisabledIndependentBlend) {
TEST_DESCRIPTION(
"Generate INDEPENDENT_BLEND by disabling independent blend and then specifying different blend states for two "
"attachments");
VkPhysicalDeviceFeatures features = {};
features.independentBlend = VK_FALSE;
ASSERT_NO_FATAL_FAILURE(Init(&features));
m_errorMonitor->SetDesiredFailureMsg(
VK_DEBUG_REPORT_ERROR_BIT_EXT,
"Invalid Pipeline CreateInfo: If independent blend feature not enabled, all elements of pAttachments must be identical");
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(m_commandBuffer);
VkPipelineObj pipeline(m_device);
// Create a renderPass with two color attachments
VkAttachmentReference attachments[2] = {};
attachments[0].layout = VK_IMAGE_LAYOUT_GENERAL;
attachments[1].attachment = 1;
attachments[1].layout = VK_IMAGE_LAYOUT_GENERAL;
VkSubpassDescription subpass = {};
subpass.pColorAttachments = attachments;
subpass.colorAttachmentCount = 2;
VkRenderPassCreateInfo rpci = {};
rpci.subpassCount = 1;
rpci.pSubpasses = &subpass;
rpci.attachmentCount = 2;
VkAttachmentDescription attach_desc[2] = {};
attach_desc[0].format = VK_FORMAT_B8G8R8A8_UNORM;
attach_desc[0].samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc[0].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
attach_desc[1].format = VK_FORMAT_B8G8R8A8_UNORM;
attach_desc[1].samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
rpci.pAttachments = attach_desc;
rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
VkRenderPass renderpass;
vk::CreateRenderPass(m_device->device(), &rpci, NULL, &renderpass);
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
pipeline.AddShader(&vs);
VkPipelineColorBlendAttachmentState att_state1 = {}, att_state2 = {};
att_state1.dstAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR;
att_state1.blendEnable = VK_TRUE;
att_state2.dstAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR;
att_state2.blendEnable = VK_FALSE;
pipeline.AddColorAttachment(0, att_state1);
pipeline.AddColorAttachment(1, att_state2);
pipeline.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderpass);
m_errorMonitor->VerifyFound();
vk::DestroyRenderPass(m_device->device(), renderpass, NULL);
}
// Is the Pipeline compatible with the expectations of the Renderpass/subpasses?
TEST_F(VkLayerTest, PipelineRenderpassCompatibility) {
TEST_DESCRIPTION(
"Create a graphics pipeline that is incompatible with the requirements of its contained Renderpass/subpasses.");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkPipelineColorBlendAttachmentState att_state1 = {};
att_state1.dstAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR;
att_state1.blendEnable = VK_TRUE;
auto set_info = [&](CreatePipelineHelper &helper) {
helper.cb_attachments_ = att_state1;
helper.gp_ci_.pColorBlendState = nullptr;
};
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00753");
}
TEST_F(VkLayerTest, PointSizeFailure) {
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST but do not set PointSize in vertex shader.");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
ASSERT_NO_FATAL_FAILURE(InitViewport());
// Create VS declaring PointSize but not writing to it
const char NoPointSizeVertShader[] =
"#version 450\n"
"vec2 vertices[3];\n"
"out gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
" float gl_PointSize;\n"
"};\n"
"void main() {\n"
" vertices[0] = vec2(-1.0, -1.0);\n"
" vertices[1] = vec2( 1.0, -1.0);\n"
" vertices[2] = vec2( 0.0, 1.0);\n"
" gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);\n"
"}\n";
VkShaderObj vs(m_device, NoPointSizeVertShader, VK_SHADER_STAGE_VERTEX_BIT, this);
// Set Input Assembly to TOPOLOGY POINT LIST
auto set_info = [&](CreatePipelineHelper &helper) {
// Set Input Assembly to TOPOLOGY POINT LIST
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.shader_stages_ = {vs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT, "Pipeline topology is set to POINT_LIST");
}
TEST_F(VkLayerTest, InvalidTopology) {
TEST_DESCRIPTION("InvalidTopology.");
VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.geometryShader = VK_FALSE;
deviceFeatures.tessellationShader = VK_FALSE;
ASSERT_NO_FATAL_FAILURE(Init(&deviceFeatures));
ASSERT_NO_FATAL_FAILURE(InitViewport());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkShaderObj vs(m_device, bindStateVertPointSizeShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkPrimitiveTopology topology;
auto set_info = [&](CreatePipelineHelper &helper) {
helper.ia_ci_.topology = topology;
helper.ia_ci_.primitiveRestartEnable = VK_TRUE;
helper.shader_stages_ = {vs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428");
topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428");
topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428");
topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
std::vector<string>{"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428",
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429"});
topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
std::vector<string>{"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428",
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429"});
topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
std::vector<string>{"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428",
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00430",
"VUID-VkGraphicsPipelineCreateInfo-topology-00737"});
topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429");
topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429");
}
TEST_F(VkLayerTest, PointSizeGeomShaderFailure) {
TEST_DESCRIPTION(
"Create a pipeline using TOPOLOGY_POINT_LIST, set PointSize vertex shader, but not in the final geometry stage.");
ASSERT_NO_FATAL_FAILURE(Init());
if ((!m_device->phy().features().geometryShader) || (!m_device->phy().features().shaderTessellationAndGeometryPointSize)) {
printf("%s Device does not support the required geometry shader features; skipped.\n", kSkipPrefix);
return;
}
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
ASSERT_NO_FATAL_FAILURE(InitViewport());
// Create VS declaring PointSize and writing to it
static char const *gsSource =
"#version 450\n"
"layout (points) in;\n"
"layout (points) out;\n"
"layout (max_vertices = 1) out;\n"
"void main() {\n"
" gl_Position = vec4(1.0, 0.5, 0.5, 0.0);\n"
" EmitVertex();\n"
"}\n";
VkShaderObj vs(m_device, bindStateVertPointSizeShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkShaderObj gs(m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT, this);
auto set_info = [&](CreatePipelineHelper &helper) {
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT, "Pipeline topology is set to POINT_LIST");
}
TEST_F(VkLayerTest, BuiltinBlockOrderMismatchVsGs) {
TEST_DESCRIPTION("Use different order of gl_Position and gl_PointSize in builtin block interface between VS and GS.");
ASSERT_NO_FATAL_FAILURE(Init());
if (!m_device->phy().features().geometryShader) {
printf("%s Device does not support geometry shaders; Skipped.\n", kSkipPrefix);
return;
}
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
ASSERT_NO_FATAL_FAILURE(InitViewport());
// Compiled using the GLSL code below. GlslangValidator rearranges the members, but here they are kept in the order provided.
// #version 450
// layout (points) in;
// layout (points) out;
// layout (max_vertices = 1) out;
// in gl_PerVertex {
// float gl_PointSize;
// vec4 gl_Position;
// } gl_in[];
// void main() {
// gl_Position = gl_in[0].gl_Position;
// gl_PointSize = gl_in[0].gl_PointSize;
// EmitVertex();
// }
const std::string gsSource = R"(
OpCapability Geometry
OpCapability GeometryPointSize
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %main "main" %_ %gl_in
OpExecutionMode %main InputPoints
OpExecutionMode %main Invocations 1
OpExecutionMode %main OutputPoints
OpExecutionMode %main OutputVertices 1
OpSource GLSL 450
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
OpDecorate %gl_PerVertex Block
OpMemberDecorate %gl_PerVertex_0 0 BuiltIn PointSize
OpMemberDecorate %gl_PerVertex_0 1 BuiltIn Position
OpDecorate %gl_PerVertex_0 Block
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_arr_float_uint_1 = OpTypeArray %float %uint_1
%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%gl_PerVertex_0 = OpTypeStruct %float %v4float
%_arr_gl_PerVertex_0_uint_1 = OpTypeArray %gl_PerVertex_0 %uint_1
%_ptr_Input__arr_gl_PerVertex_0_uint_1 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_1
%gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_1 Input
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
%int_1 = OpConstant %int 1
%_ptr_Input_float = OpTypePointer Input %float
%_ptr_Output_float = OpTypePointer Output %float
%main = OpFunction %void None %3
%5 = OpLabel
%21 = OpAccessChain %_ptr_Input_v4float %gl_in %int_0 %int_1
%22 = OpLoad %v4float %21
%24 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %24 %22
%27 = OpAccessChain %_ptr_Input_float %gl_in %int_0 %int_0
%28 = OpLoad %float %27
%30 = OpAccessChain %_ptr_Output_float %_ %int_1
OpStore %30 %28
OpEmitVertex
OpReturn
OpFunctionEnd
)";
VkShaderObj vs(m_device, bindStateVertPointSizeShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkShaderObj gs(m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT, this);
auto set_info = [&](CreatePipelineHelper &helper) {
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"Builtin variable inside block doesn't match between");
}
TEST_F(VkLayerTest, BuiltinBlockSizeMismatchVsGs) {
TEST_DESCRIPTION("Use different number of elements in builtin block interface between VS and GS.");
ASSERT_NO_FATAL_FAILURE(Init());
if (!m_device->phy().features().geometryShader) {
printf("%s Device does not support geometry shaders; Skipped.\n", kSkipPrefix);
return;
}
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
ASSERT_NO_FATAL_FAILURE(InitViewport());
static const char *gsSource =
"#version 450\n"
"layout (points) in;\n"
"layout (points) out;\n"
"layout (max_vertices = 1) out;\n"
"in gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
" float gl_PointSize;\n"
" float gl_ClipDistance[];\n"
"} gl_in[];\n"
"void main()\n"
"{\n"
" gl_Position = gl_in[0].gl_Position;\n"
" gl_PointSize = gl_in[0].gl_PointSize;\n"
" EmitVertex();\n"
"}\n";
VkShaderObj vs(m_device, bindStateVertPointSizeShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkShaderObj gs(m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT, this);
auto set_info = [&](CreatePipelineHelper &helper) {
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"Number of elements inside builtin block differ between stages");
}
TEST_F(VkLayerTest, CreatePipelineLayoutExceedsSetLimit) {
TEST_DESCRIPTION("Attempt to create a pipeline layout using more than the physical limit of SetLayouts.");
ASSERT_NO_FATAL_FAILURE(Init());
VkDescriptorSetLayoutBinding layout_binding = {};
layout_binding.binding = 0;
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layout_binding.descriptorCount = 1;
layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layout_binding.pImmutableSamplers = NULL;
VkDescriptorSetLayoutCreateInfo ds_layout_ci = {};
ds_layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
ds_layout_ci.bindingCount = 1;
ds_layout_ci.pBindings = &layout_binding;
VkDescriptorSetLayout ds_layout = {};
VkResult err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
// Create an array of DSLs, one larger than the physical limit
const auto excess_layouts = 1 + m_device->phy().properties().limits.maxBoundDescriptorSets;
std::vector<VkDescriptorSetLayout> dsl_array(excess_layouts, ds_layout);
VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_ci.pNext = NULL;
pipeline_layout_ci.setLayoutCount = excess_layouts;
pipeline_layout_ci.pSetLayouts = dsl_array.data();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-setLayoutCount-00286");
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
// Clean up
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
}
TEST_F(VkLayerTest, CreatePipelineLayoutExcessPerStageDescriptors) {
TEST_DESCRIPTION("Attempt to create a pipeline layout where total descriptors exceed per-stage limits");
ASSERT_NO_FATAL_FAILURE(Init());
uint32_t max_uniform_buffers = m_device->phy().properties().limits.maxPerStageDescriptorUniformBuffers;
uint32_t max_storage_buffers = m_device->phy().properties().limits.maxPerStageDescriptorStorageBuffers;
uint32_t max_sampled_images = m_device->phy().properties().limits.maxPerStageDescriptorSampledImages;
uint32_t max_storage_images = m_device->phy().properties().limits.maxPerStageDescriptorStorageImages;
uint32_t max_samplers = m_device->phy().properties().limits.maxPerStageDescriptorSamplers;
uint32_t max_combined = std::min(max_samplers, max_sampled_images);
uint32_t max_input_attachments = m_device->phy().properties().limits.maxPerStageDescriptorInputAttachments;
uint32_t sum_dyn_uniform_buffers = m_device->phy().properties().limits.maxDescriptorSetUniformBuffersDynamic;
uint32_t sum_uniform_buffers = m_device->phy().properties().limits.maxDescriptorSetUniformBuffers;
uint32_t sum_dyn_storage_buffers = m_device->phy().properties().limits.maxDescriptorSetStorageBuffersDynamic;
uint32_t sum_storage_buffers = m_device->phy().properties().limits.maxDescriptorSetStorageBuffers;
uint32_t sum_sampled_images = m_device->phy().properties().limits.maxDescriptorSetSampledImages;
uint32_t sum_storage_images = m_device->phy().properties().limits.maxDescriptorSetStorageImages;
uint32_t sum_samplers = m_device->phy().properties().limits.maxDescriptorSetSamplers;
uint32_t sum_input_attachments = m_device->phy().properties().limits.maxDescriptorSetInputAttachments;
// Devices that report UINT32_MAX for any of these limits can't run this test
if (UINT32_MAX == std::max({max_uniform_buffers, max_storage_buffers, max_sampled_images, max_storage_images, max_samplers})) {
printf("%s Physical device limits report as 2^32-1. Skipping test.\n", kSkipPrefix);
return;
}
VkDescriptorSetLayoutBinding dslb = {};
std::vector<VkDescriptorSetLayoutBinding> dslb_vec = {};
VkDescriptorSetLayout ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayoutCreateInfo ds_layout_ci = {};
ds_layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
ds_layout_ci.pNext = NULL;
VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_ci.pNext = NULL;
pipeline_layout_ci.setLayoutCount = 1;
pipeline_layout_ci.pSetLayouts = &ds_layout;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
// VU 0fe0023e - too many sampler type descriptors in fragment stage
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
dslb.descriptorCount = max_samplers;
dslb.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
dslb.descriptorCount = max_combined;
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
VkResult err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00287");
if ((max_samplers + max_combined) > sum_samplers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01677"); // expect all-stages sum too
}
if (max_combined > sum_sampled_images) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01682"); // expect all-stages sum too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00240 - too many uniform buffer type descriptors in vertex stage
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
dslb.descriptorCount = max_uniform_buffers + 1;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00288");
if (dslb.descriptorCount > sum_uniform_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01678"); // expect all-stages sum too
}
if (dslb.descriptorCount > sum_dyn_uniform_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01679"); // expect all-stages sum too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00242 - too many storage buffer type descriptors in compute stage
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
dslb.descriptorCount = max_storage_buffers + 1;
dslb.stageFlags = VK_SHADER_STAGE_ALL;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
dslb_vec.push_back(dslb);
dslb.binding = 2;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
dslb.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00289");
if (dslb.descriptorCount > sum_dyn_storage_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01681"); // expect all-stages sum too
}
if (dslb_vec[0].descriptorCount + dslb_vec[2].descriptorCount > sum_storage_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01680"); // expect all-stages sum too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00244 - too many sampled image type descriptors in multiple stages
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
dslb.descriptorCount = max_sampled_images;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
dslb.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
dslb_vec.push_back(dslb);
dslb.binding = 2;
dslb.descriptorCount = max_combined;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00290");
if (max_combined + 2 * max_sampled_images > sum_sampled_images) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01682"); // expect all-stages sum too
}
if (max_combined > sum_samplers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01677"); // expect all-stages sum too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00246 - too many storage image type descriptors in fragment stage
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
dslb.descriptorCount = 1 + (max_storage_images / 2);
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00291");
if (2 * dslb.descriptorCount > sum_storage_images) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01683"); // expect all-stages sum too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d18 - too many input attachments in fragment stage
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
dslb.descriptorCount = 1 + max_input_attachments;
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01676");
if (dslb.descriptorCount > sum_input_attachments) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01684"); // expect all-stages sum too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
}
TEST_F(VkLayerTest, CreatePipelineLayoutExcessDescriptorsOverall) {
TEST_DESCRIPTION("Attempt to create a pipeline layout where total descriptors exceed limits");
ASSERT_NO_FATAL_FAILURE(Init());
uint32_t max_uniform_buffers = m_device->phy().properties().limits.maxPerStageDescriptorUniformBuffers;
uint32_t max_storage_buffers = m_device->phy().properties().limits.maxPerStageDescriptorStorageBuffers;
uint32_t max_sampled_images = m_device->phy().properties().limits.maxPerStageDescriptorSampledImages;
uint32_t max_storage_images = m_device->phy().properties().limits.maxPerStageDescriptorStorageImages;
uint32_t max_samplers = m_device->phy().properties().limits.maxPerStageDescriptorSamplers;
uint32_t max_input_attachments = m_device->phy().properties().limits.maxPerStageDescriptorInputAttachments;
uint32_t sum_dyn_uniform_buffers = m_device->phy().properties().limits.maxDescriptorSetUniformBuffersDynamic;
uint32_t sum_uniform_buffers = m_device->phy().properties().limits.maxDescriptorSetUniformBuffers;
uint32_t sum_dyn_storage_buffers = m_device->phy().properties().limits.maxDescriptorSetStorageBuffersDynamic;
uint32_t sum_storage_buffers = m_device->phy().properties().limits.maxDescriptorSetStorageBuffers;
uint32_t sum_sampled_images = m_device->phy().properties().limits.maxDescriptorSetSampledImages;
uint32_t sum_storage_images = m_device->phy().properties().limits.maxDescriptorSetStorageImages;
uint32_t sum_samplers = m_device->phy().properties().limits.maxDescriptorSetSamplers;
uint32_t sum_input_attachments = m_device->phy().properties().limits.maxDescriptorSetInputAttachments;
// Devices that report UINT32_MAX for any of these limits can't run this test
if (UINT32_MAX == std::max({sum_dyn_uniform_buffers, sum_uniform_buffers, sum_dyn_storage_buffers, sum_storage_buffers,
sum_sampled_images, sum_storage_images, sum_samplers, sum_input_attachments})) {
printf("%s Physical device limits report as 2^32-1. Skipping test.\n", kSkipPrefix);
return;
}
VkDescriptorSetLayoutBinding dslb = {};
std::vector<VkDescriptorSetLayoutBinding> dslb_vec = {};
VkDescriptorSetLayout ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayoutCreateInfo ds_layout_ci = {};
ds_layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
ds_layout_ci.pNext = NULL;
VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_ci.pNext = NULL;
pipeline_layout_ci.setLayoutCount = 1;
pipeline_layout_ci.pSetLayouts = &ds_layout;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
// VU 0fe00d1a - too many sampler type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
dslb.descriptorCount = sum_samplers / 2;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
dslb.descriptorCount = sum_samplers - dslb.descriptorCount + 1;
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
VkResult err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01677");
if (dslb.descriptorCount > max_samplers) {
m_errorMonitor->SetDesiredFailureMsg(
VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00287"); // Expect max-per-stage samplers exceeds limits
}
if (dslb.descriptorCount > sum_sampled_images) {
m_errorMonitor->SetDesiredFailureMsg(
VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01682"); // Expect max overall sampled image count exceeds limits
}
if (dslb.descriptorCount > max_sampled_images) {
m_errorMonitor->SetDesiredFailureMsg(
VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00290"); // Expect max per-stage sampled image count exceeds limits
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d1c - too many uniform buffer type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
dslb.descriptorCount = sum_uniform_buffers + 1;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01678");
if (dslb.descriptorCount > max_uniform_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00288"); // expect max-per-stage too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d1e - too many dynamic uniform buffer type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dslb.descriptorCount = sum_dyn_uniform_buffers + 1;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01679");
if (dslb.descriptorCount > max_uniform_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00288"); // expect max-per-stage too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d20 - too many storage buffer type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
dslb.descriptorCount = sum_storage_buffers + 1;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01680");
if (dslb.descriptorCount > max_storage_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00289"); // expect max-per-stage too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d22 - too many dynamic storage buffer type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
dslb.descriptorCount = sum_dyn_storage_buffers + 1;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01681");
if (dslb.descriptorCount > max_storage_buffers) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00289"); // expect max-per-stage too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d24 - too many sampled image type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
dslb.descriptorCount = max_samplers;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
// revisit: not robust to odd limits.
uint32_t remaining = (max_samplers > sum_sampled_images ? 0 : (sum_sampled_images - max_samplers) / 2);
dslb.descriptorCount = 1 + remaining;
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
dslb.binding = 2;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
dslb.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01682");
if (std::max(dslb_vec[0].descriptorCount, dslb_vec[1].descriptorCount) > max_sampled_images) {
m_errorMonitor->SetDesiredFailureMsg(
VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00290"); // Expect max-per-stage sampled images to exceed limits
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d26 - too many storage image type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
dslb.descriptorCount = sum_storage_images / 2;
dslb.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
dslb.binding = 1;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
dslb.descriptorCount = sum_storage_images - dslb.descriptorCount + 1;
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01683");
if (dslb.descriptorCount > max_storage_images) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00291"); // expect max-per-stage too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
// VU 0fe00d28 - too many input attachment type descriptors overall
dslb_vec.clear();
dslb.binding = 0;
dslb.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
dslb.descriptorCount = sum_input_attachments + 1;
dslb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
dslb.pImmutableSamplers = NULL;
dslb_vec.push_back(dslb);
ds_layout_ci.bindingCount = dslb_vec.size();
ds_layout_ci.pBindings = dslb_vec.data();
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL, &ds_layout);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01684");
if (dslb.descriptorCount > max_input_attachments) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01676"); // expect max-per-stage too
}
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL); // Unnecessary but harmless if test passed
pipeline_layout = VK_NULL_HANDLE;
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
}
TEST_F(VkLayerTest, InvalidCmdBufferPipelineDestroyed) {
TEST_DESCRIPTION("Attempt to draw with a command buffer that is invalid due to a pipeline dependency being destroyed.");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
{
// Use helper to create graphics pipeline
CreatePipelineHelper helper(*this);
helper.InitInfo();
helper.InitState();
helper.CreateGraphicsPipeline();
// Bind helper pipeline to command buffer
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, helper.pipeline_);
m_commandBuffer->end();
// pipeline will be destroyed when helper goes out of scope
}
// Cause error by submitting command buffer that references destroyed pipeline
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"UNASSIGNED-CoreValidation-DrawState-InvalidCommandBuffer-VkPipeline");
m_commandBuffer->QueueCommandBuffer(false);
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, InvalidPipeline) {
uint64_t fake_pipeline_handle = 0xbaad6001;
VkPipeline bad_pipeline = reinterpret_cast<VkPipeline &>(fake_pipeline_handle);
// Enable VK_KHR_draw_indirect_count for KHR variants
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME)) {
m_device_extension_names.push_back(VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME);
}
ASSERT_NO_FATAL_FAILURE(InitState());
bool has_khr_indirect = DeviceExtensionEnabled(VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME);
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
// Attempt to bind an invalid Pipeline to a valid Command Buffer
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdBindPipeline-pipeline-parameter");
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, bad_pipeline);
m_errorMonitor->VerifyFound();
// Try each of the 6 flavors of Draw()
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); // Draw*() calls must be submitted within a renderpass
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDraw-None-02700");
m_commandBuffer->Draw(1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDrawIndexed-None-02700");
m_commandBuffer->DrawIndexed(1, 1, 0, 0, 0);
m_errorMonitor->VerifyFound();
VkBufferObj buffer;
VkBufferCreateInfo ci = {};
ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
ci.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
ci.size = 1024;
buffer.init(*m_device, ci);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDrawIndirect-None-02700");
vk::CmdDrawIndirect(m_commandBuffer->handle(), buffer.handle(), 0, 1, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDrawIndexedIndirect-None-02700");
vk::CmdDrawIndexedIndirect(m_commandBuffer->handle(), buffer.handle(), 0, 1, 0);
m_errorMonitor->VerifyFound();
if (has_khr_indirect) {
auto fpCmdDrawIndirectCountKHR =
(PFN_vkCmdDrawIndirectCountKHR)vk::GetDeviceProcAddr(m_device->device(), "vkCmdDrawIndirectCountKHR");
ASSERT_NE(fpCmdDrawIndirectCountKHR, nullptr);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDrawIndirectCountKHR-None-02700");
// stride must be a multiple of 4 and must be greater than or equal to sizeof(VkDrawIndirectCommand)
fpCmdDrawIndirectCountKHR(m_commandBuffer->handle(), buffer.handle(), 0, buffer.handle(), 512, 1, 512);
m_errorMonitor->VerifyFound();
auto fpCmdDrawIndexedIndirectCountKHR =
(PFN_vkCmdDrawIndexedIndirectCountKHR)vk::GetDeviceProcAddr(m_device->device(), "vkCmdDrawIndexedIndirectCountKHR");
ASSERT_NE(fpCmdDrawIndexedIndirectCountKHR, nullptr);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDrawIndexedIndirectCountKHR-None-02700");
// stride must be a multiple of 4 and must be greater than or equal to sizeof(VkDrawIndexedIndirectCommand)
fpCmdDrawIndexedIndirectCountKHR(m_commandBuffer->handle(), buffer.handle(), 0, buffer.handle(), 512, 1, 512);
m_errorMonitor->VerifyFound();
}
// Also try the Dispatch variants
vk::CmdEndRenderPass(m_commandBuffer->handle()); // Compute submissions must be outside a renderpass
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatch-None-02700");
vk::CmdDispatch(m_commandBuffer->handle(), 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchIndirect-None-02700");
vk::CmdDispatchIndirect(m_commandBuffer->handle(), buffer.handle(), 0);
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, CmdDispatchExceedLimits) {
TEST_DESCRIPTION("Compute dispatch with dimensions that exceed device limits");
// Enable KHX device group extensions, if available
if (InstanceExtensionSupported(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) {
m_instance_extension_names.push_back(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME);
}
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
bool khx_dg_ext_available = false;
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_DEVICE_GROUP_EXTENSION_NAME)) {
m_device_extension_names.push_back(VK_KHR_DEVICE_GROUP_EXTENSION_NAME);
khx_dg_ext_available = true;
}
ASSERT_NO_FATAL_FAILURE(InitState());
uint32_t x_count_limit = m_device->props.limits.maxComputeWorkGroupCount[0];
uint32_t y_count_limit = m_device->props.limits.maxComputeWorkGroupCount[1];
uint32_t z_count_limit = m_device->props.limits.maxComputeWorkGroupCount[2];
if (std::max({x_count_limit, y_count_limit, z_count_limit}) == UINT32_MAX) {
printf("%s device maxComputeWorkGroupCount limit reports UINT32_MAX, test not possible, skipping.\n", kSkipPrefix);
return;
}
uint32_t x_size_limit = m_device->props.limits.maxComputeWorkGroupSize[0];
uint32_t y_size_limit = m_device->props.limits.maxComputeWorkGroupSize[1];
uint32_t z_size_limit = m_device->props.limits.maxComputeWorkGroupSize[2];
std::string spv_source = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize )";
spv_source.append(std::to_string(x_size_limit + 1) + " " + std::to_string(y_size_limit + 1) + " " +
std::to_string(z_size_limit + 1));
spv_source.append(R"(
%void = OpTypeVoid
%3 = OpTypeFunction %void
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd)");
CreateComputePipelineHelper pipe(*this);
pipe.InitInfo();
pipe.cs_.reset(new VkShaderObj(m_device, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, this));
pipe.InitState();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "exceeds device limit maxComputeWorkGroupSize[0]");
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "exceeds device limit maxComputeWorkGroupSize[1]");
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "exceeds device limit maxComputeWorkGroupSize[2]");
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "features-limits-maxComputeWorkGroupInvocations");
pipe.CreateComputePipeline();
m_errorMonitor->VerifyFound();
// Create a minimal compute pipeline
x_size_limit = (x_size_limit > 1024) ? 1024 : x_size_limit;
y_size_limit = (y_size_limit > 1024) ? 1024 : y_size_limit;
z_size_limit = (z_size_limit > 64) ? 64 : z_size_limit;
uint32_t invocations_limit = m_device->props.limits.maxComputeWorkGroupInvocations;
x_size_limit = (x_size_limit > invocations_limit) ? invocations_limit : x_size_limit;
invocations_limit /= x_size_limit;
y_size_limit = (y_size_limit > invocations_limit) ? invocations_limit : y_size_limit;
invocations_limit /= y_size_limit;
z_size_limit = (z_size_limit > invocations_limit) ? invocations_limit : z_size_limit;
char cs_text[128] = "";
sprintf(cs_text, "#version 450\nlayout(local_size_x = %d, local_size_y = %d, local_size_z = %d) in;\nvoid main() {}\n",
x_size_limit, y_size_limit, z_size_limit);
VkShaderObj cs_obj(m_device, cs_text, VK_SHADER_STAGE_COMPUTE_BIT, this);
pipe.cs_.reset(new VkShaderObj(m_device, cs_text, VK_SHADER_STAGE_COMPUTE_BIT, this));
pipe.CreateComputePipeline();
// Bind pipeline to command buffer
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_);
// Dispatch counts that exceed device limits
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatch-groupCountX-00386");
vk::CmdDispatch(m_commandBuffer->handle(), x_count_limit + 1, y_count_limit, z_count_limit);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatch-groupCountY-00387");
vk::CmdDispatch(m_commandBuffer->handle(), x_count_limit, y_count_limit + 1, z_count_limit);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatch-groupCountZ-00388");
vk::CmdDispatch(m_commandBuffer->handle(), x_count_limit, y_count_limit, z_count_limit + 1);
m_errorMonitor->VerifyFound();
if (khx_dg_ext_available) {
PFN_vkCmdDispatchBaseKHR fp_vkCmdDispatchBaseKHR =
(PFN_vkCmdDispatchBaseKHR)vk::GetInstanceProcAddr(instance(), "vkCmdDispatchBaseKHR");
// Base equals or exceeds limit
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchBase-baseGroupX-00421");
fp_vkCmdDispatchBaseKHR(m_commandBuffer->handle(), x_count_limit, y_count_limit - 1, z_count_limit - 1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchBase-baseGroupX-00422");
fp_vkCmdDispatchBaseKHR(m_commandBuffer->handle(), x_count_limit - 1, y_count_limit, z_count_limit - 1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchBase-baseGroupZ-00423");
fp_vkCmdDispatchBaseKHR(m_commandBuffer->handle(), x_count_limit - 1, y_count_limit - 1, z_count_limit, 0, 0, 0);
m_errorMonitor->VerifyFound();
// (Base + count) exceeds limit
uint32_t x_base = x_count_limit / 2;
uint32_t y_base = y_count_limit / 2;
uint32_t z_base = z_count_limit / 2;
x_count_limit -= x_base;
y_count_limit -= y_base;
z_count_limit -= z_base;
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchBase-groupCountX-00424");
fp_vkCmdDispatchBaseKHR(m_commandBuffer->handle(), x_base, y_base, z_base, x_count_limit + 1, y_count_limit, z_count_limit);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchBase-groupCountY-00425");
fp_vkCmdDispatchBaseKHR(m_commandBuffer->handle(), x_base, y_base, z_base, x_count_limit, y_count_limit + 1, z_count_limit);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdDispatchBase-groupCountZ-00426");
fp_vkCmdDispatchBaseKHR(m_commandBuffer->handle(), x_base, y_base, z_base, x_count_limit, y_count_limit, z_count_limit + 1);
m_errorMonitor->VerifyFound();
} else {
printf("%s KHX_DEVICE_GROUP_* extensions not supported, skipping CmdDispatchBaseKHR() tests.\n", kSkipPrefix);
}
}
TEST_F(VkLayerTest, InvalidPipelineCreateState) {
// Attempt to Create Gfx Pipeline w/o a VS
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
ASSERT_NO_FATAL_FAILURE(InitViewport());
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
VkPipelineShaderStageCreateInfo shaderStage = fs.GetStageCreateInfo(); // should be: vs.GetStageCreateInfo();
auto set_info = [&](CreatePipelineHelper &helper) { helper.shader_stages_ = {shaderStage}; };
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"Invalid Pipeline CreateInfo State: Vertex Shader required");
// Finally, check the string validation for the shader stage pName variable. Correct the shader stage data, and bork the
// string before calling again
shaderStage = vs.GetStageCreateInfo();
const uint8_t cont_char = 0xf8;
char bad_string[] = {static_cast<char>(cont_char), static_cast<char>(cont_char), static_cast<char>(cont_char),
static_cast<char>(cont_char)};
shaderStage.pName = bad_string;
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"contains invalid characters or is badly formed");
}
TEST_F(VkLayerTest, InvalidPipelineSampleRateFeatureDisable) {
// Enable sample shading in pipeline when the feature is disabled.
// Disable sampleRateShading here
VkPhysicalDeviceFeatures device_features = {};
device_features.sampleRateShading = VK_FALSE;
ASSERT_NO_FATAL_FAILURE(Init(&device_features));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
// Cause the error by enabling sample shading...
auto set_shading_enable = [](CreatePipelineHelper &helper) { helper.pipe_ms_state_ci_.sampleShadingEnable = VK_TRUE; };
CreatePipelineHelper::OneshotTest(*this, set_shading_enable, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineMultisampleStateCreateInfo-sampleShadingEnable-00784");
}
TEST_F(VkLayerTest, InvalidPipelineSampleRateFeatureEnable) {
// Enable sample shading in pipeline when the feature is disabled.
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
// Require sampleRateShading here
VkPhysicalDeviceFeatures device_features = {};
ASSERT_NO_FATAL_FAILURE(GetPhysicalDeviceFeatures(&device_features));
if (device_features.sampleRateShading == VK_FALSE) {
printf("%s SampleRateShading feature is disabled -- skipping related checks.\n", kSkipPrefix);
return;
}
ASSERT_NO_FATAL_FAILURE(InitState(&device_features));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
auto range_test = [this](float value, bool positive_test) {
auto info_override = [value](CreatePipelineHelper &helper) {
helper.pipe_ms_state_ci_.sampleShadingEnable = VK_TRUE;
helper.pipe_ms_state_ci_.minSampleShading = value;
};
CreatePipelineHelper::OneshotTest(*this, info_override, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkPipelineMultisampleStateCreateInfo-minSampleShading-00786", positive_test);
};
range_test(NearestSmaller(0.0F), false);
range_test(NearestGreater(1.0F), false);
range_test(0.0F, /* positive_test= */ true);
range_test(1.0F, /* positive_test= */ true);
}
TEST_F(VkLayerTest, InvalidPipelineSamplePNext) {
// Enable sample shading in pipeline when the feature is disabled.
// Check for VK_KHR_get_physical_device_properties2
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
}
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
// Set up the extension structs
auto sampleLocations = chain_util::Init<VkPipelineSampleLocationsStateCreateInfoEXT>();
sampleLocations.sampleLocationsInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT;
auto coverageToColor = chain_util::Init<VkPipelineCoverageToColorStateCreateInfoNV>();
auto coverageModulation = chain_util::Init<VkPipelineCoverageModulationStateCreateInfoNV>();
auto discriminatrix = [this](const char *name) { return DeviceExtensionSupported(gpu(), nullptr, name); };
chain_util::ExtensionChain chain(discriminatrix, &m_device_extension_names);
chain.Add(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME, sampleLocations);
chain.Add(VK_NV_FRAGMENT_COVERAGE_TO_COLOR_EXTENSION_NAME, coverageToColor);
chain.Add(VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME, coverageModulation);
const void *extension_head = chain.Head();
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
if (extension_head) {
auto good_chain = [extension_head](CreatePipelineHelper &helper) { helper.pipe_ms_state_ci_.pNext = extension_head; };
CreatePipelineHelper::OneshotTest(*this, good_chain, (VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT),
"No error", true);
} else {
printf("%s Required extension not present -- skipping positive checks.\n", kSkipPrefix);
}
auto instance_ci = chain_util::Init<VkInstanceCreateInfo>();
auto bad_chain = [&instance_ci](CreatePipelineHelper &helper) { helper.pipe_ms_state_ci_.pNext = &instance_ci; };
CreatePipelineHelper::OneshotTest(*this, bad_chain, VK_DEBUG_REPORT_WARNING_BIT_EXT,
"VUID-VkPipelineMultisampleStateCreateInfo-pNext-pNext");
}
TEST_F(VkLayerTest, CreateGraphicsPipelineWithBadBasePointer) {
TEST_DESCRIPTION("Create Graphics Pipeline with pointers that must be ignored by layers");
ASSERT_NO_FATAL_FAILURE(Init());
m_depth_stencil_fmt = FindSupportedDepthStencilFormat(gpu());
ASSERT_TRUE(m_depth_stencil_fmt != 0);
m_depthStencil->Init(m_device, static_cast<int32_t>(m_width), static_cast<int32_t>(m_height), m_depth_stencil_fmt);
ASSERT_NO_FATAL_FAILURE(InitRenderTarget(m_depthStencil->BindInfo()));
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
const VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, nullptr, 0, 0, nullptr, 0, nullptr};
const VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info{
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_FALSE};
const VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info_template{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
nullptr,
0,
VK_FALSE,
VK_FALSE,
VK_POLYGON_MODE_FILL,
VK_CULL_MODE_NONE,
VK_FRONT_FACE_COUNTER_CLOCKWISE,
VK_FALSE,
0.0f,
0.0f,
0.0f,
1.0f};
VkPipelineLayout pipeline_layout;
auto pipeline_layout_create_info = lvl_init_struct<VkPipelineLayoutCreateInfo>();
VkResult err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_create_info, nullptr, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
pipeline_rasterization_state_create_info_template;
pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_TRUE;
uint64_t fake_pipeline_id = 0xCADECADE;
VkPipeline fake_pipeline_handle = reinterpret_cast<VkPipeline &>(fake_pipeline_id);
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
nullptr,
VK_PIPELINE_CREATE_DERIVATIVE_BIT,
1,
&vs.GetStageCreateInfo(),
&pipeline_vertex_input_state_create_info,
&pipeline_input_assembly_state_create_info,
nullptr,
nullptr,
&pipeline_rasterization_state_create_info,
nullptr,
nullptr,
nullptr,
nullptr,
pipeline_layout,
m_renderPass,
0,
fake_pipeline_handle,
-1};
VkPipeline pipeline;
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkGraphicsPipelineCreateInfo-flags-00722");
vk::CreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
m_errorMonitor->VerifyFound();
graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
graphics_pipeline_create_info.basePipelineIndex = 6;
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-VkGraphicsPipelineCreateInfo-flags-00723");
vk::CreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineLayout(m_device->handle(), pipeline_layout, nullptr);
}
TEST_F(VkLayerTest, VertexAttributeDivisorExtension) {
TEST_DESCRIPTION("Test VUIDs added with VK_EXT_vertex_attribute_divisor extension.");
bool inst_ext = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (inst_ext) {
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
}
if (inst_ext && DeviceExtensionSupported(gpu(), nullptr, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
m_device_extension_names.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
} else {
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
return;
}
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT vadf = {};
vadf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
vadf.vertexAttributeInstanceRateDivisor = VK_TRUE;
vadf.vertexAttributeInstanceRateZeroDivisor = VK_TRUE;
VkPhysicalDeviceFeatures2 pd_features2 = {};
pd_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
pd_features2.pNext = &vadf;
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &pd_features2));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
const VkPhysicalDeviceLimits &dev_limits = m_device->props.limits;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT pdvad_props = {};
pdvad_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
VkPhysicalDeviceProperties2 pd_props2 = {};
pd_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
pd_props2.pNext = &pdvad_props;
vk::GetPhysicalDeviceProperties2(gpu(), &pd_props2);
VkVertexInputBindingDivisorDescriptionEXT vibdd = {};
VkPipelineVertexInputDivisorStateCreateInfoEXT pvids_ci = {};
pvids_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
pvids_ci.vertexBindingDivisorCount = 1;
pvids_ci.pVertexBindingDivisors = &vibdd;
VkVertexInputBindingDescription vibd = {};
vibd.stride = 12;
vibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
if (pdvad_props.maxVertexAttribDivisor < pvids_ci.vertexBindingDivisorCount) {
printf("%sThis device does not support %d vertexBindingDivisors, skipping tests\n", kSkipPrefix,
pvids_ci.vertexBindingDivisorCount);
return;
}
using std::vector;
struct TestCase {
uint32_t div_binding;
uint32_t div_divisor;
uint32_t desc_binding;
VkVertexInputRate desc_rate;
vector<std::string> vuids;
};
// clang-format off
vector<TestCase> test_cases = {
{ 0,
1,
0,
VK_VERTEX_INPUT_RATE_VERTEX,
{"VUID-VkVertexInputBindingDivisorDescriptionEXT-inputRate-01871"}
},
{ dev_limits.maxVertexInputBindings + 1,
1,
0,
VK_VERTEX_INPUT_RATE_INSTANCE,
{"VUID-VkVertexInputBindingDivisorDescriptionEXT-binding-01869",
"VUID-VkVertexInputBindingDivisorDescriptionEXT-inputRate-01871"}
}
};
if (UINT32_MAX != pdvad_props.maxVertexAttribDivisor) { // Can't test overflow if maxVAD is UINT32_MAX
test_cases.push_back(
{ 0,
pdvad_props.maxVertexAttribDivisor + 1,
0,
VK_VERTEX_INPUT_RATE_INSTANCE,
{"VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870"}
} );
}
// clang-format on
for (const auto &test_case : test_cases) {
const auto bad_divisor_state = [&test_case, &vibdd, &pvids_ci, &vibd](CreatePipelineHelper &helper) {
vibdd.binding = test_case.div_binding;
vibdd.divisor = test_case.div_divisor;
vibd.binding = test_case.desc_binding;
vibd.inputRate = test_case.desc_rate;
helper.vi_ci_.pNext = &pvids_ci;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
helper.vi_ci_.pVertexBindingDescriptions = &vibd;
};
CreatePipelineHelper::OneshotTest(*this, bad_divisor_state, VK_DEBUG_REPORT_ERROR_BIT_EXT, test_case.vuids);
}
}
TEST_F(VkLayerTest, VertexAttributeDivisorDisabled) {
TEST_DESCRIPTION("Test instance divisor feature disabled for VK_EXT_vertex_attribute_divisor extension.");
bool inst_ext = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (inst_ext) {
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
}
if (inst_ext && DeviceExtensionSupported(gpu(), nullptr, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
m_device_extension_names.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
} else {
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
return;
}
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT vadf = {};
vadf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
vadf.vertexAttributeInstanceRateDivisor = VK_FALSE;
vadf.vertexAttributeInstanceRateZeroDivisor = VK_FALSE;
VkPhysicalDeviceFeatures2 pd_features2 = {};
pd_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
pd_features2.pNext = &vadf;
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &pd_features2));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT pdvad_props = {};
pdvad_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
VkPhysicalDeviceProperties2 pd_props2 = {};
pd_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
pd_props2.pNext = &pdvad_props;
vk::GetPhysicalDeviceProperties2(gpu(), &pd_props2);
VkVertexInputBindingDivisorDescriptionEXT vibdd = {};
vibdd.binding = 0;
vibdd.divisor = 2;
VkPipelineVertexInputDivisorStateCreateInfoEXT pvids_ci = {};
pvids_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
pvids_ci.vertexBindingDivisorCount = 1;
pvids_ci.pVertexBindingDivisors = &vibdd;
VkVertexInputBindingDescription vibd = {};
vibd.binding = vibdd.binding;
vibd.stride = 12;
vibd.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
if (pdvad_props.maxVertexAttribDivisor < pvids_ci.vertexBindingDivisorCount) {
printf("%sThis device does not support %d vertexBindingDivisors, skipping tests\n", kSkipPrefix,
pvids_ci.vertexBindingDivisorCount);
return;
}
const auto instance_rate = [&pvids_ci, &vibd](CreatePipelineHelper &helper) {
helper.vi_ci_.pNext = &pvids_ci;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
helper.vi_ci_.pVertexBindingDescriptions = &vibd;
};
CreatePipelineHelper::OneshotTest(*this, instance_rate, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateDivisor-02229");
}
TEST_F(VkLayerTest, VertexAttributeDivisorInstanceRateZero) {
TEST_DESCRIPTION("Test instanceRateZero feature of VK_EXT_vertex_attribute_divisor extension.");
bool inst_ext = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (inst_ext) {
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
}
if (inst_ext && DeviceExtensionSupported(gpu(), nullptr, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
m_device_extension_names.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
} else {
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
return;
}
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT vadf = {};
vadf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
vadf.vertexAttributeInstanceRateDivisor = VK_TRUE;
vadf.vertexAttributeInstanceRateZeroDivisor = VK_FALSE;
VkPhysicalDeviceFeatures2 pd_features2 = {};
pd_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
pd_features2.pNext = &vadf;
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &pd_features2));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkVertexInputBindingDivisorDescriptionEXT vibdd = {};
vibdd.binding = 0;
vibdd.divisor = 0;
VkPipelineVertexInputDivisorStateCreateInfoEXT pvids_ci = {};
pvids_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
pvids_ci.vertexBindingDivisorCount = 1;
pvids_ci.pVertexBindingDivisors = &vibdd;
VkVertexInputBindingDescription vibd = {};
vibd.binding = vibdd.binding;
vibd.stride = 12;
vibd.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
const auto instance_rate = [&pvids_ci, &vibd](CreatePipelineHelper &helper) {
helper.vi_ci_.pNext = &pvids_ci;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
helper.vi_ci_.pVertexBindingDescriptions = &vibd;
};
CreatePipelineHelper::OneshotTest(
*this, instance_rate, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228");
}
/*// TODO : This test should be good, but needs Tess support in compiler to run
TEST_F(VkLayerTest, InvalidPatchControlPoints)
{
// Attempt to Create Gfx Pipeline w/o a VS
VkResult err;
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"Invalid Pipeline CreateInfo State: VK_PRIMITIVE_TOPOLOGY_PATCH
primitive ");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkDescriptorPoolSize ds_type_count = {};
ds_type_count.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
ds_type_count.descriptorCount = 1;
VkDescriptorPoolCreateInfo ds_pool_ci = {};
ds_pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
ds_pool_ci.pNext = NULL;
ds_pool_ci.poolSizeCount = 1;
ds_pool_ci.pPoolSizes = &ds_type_count;
VkDescriptorPool ds_pool;
err = vk::CreateDescriptorPool(m_device->device(),
VK_DESCRIPTOR_POOL_USAGE_NON_FREE, 1, &ds_pool_ci, NULL, &ds_pool);
ASSERT_VK_SUCCESS(err);
VkDescriptorSetLayoutBinding dsl_binding = {};
dsl_binding.binding = 0;
dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
dsl_binding.descriptorCount = 1;
dsl_binding.stageFlags = VK_SHADER_STAGE_ALL;
dsl_binding.pImmutableSamplers = NULL;
VkDescriptorSetLayoutCreateInfo ds_layout_ci = {};
ds_layout_ci.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
ds_layout_ci.pNext = NULL;
ds_layout_ci.bindingCount = 1;
ds_layout_ci.pBindings = &dsl_binding;
VkDescriptorSetLayout ds_layout;
err = vk::CreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL,
&ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
err = vk::AllocateDescriptorSets(m_device->device(), ds_pool,
VK_DESCRIPTOR_SET_USAGE_NON_FREE, 1, &ds_layout, &descriptorSet);
ASSERT_VK_SUCCESS(err);
VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
pipeline_layout_ci.sType =
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_ci.pNext = NULL;
pipeline_layout_ci.setLayoutCount = 1;
pipeline_layout_ci.pSetLayouts = &ds_layout;
VkPipelineLayout pipeline_layout;
err = vk::CreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL,
&pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkPipelineShaderStageCreateInfo shaderStages[3];
memset(&shaderStages, 0, 3 * sizeof(VkPipelineShaderStageCreateInfo));
VkShaderObj vs(m_device,bindStateVertShaderText,VK_SHADER_STAGE_VERTEX_BIT,
this);
// Just using VS txt for Tess shaders as we don't care about functionality
VkShaderObj
tc(m_device,bindStateVertShaderText,VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
this);
VkShaderObj
te(m_device,bindStateVertShaderText,VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
this);
shaderStages[0].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStages[0].shader = vs.handle();
shaderStages[1].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[1].stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
shaderStages[1].shader = tc.handle();
shaderStages[2].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[2].stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
shaderStages[2].shader = te.handle();
VkPipelineInputAssemblyStateCreateInfo iaCI = {};
iaCI.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
iaCI.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
VkPipelineTessellationStateCreateInfo tsCI = {};
tsCI.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tsCI.patchControlPoints = 0; // This will cause an error
VkGraphicsPipelineCreateInfo gp_ci = {};
gp_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
gp_ci.pNext = NULL;
gp_ci.stageCount = 3;
gp_ci.pStages = shaderStages;
gp_ci.pVertexInputState = NULL;
gp_ci.pInputAssemblyState = &iaCI;
gp_ci.pTessellationState = &tsCI;
gp_ci.pViewportState = NULL;
gp_ci.pRasterizationState = NULL;
gp_ci.pMultisampleState = NULL;
gp_ci.pDepthStencilState = NULL;
gp_ci.pColorBlendState = NULL;
gp_ci.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT;
gp_ci.layout = pipeline_layout;
gp_ci.renderPass = renderPass();
VkPipelineCacheCreateInfo pc_ci = {};
pc_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
pc_ci.pNext = NULL;
pc_ci.initialSize = 0;
pc_ci.initialData = 0;
pc_ci.maxSize = 0;
VkPipeline pipeline;
VkPipelineCache pipelineCache;
err = vk::CreatePipelineCache(m_device->device(), &pc_ci, NULL,
&pipelineCache);
ASSERT_VK_SUCCESS(err);
err = vk::CreateGraphicsPipelines(m_device->device(), pipelineCache, 1,
&gp_ci, NULL, &pipeline);
m_errorMonitor->VerifyFound();
vk::DestroyPipelineCache(m_device->device(), pipelineCache, NULL);
vk::DestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
vk::DestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
vk::DestroyDescriptorPool(m_device->device(), ds_pool, NULL);
}
*/
TEST_F(VkLayerTest, PSOViewportStateTests) {
TEST_DESCRIPTION("Test VkPipelineViewportStateCreateInfo viewport and scissor count validation for non-multiViewport");
VkPhysicalDeviceFeatures features{};
ASSERT_NO_FATAL_FAILURE(Init(&features));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
const auto break_vp_state = [](CreatePipelineHelper &helper) {
helper.rs_state_ci_.rasterizerDiscardEnable = VK_FALSE;
helper.gp_ci_.pViewportState = nullptr;
};
CreatePipelineHelper::OneshotTest(*this, break_vp_state, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750");
VkViewport viewport = {0.0f, 0.0f, 64.0f, 64.0f, 0.0f, 1.0f};
VkViewport viewports[] = {viewport, viewport};
VkRect2D scissor = {{0, 0}, {64, 64}};
VkRect2D scissors[] = {scissor, scissor};
// test viewport and scissor arrays
using std::vector;
struct TestCase {
uint32_t viewport_count;
VkViewport *viewports;
uint32_t scissor_count;
VkRect2D *scissors;
vector<std::string> vuids;
};
vector<TestCase> test_cases = {
{0,
viewports,
1,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
viewports,
1,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{1,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{1,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{0,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"}},
{2,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"}},
{0,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216", "VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216", "VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{1, nullptr, 1, scissors, {"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747"}},
{1, viewports, 1, nullptr, {"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}},
{1,
nullptr,
1,
nullptr,
{"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747", "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}},
{2,
nullptr,
3,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216", "VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220", "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747",
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}},
{0,
nullptr,
0,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"}},
};
for (const auto &test_case : test_cases) {
const auto break_vp = [&test_case](CreatePipelineHelper &helper) {
helper.vp_state_ci_.viewportCount = test_case.viewport_count;
helper.vp_state_ci_.pViewports = test_case.viewports;
helper.vp_state_ci_.scissorCount = test_case.scissor_count;
helper.vp_state_ci_.pScissors = test_case.scissors;
};
CreatePipelineHelper::OneshotTest(*this, break_vp, VK_DEBUG_REPORT_ERROR_BIT_EXT, test_case.vuids);
}
vector<TestCase> dyn_test_cases = {
{0,
viewports,
1,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
viewports,
1,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{1,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{1,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{0,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"}},
{2,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"}},
{0,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216", "VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216", "VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
nullptr,
3,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216", "VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{0,
nullptr,
0,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217"}},
};
const VkDynamicState dyn_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
for (const auto &test_case : dyn_test_cases) {
const auto break_vp = [&](CreatePipelineHelper &helper) {
VkPipelineDynamicStateCreateInfo dyn_state_ci = {};
dyn_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dyn_state_ci.dynamicStateCount = size(dyn_states);
dyn_state_ci.pDynamicStates = dyn_states;
helper.dyn_state_ci_ = dyn_state_ci;
helper.vp_state_ci_.viewportCount = test_case.viewport_count;
helper.vp_state_ci_.pViewports = test_case.viewports;
helper.vp_state_ci_.scissorCount = test_case.scissor_count;
helper.vp_state_ci_.pScissors = test_case.scissors;
};
CreatePipelineHelper::OneshotTest(*this, break_vp, VK_DEBUG_REPORT_ERROR_BIT_EXT, test_case.vuids);
}
}
// Set Extension dynamic states without enabling the required Extensions.
TEST_F(VkLayerTest, ExtensionDynamicStatesSetWOExtensionEnabled) {
TEST_DESCRIPTION("Create a graphics pipeline with Extension dynamic states without enabling the required Extensions.");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
using std::vector;
struct TestCase {
uint32_t dynamic_state_count;
VkDynamicState dynamic_state;
char const *errmsg;
};
vector<TestCase> dyn_test_cases = {
{1, VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV,
"contains VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV, but VK_NV_clip_space_w_scaling"},
{1, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT,
"contains VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT, but VK_EXT_discard_rectangles"},
{1, VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT, "contains VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT, but VK_EXT_sample_locations"},
};
for (const auto &test_case : dyn_test_cases) {
VkDynamicState state[1];
state[0] = test_case.dynamic_state;
const auto break_vp = [&](CreatePipelineHelper &helper) {
VkPipelineDynamicStateCreateInfo dyn_state_ci = {};
dyn_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dyn_state_ci.dynamicStateCount = test_case.dynamic_state_count;
dyn_state_ci.pDynamicStates = state;
helper.dyn_state_ci_ = dyn_state_ci;
};
CreatePipelineHelper::OneshotTest(*this, break_vp, VK_DEBUG_REPORT_ERROR_BIT_EXT, test_case.errmsg);
}
}
TEST_F(VkLayerTest, PSOViewportStateMultiViewportTests) {
TEST_DESCRIPTION("Test VkPipelineViewportStateCreateInfo viewport and scissor count validation for multiViewport feature");
ASSERT_NO_FATAL_FAILURE(Init()); // enables all supported features
if (!m_device->phy().features().multiViewport) {
printf("%s VkPhysicalDeviceFeatures::multiViewport is not supported -- skipping test.\n", kSkipPrefix);
return;
}
// at least 16 viewports supported from here on
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkViewport viewport = {0.0f, 0.0f, 64.0f, 64.0f, 0.0f, 1.0f};
VkViewport viewports[] = {viewport, viewport};
VkRect2D scissor = {{0, 0}, {64, 64}};
VkRect2D scissors[] = {scissor, scissor};
using std::vector;
struct TestCase {
uint32_t viewport_count;
VkViewport *viewports;
uint32_t scissor_count;
VkRect2D *scissors;
vector<std::string> vuids;
};
vector<TestCase> test_cases = {
{0,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{0,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-arraylength"}},
{2, nullptr, 2, scissors, {"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747"}},
{2, viewports, 2, nullptr, {"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}},
{2,
nullptr,
2,
nullptr,
{"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747", "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}},
{0,
nullptr,
0,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-arraylength"}},
};
const auto max_viewports = m_device->phy().properties().limits.maxViewports;
const bool max_viewports_maxxed = max_viewports == std::numeric_limits<decltype(max_viewports)>::max();
if (max_viewports_maxxed) {
printf("%s VkPhysicalDeviceLimits::maxViewports is UINT32_MAX -- skipping part of test requiring to exceed maxViewports.\n",
kSkipPrefix);
} else {
const auto too_much_viewports = max_viewports + 1;
// avoid potentially big allocations by using only nullptr
test_cases.push_back({too_much_viewports,
nullptr,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01218",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220",
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747"}});
test_cases.push_back({2,
viewports,
too_much_viewports,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01219",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220",
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}});
test_cases.push_back(
{too_much_viewports,
nullptr,
too_much_viewports,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01218",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01219", "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00747",
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00748"}});
}
for (const auto &test_case : test_cases) {
const auto break_vp = [&test_case](CreatePipelineHelper &helper) {
helper.vp_state_ci_.viewportCount = test_case.viewport_count;
helper.vp_state_ci_.pViewports = test_case.viewports;
helper.vp_state_ci_.scissorCount = test_case.scissor_count;
helper.vp_state_ci_.pScissors = test_case.scissors;
};
CreatePipelineHelper::OneshotTest(*this, break_vp, VK_DEBUG_REPORT_ERROR_BIT_EXT, test_case.vuids);
}
vector<TestCase> dyn_test_cases = {
{0,
viewports,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{2,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}},
{0,
viewports,
0,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-arraylength"}},
{0,
nullptr,
0,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-arraylength",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-arraylength"}},
};
if (!max_viewports_maxxed) {
const auto too_much_viewports = max_viewports + 1;
// avoid potentially big allocations by using only nullptr
dyn_test_cases.push_back({too_much_viewports,
nullptr,
2,
scissors,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01218",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}});
dyn_test_cases.push_back({2,
viewports,
too_much_viewports,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01219",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01220"}});
dyn_test_cases.push_back({too_much_viewports,
nullptr,
too_much_viewports,
nullptr,
{"VUID-VkPipelineViewportStateCreateInfo-viewportCount-01218",
"VUID-VkPipelineViewportStateCreateInfo-scissorCount-01219"}});
}
const VkDynamicState dyn_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
for (const auto &test_case : dyn_test_cases) {
const auto break_vp = [&](CreatePipelineHelper &helper) {
VkPipelineDynamicStateCreateInfo dyn_state_ci = {};
dyn_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dyn_state_ci.dynamicStateCount = size(dyn_states);
dyn_state_ci.pDynamicStates = dyn_states;
helper.dyn_state_ci_ = dyn_state_ci;
helper.vp_state_ci_.viewportCount = test_case.viewport_count;
helper.vp_state_ci_.pViewports = test_case.viewports;
helper.vp_state_ci_.scissorCount = test_case.scissor_count;
helper.vp_state_ci_.pScissors = test_case.scissors;
};
CreatePipelineHelper::OneshotTest(*this, break_vp, VK_DEBUG_REPORT_ERROR_BIT_EXT, test_case.vuids);
}
}
TEST_F(VkLayerTest, DynViewportAndScissorUndefinedDrawState) {
TEST_DESCRIPTION("Test viewport and scissor dynamic state that is not set before draw");
ASSERT_NO_FATAL_FAILURE(Init());
// TODO: should also test on !multiViewport
if (!m_device->phy().features().multiViewport) {
printf("%s Device does not support multiple viewports/scissors; skipped.\n", kSkipPrefix);
return;
}
ASSERT_NO_FATAL_FAILURE(InitViewport());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
const VkPipelineLayoutObj pipeline_layout(m_device);
VkPipelineObj pipeline_dyn_vp(m_device);
pipeline_dyn_vp.AddShader(&vs);
pipeline_dyn_vp.AddShader(&fs);
pipeline_dyn_vp.AddDefaultColorAttachment();
pipeline_dyn_vp.MakeDynamic(VK_DYNAMIC_STATE_VIEWPORT);
pipeline_dyn_vp.SetScissor(m_scissors);
ASSERT_VK_SUCCESS(pipeline_dyn_vp.CreateVKPipeline(pipeline_layout.handle(), m_renderPass));
VkPipelineObj pipeline_dyn_sc(m_device);
pipeline_dyn_sc.AddShader(&vs);
pipeline_dyn_sc.AddShader(&fs);
pipeline_dyn_sc.AddDefaultColorAttachment();
pipeline_dyn_sc.SetViewport(m_viewports);
pipeline_dyn_sc.MakeDynamic(VK_DYNAMIC_STATE_SCISSOR);
ASSERT_VK_SUCCESS(pipeline_dyn_sc.CreateVKPipeline(pipeline_layout.handle(), m_renderPass));
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
"Dynamic viewport(s) 0 are used by pipeline state object, ");
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_dyn_vp.handle());
vk::CmdSetViewport(m_commandBuffer->handle(), 1, 1,
&m_viewports[0]); // Forgetting to set needed 0th viewport (PSO viewportCount == 1)
m_commandBuffer->Draw(1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "Dynamic scissor(s) 0 are used by pipeline state object, ");
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_dyn_sc.handle());
vk::CmdSetScissor(m_commandBuffer->handle(), 1, 1,
&m_scissors[0]); // Forgetting to set needed 0th scissor (PSO scissorCount == 1)
m_commandBuffer->Draw(1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
}
TEST_F(VkLayerTest, PSOLineWidthInvalid) {
TEST_DESCRIPTION("Test non-1.0 lineWidth errors when pipeline is created and in vkCmdSetLineWidth");
VkPhysicalDeviceFeatures features{};
ASSERT_NO_FATAL_FAILURE(Init(&features));
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
const std::vector<float> test_cases = {-1.0f, 0.0f, NearestSmaller(1.0f), NearestGreater(1.0f), NAN};
// test VkPipelineRasterizationStateCreateInfo::lineWidth
for (const auto test_case : test_cases) {
const auto set_lineWidth = [&](CreatePipelineHelper &helper) { helper.rs_state_ci_.lineWidth = test_case; };
CreatePipelineHelper::OneshotTest(*this, set_lineWidth, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00749");
}
// test vk::CmdSetLineWidth
m_commandBuffer->begin();
for (const auto test_case : test_cases) {
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdSetLineWidth-lineWidth-00788");
vk::CmdSetLineWidth(m_commandBuffer->handle(), test_case);
m_errorMonitor->VerifyFound();
}
}
TEST_F(VkLayerTest, VUID_VkVertexInputBindingDescription_binding_00618) {
TEST_DESCRIPTION(
"Test VUID-VkVertexInputBindingDescription-binding-00618: binding must be less than "
"VkPhysicalDeviceLimits::maxVertexInputBindings");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
// Test when binding is greater than or equal to VkPhysicalDeviceLimits::maxVertexInputBindings.
VkVertexInputBindingDescription vertex_input_binding_description{};
vertex_input_binding_description.binding = m_device->props.limits.maxVertexInputBindings;
const auto set_binding = [&](CreatePipelineHelper &helper) {
helper.vi_ci_.pVertexBindingDescriptions = &vertex_input_binding_description;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
};
CreatePipelineHelper::OneshotTest(*this, set_binding, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkVertexInputBindingDescription-binding-00618");
}
TEST_F(VkLayerTest, VUID_VkVertexInputBindingDescription_stride_00619) {
TEST_DESCRIPTION(
"Test VUID-VkVertexInputBindingDescription-stride-00619: stride must be less than or equal to "
"VkPhysicalDeviceLimits::maxVertexInputBindingStride");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
// Test when stride is greater than VkPhysicalDeviceLimits::maxVertexInputBindingStride.
VkVertexInputBindingDescription vertex_input_binding_description{};
vertex_input_binding_description.stride = m_device->props.limits.maxVertexInputBindingStride + 1;
const auto set_binding = [&](CreatePipelineHelper &helper) {
helper.vi_ci_.pVertexBindingDescriptions = &vertex_input_binding_description;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
};
CreatePipelineHelper::OneshotTest(*this, set_binding, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkVertexInputBindingDescription-stride-00619");
}
TEST_F(VkLayerTest, VUID_VkVertexInputAttributeDescription_location_00620) {
TEST_DESCRIPTION(
"Test VUID-VkVertexInputAttributeDescription-location-00620: location must be less than "
"VkPhysicalDeviceLimits::maxVertexInputAttributes");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
// Test when location is greater than or equal to VkPhysicalDeviceLimits::maxVertexInputAttributes.
VkVertexInputAttributeDescription vertex_input_attribute_description{};
vertex_input_attribute_description.location = m_device->props.limits.maxVertexInputAttributes;
const auto set_attribute = [&](CreatePipelineHelper &helper) {
helper.vi_ci_.pVertexAttributeDescriptions = &vertex_input_attribute_description;
helper.vi_ci_.vertexAttributeDescriptionCount = 1;
};
CreatePipelineHelper::OneshotTest(*this, set_attribute, VK_DEBUG_REPORT_ERROR_BIT_EXT,
vector<string>{"VUID-VkVertexInputAttributeDescription-location-00620",
"VUID-VkPipelineVertexInputStateCreateInfo-binding-00615"});
}
TEST_F(VkLayerTest, VUID_VkVertexInputAttributeDescription_binding_00621) {
TEST_DESCRIPTION(
"Test VUID-VkVertexInputAttributeDescription-binding-00621: binding must be less than "
"VkPhysicalDeviceLimits::maxVertexInputBindings");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
// Test when binding is greater than or equal to VkPhysicalDeviceLimits::maxVertexInputBindings.
VkVertexInputAttributeDescription vertex_input_attribute_description{};
vertex_input_attribute_description.binding = m_device->props.limits.maxVertexInputBindings;
const auto set_attribute = [&](CreatePipelineHelper &helper) {
helper.vi_ci_.pVertexAttributeDescriptions = &vertex_input_attribute_description;
helper.vi_ci_.vertexAttributeDescriptionCount = 1;
};
CreatePipelineHelper::OneshotTest(*this, set_attribute, VK_DEBUG_REPORT_ERROR_BIT_EXT,
vector<string>{"VUID-VkVertexInputAttributeDescription-binding-00621",
"VUID-VkPipelineVertexInputStateCreateInfo-binding-00615"});
}
TEST_F(VkLayerTest, VUID_VkVertexInputAttributeDescription_offset_00622) {
TEST_DESCRIPTION(
"Test VUID-VkVertexInputAttributeDescription-offset-00622: offset must be less than or equal to "
"VkPhysicalDeviceLimits::maxVertexInputAttributeOffset");
EnableDeviceProfileLayer();
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
uint32_t maxVertexInputAttributeOffset = 0;
{
VkPhysicalDeviceProperties device_props = {};
vk::GetPhysicalDeviceProperties(gpu(), &device_props);
maxVertexInputAttributeOffset = device_props.limits.maxVertexInputAttributeOffset;
if (maxVertexInputAttributeOffset == 0xFFFFFFFF) {
// Attempt to artificially lower maximum offset
PFN_vkSetPhysicalDeviceLimitsEXT fpvkSetPhysicalDeviceLimitsEXT =
(PFN_vkSetPhysicalDeviceLimitsEXT)vk::GetInstanceProcAddr(instance(), "vkSetPhysicalDeviceLimitsEXT");
if (!fpvkSetPhysicalDeviceLimitsEXT) {
printf("%s All offsets are valid & device_profile_api not found; skipped.\n", kSkipPrefix);
return;
}
device_props.limits.maxVertexInputAttributeOffset = device_props.limits.maxVertexInputBindingStride - 2;
fpvkSetPhysicalDeviceLimitsEXT(gpu(), &device_props.limits);
maxVertexInputAttributeOffset = device_props.limits.maxVertexInputAttributeOffset;
}
}
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkVertexInputBindingDescription vertex_input_binding_description{};
vertex_input_binding_description.binding = 0;
vertex_input_binding_description.stride = m_device->props.limits.maxVertexInputBindingStride;
vertex_input_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
// Test when offset is greater than maximum.
VkVertexInputAttributeDescription vertex_input_attribute_description{};
vertex_input_attribute_description.format = VK_FORMAT_R8_UNORM;
vertex_input_attribute_description.offset = maxVertexInputAttributeOffset + 1;
const auto set_attribute = [&](CreatePipelineHelper &helper) {
helper.vi_ci_.pVertexBindingDescriptions = &vertex_input_binding_description;
helper.vi_ci_.vertexBindingDescriptionCount = 1;
helper.vi_ci_.pVertexAttributeDescriptions = &vertex_input_attribute_description;
helper.vi_ci_.vertexAttributeDescriptionCount = 1;
};
CreatePipelineHelper::OneshotTest(*this, set_attribute, VK_DEBUG_REPORT_ERROR_BIT_EXT,
"VUID-VkVertexInputAttributeDescription-offset-00622");
}
TEST_F(VkLayerTest, NumSamplesMismatch) {
// Create CommandBuffer where MSAA samples doesn't match RenderPass
// sampleCount
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "Num samples mismatch! ");
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
OneOffDescriptorSet descriptor_set(m_device, {
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
});
VkPipelineMultisampleStateCreateInfo pipe_ms_state_ci = {};
pipe_ms_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pipe_ms_state_ci.pNext = NULL;
pipe_ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_4_BIT;
pipe_ms_state_ci.sampleShadingEnable = 0;
pipe_ms_state_ci.minSampleShading = 1.0;
pipe_ms_state_ci.pSampleMask = NULL;
const VkPipelineLayoutObj pipeline_layout(m_device, {&descriptor_set.layout_});
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this); // We shouldn't need a fragment shader
// but add it to be able to run on more devices
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
pipe.AddDefaultColorAttachment();
pipe.SetMSAA(&pipe_ms_state_ci);
m_errorMonitor->SetUnexpectedError("VUID-VkGraphicsPipelineCreateInfo-subpass-00757");
pipe.CreateVKPipeline(pipeline_layout.handle(), renderPass());
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
VkViewport viewport = {0, 0, 16, 16, 0, 1};
vk::CmdSetViewport(m_commandBuffer->handle(), 0, 1, &viewport);
VkRect2D scissor = {{0, 0}, {16, 16}};
vk::CmdSetScissor(m_commandBuffer->handle(), 0, 1, &scissor);
// Render triangle (the error should trigger on the attempt to draw).
m_commandBuffer->Draw(3, 1, 0, 0);
// Finalize recording of the command buffer
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
m_errorMonitor->VerifyFound();
}
TEST_F(VkLayerTest, NumBlendAttachMismatch) {
// Create Pipeline where the number of blend attachments doesn't match the
// number of color attachments. In this case, we don't add any color
// blend attachments even though we have a color attachment.
ASSERT_NO_FATAL_FAILURE(Init());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkPipelineMultisampleStateCreateInfo pipe_ms_state_ci = {};
pipe_ms_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pipe_ms_state_ci.pNext = NULL;
pipe_ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
pipe_ms_state_ci.sampleShadingEnable = 0;
pipe_ms_state_ci.minSampleShading = 1.0;