diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index 031229241c..eaca8f0f79 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -233,7 +233,8 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec, case spv::Decoration::DescriptorSet: if (sc != spv::StorageClass::StorageBuffer && sc != spv::StorageClass::Uniform && - sc != spv::StorageClass::UniformConstant) { + sc != spv::StorageClass::UniformConstant && + sc != spv::StorageClass::TileAttachmentQCOM) { return fail(6491) << "must be in the StorageBuffer, Uniform, or " "UniformConstant storage class"; } diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 37a5c68e6f..70a11b2cbd 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -404,8 +404,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) { // or row major-ness. spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, const char* decoration_str, bool blockRules, - bool scalar_block_layout, - uint32_t incoming_offset, + bool scalar_block_layout, uint32_t incoming_offset, MemberConstraints& constraints, ValidationState_t& vstate) { if (vstate.options()->skip_block_layout) return SPV_SUCCESS; @@ -1023,7 +1022,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { } if (num_workgroup_variables_with_block > 1 && num_workgroup_variables_with_block != - num_workgroup_variables_with_aliased) { + num_workgroup_variables_with_aliased) { return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "When declaring WorkgroupMemoryExplicitLayoutKHR, " @@ -1246,10 +1245,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { } // Prepare for messages const char* sc_str = - uniform ? "Uniform" - : (push_constant ? "PushConstant" - : (workgroup ? "Workgroup" - : "StorageBuffer")); + uniform + ? "Uniform" + : (push_constant ? "PushConstant" + : (workgroup ? "Workgroup" : "StorageBuffer")); if (spvIsVulkanEnv(vstate.context()->target_env)) { const bool block = hasDecoration(id, spv::Decoration::Block, vstate); @@ -1765,6 +1764,7 @@ spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate, var_storage_class == spv::StorageClass::Private) && vstate.features().nonwritable_var_in_function_or_private) { // New permitted feature in SPIR-V 1.4. + } else if (var_storage_class == spv::StorageClass::TileAttachmentQCOM) { } else if ( // It may point to a UBO, SSBO, storage image, or raw access chain. vstate.IsPointerToUniformBlock(type_id) || @@ -2030,7 +2030,8 @@ spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate, { \ spv_result_t e##LINE = (X); \ if (e##LINE != SPV_SUCCESS) return e##LINE; \ - } static_assert(true, "require extra semicolon") + } \ + static_assert(true, "require extra semicolon") #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__) // Check rules for decorations where we start from the decoration rather diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index 86ae759c6a..d6c95605b2 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -937,6 +937,65 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } + if (_.HasCapability(spv::Capability::TileShadingQCOM) && + storage_class == spv::StorageClass::TileAttachmentQCOM) { + if (result_type->opcode() == spv::Op::OpTypePointer) { + const auto pointee_type = + _.FindDef(result_type->GetOperandAs(2)); + if (pointee_type && pointee_type->opcode() == spv::Op::OpTypeImage) { + spv::Dim dim = static_cast(pointee_type->word(3)); + if (dim != spv::Dim::Dim2D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Any OpTypeImage variable in the TileAttachmentQCOM " + "Storage Class must " + "have 2D as its dimension"; + } + unsigned sampled = pointee_type->word(7); + if (sampled != 1 && sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Any OpyTpeImage variable in the TileAttachmentQCOM " + "Storage Class must " + "have 1 or 2 as Image 'Sampled' parameter"; + } + for (const auto& pair_o : inst->uses()) { + const auto* use_inst_o = pair_o.first; + if (use_inst_o->opcode() == spv::Op::OpLoad) { + for (const auto& pair_i : use_inst_o->uses()) { + const auto* use_inst_i = pair_i.first; + switch (use_inst_i->opcode()) { + case spv::Op::OpImageQueryFormat: + case spv::Op::OpImageQueryOrder: + case spv::Op::OpImageQuerySizeLod: + case spv::Op::OpImageQuerySize: + case spv::Op::OpImageQueryLod: + case spv::Op::OpImageQueryLevels: + case spv::Op::OpImageQuerySamples: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Any variable in the TileAttachmentQCOM Storage " + "Class must " + "not be consumed by an OpImageQuery* instruction"; + default: + break; + } + } + } + } + } + } + + if (!(_.HasDecoration(inst->id(), spv::Decoration::DescriptorSet) && + _.HasDecoration(inst->id(), spv::Decoration::Binding))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Any variable in the TileAttachmentQCOM Storage Class must " + "be decorated with DescriptorSet and Binding"; + } + if (_.HasDecoration(inst->id(), spv::Decoration::Component)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Any variable in the TileAttachmentQCOM Storage Class must " + "not be decorated with Component decoration"; + } + } + return SPV_SUCCESS; } diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index b0da343184..24b9db9db2 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp @@ -311,17 +311,84 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { } } } + if (!ok && _.HasCapability(spv::Capability::TileShadingQCOM)) { + ok = + execution_modes && + execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM); + } if (!ok) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(6426) + << (_.HasCapability(spv::Capability::TileShadingQCOM) + ? _.VkErrorID(10685) + : _.VkErrorID(6426)) << "In the Vulkan environment, GLCompute execution model " - "entry points require either the LocalSize or " - "LocalSizeId execution mode or an object decorated with " - "WorkgroupSize must be specified."; + "entry points require either the " + << (_.HasCapability(spv::Capability::TileShadingQCOM) + ? "TileShadingRateQCOM, " + : "") + << "LocalSize or LocalSizeId execution mode or an object " + "decorated with WorkgroupSize must be specified."; + } + } + + if (_.HasCapability(spv::Capability::TileShadingQCOM)) { + if (execution_modes) { + if (execution_modes->count( + spv::ExecutionMode::TileShadingRateQCOM) && + (execution_modes->count(spv::ExecutionMode::LocalSize) || + execution_modes->count(spv::ExecutionMode::LocalSizeId))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If the TileShadingRateQCOM execution mode is used, " + << "LocalSize and LocalSizeId must not be specified."; + } + if (execution_modes->count( + spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The NonCoherentTileAttachmentQCOM execution mode must " + "not be used in any stage other than fragment."; + } + } + } else { + if (execution_modes && + execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If the TileShadingRateQCOM execution mode is used, the " + "TileShadingQCOM capability must be enabled."; } } break; default: + if (execution_modes && + execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The TileShadingRateQCOM execution mode must not be used " + "in any stage other than compute."; + } + if (execution_model != spv::ExecutionModel::Fragment) { + if (execution_modes && + execution_modes->count( + spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The NonCoherentTileAttachmentQCOM execution mode must " + "not be used in any stage other than fragment."; + } + if (_.HasCapability(spv::Capability::TileShadingQCOM)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "The TileShadingQCOM capability must not be enabled in " + "any stage other than compute or fragment."; + } + } else { + if (execution_modes && + execution_modes->count( + spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) { + if (!_.HasCapability(spv::Capability::TileShadingQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If the NonCoherentTileAttachmentReadQCOM execution " + "mode is used, the TileShadingQCOM capability must be " + "enabled."; + } + } + } break; } } @@ -758,6 +825,14 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, << "In the Vulkan environment, the PixelCenterInteger execution " "mode must not be used."; } + if (mode == spv::ExecutionMode::TileShadingRateQCOM) { + const auto rateX = inst->GetOperandAs(2); + const auto rateY = inst->GetOperandAs(3); + if ((rateX & (rateX - 1)) != 0 || (rateY & (rateY - 1)) != 0) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The TileShadingRateQCOM execution mode's x and y values " + "must be powers of 2."; + } } return SPV_SUCCESS; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 4aedbfe350..6769fdd832 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -1883,6 +1883,7 @@ bool ValidationState_t::IsValidStorageClass( case spv::StorageClass::HitObjectAttributeNV: case spv::StorageClass::TileImageEXT: case spv::StorageClass::NodePayloadAMDX: + case spv::StorageClass::TileAttachmentQCOM: return true; default: return false; @@ -2573,6 +2574,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-Component-10583); case 10684: return VUID_WRAP(VUID-StandaloneSpirv-None-10684); + case 10685: + return VUID_WRAP(VUID-StandaloneSpirv-None-10685); default: return ""; // unknown id } diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 454b31181a..cd4f8527a5 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -3214,7 +3214,8 @@ std::string MinimalShaderModuleWithCapability(std::string cap) { : ""; return std::string("OpCapability ") + cap + extra_cap + R"( OpCapability Shader -OpMemoryModel Logical )" + mem_model + R"( +OpMemoryModel Logical )" + + mem_model + R"( OpEntryPoint Vertex %main "main" %void = OpTypeVoid %void_fn = OpTypeFunction %void @@ -3383,6 +3384,23 @@ OpMemoryModel Logical GLSL450 "the VulkanMemoryModel capability must also be declared")); } +TEST_F(ValidateCapability, TileShadingQCOM) { + const auto spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "main" +)" + std::string(kVoidFVoid); + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingQCOM capability must not be enabled " + "in any stage other than compute or fragment")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 256ab43c4b..293940c80c 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -2736,6 +2736,187 @@ OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 "Operands that are not id operands")); } +TEST_F(ValidateMode, GLComputeNoModeVulkanQCOM) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main NonCoherentTileAttachmentReadQCOM +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10685")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, GLCompute execution model entry " + "points require either the TileShadingRateQCOM, LocalSize or " + "LocalSizeId execution mode or an object decorated with " + "WorkgroupSize " + "must be specified.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeBadQCOM) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpExecutionMode %main LocalSize 16 16 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If the TileShadingRateQCOM execution mode is used, " + "LocalSize and LocalSizeId must not be specified.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBadQCOM) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If the TileShadingRateQCOM execution mode is used, " + "LocalSize and LocalSizeId must not be specified.")); +} + +TEST_F(ValidateMode, NonCoherentTileAttachmentReadQCOMBad1) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main NonCoherentTileAttachmentReadQCOM +OpExecutionMode %main OriginUpperLeft +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: TileShadingQCOM")); +} + +TEST_F(ValidateMode, NonCoherentTileAttachmentReadQCOMBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 16 16 1 +OpExecutionMode %main NonCoherentTileAttachmentReadQCOM +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%uint = OpTypeInt 32 0 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The NonCoherentTileAttachmentQCOM execution mode must " + "not be used in any stage other than fragment")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad1) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%uint = OpTypeInt 32 0 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: TileShadingQCOM")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpExecutionMode %main OriginUpperLeft +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingRateQCOM execution mode must not be " + "used in any stage other than compute")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 3 2 3 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingRateQCOM execution mode's x and y " + "values must be powers of 2")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad4) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 3 3 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingRateQCOM execution mode's x and y " + "values must be powers of 2")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index d4170e6dc7..b583fe30e3 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp @@ -249,6 +249,194 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) { HasSubstr("OpFunctionCall Argument '")); } +TEST_F(ValidateStorage, TileAttachmentQCOMBad1) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 1D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Any OpTypeImage variable in the TileAttachmentQCOM " + "Storage Class must have 2D as its dimension")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad2) { + const std::string spirv = R"( + OpCapability Shader + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 Binding 2 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Any variable in the TileAttachmentQCOM Storage Class " + "must be decorated with DescriptorSet and Binding")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad3) { + const std::string spirv = R"( + OpCapability Shader + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Any variable in the TileAttachmentQCOM Storage Class " + "must be decorated with DescriptorSet and Binding")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad4) { + const std::string spirv = R"( + OpCapability Shader + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 0 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04657")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled must be 1 or 2 in the Vulkan environment")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad5) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ImageQuery + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_2 = OpConstant %int 2 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %main = OpFunction %void None %3 + %5 = OpLabel + %154 = OpLoad %44 %color1 + %156 = OpImageQuerySizeLod %v2int %154 %int_2 + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Any variable in the TileAttachmentQCOM Storage Class " + "must not be consumed by an OpImageQuery* instruction")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad6) { + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: TileShadingQCOM")); +} + std::string GenerateExecutionModelCode(const std::string& execution_model, const std::string& storage_class, bool store) { @@ -265,7 +453,8 @@ std::string GenerateExecutionModelCode(const std::string& execution_model, OpMemoryModel Logical GLSL450 OpEntryPoint )" << execution_model << R"( %func "func" %var - )" << mode << R"( + )" + << mode << R"( OpDecorate %var Location 0 %intt = OpTypeInt 32 0 %int0 = OpConstant %intt 0 @@ -273,10 +462,12 @@ std::string GenerateExecutionModelCode(const std::string& execution_model, %vfunct = OpTypeFunction %voidt %ptr = OpTypePointer )" << storage_class << R"( %intt -%var = OpVariable %ptr )" << storage_class << R"( +%var = OpVariable %ptr )" + << storage_class << R"( %func = OpFunction %voidt None %vfunct %funcl = OpLabel - )" << operation << R"( + )" + << operation << R"( OpReturn OpFunctionEnd )";