Add WebGPU specific validation for FragDepth BuiltIn decoration (#2332)
Part of resolving #2276
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 21a27a4..0f84641 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -998,12 +998,14 @@
spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition(
const Decoration& decoration, const Instruction& inst) {
- if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
if (spv_result_t error = ValidateF32(
decoration, inst,
[this, &inst](const std::string& message) -> spv_result_t {
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
- << "According to the Vulkan spec BuiltIn FragDepth "
+ << "According to the "
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec BuiltIn FragDepth "
"variable needs to be a 32-bit float scalar. "
<< message;
})) {
@@ -1019,12 +1021,13 @@
const Decoration& decoration, const Instruction& built_in_inst,
const Instruction& referenced_inst,
const Instruction& referenced_from_inst) {
- if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
if (storage_class != SpvStorageClassMax &&
storage_class != SpvStorageClassOutput) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
- << "Vulkan spec allows BuiltIn FragDepth to be only used for "
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec allows BuiltIn FragDepth to be only used for "
"variables with Output storage class. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst)
@@ -1034,7 +1037,8 @@
for (const SpvExecutionModel execution_model : execution_models_) {
if (execution_model != SpvExecutionModelFragment) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
- << "Vulkan spec allows BuiltIn FragDepth to be used only with "
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec allows BuiltIn FragDepth to be used only with "
"Fragment execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
@@ -1047,7 +1051,8 @@
const auto* modes = _.GetExecutionModes(entry_point);
if (!modes || !modes->count(SpvExecutionModeDepthReplacing)) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
- << "Vulkan spec requires DepthReplacing execution mode to be "
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec requires DepthReplacing execution mode to be "
"declared when using BuiltIn FragDepth. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst);
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index f3b9b43..0c0b5d4 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -596,6 +596,11 @@
Values("%f32"), Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
+ FragDepthSuccess, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("FragDepth"), Values("Fragment"), Values("Output"),
+ Values("%f32"), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
FragDepthNotFragment,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
Combine(
@@ -607,6 +612,15 @@
"to be used only with Fragment execution model"))));
INSTANTIATE_TEST_SUITE_P(
+ FragDepthNotFragment,
+ ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+ Combine(
+ Values("FragDepth"), Values("Vertex", "GLCompute"), Values("Output"),
+ Values("%f32"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "to be used only with Fragment execution model"))));
+
+INSTANTIATE_TEST_SUITE_P(
FragDepthNotOutput,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("FragDepth"), Values("Fragment"), Values("Input"),
@@ -617,6 +631,16 @@
"uses storage class Input"))));
INSTANTIATE_TEST_SUITE_P(
+ FragDepthNotOutput,
+ ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("FragDepth"), Values("Fragment"), Values("Input"),
+ Values("%f32"),
+ Values(TestResult(
+ SPV_ERROR_INVALID_DATA,
+ "to be only used for variables with Output storage class",
+ "uses storage class Input"))));
+
+INSTANTIATE_TEST_SUITE_P(
FragDepthNotFloatScalar,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("FragDepth"), Values("Fragment"), Values("Output"),
@@ -626,6 +650,15 @@
"is not a float scalar"))));
INSTANTIATE_TEST_SUITE_P(
+ FragDepthNotFloatScalar,
+ ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("FragDepth"), Values("Fragment"), Values("Output"),
+ Values("%f32vec4", "%u32"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 32-bit float scalar",
+ "is not a float scalar"))));
+
+INSTANTIATE_TEST_SUITE_P(
FragDepthNotF32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("FragDepth"), Values("Fragment"), Values("Output"),
Values("%f64"),
@@ -2335,16 +2368,20 @@
HasSubstr("called with execution model Fragment"));
}
-TEST_F(ValidateBuiltIns, FragmentFragDepthNoDepthReplacing) {
- CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
+CodeGenerator GetNoDepthReplacingGenerator(spv_target_env env) {
+ CodeGenerator generator =
+ spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
+ : CodeGenerator::GetDefaultShaderCodeGenerator();
+
generator.before_types_ = R"(
OpMemberDecorate %output_type 0 BuiltIn FragDepth
)";
generator.after_types_ = R"(
%output_type = OpTypeStruct %f32
+%output_null = OpConstantNull %output_type
%output_ptr = OpTypePointer Output %output_type
-%output = OpVariable %output_ptr Output
+%output = OpVariable %output_ptr Output %output_null
%output_f32_ptr = OpTypePointer Output %f32
)";
@@ -2358,7 +2395,7 @@
)";
generator.entry_points_.push_back(std::move(entry_point));
- generator.add_at_the_end_ = R"(
+ const std::string function_body = R"(
%foo = OpFunction %void None %func
%foo_entry = OpLabel
%frag_depth = OpAccessChain %output_f32_ptr %output %u32_0
@@ -2367,6 +2404,18 @@
OpFunctionEnd
)";
+ if (spvIsWebGPUEnv(env)) {
+ generator.after_types_ += function_body;
+ } else {
+ generator.add_at_the_end_ = function_body;
+ }
+
+ return generator;
+}
+
+TEST_F(ValidateBuiltIns, VulkanFragmentFragDepthNoDepthReplacing) {
+ CodeGenerator generator = GetNoDepthReplacingGenerator(SPV_ENV_VULKAN_1_0);
+
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
@@ -2374,16 +2423,31 @@
"be declared when using BuiltIn FragDepth"));
}
-TEST_F(ValidateBuiltIns, FragmentFragDepthOneMainHasDepthReplacingOtherHasnt) {
- CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
+TEST_F(ValidateBuiltIns, WebGPUFragmentFragDepthNoDepthReplacing) {
+ CodeGenerator generator = GetNoDepthReplacingGenerator(SPV_ENV_WEBGPU_0);
+
+ CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("WebGPU spec requires DepthReplacing execution mode to "
+ "be declared when using BuiltIn FragDepth"));
+}
+
+CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator(
+ spv_target_env env) {
+ CodeGenerator generator =
+ spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
+ : CodeGenerator::GetDefaultShaderCodeGenerator();
+
generator.before_types_ = R"(
OpMemberDecorate %output_type 0 BuiltIn FragDepth
)";
generator.after_types_ = R"(
%output_type = OpTypeStruct %f32
+%output_null = OpConstantNull %output_type
%output_ptr = OpTypePointer Output %output_type
-%output = OpVariable %output_ptr Output
+%output = OpVariable %output_ptr Output %output_null
%output_f32_ptr = OpTypePointer Output %f32
)";
@@ -2408,7 +2472,7 @@
)";
generator.entry_points_.push_back(std::move(entry_point));
- generator.add_at_the_end_ = R"(
+ const std::string function_body = R"(
%foo = OpFunction %void None %func
%foo_entry = OpLabel
%frag_depth = OpAccessChain %output_f32_ptr %output %u32_0
@@ -2417,6 +2481,20 @@
OpFunctionEnd
)";
+ if (spvIsWebGPUEnv(env)) {
+ generator.after_types_ += function_body;
+ } else {
+ generator.add_at_the_end_ = function_body;
+ }
+
+ return generator;
+}
+
+TEST_F(ValidateBuiltIns,
+ VulkanFragmentFragDepthOneMainHasDepthReplacingOtherHasnt) {
+ CodeGenerator generator =
+ GetOneMainHasDepthReplacingOtherHasntGenerator(SPV_ENV_VULKAN_1_0);
+
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
@@ -2424,6 +2502,18 @@
"be declared when using BuiltIn FragDepth"));
}
+TEST_F(ValidateBuiltIns,
+ WebGPUFragmentFragDepthOneMainHasDepthReplacingOtherHasnt) {
+ CodeGenerator generator =
+ GetOneMainHasDepthReplacingOtherHasntGenerator(SPV_ENV_WEBGPU_0);
+
+ CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("WebGPU spec requires DepthReplacing execution mode to "
+ "be declared when using BuiltIn FragDepth"));
+}
+
TEST_F(ValidateBuiltIns, AllowInstanceIdWithIntersectionShader) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.capabilities_ += R"(