blob: f2b0228cebe0ccb2688c2bd2c2c95bef5864fe86 [file] [log] [blame]
/*
* Copyright (c) 2015-2023 The Khronos Group Inc.
* Copyright (c) 2015-2023 Valve Corporation
* Copyright (c) 2015-2023 LunarG, Inc.
* Copyright (c) 2015-2023 Google, Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include "utils/cast_utils.h"
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
TEST_F(NegativeMesh, BasicUsage) {
TEST_DESCRIPTION("Test VK_EXT_mesh_shader.");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_MULTIVIEW_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
// Create a device that enables mesh_shader
VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input_dynamic_state = vku::InitStructHelper();
VkPhysicalDeviceExtendedDynamicState2FeaturesEXT extended_dynamic_state2 = vku::InitStructHelper(&vertex_input_dynamic_state);
VkPhysicalDeviceMaintenance4Features maintenance4 = vku::InitStructHelper(&extended_dynamic_state2);
VkPhysicalDeviceMultiviewFeatures multiview_feature = vku::InitStructHelper(&maintenance4);
VkPhysicalDeviceTransformFeedbackFeaturesEXT xfb_feature = vku::InitStructHelper(&multiview_feature);
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = vku::InitStructHelper(&xfb_feature);
auto features2 = GetPhysicalDeviceFeatures2(mesh_shader_features);
mesh_shader_features.multiviewMeshShader = VK_FALSE;
features2.features.multiDrawIndirect = VK_FALSE;
RETURN_IF_SKIP(InitState(nullptr, &features2))
InitRenderTarget();
static const char vert_shader_text[] = R"glsl(
#version 460
vec2 vertices[3];
void main() {
vertices[0] = vec2(-1.0, -1.0);
vertices[1] = vec2( 1.0, -1.0);
vertices[2] = vec2( 0.0, 1.0);
gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);
gl_PointSize = 1.0f;
}
)glsl";
// #version 460
// #extension GL_EXT_mesh_shader : require
// layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
// layout(max_vertices = 3) out;
// layout(max_primitives = 1) out;
// layout(triangles) out;
// void main() {
// SetMeshOutputsEXT(3,1);
// gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0, 1);
// gl_MeshVerticesEXT[1].gl_Position = vec4( 1.0, -1.0, 0, 1);
// gl_MeshVerticesEXT[2].gl_Position = vec4( 0.0, 1.0, 0, 1);
// gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0,1,2);
// }
static const char mesh_shader_text[] = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main" %gl_MeshVerticesEXT %gl_PrimitiveTriangleIndicesEXT
OpExecutionMode %main LocalSize 1 1 1
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main OutputPrimitivesNV 1
OpExecutionMode %main OutputTrianglesNV
OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position
OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize
OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance
OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance
OpDecorate %gl_MeshPerVertexEXT Block
OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_arr_float_uint_1 = OpTypeArray %float %uint_1
%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3
%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3
%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%float_n1 = OpConstant %float -1
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%21 = OpConstantComposite %v4float %float_n1 %float_n1 %float_0 %float_1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%int_1 = OpConstant %int 1
%25 = OpConstantComposite %v4float %float_1 %float_n1 %float_0 %float_1
%int_2 = OpConstant %int 2
%28 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1
%v3uint = OpTypeVector %uint 3
%_arr_v3uint_uint_3 = OpTypeArray %v3uint %uint_3
%_ptr_Output__arr_v3uint_uint_3 = OpTypePointer Output %_arr_v3uint_uint_3
%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_3 Output
%uint_0 = OpConstant %uint 0
%uint_2 = OpConstant %uint 2
%36 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
%_ptr_Output_v3uint = OpTypePointer Output %v3uint
%39 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpSetMeshOutputsEXT %uint_3 %uint_1
%23 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0
OpStore %23 %21
%26 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_1 %int_0
OpStore %26 %25
%29 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_2 %int_0
OpStore %29 %28
%38 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0
OpStore %38 %36
OpReturn
OpFunctionEnd
)";
static const char mesh_shader_xfb_text[] = R"(
OpCapability MeshShadingEXT
OpCapability TransformFeedback
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main" %tf
OpExecutionModeId %main LocalSizeId %uint_2 %uint_1 %uint_1
OpExecutionMode %main Xfb
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main OutputPrimitivesEXT 1
OpExecutionMode %main OutputTrianglesEXT
OpSource GLSL 450
OpSourceExtension "GL_EXT_mesh_shader"
OpName %main "main"
OpName %tf "tf"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Output_float = OpTypePointer Output %float
%tf = OpVariable %_ptr_Output_float Output
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%10 = OpConstantComposite %v3uint %uint_2 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
VkShaderObj vs(this, vert_shader_text, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj ms(this, mesh_shader_text, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj ms_xfb(this, mesh_shader_xfb_text, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
// Test pipeline creation
{
// can't mix mesh with vertex
const auto break_vp = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo(), ms.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-pStages-02095"}));
// vertex or mesh must be present
// 02096 overlaps with 06896
const auto break_vp2 = [&](CreatePipelineHelper &helper) { helper.shader_stages_ = {fs.GetStageCreateInfo()}; };
CreatePipelineHelper::OneshotTest(*this, break_vp2, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-stage-02096",
"VUID-VkGraphicsPipelineCreateInfo-pStages-06896"}));
// vertexinput and inputassembly must be valid when vertex stage is present
const auto break_vp3 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.gp_ci_.pVertexInputState = nullptr;
helper.gp_ci_.pInputAssemblyState = nullptr;
};
CreatePipelineHelper::OneshotTest(
*this, break_vp3, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-pStages-02097",
"VUID-VkGraphicsPipelineCreateInfo-dynamicPrimitiveTopologyUnrestricted-09031"}));
// xfb with mesh shader
const auto break_vp4 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {ms_xfb.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp4, kErrorBit,
vector<std::string>({" VUID-VkGraphicsPipelineCreateInfo-None-02322"}));
// invalid dynamic state with mesh shader
vector<VkDynamicState> dyn_states[] = {
{VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT},
{VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT},
{VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT},
{VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT},
{VK_DYNAMIC_STATE_VERTEX_INPUT_EXT},
};
const char *err_vuids[] = {
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07065", "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07065",
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066", "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066",
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07067"};
VkPipelineDynamicStateCreateInfo dyn_state = vku::InitStructHelper();
for (int i = 0; i < 5; i++) {
dyn_state.dynamicStateCount = dyn_states[i].size();
dyn_state.pDynamicStates = dyn_states[i].data();
if (!extended_dynamic_state2.extendedDynamicState2PatchControlPoints &&
*dyn_state.pDynamicStates == VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT)
continue;
const auto break_vp5 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {ms.GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.gp_ci_.pDynamicState = &dyn_state;
};
CreatePipelineHelper::OneshotTest(*this, break_vp5, kErrorBit, vector<std::string>({err_vuids[i]}));
}
// viewMask without enabling multiviewMeshShader feature
VkPipelineRenderingCreateInfo pipeline_rendering_info = vku::InitStructHelper();
pipeline_rendering_info.viewMask = 0x2;
VkFormat color_formats[] = {VK_FORMAT_UNDEFINED};
pipeline_rendering_info.colorAttachmentCount = 1;
pipeline_rendering_info.pColorAttachmentFormats = color_formats;
const auto break_vp5 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {ms.GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.gp_ci_.pNext = &pipeline_rendering_info;
};
CreatePipelineHelper::OneshotTest(*this, break_vp5, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-renderPass-07064"}));
}
}
TEST_F(NegativeMesh, ExtensionDisabled) {
TEST_DESCRIPTION("Test VK_EXT_mesh_shader VUs with EXT_mesh_shader disabled.");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4Features maintenance4 = vku::InitStructHelper();
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = vku::InitStructHelper(&maintenance4);
GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
mesh_shader_features.meshShader = VK_FALSE;
mesh_shader_features.taskShader = VK_FALSE;
RETURN_IF_SKIP(InitState(nullptr, &mesh_shader_features));
InitRenderTarget();
VkShaderObj vs(this, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT);
// #version 450
// #extension GL_EXT_mesh_shader : require
// layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
// struct Task {
// uint baseID;
// };
// taskPayloadSharedEXT Task OUT;
// void main() {
// OUT.baseID = 1;
// }
const char *task_src = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint TaskEXT %main "main" %OUT
OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
OpMemberName %Task 0 "baseID"
OpName %OUT "OUT"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%Task = OpTypeStruct %uint
%_ptr_TaskPayloadWorkgroupEXT_Task = OpTypePointer TaskPayloadWorkgroupEXT %Task
%OUT = OpVariable %_ptr_TaskPayloadWorkgroupEXT_Task TaskPayloadWorkgroupEXT
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_TaskPayloadWorkgroupEXT_uint = OpTypePointer TaskPayloadWorkgroupEXT %uint
%v3uint = OpTypeVector %uint 3
%16 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
%14 = OpAccessChain %_ptr_TaskPayloadWorkgroupEXT_uint %OUT %int_0
OpStore %14 %uint_1
OpReturn
OpFunctionEnd
)";
// #version 450
// #extension GL_EXT_mesh_shader : enable
// layout (triangles) out;
// layout (max_vertices = 3, max_primitives = 1) out;
// struct Task {
// uint baseID;
// };
// taskPayloadSharedEXT Task IN;
// void main() {}
const char *mesh_src = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main" %IN
OpExecutionMode %main LocalSize 1 1 1
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main OutputPrimitivesEXT 1
OpExecutionMode %main OutputTrianglesEXT
OpSource GLSL 450
OpSourceExtension "GL_EXT_mesh_shader"
OpName %main "main"
OpName %Task "Task"
OpMemberName %Task 0 "baseID"
OpName %IN "IN"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%Task = OpTypeStruct %uint
%_ptr_TaskPayloadWorkgroupEXT_Task = OpTypePointer TaskPayloadWorkgroupEXT %Task
%IN = OpVariable %_ptr_TaskPayloadWorkgroupEXT_Task TaskPayloadWorkgroupEXT
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
VkShaderObj task_shader(this, task_src, VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj mesh_shader(this, mesh_src, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
// mesh and task shaders not supported
const auto break_vp = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task_shader.GetStageCreateInfo(), mesh_shader.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp, kErrorBit,
vector<std::string>({"VUID-VkPipelineShaderStageCreateInfo-stage-02091",
"VUID-VkPipelineShaderStageCreateInfo-stage-02092"}));
}
TEST_F(NegativeMesh, RuntimeSpirv) {
TEST_DESCRIPTION("Test VK_EXT_mesh_shader spirv related VUIDs.");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4Features maintenance4 = vku::InitStructHelper();
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = vku::InitStructHelper(&maintenance4);
GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
RETURN_IF_SKIP(InitState(nullptr, &mesh_shader_features));
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
vector<std::string> error_vuids, error_vuids_1;
uint32_t max_task_workgroup_size_x = mesh_shader_properties.maxTaskWorkGroupSize[0];
uint32_t max_task_workgroup_size_y = mesh_shader_properties.maxTaskWorkGroupSize[1];
uint32_t max_task_workgroup_size_z = mesh_shader_properties.maxTaskWorkGroupSize[2];
uint32_t max_mesh_workgroup_size_x = mesh_shader_properties.maxMeshWorkGroupSize[0];
uint32_t max_mesh_workgroup_size_y = mesh_shader_properties.maxMeshWorkGroupSize[1];
uint32_t max_mesh_workgroup_size_z = mesh_shader_properties.maxMeshWorkGroupSize[2];
uint32_t max_mesh_output_vertices = mesh_shader_properties.maxMeshOutputVertices;
uint32_t max_mesh_output_primitives = mesh_shader_properties.maxMeshOutputPrimitives;
if (max_task_workgroup_size_x < vvl::MaxTypeValue(max_task_workgroup_size_x)) {
error_vuids.push_back("VUID-RuntimeSpirv-TaskEXT-07291");
max_task_workgroup_size_x += 1;
}
if (max_task_workgroup_size_y < vvl::MaxTypeValue(max_task_workgroup_size_y)) {
error_vuids.push_back("VUID-RuntimeSpirv-TaskEXT-07292");
max_task_workgroup_size_y += 1;
}
if (max_task_workgroup_size_z < vvl::MaxTypeValue(max_task_workgroup_size_z)) {
error_vuids.push_back("VUID-RuntimeSpirv-TaskEXT-07293");
max_task_workgroup_size_z += 1;
}
error_vuids.push_back("VUID-RuntimeSpirv-TaskEXT-07294");
if (max_mesh_workgroup_size_x < vvl::MaxTypeValue(max_mesh_workgroup_size_x)) {
error_vuids.push_back("VUID-RuntimeSpirv-MeshEXT-07295");
max_mesh_workgroup_size_x += 1;
}
if (max_mesh_workgroup_size_y < vvl::MaxTypeValue(max_mesh_workgroup_size_y)) {
error_vuids.push_back("VUID-RuntimeSpirv-MeshEXT-07296");
max_mesh_workgroup_size_y += 1;
}
if (max_mesh_workgroup_size_z < vvl::MaxTypeValue(max_mesh_workgroup_size_z)) {
error_vuids.push_back("VUID-RuntimeSpirv-MeshEXT-07297");
max_mesh_workgroup_size_z += 1;
}
error_vuids.push_back("VUID-RuntimeSpirv-MeshEXT-07298");
if (max_mesh_output_vertices < vvl::MaxTypeValue(max_mesh_output_vertices)) {
error_vuids_1.push_back("VUID-RuntimeSpirv-MeshEXT-07115");
max_mesh_output_vertices += 1;
}
if (max_mesh_output_primitives < vvl::MaxTypeValue(max_mesh_output_primitives)) {
error_vuids_1.push_back("VUID-RuntimeSpirv-MeshEXT-07116");
max_mesh_output_primitives += 1;
}
std::string task_src = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint TaskEXT %main "main" %OUT
OpExecutionMode %main LocalSize )";
task_src += std::to_string(max_task_workgroup_size_x);
task_src += " ";
task_src += std::to_string(max_task_workgroup_size_y);
task_src += " ";
task_src += std::to_string(max_task_workgroup_size_z);
task_src += R"(
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%9 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%Task = OpTypeStruct %uint
%_ptr_TaskPayloadWorkgroupEXT_Task = OpTypePointer TaskPayloadWorkgroupEXT %Task
%OUT = OpVariable %_ptr_TaskPayloadWorkgroupEXT_Task TaskPayloadWorkgroupEXT
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
std::string mesh_src = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main"
OpExecutionMode %main LocalSize )";
mesh_src += std::to_string(max_mesh_workgroup_size_x);
mesh_src += " ";
mesh_src += std::to_string(max_mesh_workgroup_size_y);
mesh_src += " ";
mesh_src += std::to_string(max_mesh_workgroup_size_z);
mesh_src += R"(
OpExecutionMode %main OutputVertices 1
OpExecutionMode %main OutputPrimitivesEXT 1
OpExecutionMode %main OutputTrianglesEXT
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%9 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
std::string mesh_src_2 = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main"
OpExecutionModeId %main LocalSizeId %uint_2 %uint_1 %uint_1
OpExecutionMode %main OutputVertices )";
mesh_src_2 += std::to_string(max_mesh_output_vertices);
mesh_src_2 += R"(
OpExecutionMode %main OutputPrimitivesEXT )";
mesh_src_2 += std::to_string(max_mesh_output_primitives);
mesh_src_2 += R"(
OpExecutionMode %main OutputTrianglesEXT
OpSource GLSL 450
OpSourceExtension "GL_EXT_mesh_shader"
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%10 = OpConstantComposite %v3uint %uint_2 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
VkShaderObj task_shader(this, task_src.c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj mesh_shader(this, mesh_src.c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj mesh_shader_2(this, mesh_src_2.c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
// mesh and task shaders which exceeds workgroup size limits
const auto break_vp = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task_shader.GetStageCreateInfo(), mesh_shader.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp, kErrorBit, error_vuids);
const auto break_vp1 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {mesh_shader_2.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp1, kErrorBit, error_vuids_1);
}
TEST_F(NegativeMesh, RuntimeSpirvNV) {
TEST_DESCRIPTION("Test VK_NV_mesh_shader spirv related VUIDs");
AddRequiredExtensions(VK_NV_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMeshShaderFeaturesNV mesh_shader_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
RETURN_IF_SKIP(InitState(nullptr, &features2))
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesNV mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
std::string mesh_src = R"(
OpCapability MeshShadingNV
OpExtension "SPV_NV_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshNV %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpExecutionMode %main OutputVertices )";
mesh_src += std::to_string(mesh_shader_properties.maxMeshOutputVertices + 1);
mesh_src += R"(
OpExecutionMode %main OutputPrimitivesNV )";
mesh_src += std::to_string(mesh_shader_properties.maxMeshOutputPrimitives + 1);
mesh_src += R"(
OpExecutionMode %main OutputTrianglesNV
OpSource GLSL 450
OpSourceExtension "GL_NV_mesh_shader"
OpName %main "main"
OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%v3uint = OpTypeVector %uint 3
%uint_1 = OpConstant %uint 1
%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
VkShaderObj ms(this, mesh_src.c_str(), VK_SHADER_STAGE_MESH_BIT_NV, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
const auto break_vp1 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {ms.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp1, kErrorBit,
vector<std::string>({"VUID-RuntimeSpirv-MeshNV-07113", "VUID-RuntimeSpirv-MeshNV-07114"}));
}
TEST_F(NegativeMesh, BasicUsageNV) {
TEST_DESCRIPTION("Test VK_NV_mesh_shader.");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_NV_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
// Create a device that enables mesh_shader
VkPhysicalDeviceMeshShaderFeaturesNV mesh_shader_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(mesh_shader_features);
features2.features.multiDrawIndirect = VK_FALSE;
RETURN_IF_SKIP(InitState(nullptr, &features2))
InitRenderTarget();
static const char vertShaderText[] = R"glsl(
#version 450
vec2 vertices[3];
void main() {
vertices[0] = vec2(-1.0, -1.0);
vertices[1] = vec2( 1.0, -1.0);
vertices[2] = vec2( 0.0, 1.0);
gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);
gl_PointSize = 1.0f;
}
)glsl";
static const char meshShaderText[] = R"glsl(
#version 450
#extension GL_NV_mesh_shader : require
layout(local_size_x = 1) in;
layout(max_vertices = 3) out;
layout(max_primitives = 1) out;
layout(triangles) out;
void main() {
gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0, 1);
gl_MeshVerticesNV[1].gl_Position = vec4( 1.0, -1.0, 0, 1);
gl_MeshVerticesNV[2].gl_Position = vec4( 0.0, 1.0, 0, 1);
gl_PrimitiveIndicesNV[0] = 0;
gl_PrimitiveIndicesNV[1] = 1;
gl_PrimitiveIndicesNV[2] = 2;
gl_PrimitiveCountNV = 1;
}
)glsl";
VkShaderObj vs(this, vertShaderText, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj ms(this, meshShaderText, VK_SHADER_STAGE_MESH_BIT_NV);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
// Test pipeline creation
{
// can't mix mesh with vertex
const auto break_vp = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo(), ms.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-pStages-02095"}));
// vertex or mesh must be present
// 02096 overlaps with 06896
const auto break_vp2 = [&](CreatePipelineHelper &helper) { helper.shader_stages_ = {fs.GetStageCreateInfo()}; };
CreatePipelineHelper::OneshotTest(*this, break_vp2, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-stage-02096",
"VUID-VkGraphicsPipelineCreateInfo-pStages-06896"}));
// vertexinput and inputassembly must be valid when vertex stage is present
const auto break_vp3 = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.gp_ci_.pVertexInputState = nullptr;
helper.gp_ci_.pInputAssemblyState = nullptr;
};
CreatePipelineHelper::OneshotTest(
*this, break_vp3, kErrorBit,
vector<std::string>({"VUID-VkGraphicsPipelineCreateInfo-pStages-02097",
"VUID-VkGraphicsPipelineCreateInfo-dynamicPrimitiveTopologyUnrestricted-09031"}));
}
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.size = sizeof(uint32_t);
buffer_create_info.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
VkBuffer buffer;
VkResult result = vk::CreateBuffer(m_device->device(), &buffer_create_info, nullptr, &buffer);
ASSERT_EQ(VK_SUCCESS, result);
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-None-08606");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-buffer-02708");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-drawCount-02157");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-drawCount-02146");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-drawCount-02718");
vk::CmdDrawMeshTasksIndirectNV(m_commandBuffer->handle(), buffer, 0, 2, 0);
m_errorMonitor->VerifyFound();
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
vk::DestroyBuffer(m_device->device(), buffer, 0);
}
TEST_F(NegativeMesh, ExtensionDisabledNV) {
TEST_DESCRIPTION("Test VK_NV_mesh_shader VUs with NV_mesh_shader disabled.");
AddRequiredExtensions(VK_NV_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMeshShaderFeaturesNV mesh_shader_features = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
mesh_shader_features.meshShader = VK_FALSE;
mesh_shader_features.taskShader = VK_FALSE;
RETURN_IF_SKIP(InitState(nullptr, &mesh_shader_features));
InitRenderTarget();
vkt::Event event_obj(*m_device);
const auto event = event_obj.handle();
ASSERT_TRUE(event_obj.initialized());
m_commandBuffer->begin();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdSetEvent-stageMask-04095");
vk::CmdSetEvent(m_commandBuffer->handle(), event, VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdSetEvent-stageMask-04096");
vk::CmdSetEvent(m_commandBuffer->handle(), event, VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdResetEvent-stageMask-04095");
vk::CmdResetEvent(m_commandBuffer->handle(), event, VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdResetEvent-stageMask-04096");
vk::CmdResetEvent(m_commandBuffer->handle(), event, VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdWaitEvents-srcStageMask-04095");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdWaitEvents-dstStageMask-04095");
vk::CmdWaitEvents(m_commandBuffer->handle(), 1, &event, VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV,
VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV, 0, nullptr, 0, nullptr, 0, nullptr);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdWaitEvents-srcStageMask-04096");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdWaitEvents-dstStageMask-04096");
vk::CmdWaitEvents(m_commandBuffer->handle(), 1, &event, VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV,
VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV, 0, nullptr, 0, nullptr, 0, nullptr);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdPipelineBarrier-srcStageMask-04095");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdPipelineBarrier-dstStageMask-04095");
vk::CmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV, VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV, 0,
0, nullptr, 0, nullptr, 0, nullptr);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdPipelineBarrier-srcStageMask-04096");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdPipelineBarrier-dstStageMask-04096");
vk::CmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV, VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV, 0,
0, nullptr, 0, nullptr, 0, nullptr);
m_errorMonitor->VerifyFound();
m_commandBuffer->end();
vkt::Semaphore semaphore_obj(*m_device);
const auto semaphore = semaphore_obj.handle();
ASSERT_TRUE(semaphore_obj.initialized());
VkPipelineStageFlags stage_flags = VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV | VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV;
VkSubmitInfo submit_info = vku::InitStructHelper();
// Signal the semaphore so the next test can wait on it.
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &semaphore;
vk::QueueSubmit(m_default_queue, 1, &submit_info, VK_NULL_HANDLE);
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = nullptr;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &semaphore;
submit_info.pWaitDstStageMask = &stage_flags;
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-VkSubmitInfo-pWaitDstStageMask-04095");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-VkSubmitInfo-pWaitDstStageMask-04096");
vk::QueueSubmit(m_default_queue, 1, &submit_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
vk::QueueWaitIdle(m_default_queue);
VkShaderObj vs(this, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT);
// #version 450
// #extension GL_NV_mesh_shader : require
// layout(local_size_x = 1) in;
// taskNV out Task {
// uint baseID;
// } OUT;
// void main() {
// OUT.baseID = 1;
// }
const char *task_src = R"(
OpCapability MeshShadingNV
OpExtension "SPV_NV_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint TaskNV %main "main" %OUT
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %Task 0 PerTaskNV
OpMemberDecorate %Task 0 Offset 0
OpDecorate %Task Block
OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%Task = OpTypeStruct %uint
%_ptr_Output_Task = OpTypePointer Output %Task
%OUT = OpVariable %_ptr_Output_Task Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint_1 = OpConstant %uint 1
%_ptr_Output_uint = OpTypePointer Output %uint
%v3uint = OpTypeVector %uint 3
%uint_32 = OpConstant %uint 32
%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
%14 = OpAccessChain %_ptr_Output_uint %OUT %int_0
OpStore %14 %uint_1
OpReturn
OpFunctionEnd
)";
static const char mesh_src[] = R"glsl(
#version 450
#extension GL_NV_mesh_shader : require
layout(local_size_x = 1) in;
layout(max_vertices = 3) out;
layout(max_primitives = 1) out;
layout(triangles) out;
taskNV in Task {
uint baseID;
} IN;
void main() {
}
)glsl";
VkShaderObj task_shader(this, task_src, VK_SHADER_STAGE_TASK_BIT_NV, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
VkShaderObj mesh_shader(this, mesh_src, VK_SHADER_STAGE_MESH_BIT_NV);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
// mesh and task shaders not supported
const auto break_vp = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task_shader.GetStageCreateInfo(), mesh_shader.GetStageCreateInfo(), fs.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, break_vp, kErrorBit,
vector<std::string>({"VUID-VkPipelineShaderStageCreateInfo-stage-02091",
"VUID-VkPipelineShaderStageCreateInfo-stage-02092"}));
}
TEST_F(NegativeMesh, DrawCmds) {
TEST_DESCRIPTION("Test VK_EXT_mesh_shader draw commands.");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4Features maintenance4 = vku::InitStructHelper();
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = vku::InitStructHelper(&maintenance4);
auto features2 = GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
features2.features.multiDrawIndirect = VK_FALSE;
RETURN_IF_SKIP(InitState(nullptr, &features2))
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
// #version 450
// #extension GL_EXT_mesh_shader : enable
// layout (triangles) out;
// layout (max_vertices = 3, max_primitives = 1) out;
// struct Task {
// uint baseID;
// };
// taskPayloadSharedEXT Task IN;
// void main() {}
const char *mesh_src = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main" %IN
OpExecutionMode %main LocalSize 1 1 1
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main OutputPrimitivesEXT 1
OpExecutionMode %main OutputTrianglesEXT
OpSource GLSL 450
OpSourceExtension "GL_EXT_mesh_shader"
OpName %main "main"
OpName %Task "Task"
OpMemberName %Task 0 "baseID"
OpName %IN "IN"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%Task = OpTypeStruct %uint
%_ptr_TaskPayloadWorkgroupEXT_Task = OpTypePointer TaskPayloadWorkgroupEXT %Task
%IN = OpVariable %_ptr_TaskPayloadWorkgroupEXT_Task TaskPayloadWorkgroupEXT
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
VkShaderObj mesh_shader(this, mesh_src, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
buffer_create_info.size = 2 * sizeof(VkDrawMeshTasksIndirectCommandEXT);
vkt::Buffer buffer(*m_device, buffer_create_info);
buffer_create_info.size = 64;
vkt::Buffer count_buffer(*m_device, buffer_create_info);
CreatePipelineHelper pipe(*this);
pipe.InitState();
pipe.shader_stages_[0] = mesh_shader.GetStageCreateInfo();
pipe.CreateGraphicsPipeline();
CreatePipelineHelper pipe1(*this);
pipe1.InitState();
pipe1.CreateGraphicsPipeline();
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(renderPassBeginInfo());
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle());
uint32_t max_group_count_X = mesh_shader_properties.maxTaskWorkGroupCount[0];
uint32_t max_group_count_Y = mesh_shader_properties.maxTaskWorkGroupCount[1];
uint32_t max_group_count_Z = mesh_shader_properties.maxTaskWorkGroupCount[2];
if (max_group_count_X < vvl::MaxTypeValue(max_group_count_X)) {
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksEXT-TaskEXT-07322");
max_group_count_X = vvl::MaxTypeValue(max_group_count_X);
}
if (max_group_count_Y < vvl::MaxTypeValue(max_group_count_Y)) {
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksEXT-TaskEXT-07323");
max_group_count_Y = vvl::MaxTypeValue(max_group_count_Y);
}
if (max_group_count_Z < vvl::MaxTypeValue(max_group_count_Z)) {
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksEXT-TaskEXT-07324");
max_group_count_Z = vvl::MaxTypeValue(max_group_count_Z);
}
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksEXT-TaskEXT-07325");
vk::CmdDrawMeshTasksEXT(m_commandBuffer->handle(), max_group_count_X, max_group_count_Y, max_group_count_Z);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-02718");
vk::CmdDrawMeshTasksIndirectEXT(m_commandBuffer->handle(), buffer.handle(), 0, 2, sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
if (m_device->phy().limits_.maxDrawIndirectCount < vvl::MaxTypeValue(m_device->phy().limits_.maxDrawIndirectCount)) {
m_errorMonitor->SetUnexpectedError("VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-02718");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-07090");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-02719");
vk::CmdDrawMeshTasksIndirectEXT(m_commandBuffer->handle(), buffer.handle(), 0,
m_device->phy().limits_.maxDrawIndirectCount + 1,
sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
}
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-07089");
vk::CmdDrawMeshTasksIndirectEXT(m_commandBuffer->handle(), buffer.handle(), sizeof(VkDrawMeshTasksIndirectCommandEXT) * 2, 1,
sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDraw-stage-06481");
vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
m_errorMonitor->VerifyFound();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe1.Handle());
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksEXT-stage-06480");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksEXT-MeshEXT-07087");
vk::CmdDrawMeshTasksEXT(m_commandBuffer->handle(), 1, 1, 1);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-MeshEXT-07091");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-stage-06480");
vk::CmdDrawMeshTasksIndirectEXT(m_commandBuffer->handle(), buffer.handle(), 0, 1, sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountEXT-MeshEXT-07100");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountEXT-stage-06480");
vk::CmdDrawMeshTasksIndirectCountEXT(m_commandBuffer->handle(), buffer.handle(), 0, count_buffer.handle(), 0, 1,
sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
}
TEST_F(NegativeMesh, MultiDrawIndirect) {
TEST_DESCRIPTION("Test VK_EXT_mesh_shader indirect draw command.");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4Features maintenance4 = vku::InitStructHelper();
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = vku::InitStructHelper(&maintenance4);
auto features2 = GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
RETURN_IF_SKIP(InitState(nullptr, &features2))
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
// #version 450
// #extension GL_EXT_mesh_shader : require
// layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
// layout(max_vertices = 3, max_primitives = 1) out;
// layout(triangles) out;
// struct Task {
// uint baseID;
// };
// taskPayloadSharedEXT Task IN;
// void main() {}
static const char mesh_src[] = R"(
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main" %IN
OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main OutputPrimitivesNV 1
OpExecutionMode %main OutputTrianglesNV
OpSource GLSL 450
OpSourceExtension "GL_EXT_mesh_shader"
OpName %main "main"
OpName %Task "Task"
OpMemberName %Task 0 "baseID"
OpName %IN "IN"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%9 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%Task = OpTypeStruct %uint
%_ptr_TaskPayloadWorkgroupEXT_Task = OpTypePointer TaskPayloadWorkgroupEXT %Task
%IN = OpVariable %_ptr_TaskPayloadWorkgroupEXT_Task TaskPayloadWorkgroupEXT
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
VkShaderObj mesh_shader(this, mesh_src, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3, SPV_SOURCE_ASM);
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
buffer_create_info.size = 2 * sizeof(VkDrawMeshTasksIndirectCommandEXT);
vkt::Buffer buffer(*m_device, buffer_create_info);
CreatePipelineHelper pipe(*this);
pipe.InitState();
pipe.shader_stages_[0] = mesh_shader.GetStageCreateInfo();
pipe.CreateGraphicsPipeline();
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(renderPassBeginInfo());
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle());
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-07088");
vk::CmdDrawMeshTasksIndirectEXT(m_commandBuffer->handle(), buffer.handle(), 0, 2,
sizeof(VkDrawMeshTasksIndirectCommandEXT) - 2);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectEXT-drawCount-07090");
vk::CmdDrawMeshTasksIndirectEXT(m_commandBuffer->handle(), buffer.handle(), 0, 4, sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
vkt::Buffer draw_buffer;
draw_buffer.init_no_mem(*m_device, buffer_create_info);
ASSERT_TRUE(draw_buffer.initialized());
draw_buffer.allocate_and_bind_memory(*m_device);
VkBufferCreateInfo count_buffer_create_info = vku::InitStructHelper();
count_buffer_create_info.size = 64;
count_buffer_create_info.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
vkt::Buffer count_buffer(*m_device, count_buffer_create_info);
ASSERT_TRUE(count_buffer.initialized());
vkt::Buffer count_buffer_unbound;
count_buffer_unbound.init_no_mem(*m_device, count_buffer_create_info);
ASSERT_TRUE(count_buffer_unbound.initialized());
vkt::Buffer count_buffer_wrong_usage;
count_buffer_create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
count_buffer_wrong_usage.init(*m_device, count_buffer_create_info);
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountEXT-countBuffer-02714");
vk::CmdDrawMeshTasksIndirectCountEXT(m_commandBuffer->handle(), draw_buffer.handle(), 0, count_buffer_unbound.handle(), 0, 1,
sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountEXT-countBuffer-02715");
vk::CmdDrawMeshTasksIndirectCountEXT(m_commandBuffer->handle(), draw_buffer.handle(), 0, count_buffer_wrong_usage.handle(), 0,
1, sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountEXT-stride-07096");
vk::CmdDrawMeshTasksIndirectCountEXT(m_commandBuffer->handle(), draw_buffer.handle(), 0, count_buffer.handle(), 0, 1,
sizeof(VkDrawMeshTasksIndirectCommandEXT) - 3);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountEXT-maxDrawCount-07097");
vk::CmdDrawMeshTasksIndirectCountEXT(m_commandBuffer->handle(), draw_buffer.handle(),
2 * sizeof(VkDrawMeshTasksIndirectCommandEXT), count_buffer.handle(), 0, 4,
sizeof(VkDrawMeshTasksIndirectCommandEXT));
m_errorMonitor->VerifyFound();
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
}
TEST_F(NegativeMesh, DrawCmdsNV) {
TEST_DESCRIPTION("Test VK_NV_mesh_shader draw commands.");
AddRequiredExtensions(VK_NV_MESH_SHADER_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMeshShaderFeaturesNV mesh_shader_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(mesh_shader_features);
if (mesh_shader_features.meshShader != VK_TRUE) {
GTEST_SKIP() << "Mesh shader feature not supported";
}
features2.features.multiDrawIndirect = VK_FALSE;
RETURN_IF_SKIP(InitState(nullptr, &features2))
InitRenderTarget();
static const char mesh_src[] = R"glsl(
#version 450
#extension GL_NV_mesh_shader : require
layout(local_size_x = 1) in;
layout(max_vertices = 3) out;
layout(max_primitives = 1) out;
layout(triangles) out;
void main() {
gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0, 1);
gl_MeshVerticesNV[1].gl_Position = vec4( 1.0, -1.0, 0, 1);
gl_MeshVerticesNV[2].gl_Position = vec4( 0.0, 1.0, 0, 1);
gl_PrimitiveIndicesNV[0] = 0;
gl_PrimitiveIndicesNV[1] = 1;
gl_PrimitiveIndicesNV[2] = 2;
gl_PrimitiveCountNV = 1;
}
)glsl";
VkShaderObj mesh_shader(this, mesh_src, VK_SHADER_STAGE_MESH_BIT_NV);
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
buffer_create_info.size = 2 * sizeof(VkDrawMeshTasksIndirectCommandNV);
vkt::Buffer buffer(*m_device, buffer_create_info);
buffer_create_info.size = 64;
vkt::Buffer count_buffer(*m_device, buffer_create_info);
CreatePipelineHelper pipe(*this);
pipe.InitState();
pipe.shader_stages_[0] = mesh_shader.GetStageCreateInfo();
pipe.CreateGraphicsPipeline();
CreatePipelineHelper pipe1(*this);
pipe1.InitState();
pipe1.CreateGraphicsPipeline();
m_commandBuffer->begin();
m_commandBuffer->BeginRenderPass(renderPassBeginInfo());
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle());
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-drawCount-02156");
vk::CmdDrawMeshTasksIndirectNV(m_commandBuffer->handle(), buffer.handle(), sizeof(VkDrawMeshTasksIndirectCommandNV) * 2, 1,
sizeof(VkDrawMeshTasksIndirectCommandNV));
m_errorMonitor->VerifyFound();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe1.Handle());
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksNV-MeshNV-07080");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksNV-stage-06480");
vk::CmdDrawMeshTasksNV(m_commandBuffer->handle(), 1, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-MeshNV-07081");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectNV-stage-06480");
vk::CmdDrawMeshTasksIndirectNV(m_commandBuffer->handle(), buffer.handle(), 0, 1, sizeof(VkDrawMeshTasksIndirectCommandNV));
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountNV-MeshNV-07082");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDrawMeshTasksIndirectCountNV-stage-06480");
vk::CmdDrawMeshTasksIndirectCountNV(m_commandBuffer->handle(), buffer.handle(), 0, count_buffer.handle(), 0, 1,
sizeof(VkDrawMeshTasksIndirectCommandNV));
m_errorMonitor->VerifyFound();
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
}
TEST_F(NegativeMesh, MeshTasksWorkgroupCount) {
TEST_DESCRIPTION("Test mesh tasks emitted from task shader.");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_KHR_SPIRV_1_4_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4Features maintenance_4_features = vku::InitStructHelper();
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = vku::InitStructHelper(&maintenance_4_features);
GetPhysicalDeviceFeatures2(mesh_shader_features);
if (!maintenance_4_features.maintenance4) {
GTEST_SKIP() << "maintenance4 feature not supported";
}
if (!mesh_shader_features.taskShader || !mesh_shader_features.meshShader) {
GTEST_SKIP() << "Task or mesh shader feature not supported";
}
RETURN_IF_SKIP(InitState(nullptr, &mesh_shader_features));
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
std::string task_src_x = R"glsl(
#version 450
#extension GL_EXT_mesh_shader : require
layout (local_size_x=1, local_size_y=1, local_size_z=1) in;
void main () {
EmitMeshTasksEXT()glsl";
task_src_x += std::to_string(mesh_shader_properties.maxMeshWorkGroupCount[0] + 1);
task_src_x += R"glsl(u, 1u, 1u);
}
)glsl";
std::string task_src_y = R"glsl(
#version 450
#extension GL_EXT_mesh_shader : require
layout (local_size_x=1, local_size_y=1, local_size_z=1) in;
void main () {
EmitMeshTasksEXT(1u, )glsl";
task_src_y += std::to_string(mesh_shader_properties.maxMeshWorkGroupCount[1] + 1);
task_src_y += R"glsl(u, 1u);
}
)glsl";
std::string task_src_z = R"glsl(
#version 450
#extension GL_EXT_mesh_shader : require
layout (local_size_x=1, local_size_y=1, local_size_z=1) in;
void main () {
EmitMeshTasksEXT(1u, 1u, )glsl";
task_src_z += std::to_string(mesh_shader_properties.maxMeshWorkGroupCount[2] + 1);
task_src_z += R"glsl(u);
}
)glsl";
static const char mesh_src[] = R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(max_vertices = 3) out;
layout(max_primitives = 1) out;
layout(triangles) out;
void main() {
SetMeshOutputsEXT(3, 1);
gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0f, 1.0f);
gl_MeshVerticesEXT[1].gl_Position = vec4( 3.0, -1.0, 0.0f, 1.0f);
gl_MeshVerticesEXT[2].gl_Position = vec4(-1.0, 3.0, 0.0f, 1.0f);
gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);
}
)glsl";
static const char frag_src[] = R"glsl(
#version 460
layout(location = 0) out vec4 uFragColor;
void main(){
uFragColor = vec4(0.2f, 0.4f, 0.6f, 0.8f);
}
)glsl";
VkShaderObj task_shader_x(this, task_src_x.c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_3);
VkShaderObj task_shader_y(this, task_src_y.c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_3);
VkShaderObj task_shader_z(this, task_src_z.c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_3);
VkShaderObj mesh_shader(this, mesh_src, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_3);
VkShaderObj frag_shader(this, frag_src, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_3);
// mesh and task shaders not supported
const auto mesh_tasks_x = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task_shader_x.GetStageCreateInfo(), mesh_shader.GetStageCreateInfo(),
frag_shader.GetStageCreateInfo()};
};
const auto mesh_tasks_y = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task_shader_y.GetStageCreateInfo(), mesh_shader.GetStageCreateInfo(),
frag_shader.GetStageCreateInfo()};
};
const auto mesh_tasks_z = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task_shader_z.GetStageCreateInfo(), mesh_shader.GetStageCreateInfo(),
frag_shader.GetStageCreateInfo()};
};
std::vector<std::string> vuids = {"VUID-RuntimeSpirv-TaskEXT-07299"};
if (mesh_shader_properties.maxMeshWorkGroupCount[0] == mesh_shader_properties.maxMeshWorkGroupTotalCount) {
vuids.push_back("VUID-RuntimeSpirv-TaskEXT-07302");
}
CreatePipelineHelper::OneshotTest(*this, mesh_tasks_x, kErrorBit, vuids);
CreatePipelineHelper::OneshotTest(*this, mesh_tasks_y, kErrorBit, "VUID-RuntimeSpirv-TaskEXT-07300");
CreatePipelineHelper::OneshotTest(*this, mesh_tasks_z, kErrorBit, "VUID-RuntimeSpirv-TaskEXT-07301");
}