blob: fbdfcec450724a30cf867e201a4a01e8f32a191c [file] [log] [blame]
/*
* Copyright (c) 2023-2025 Valve Corporation
* Copyright (c) 2023-2025 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
class NegativeShaderMesh : public VkLayerTest {};
TEST_F(NegativeShaderMesh, SharedMemoryOverLimit) {
TEST_DESCRIPTION("Validate mesh shader shared memory does not exceed maxMeshSharedMemorySize");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_shared_memory_size = mesh_shader_properties.maxMeshSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::ostringstream mesh_source;
mesh_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(max_vertices = 3, max_primitives=1) out;
layout(triangles) out;
shared int a[)glsl";
mesh_source << (max_shared_ints + 16);
mesh_source << R"glsl(];
void main(){}
)glsl";
VkShaderObj mesh(*m_device, mesh_source.str().c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {helper.fs_->GetStageCreateInfo(), mesh.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-maxMeshSharedMemorySize-08754");
}
TEST_F(NegativeShaderMesh, SharedMemoryOverLimitWorkgroupMemoryExplicitLayout) {
TEST_DESCRIPTION(
"Validate mesh shader shared memory does not exceed maxMeshSharedMemorySize when using "
"VK_KHR_workgroup_memory_explicit_layout");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::meshShader);
AddRequiredFeature(vkt::Feature::workgroupMemoryExplicitLayout);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_shared_memory_size = mesh_shader_properties.maxMeshSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::ostringstream mesh_source;
mesh_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
#extension GL_EXT_shared_memory_block : enable
layout(max_vertices = 3, max_primitives=1) out;
layout(triangles) out;
shared X {
int x;
};
shared Y {
int y1[)glsl";
mesh_source << (max_shared_ints + 16);
mesh_source << R"glsl(];
int y2;
};
void main() {
x = 0; // prevent dead-code elimination
y2 = 0;
}
)glsl";
VkShaderObj mesh(*m_device, mesh_source.str().c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {helper.fs_->GetStageCreateInfo(), mesh.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-maxMeshSharedMemorySize-08754");
}
TEST_F(NegativeShaderMesh, SharedMemorySpecConstantDefault) {
TEST_DESCRIPTION("Validate shared memory exceed maxMeshSharedMemorySize limit with spec constants default");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_shared_memory_size = mesh_shader_properties.maxMeshSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::ostringstream mesh_source;
mesh_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(max_vertices = 3, max_primitives=1) out;
layout(triangles) out;
layout(constant_id = 0) const uint Condition = 1;
layout(constant_id = 1) const uint SharedSize = )glsl";
mesh_source << (max_shared_ints + 16);
mesh_source << R"glsl(;
#define enableSharedMemoryOpt (Condition == 1)
shared uint arr[enableSharedMemoryOpt ? SharedSize : 1];
void main(){}
)glsl";
VkShaderObj mesh(*m_device, mesh_source.str().c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {helper.fs_->GetStageCreateInfo(), mesh.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-maxMeshSharedMemorySize-08754");
}
TEST_F(NegativeShaderMesh, SharedMemorySpecConstantSet) {
TEST_DESCRIPTION("Validate shared memory exceed maxMeshSharedMemorySize limit with spec constants set");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_shared_memory_size = mesh_shader_properties.maxMeshSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::ostringstream mesh_source;
mesh_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(max_vertices = 3, max_primitives=1) out;
layout(triangles) out;
layout(constant_id = 0) const uint Condition = 1;
layout(constant_id = 1) const uint SharedSize = )glsl";
mesh_source << (max_shared_ints + 16);
mesh_source << R"glsl(;
#define enableSharedMemoryOpt (Condition == 1)
shared uint arr[enableSharedMemoryOpt ? SharedSize : 1];
void main(){}
)glsl";
uint32_t data = 1; // set Condition
VkSpecializationMapEntry entry;
entry.constantID = 0;
entry.offset = 0;
entry.size = sizeof(uint32_t);
VkSpecializationInfo specialization_info = {};
specialization_info.mapEntryCount = 1;
specialization_info.pMapEntries = &entry;
specialization_info.dataSize = sizeof(uint32_t);
specialization_info.pData = &data;
VkShaderObj mesh(*m_device, mesh_source.str().c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_GLSL,
&specialization_info);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {helper.fs_->GetStageCreateInfo(), mesh.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-maxMeshSharedMemorySize-08754");
}
TEST_F(NegativeShaderMesh, TaskSharedMemoryOverLimit) {
TEST_DESCRIPTION("Validate Task shader shared memory does not exceed maxTaskSharedMemorySize");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::meshShader);
AddRequiredFeature(vkt::Feature::taskShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_shared_memory_size = mesh_shader_properties.maxTaskSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::ostringstream task_source;
task_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
shared int a[)glsl";
task_source << (max_shared_ints + 16);
task_source << R"glsl(];
void main(){}
)glsl";
VkShaderObj task(*m_device, task_source.str().c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_2);
VkShaderObj mesh(*m_device, kMeshMinimalGlsl, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
if (mesh_shader_properties.maxTaskSharedMemorySize == mesh_shader_properties.maxTaskPayloadAndSharedMemorySize) {
m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-maxTaskPayloadAndSharedMemorySize-08760");
}
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {task.GetStageCreateInfo(), mesh.GetStageCreateInfo()};
m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-maxTaskSharedMemorySize-08759");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderMesh, MeshAndTaskShaderDerivatives) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::computeDerivativeGroupQuads);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceComputeShaderDerivativesPropertiesKHR derivatives_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(derivatives_properties);
if (derivatives_properties.meshAndTaskShaderDerivatives) {
GTEST_SKIP() << "meshAndTaskShaderDerivatives is supported";
}
const char *ms_source = R"(
OpCapability ComputeDerivativeGroupQuadsKHR
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
OpExtension "SPV_KHR_compute_shader_derivatives"
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %main "main" %gl_Position %3
OpExecutionMode %main LocalSize 4 4 1
OpExecutionMode %main DerivativeGroupQuadsKHR
OpExecutionMode %main OutputTrianglesEXT
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main OutputPrimitivesEXT 1
OpSource HLSL 660
OpName %main "main"
OpDecorate %gl_Position BuiltIn Position
OpDecorate %3 BuiltIn PrimitiveTriangleIndicesEXT
%uint = OpTypeInt 32 0
%v3uint = OpTypeVector %uint 3
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3
%uint_1 = OpConstant %uint 1
%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1
%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1
%void = OpTypeVoid
%15 = OpTypeFunction %void
%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_3 Output
%3 = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output
%main = OpFunction %void None %15
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-meshAndTaskShaderDerivatives-10153");
VkShaderObj ms(*m_device, ms_source, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderMesh, MeshShaderPayloadMemoryOverLimit) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::taskShader);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
if (!IsPlatformMockICD()) {
GTEST_SKIP() << "Hard to get limit tests to work everywhere without being too complex";
}
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_mesh_payload_and_shared_memory_size = mesh_shader_properties.maxMeshPayloadAndSharedMemorySize;
const uint32_t max_mesh_payload_and_shared_ints = max_mesh_payload_and_shared_memory_size / 4;
std::ostringstream task_source;
task_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
struct Task {
uint baseID[)glsl";
task_source << (max_mesh_payload_and_shared_ints / 2 + 1);
task_source << R"glsl(];
};
taskPayloadSharedEXT Task IN;
void main(){
IN.baseID[0] = 0;
EmitMeshTasksEXT(1u, 1u, 1u);
}
)glsl";
std::ostringstream mesh_source;
mesh_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(max_vertices = 3, max_primitives=1) out;
layout(triangles) out;
struct Task {
uint baseID[)glsl";
mesh_source << (max_mesh_payload_and_shared_ints / 2 + 1);
mesh_source << R"glsl(];
};
taskPayloadSharedEXT Task IN;
shared int a[)glsl";
mesh_source << (max_mesh_payload_and_shared_ints / 2 + 1);
mesh_source << R"glsl(];
void main(){}
)glsl";
VkShaderObj task(*m_device, task_source.str().c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_2);
VkShaderObj mesh(*m_device, mesh_source.str().c_str(), VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task.GetStageCreateInfo(), mesh.GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-maxMeshPayloadAndSharedMemorySize-08755");
}
TEST_F(NegativeShaderMesh, MeshShaderPayloadSpecConstantSet) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::meshShader);
AddRequiredFeature(vkt::Feature::taskShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
if (!IsPlatformMockICD()) {
GTEST_SKIP() << "Hard to get limit tests to work everywhere without being too complex";
}
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_mesh_payload_and_shared_memory_size = mesh_shader_properties.maxMeshPayloadAndSharedMemorySize;
const uint32_t max_mesh_payload_and_shared_ints = max_mesh_payload_and_shared_memory_size / 4;
const char *task_source = R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(constant_id = 0) const int SIZE = 64;
struct Task {
uint baseID[SIZE];
};
taskPayloadSharedEXT Task IN;
void main() {
IN.baseID[0] = 0;
EmitMeshTasksEXT(1u, 1u, 1u);
}
)glsl";
const char *mesh_source = R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
layout(max_vertices = 3, max_primitives=1) out;
layout(triangles) out;
layout(constant_id = 0) const int SIZE = 64;
struct Task {
uint baseID[SIZE];
};
taskPayloadSharedEXT Task IN;
shared int a[SIZE];
void main(){}
)glsl";
uint32_t size = max_mesh_payload_and_shared_ints / 2 + 1;
VkSpecializationMapEntry map_entry;
map_entry.constantID = 0u;
map_entry.offset = 0u;
map_entry.size = sizeof(uint32_t);
VkSpecializationInfo spec_info;
spec_info.mapEntryCount = 1u;
spec_info.pMapEntries = &map_entry;
spec_info.dataSize = sizeof(uint32_t);
spec_info.pData = &size;
VkShaderObj task(*m_device, task_source, VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_2);
VkShaderObj mesh(*m_device, mesh_source, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_GLSL, &spec_info);
const auto set_info = [&](CreatePipelineHelper &helper) {
helper.shader_stages_ = {task.GetStageCreateInfo(), mesh.GetStageCreateInfo()};
;
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-maxMeshPayloadAndSharedMemorySize-08755");
}
TEST_F(NegativeShaderMesh, TaskPayloadMemoryOverLimit) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::taskShader);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_payload_memory_size = mesh_shader_properties.maxTaskPayloadSize;
const uint32_t max_payload_ints = max_payload_memory_size / 4;
VkShaderObj mesh(*m_device, kMeshMinimalGlsl, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
std::ostringstream task_source;
task_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
struct Task {
uint baseID[)glsl";
task_source << (max_payload_ints + 16);
task_source << R"glsl(];
};
taskPayloadSharedEXT Task OUT;
void main(){}
)glsl";
VkShaderObj task(*m_device, task_source.str().c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_2);
if (mesh_shader_properties.maxTaskPayloadSize == mesh_shader_properties.maxTaskPayloadAndSharedMemorySize) {
m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-maxTaskPayloadAndSharedMemorySize-08760");
}
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {task.GetStageCreateInfo(), mesh.GetStageCreateInfo()};
m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-maxTaskPayloadSize-08758");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderMesh, TaskShaderAndPayloadMemoryOverLimit) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::taskShader);
AddRequiredFeature(vkt::Feature::meshShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(mesh_shader_properties);
const uint32_t max_shared_memory_size = mesh_shader_properties.maxTaskSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
const uint32_t max_payload_memory_size = mesh_shader_properties.maxTaskPayloadSize;
const uint32_t max_payload_ints = max_payload_memory_size / 4;
const uint32_t max_payload_and_shared_memory_size = mesh_shader_properties.maxTaskPayloadAndSharedMemorySize;
const uint32_t max_payload_and_shared_ints = max_payload_and_shared_memory_size / 4;
VkShaderObj mesh(*m_device, kMeshMinimalGlsl, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2);
std::ostringstream task_source;
task_source << R"glsl(
#version 460
#extension GL_EXT_mesh_shader : require
struct Task {
uint baseID[)glsl";
task_source << (max_payload_and_shared_ints / 2 + 1);
task_source << R"glsl(];
};
taskPayloadSharedEXT Task OUT;
shared int a[)glsl";
task_source << (max_payload_and_shared_ints / 2 + 1);
task_source << R"glsl(];
void main(){}
)glsl";
VkShaderObj task(*m_device, task_source.str().c_str(), VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_2);
if (max_payload_and_shared_ints / 2 + 1 > max_payload_ints) {
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-maxTaskPayloadSize-08758");
}
if (max_payload_and_shared_ints / 2 + 1 > max_shared_ints) {
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-maxTaskSharedMemorySize-08759");
}
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {task.GetStageCreateInfo(), mesh.GetStageCreateInfo()};
m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-maxTaskPayloadAndSharedMemorySize-08760");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}