blob: e3cba61143ec933f3da7d6412c123bd181b03fff [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 "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
TEST_F(NegativeShaderCompute, SharedMemoryOverLimit) {
TEST_DESCRIPTION("Validate compute shader shared memory does not exceed maxComputeSharedMemorySize");
RETURN_IF_SKIP(Init())
const uint32_t max_shared_memory_size = m_device->phy().limits_.maxComputeSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::stringstream csSource;
// Make sure compute pipeline has a compute shader stage set
csSource << R"glsl(
#version 450
shared int a[)glsl";
csSource << (max_shared_ints + 16);
csSource << R"glsl(];
void main(){
}
)glsl";
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, csSource.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT);
pipe.InitState();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-Workgroup-06530");
pipe.CreateComputePipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderCompute, SharedMemoryBooleanOverLimit) {
TEST_DESCRIPTION("Validate compute shader shared memory does not exceed maxComputeSharedMemorySize with booleans");
RETURN_IF_SKIP(Init())
const uint32_t max_shared_memory_size = m_device->phy().limits_.maxComputeSharedMemorySize;
// "Boolean values considered as 32-bit integer values for the purpose of this calculation."
const uint32_t max_shared_bools = max_shared_memory_size / 4;
std::stringstream csSource;
// Make sure compute pipeline has a compute shader stage set
csSource << R"glsl(
#version 450
shared bool a[)glsl";
csSource << (max_shared_bools + 16);
csSource << R"glsl(];
void main(){
}
)glsl";
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, csSource.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT);
pipe.InitState();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-Workgroup-06530");
pipe.CreateComputePipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderCompute, SharedMemoryOverLimitWorkgroupMemoryExplicitLayout) {
TEST_DESCRIPTION(
"Validate compute shader shared memory does not exceed maxComputeSharedMemorySize when using "
"VK_KHR_workgroup_memory_explicit_layout");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
// need at least SPIR-V 1.4 for SPV_KHR_workgroup_memory_explicit_layout
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR explicit_layout_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(explicit_layout_features);
RETURN_IF_SKIP(InitState(nullptr, &features2))
if (!explicit_layout_features.workgroupMemoryExplicitLayout) {
GTEST_SKIP() << "workgroupMemoryExplicitLayout feature not supported";
}
const uint32_t max_shared_memory_size = m_device->phy().limits_.maxComputeSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::stringstream csSource;
csSource << R"glsl(
#version 450
#extension GL_EXT_shared_memory_block : enable
shared X {
int x;
};
shared Y {
int y1[)glsl";
csSource << (max_shared_ints + 16);
csSource << R"glsl(];
int y2;
};
void main() {
x = 0; // prevent dead-code elimination
y2 = 0;
}
)glsl";
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, csSource.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
pipe.InitState();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-Workgroup-06530");
pipe.CreateComputePipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderCompute, SharedMemorySpecConstantDefault) {
TEST_DESCRIPTION("Validate shared memory exceed maxComputeSharedMemorySize limit with spec constants default");
RETURN_IF_SKIP(Init())
const uint32_t max_shared_memory_size = m_device->phy().limits_.maxComputeSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::stringstream cs_source;
cs_source << R"glsl(
#version 450
layout(constant_id = 0) const uint Condition = 1;
layout(constant_id = 1) const uint SharedSize = )glsl";
cs_source << (max_shared_ints + 16);
cs_source << R"glsl(;
#define enableSharedMemoryOpt (Condition == 1)
shared uint arr[enableSharedMemoryOpt ? SharedSize : 1];
void main(){}
)glsl";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, cs_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT);
};
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-Workgroup-06530");
}
TEST_F(NegativeShaderCompute, SharedMemorySpecConstantSet) {
TEST_DESCRIPTION("Validate shared memory exceed maxComputeSharedMemorySize limit with spec constants set");
RETURN_IF_SKIP(Init())
const uint32_t max_shared_memory_size = m_device->phy().limits_.maxComputeSharedMemorySize;
const uint32_t max_shared_ints = max_shared_memory_size / 4;
std::stringstream cs_source;
cs_source << R"glsl(
#version 450
layout(constant_id = 0) const uint Condition = 0;
layout(constant_id = 1) const uint SharedSize = )glsl";
cs_source << (max_shared_ints + 16);
cs_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;
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, cs_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0,
SPV_SOURCE_GLSL, &specialization_info);
};
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-Workgroup-06530");
}
TEST_F(NegativeShaderCompute, WorkGroupSizeSpecConstant) {
TEST_DESCRIPTION("Validate compute shader shared memory does not exceed maxComputeWorkGroupSize");
RETURN_IF_SKIP(Init())
const VkPhysicalDeviceLimits limits = m_device->phy().limits_;
// Make sure compute pipeline has a compute shader stage set
const char *cs_source = R"glsl(
#version 450
layout(local_size_x_id = 3, local_size_y_id = 4) in;
void main(){}
)glsl";
VkSpecializationMapEntry entries[2];
entries[0].constantID = 3;
entries[0].offset = 0;
entries[0].size = sizeof(uint32_t);
entries[1].constantID = 4;
entries[1].offset = sizeof(uint32_t);
entries[1].size = sizeof(uint32_t);
uint32_t data[2] = {
1,
limits.maxComputeWorkGroupSize[1] + 1, // Invalid
};
VkSpecializationInfo specialization_info = {};
specialization_info.mapEntryCount = 2;
specialization_info.pMapEntries = entries;
specialization_info.dataSize = sizeof(uint32_t) * 2;
specialization_info.pData = data;
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0,
SPV_SOURCE_GLSL, &specialization_info);
};
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-y-06430");
data[0] = limits.maxComputeWorkGroupSize[0] + 1; // Invalid
data[1] = 1;
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06429");
data[0] = limits.maxComputeWorkGroupSize[0];
data[1] = limits.maxComputeWorkGroupSize[1];
if ((data[0] + data[1]) > limits.maxComputeWorkGroupInvocations) {
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06432");
}
}
TEST_F(NegativeShaderCompute, WorkGroupSizeConstantDefault) {
TEST_DESCRIPTION("Make sure constant are applied for maxComputeWorkGroupSize using WorkgroupSize");
RETURN_IF_SKIP(Init())
uint32_t x_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[0];
std::stringstream spv_source;
spv_source << R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%limit = OpConstant %uint )";
spv_source << std::to_string(x_size_limit + 1);
spv_source << R"(
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%gl_WorkGroupSize = OpConstantComposite %v3uint %limit %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, spv_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0,
SPV_SOURCE_ASM);
};
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06429");
}
TEST_F(NegativeShaderCompute, WorkGroupSizeSpecConstantDefault) {
TEST_DESCRIPTION("Make sure spec constant are applied for maxComputeWorkGroupSize using WorkgroupSize");
RETURN_IF_SKIP(Init())
uint32_t x_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[0];
std::stringstream spv_source;
spv_source << R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpDecorate %limit SpecId 0
OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%limit = OpSpecConstant %uint )";
spv_source << std::to_string(x_size_limit + 1);
spv_source << R"(
%uint_1 = OpConstant %uint 1
%v3uint = OpTypeVector %uint 3
%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %limit %uint_1 %uint_1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, spv_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0,
SPV_SOURCE_ASM);
};
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06429");
}
TEST_F(NegativeShaderCompute, WorkGroupSizeLocalSizeId) {
TEST_DESCRIPTION("Validate LocalSizeId also triggers maxComputeWorkGroupSize limit");
SetTargetApiVersion(VK_API_VERSION_1_3);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan13Features features13 = vku::InitStructHelper();
features13.maintenance4 = VK_TRUE; // required to be supported in 1.3
RETURN_IF_SKIP(InitState(nullptr, &features13));
uint32_t x_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[0];
std::stringstream spv_source;
spv_source << R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main LocalSizeId %limit %uint_1 %uint_1
OpSource GLSL 450
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%limit = OpConstant %uint )";
spv_source << std::to_string(x_size_limit + 1);
spv_source << R"(
%uint_1 = OpConstant %uint 1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, spv_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_3,
SPV_SOURCE_ASM);
};
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06429");
}
TEST_F(NegativeShaderCompute, WorkGroupSizeLocalSizeIdSpecConstantDefault) {
TEST_DESCRIPTION("Validate LocalSizeId also triggers maxComputeWorkGroupSize limit with spec constants default");
SetTargetApiVersion(VK_API_VERSION_1_3);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan13Features features13 = vku::InitStructHelper();
features13.maintenance4 = VK_TRUE; // required to be supported in 1.3
RETURN_IF_SKIP(InitState(nullptr, &features13));
uint32_t x_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[0];
// layout(local_size_x_id = 18, local_size_z_id = 19) in;
// layout(local_size_x = 32) in;
std::stringstream spv_source;
spv_source << R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main LocalSizeId %spec_x %uint_1 %spec_z
OpSource GLSL 450
OpDecorate %spec_x SpecId 18
OpDecorate %spec_z SpecId 19
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%spec_x = OpSpecConstant %uint )";
spv_source << std::to_string(x_size_limit + 1);
spv_source << R"(
%uint_1 = OpConstant %uint 1
%spec_z = OpSpecConstant %uint 1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, spv_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_3,
SPV_SOURCE_ASM);
};
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06429");
}
TEST_F(NegativeShaderCompute, WorkGroupSizeLocalSizeIdSpecConstantSet) {
TEST_DESCRIPTION("Validate LocalSizeId also triggers maxComputeWorkGroupSize limit with spec constants");
SetTargetApiVersion(VK_API_VERSION_1_3);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceVulkan13Features features13 = vku::InitStructHelper();
features13.maintenance4 = VK_TRUE; // required to be supported in 1.3
RETURN_IF_SKIP(InitState(nullptr, &features13));
uint32_t x_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[0];
// layout(local_size_x_id = 18, local_size_z_id = 19) in;
// layout(local_size_x = 32) in;
std::stringstream spv_source;
spv_source << R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main LocalSizeId %spec_x %uint_1 %spec_z
OpSource GLSL 450
OpDecorate %spec_x SpecId 18
OpDecorate %spec_z SpecId 19
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%spec_x = OpSpecConstant %uint 32
%uint_1 = OpConstant %uint 1
%spec_z = OpSpecConstant %uint 1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
uint32_t data = x_size_limit + 1;
VkSpecializationMapEntry entry;
entry.constantID = 18;
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;
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ = std::make_unique<VkShaderObj>(this, spv_source.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_3,
SPV_SOURCE_ASM, &specialization_info);
};
m_errorMonitor->SetUnexpectedError("VUID-RuntimeSpirv-x-06432");
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-x-06429");
}
TEST_F(NegativeShaderCompute, WorkgroupMemoryExplicitLayout) {
TEST_DESCRIPTION("Test VK_KHR_workgroup_memory_explicit_layout");
SetTargetApiVersion(VK_API_VERSION_1_2);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceShaderFloat16Int8Features float16int8_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(float16int8_features);
RETURN_IF_SKIP(InitState(nullptr, &features2))
const bool support_8_bit = (float16int8_features.shaderInt8 == VK_TRUE);
const bool support_16_bit = (float16int8_features.shaderFloat16 == VK_TRUE) && (features2.features.shaderInt16 == VK_TRUE);
// WorkgroupMemoryExplicitLayoutKHR
{
const char *spv_source = R"(
OpCapability Shader
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %_
OpExecutionMode %main LocalSize 8 1 1
OpMemberDecorate %first 0 Offset 0
OpDecorate %first Block
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%first = OpTypeStruct %int
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
%_ = OpVariable %_ptr_Workgroup_first Workgroup
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
%main = OpFunction %void None %3
%5 = OpLabel
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
OpStore %13 %int_2
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ =
std::make_unique<VkShaderObj>(this, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
};
// Both missing enabling the extension and capability feature
CreateComputePipelineHelper::OneshotTest(
*this, set_info, kErrorBit,
std::vector<string>{"VUID-VkShaderModuleCreateInfo-pCode-08740", "VUID-VkShaderModuleCreateInfo-pCode-08742"});
}
// WorkgroupMemoryExplicitLayout8BitAccessKHR
if (support_8_bit) {
const char *spv_source = R"(
OpCapability Shader
OpCapability Int8
OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %_
OpExecutionMode %main LocalSize 2 1 1
OpMemberDecorate %first 0 Offset 0
OpDecorate %first Block
%void = OpTypeVoid
%3 = OpTypeFunction %void
%char = OpTypeInt 8 1
%first = OpTypeStruct %char
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
%_ = OpVariable %_ptr_Workgroup_first Workgroup
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%char_2 = OpConstant %char 2
%_ptr_Workgroup_char = OpTypePointer Workgroup %char
%main = OpFunction %void None %3
%5 = OpLabel
%14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0
OpStore %14 %char_2
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ =
std::make_unique<VkShaderObj>(this, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
};
// Both missing enabling the extension and capability feature
CreateComputePipelineHelper::OneshotTest(
*this, set_info, kErrorBit,
std::vector<string>{"VUID-VkShaderModuleCreateInfo-pCode-08740", "VUID-VkShaderModuleCreateInfo-pCode-08742"});
}
// WorkgroupMemoryExplicitLayout16BitAccessKHR
if (support_16_bit) {
const char *spv_source = R"(
OpCapability Shader
OpCapability Float16
OpCapability Int16
OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %_
OpExecutionMode %main LocalSize 2 1 1
OpMemberDecorate %first 0 Offset 0
OpMemberDecorate %first 1 Offset 2
OpDecorate %first Block
%void = OpTypeVoid
%3 = OpTypeFunction %void
%short = OpTypeInt 16 1
%half = OpTypeFloat 16
%first = OpTypeStruct %short %half
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
%_ = OpVariable %_ptr_Workgroup_first Workgroup
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%short_3 = OpConstant %short 3
%_ptr_Workgroup_short = OpTypePointer Workgroup %short
%int_1 = OpConstant %int 1
%half_0x1_898p_3 = OpConstant %half 0x1.898p+3
%_ptr_Workgroup_half = OpTypePointer Workgroup %half
%main = OpFunction %void None %3
%5 = OpLabel
%15 = OpAccessChain %_ptr_Workgroup_short %_ %int_0
OpStore %15 %short_3
%19 = OpAccessChain %_ptr_Workgroup_half %_ %int_1
OpStore %19 %half_0x1_898p_3
OpReturn
OpFunctionEnd
)";
const auto set_info = [&](CreateComputePipelineHelper &helper) {
helper.cs_ =
std::make_unique<VkShaderObj>(this, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
};
// Both missing enabling the extension and capability feature
CreateComputePipelineHelper::OneshotTest(
*this, set_info, kErrorBit,
std::vector<string>{"VUID-VkShaderModuleCreateInfo-pCode-08740", "VUID-VkShaderModuleCreateInfo-pCode-08742"});
}
// workgroupMemoryExplicitLayoutScalarBlockLayout feature
// will fail from not passing --workgroup-scalar-block-layout in spirv-val
{
const char *spv_source = R"(
OpCapability Shader
OpCapability WorkgroupMemoryExplicitLayoutKHR
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %B
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpMemberDecorate %S 2 Offset 16
OpMemberDecorate %S 3 Offset 28
OpDecorate %S Block
OpDecorate %B Aliased
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%S = OpTypeStruct %float %v3float %v3float %v3float
%_ptr_Workgroup_S = OpTypePointer Workgroup %S
%B = OpVariable %_ptr_Workgroup_S Workgroup
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-VkShaderModuleCreateInfo-pCode-08737");
VkShaderObj::CreateFromASM(this, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeShaderCompute, ZeroInitializeWorkgroupMemory) {
TEST_DESCRIPTION("Test initializing workgroup memory in compute shader");
AddRequiredExtensions(VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR zero_initialize_work_group_memory_features = vku::InitStructHelper();
VkPhysicalDeviceFeatures2KHR features2 = vku::InitStructHelper(&zero_initialize_work_group_memory_features);
RETURN_IF_SKIP(InitState(nullptr, &features2))
const char *spv_source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpName %main "main"
OpName %counter "counter"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%zero_uint = OpConstantNull %uint
%counter = OpVariable %_ptr_Workgroup_uint Workgroup %zero_uint
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
auto cs = VkShaderObj::CreateFromASM(this, spv_source, VK_SHADER_STAGE_COMPUTE_BIT);
const auto set_info = [&cs](CreateComputePipelineHelper &helper) { helper.cs_ = std::move(cs); };
if (cs) {
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit,
"VUID-RuntimeSpirv-shaderZeroInitializeWorkgroupMemory-06372");
}
}
TEST_F(NegativeShaderCompute, LocalSizeIdExecutionMode) {
TEST_DESCRIPTION("Test LocalSizeId spirv execution mode");
SetTargetApiVersion(VK_API_VERSION_1_3);
RETURN_IF_SKIP(Init())
if (DeviceValidationVersion() != VK_API_VERSION_1_3) {
GTEST_SKIP() << "Test requires Vulkan exactly 1.3";
}
const char *source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
OpSource GLSL 450
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_UNIVERSAL_1_6, SPV_SOURCE_ASM);
pipe.InitState();
pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {});
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-LocalSizeId-06434");
pipe.CreateComputePipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeShaderCompute, CmdDispatchExceedLimits) {
TEST_DESCRIPTION("Compute dispatch with dimensions that exceed device limits");
AddRequiredExtensions(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_DEVICE_GROUP_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
RETURN_IF_SKIP(InitState())
const bool device_group_creation = IsExtensionsEnabled(VK_KHR_DEVICE_GROUP_EXTENSION_NAME);
uint32_t x_count_limit = m_device->phy().limits_.maxComputeWorkGroupCount[0];
uint32_t y_count_limit = m_device->phy().limits_.maxComputeWorkGroupCount[1];
uint32_t z_count_limit = m_device->phy().limits_.maxComputeWorkGroupCount[2];
if (std::max({x_count_limit, y_count_limit, z_count_limit}) == vvl::kU32Max) {
GTEST_SKIP() << "device maxComputeWorkGroupCount limit reports UINT32_MAX";
}
uint32_t x_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[0];
uint32_t y_size_limit = m_device->phy().limits_.maxComputeWorkGroupSize[1];
uint32_t z_size_limit = m_device->phy().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.cs_ =
std::make_unique<VkShaderObj>(this, spv_source.c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
pipe.InitState();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-x-06429");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-y-06430");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-z-06431");
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-x-06432");
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->phy().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;
std::stringstream cs_text;
cs_text << "#version 450\n";
cs_text << "layout(local_size_x = " << x_size_limit << ", ";
cs_text << "local_size_y = " << y_size_limit << ",";
cs_text << "local_size_z = " << z_size_limit << ") in;\n";
cs_text << "void main() {}\n";
pipe.cs_ = std::make_unique<VkShaderObj>(this, cs_text.str().c_str(), VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.flags = VK_PIPELINE_CREATE_DISPATCH_BASE;
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(kErrorBit, "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(kErrorBit, "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(kErrorBit, "VUID-vkCmdDispatch-groupCountZ-00388");
vk::CmdDispatch(m_commandBuffer->handle(), x_count_limit, y_count_limit, z_count_limit + 1);
m_errorMonitor->VerifyFound();
if (device_group_creation) {
// Base equals or exceeds limit
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDispatchBase-baseGroupX-00421");
vk::CmdDispatchBaseKHR(m_commandBuffer->handle(), x_count_limit, y_count_limit - 1, z_count_limit - 1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDispatchBase-baseGroupX-00422");
vk::CmdDispatchBaseKHR(m_commandBuffer->handle(), x_count_limit - 1, y_count_limit, z_count_limit - 1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDispatchBase-baseGroupZ-00423");
vk::CmdDispatchBaseKHR(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(kErrorBit, "VUID-vkCmdDispatchBase-groupCountX-00424");
vk::CmdDispatchBaseKHR(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(kErrorBit, "VUID-vkCmdDispatchBase-groupCountY-00425");
vk::CmdDispatchBaseKHR(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(kErrorBit, "VUID-vkCmdDispatchBase-groupCountZ-00426");
vk::CmdDispatchBaseKHR(m_commandBuffer->handle(), x_base, y_base, z_base, x_count_limit, y_count_limit, z_count_limit + 1);
m_errorMonitor->VerifyFound();
} else {
printf("KHR_DEVICE_GROUP_* extensions not supported, skipping CmdDispatchBaseKHR() tests.\n");
}
}
TEST_F(NegativeShaderCompute, DispatchBaseFlag) {
TEST_DESCRIPTION("Compute dispatch without VK_PIPELINE_CREATE_DISPATCH_BASE");
AddRequiredExtensions(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_DEVICE_GROUP_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
RETURN_IF_SKIP(InitState())
CreateComputePipelineHelper pipe(*this);
pipe.InitState();
pipe.CreateComputePipeline();
// Bind pipeline to command buffer
m_commandBuffer->begin();
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_);
m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-vkCmdDispatchBase-baseGroupX-00427");
vk::CmdDispatchBaseKHR(m_commandBuffer->handle(), 1, 1, 1, 0, 0, 0);
m_errorMonitor->VerifyFound();
m_commandBuffer->end();
}