Validate Sampled=1 for Vulkan ImageQuerySizeLod, ImageQueryLevels, ImageQueryLod (#4103)

Also require image type parameter to OpTypeSampledImage to have
Sampled=0 or Sampled=1

Fixes #4102
This commit is contained in:
David Neto 2021-01-19 17:44:45 -05:00 committed by GitHub
parent e25db023c4
commit 56f8ed48ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 303 additions and 32 deletions

View File

@ -877,6 +877,20 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected Image to be of type OpTypeImage";
}
ImageTypeInfo info;
if (!GetImageTypeInfo(_, image_type, &info)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Corrupt image type definition";
}
// OpenCL requires Sampled=0, checked elsewhere.
// Vulkan uses the Sampled=1 case.
if ((info.sampled != 0) && (info.sampled != 1)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Sampled image type requires an image type with \"Sampled\" "
"operand set to 0 or 1";
}
return SPV_SUCCESS;
}
@ -1728,6 +1742,16 @@ spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _,
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0";
}
const auto target_env = _.context()->target_env;
if (spvIsVulkanEnv(target_env)) {
if (info.sampled != 1) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4659)
<< "OpImageQuerySizeLod must only consume an \"Image\" operand "
"whose type has its \"Sampled\" operand set to 1";
}
}
uint32_t result_num_components = _.GetDimension(result_type);
if (result_num_components != expected_num_components) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@ -1902,6 +1926,13 @@ spv_result_t ValidateImageQueryLod(ValidationState_t& _,
<< "Expected Coordinate to have at least " << min_coord_size
<< " components, but given only " << actual_coord_size;
}
// The operad is a sampled image.
// The sampled image type is already checked to be parameterized by an image
// type with Sampled=0 or Sampled=1. Vulkan bans Sampled=0, and so we have
// Sampled=1. So the validator already enforces Vulkan VUID 4659:
// OpImageQuerySizeLod must only consume an “Image” operand whose type has
// its "Sampled" operand set to 1
return SPV_SUCCESS;
}
@ -1938,6 +1969,15 @@ spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _,
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Image 'Dim' must be 1D, 2D, 3D or Cube";
}
const auto target_env = _.context()->target_env;
if (spvIsVulkanEnv(target_env)) {
if (info.sampled != 1) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4659)
<< "OpImageQueryLevels must only consume an \"Image\" operand "
"whose type has its \"Sampled\" operand set to 1";
}
}
} else {
assert(opcode == SpvOpImageQuerySamples);
if (info.dim != SpvDim2D) {

View File

@ -224,7 +224,6 @@ OpDecorate %uniform_sampler Binding 0
%type_image_f32_1d_0002_rgba32f = OpTypeImage %f32 1D 0 0 0 2 Rgba32f
%ptr_image_f32_1d_0002_rgba32f = OpTypePointer UniformConstant %type_image_f32_1d_0002_rgba32f
%uniform_image_f32_1d_0002_rgba32f = OpVariable %ptr_image_f32_1d_0002_rgba32f UniformConstant
%type_sampled_image_f32_1d_0002_rgba32f = OpTypeSampledImage %type_image_f32_1d_0002_rgba32f
%type_image_f32_2d_0001 = OpTypeImage %f32 2D 0 0 0 1 Unknown
%ptr_image_f32_2d_0001 = OpTypePointer UniformConstant %type_image_f32_2d_0001
@ -244,7 +243,6 @@ OpDecorate %uniform_sampler Binding 0
%type_image_u32_2d_0002 = OpTypeImage %u32 2D 0 0 0 2 Unknown
%ptr_image_u32_2d_0002 = OpTypePointer UniformConstant %type_image_u32_2d_0002
%uniform_image_u32_2d_0002 = OpVariable %ptr_image_u32_2d_0002 UniformConstant
%type_sampled_image_u32_2d_0002 = OpTypeSampledImage %type_image_u32_2d_0002
%type_image_s32_3d_0001 = OpTypeImage %s32 3D 0 0 0 1 Unknown
%ptr_image_s32_3d_0001 = OpTypePointer UniformConstant %type_image_s32_3d_0001
@ -254,17 +252,14 @@ OpDecorate %uniform_sampler Binding 0
%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Unknown
%ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002
%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant
%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002
%type_image_s32_2d_0002 = OpTypeImage %s32 2D 0 0 0 2 Unknown
%ptr_image_s32_2d_0002 = OpTypePointer UniformConstant %type_image_s32_2d_0002
%uniform_image_s32_2d_0002 = OpVariable %ptr_image_s32_2d_0002 UniformConstant
%type_sampled_image_s32_2d_0002 = OpTypeSampledImage %type_image_s32_2d_0002
%type_image_f32_spd_0002 = OpTypeImage %f32 SubpassData 0 0 0 2 Unknown
%ptr_image_f32_spd_0002 = OpTypePointer UniformConstant %type_image_f32_spd_0002
%uniform_image_f32_spd_0002 = OpVariable %ptr_image_f32_spd_0002 UniformConstant
%type_sampled_image_f32_spd_0002 = OpTypeSampledImage %type_image_f32_spd_0002
%type_image_f32_3d_0111 = OpTypeImage %f32 3D 0 1 1 1 Unknown
%ptr_image_f32_3d_0111 = OpTypePointer UniformConstant %type_image_f32_3d_0111
@ -279,7 +274,6 @@ OpDecorate %uniform_sampler Binding 0
%type_image_f32_cube_0102_rgba32f = OpTypeImage %f32 Cube 0 1 0 2 Rgba32f
%ptr_image_f32_cube_0102_rgba32f = OpTypePointer UniformConstant %type_image_f32_cube_0102_rgba32f
%uniform_image_f32_cube_0102_rgba32f = OpVariable %ptr_image_f32_cube_0102_rgba32f UniformConstant
%type_sampled_image_f32_cube_0102_rgba32f = OpTypeSampledImage %type_image_f32_cube_0102_rgba32f
%type_sampler = OpTypeSampler
%ptr_sampler = OpTypePointer UniformConstant %type_sampler
@ -312,7 +306,6 @@ OpDecorate %uniform_sampler Binding 0
%type_image_void_2d_0002 = OpTypeImage %void 2D 0 0 0 2 Unknown
%ptr_image_void_2d_0002 = OpTypePointer UniformConstant %type_image_void_2d_0002
%uniform_image_void_2d_0002 = OpVariable %ptr_image_void_2d_0002 UniformConstant
%type_sampled_image_void_2d_0002 = OpTypeSampledImage %type_image_void_2d_0002
%type_image_f32_rect_0001 = OpTypeImage %f32 Rect 0 0 0 1 Unknown
%ptr_image_f32_rect_0001 = OpTypePointer UniformConstant %type_image_f32_rect_0001
@ -863,7 +856,7 @@ TEST_F(ValidateImage, TypeImageMultisampleSubpassData_OK) {
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateImage, TypeSampledImageNotImage) {
TEST_F(ValidateImage, TypeSampledImage_NotImage_Error) {
const std::string code = GetShaderHeader("", false) + R"(
%simg_type = OpTypeSampledImage %f32
)";
@ -874,6 +867,43 @@ TEST_F(ValidateImage, TypeSampledImageNotImage) {
HasSubstr("Expected Image to be of type OpTypeImage"));
}
TEST_F(ValidateImage, TypeSampledImage_Sampled0_Success) {
// This is ok in the OpenCL and universal environments.
// Vulkan will reject an OpTypeImage with Sampled=0, checked elsewhere.
const std::string code = GetShaderHeader() + R"(
%imty = OpTypeImage %f32 2D 0 0 0 0 Unknown
%simg_type = OpTypeSampledImage %imty
)" + TrivialMain();
CompileSuccessfully(code.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_EQ(getDiagnosticString(), "");
}
TEST_F(ValidateImage, TypeSampledImage_Sampled2_Error) {
const std::string code = GetShaderHeader() + R"(
%storage_image = OpTypeImage %f32 2D 0 0 0 2 Rgba32f
%simg_type = OpTypeSampledImage %storage_image
)" + TrivialMain();
CompileSuccessfully(code.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sampled image type requires an image type with "
"\"Sampled\" operand set to 0 or 1"));
}
TEST_F(ValidateImage, TypeSampledImage_Sampled1_Success) {
const std::string code = GetShaderHeader() + R"(
%im = OpTypeImage %f32 2D 0 0 0 1 Unknown
%simg_type = OpTypeSampledImage %im
)" + TrivialMain();
CompileSuccessfully(code.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_EQ(getDiagnosticString(), "");
}
TEST_F(ValidateImage, SampledImageSuccess) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
@ -925,31 +955,32 @@ TEST_F(ValidateImage, SampledImageNotImage) {
}
TEST_F(ValidateImage, SampledImageImageNotForSampling) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
%sampler = OpLoad %type_sampler %uniform_sampler
%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler
const std::string code = GetShaderHeader() + R"(
%im_ty = OpTypeImage %f32 2D 0 0 0 2 Unknown
%sampler_ty = OpTypeSampler
%sampled_image_ty = OpTypeSampledImage %im_ty ; will fail here first!
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%var_im = OpVariable %ptr_im_ty UniformConstant
%ptr_sampler_ty = OpTypePointer UniformConstant %sampler_ty
%var_sampler = OpVariable %ptr_sampler_ty UniformConstant
%main = OpFunction %void None %func
%entry = OpLabel
%im = OpLoad %im_ty %var_im
%sampler = OpLoad %sampler_ty %var_sampler
%sampled_image = OpSampledImage %sampled_image_ty %im %sampler
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
CompileSuccessfully(code.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Expected Image 'Sampled' parameter to be 0 or 1"));
}
TEST_F(ValidateImage, SampledImageVulkanUnknownSampled) {
const std::string body = R"(
%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
%sampler = OpLoad %type_sampler %uniform_sampler
%simg = OpSampledImage %type_sampled_image_u32_2d_0002 %img %sampler
)";
const spv_target_env env = SPV_ENV_VULKAN_1_0;
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env), env);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Expected Image 'Sampled' parameter to "
"be 1 for Vulkan environment."));
HasSubstr("Sampled image type requires an image type with "
"\"Sampled\" operand set to 0 or 1"))
<< code;
}
TEST_F(ValidateImage, SampledImageNotSampler) {
@ -1022,7 +1053,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 142[%142] cannot be a "
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a "
"type"));
}
@ -3399,7 +3430,6 @@ OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1
%type_image_f32_2d_0012 = OpTypeImage %f32 2D 0 0 1 2 Unknown
%ptr_image_f32_2d_0012 = OpTypePointer UniformConstant %type_image_f32_2d_0012
%uniform_image_f32_2d_0012 = OpVariable %ptr_image_f32_2d_0012 UniformConstant
%type_sampled_image_f32_2d_0012 = OpTypeSampledImage %type_image_f32_2d_0012
)";
CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
SPV_ENV_UNIVERSAL_1_0, "GLSL450",
@ -3581,7 +3611,6 @@ OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %f32_1
%type_image_f32_2d_0012 = OpTypeImage %f32 2D 0 0 1 2 Unknown
%ptr_image_f32_2d_0012 = OpTypePointer UniformConstant %type_image_f32_2d_0012
%uniform_image_f32_2d_0012 = OpVariable %ptr_image_f32_2d_0012 UniformConstant
%type_sampled_image_f32_2d_0012 = OpTypeSampledImage %type_image_f32_2d_0012
)";
CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
SPV_ENV_UNIVERSAL_1_0, "GLSL450",
@ -3810,6 +3839,67 @@ TEST_F(ValidateImage, QuerySizeLodSampledImageDirectly) {
"for OpImageQuerySizeLod"));
}
TEST_F(ValidateImage, QuerySizeLodMultisampledError) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011
%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1
)";
CompileSuccessfully(GenerateKernelCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'MS' must be 0"));
}
TEST_F(ValidateImage, QuerySizeLodNonSampledUniversalSuccess) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_EQ(getDiagnosticString(), "");
}
TEST_F(ValidateImage, QuerySizeLodVulkanNonSampledError) {
// Create a whole shader module. Avoid Vulkan incompatibility with
// SampledRrect images inserted by helper function GenerateShaderCode.
const std::string body = R"(
OpCapability Shader
OpCapability ImageQuery
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%u32_0 = OpConstant %u32 0
%u32vec2 = OpTypeVector %u32 2
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
; Test with a storage image.
%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Rgba32f
%ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002
%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpImageQuerySizeLod must only consume an \"Image\" operand whose "
"type has its \"Sampled\" operand set to 1"));
}
TEST_F(ValidateImage, QuerySizeLodWrongImageDim) {
const std::string body = R"(
%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001
@ -4096,6 +4186,53 @@ TEST_F(ValidateImage, QueryLevelsWrongDim) {
HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube"));
}
TEST_F(ValidateImage, QuerySizeLevelsNonSampledUniversalSuccess) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
%res1 = OpImageQueryLevels %u32 %img
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_EQ(getDiagnosticString(), "");
}
TEST_F(ValidateImage, QuerySizeLevelsVulkanNonSampledError) {
// Create a whole shader module. Avoid Vulkan incompatibility with
// SampledRrect images inserted by helper function GenerateShaderCode.
const std::string body = R"(
OpCapability Shader
OpCapability ImageQuery
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
; Test with a storage image.
%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Rgba32f
%ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002
%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
%res1 = OpImageQueryLevels %u32 %img
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpImageQueryLevels must only consume an \"Image\" operand "
"whose type has its \"Sampled\" operand set to 1"));
}
TEST_F(ValidateImage, QuerySamplesSuccess) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011
@ -4186,6 +4323,100 @@ OpExecutionMode %main DerivativeGroupLinearNV
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateImage, QueryLodUniversalSuccess) {
// Create a whole shader module. Avoid Vulkan incompatibility with
// SampledRrect images inserted by helper function GenerateShaderCode.
const std::string body = R"(
OpCapability Shader
OpCapability ImageQuery
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %uniform_image_f32_2d_0000 DescriptorSet 0
OpDecorate %uniform_image_f32_2d_0000 Binding 0
OpDecorate %sampler DescriptorSet 0
OpDecorate %sampler Binding 1
%f32 = OpTypeFloat 32
%f32vec2 = OpTypeVector %f32 2
%f32vec2_null = OpConstantNull %f32vec2
%u32 = OpTypeInt 32 0
%u32vec2 = OpTypeVector %u32 2
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
; Test with an image with sampled = 0
%type_image_f32_2d_0000 = OpTypeImage %f32 2D 0 0 0 0 Rgba32f
%ptr_image_f32_2d_0000 = OpTypePointer UniformConstant %type_image_f32_2d_0000
%uniform_image_f32_2d_0000 = OpVariable %ptr_image_f32_2d_0000 UniformConstant
%sampled_image_ty = OpTypeSampledImage %type_image_f32_2d_0000
%sampler_ty = OpTypeSampler
%ptr_sampler_ty = OpTypePointer UniformConstant %sampler_ty
%sampler = OpVariable %ptr_sampler_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%img = OpLoad %type_image_f32_2d_0000 %uniform_image_f32_2d_0000
%s = OpLoad %sampler_ty %sampler
%simg = OpSampledImage %sampled_image_ty %img %s
%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_null
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateImage, QueryLodVulkanNonSampledError) {
// Create a whole shader module. Avoid Vulkan incompatibility with
// SampledRrect images inserted by helper function GenerateShaderCode.
const std::string body = R"(
OpCapability Shader
OpCapability ImageQuery
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %sampled_image DescriptorSet 0
OpDecorate %sampled_image Binding 0
%f32 = OpTypeFloat 32
%f32vec2 = OpTypeVector %f32 2
%f32vec2_null = OpConstantNull %f32vec2
%u32 = OpTypeInt 32 0
%u32vec2 = OpTypeVector %u32 2
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
; Test with an image with Sampled = 2
; In Vulkan it Sampled must be 1 or 2, checked in another part of the
; validation flow.
%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Rgba32f
; Expect to fail here.
%sampled_image_ty = OpTypeSampledImage %type_image_f32_2d_0002
%ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty
%sampled_image = OpVariable %ptr_sampled_image_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%simg = OpLoad %sampled_image_ty %sampled_image
%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_null
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sampled image type requires an image type with "
"\"Sampled\" operand set to 0 or 1"));
}
TEST_F(ValidateImage, QueryLodComputeShaderDerivativesMissingMode) {
const std::string body = R"(
%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001