From 8d8fadbbe4e1e65ae08781bcf9d6f0a158f08a12 Mon Sep 17 00:00:00 2001 From: Jiawei Shao Date: Fri, 6 Aug 2021 20:57:18 +0000 Subject: [PATCH] Validate MaxInterStageShaderComponents when creating shader module According to the discussion[1], the sum of all the user-defined inter-stage component count and all the inter-stage builtin component count should be no more than MaxInterStageShaderComponents (currently in Dawn it is always 60). [1] https://github.com/gpuweb/gpuweb/issues/1962 BUG=dawn:1032 TEST=dawn_unittests Change-Id: Id5266b72b89c48f5b7073b8307f8f2c0512c8a33 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/61104 Commit-Queue: Kai Ninomiya Reviewed-by: Austin Eng Reviewed-by: Kai Ninomiya --- src/dawn_native/ShaderModule.cpp | 31 ++++ .../ShaderModuleValidationTests.cpp | 152 +++++++++++++++++- 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp index 54eff8dc2a..9db81ed822 100644 --- a/src/dawn_native/ShaderModule.cpp +++ b/src/dawn_native/ShaderModule.cpp @@ -1055,6 +1055,8 @@ namespace dawn_native { metadata->usedVertexInputs.set(location); } + // [[position]] must be declared in a vertex shader. + uint32_t totalInterStageShaderComponents = 4; for (const auto& output_var : entryPoint.output_variables) { if (DAWN_UNLIKELY(!output_var.has_location_decoration)) { std::stringstream ss; @@ -1082,10 +1084,20 @@ namespace dawn_native { metadata->interStageVariables[location].interpolationSampling, TintInterpolationSamplingToInterpolationSamplingType( output_var.interpolation_sampling)); + + totalInterStageShaderComponents += + metadata->interStageVariables[location].componentCount; + } + + if (DAWN_UNLIKELY(totalInterStageShaderComponents > + kMaxInterStageShaderComponents)) { + return DAWN_VALIDATION_ERROR( + "Total vertex output components count exceeds limits"); } } if (metadata->stage == SingleShaderStage::Fragment) { + uint32_t totalInterStageShaderComponents = 0; for (const auto& input_var : entryPoint.input_variables) { if (!input_var.has_location_decoration) { return DAWN_VALIDATION_ERROR( @@ -1111,6 +1123,25 @@ namespace dawn_native { metadata->interStageVariables[location].interpolationSampling, TintInterpolationSamplingToInterpolationSamplingType( input_var.interpolation_sampling)); + + totalInterStageShaderComponents += + metadata->interStageVariables[location].componentCount; + } + if (entryPoint.front_facing_used) { + totalInterStageShaderComponents += 1; + } + if (entryPoint.input_sample_mask_used) { + totalInterStageShaderComponents += 1; + } + if (entryPoint.sample_index_used) { + totalInterStageShaderComponents += 1; + } + if (entryPoint.input_position_used) { + totalInterStageShaderComponents += 4; + } + if (totalInterStageShaderComponents > kMaxInterStageShaderComponents) { + return DAWN_VALIDATION_ERROR( + "Total fragment input components count exceeds limits"); } for (const auto& output_var : entryPoint.output_variables) { diff --git a/src/tests/unittests/validation/ShaderModuleValidationTests.cpp b/src/tests/unittests/validation/ShaderModuleValidationTests.cpp index 58fff4c218..2c495babc5 100644 --- a/src/tests/unittests/validation/ShaderModuleValidationTests.cpp +++ b/src/tests/unittests/validation/ShaderModuleValidationTests.cpp @@ -219,7 +219,7 @@ TEST_F(ShaderModuleValidationTest, MaximumShaderIOLocations) { auto generateShaderForTest = [](uint32_t maximumOutputLocation, wgpu::ShaderStage shaderStage) { std::ostringstream stream; stream << "struct ShaderIO {" << std::endl; - for (uint32_t location = 0; location <= maximumOutputLocation; ++location) { + for (uint32_t location = 1; location <= maximumOutputLocation; ++location) { stream << "[[location(" << location << ")]] var" << location << ": f32;" << std::endl; } switch (shaderStage) { @@ -283,6 +283,156 @@ TEST_F(ShaderModuleValidationTest, MaximumShaderIOLocations) { } } +// Validate the maximum number of total inter-stage user-defined variable component count and +// built-in variables cannot exceed kMaxInterStageShaderComponents. +TEST_F(ShaderModuleValidationTest, MaximumInterStageShaderComponents) { + auto generateShaderForTest = [](uint32_t totalUserDefinedInterStageShaderComponentCount, + wgpu::ShaderStage shaderStage, + const char* builtInDeclarations) { + std::ostringstream stream; + stream << "struct ShaderIO {" << std::endl << builtInDeclarations << std::endl; + uint32_t vec4InputLocations = totalUserDefinedInterStageShaderComponentCount / 4; + + for (uint32_t location = 0; location < vec4InputLocations; ++location) { + stream << "[[location(" << location << ")]] var" << location << ": vec4;" + << std::endl; + } + + uint32_t lastComponentCount = totalUserDefinedInterStageShaderComponentCount % 4; + if (lastComponentCount > 0) { + stream << "[[location(" << vec4InputLocations << ")]] var" << vec4InputLocations + << ": "; + if (lastComponentCount == 1) { + stream << "f32;"; + } else { + stream << " vec" << lastComponentCount << ";"; + } + stream << std::endl; + } + + switch (shaderStage) { + case wgpu::ShaderStage::Vertex: { + stream << R"( + [[builtin(position)]] pos: vec4; + }; + [[stage(vertex)]] fn main() -> ShaderIO { + var shaderIO : ShaderIO; + shaderIO.pos = vec4(0.0, 0.0, 0.0, 1.0); + return shaderIO; + })"; + } break; + + case wgpu::ShaderStage::Fragment: { + stream << R"( + }; + [[stage(fragment)]] fn main(shaderIO: ShaderIO) -> [[location(0)]] vec4 { + return vec4(0.0, 0.0, 0.0, 1.0); + })"; + } break; + + case wgpu::ShaderStage::Compute: + default: + UNREACHABLE(); + } + + return stream.str(); + }; + + // Verify when there is no input builtin variable in a fragment shader, the total user-defined + // input component count must be less than kMaxInterStageShaderComponents. + { + constexpr uint32_t kInterStageShaderComponentCount = kMaxInterStageShaderComponents; + std::string correctFragmentShader = + generateShaderForTest(kInterStageShaderComponentCount, wgpu::ShaderStage::Fragment, ""); + utils::CreateShaderModule(device, correctFragmentShader.c_str()); + + std::string errorFragmentShader = generateShaderForTest(kInterStageShaderComponentCount + 1, + wgpu::ShaderStage::Fragment, ""); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, errorFragmentShader.c_str())); + } + + // [[position]] should be counted into the maximum inter-stage component count. + // Note that in vertex shader we always have [[position]] so we don't need to specify it + // again in the parameter "builtInDeclarations" of generateShaderForTest(). + { + constexpr uint32_t kInterStageShaderComponentCount = kMaxInterStageShaderComponents - 4; + std::string vertexShader = + generateShaderForTest(kInterStageShaderComponentCount, wgpu::ShaderStage::Vertex, ""); + utils::CreateShaderModule(device, vertexShader.c_str()); + + std::string fragmentShader = + generateShaderForTest(kInterStageShaderComponentCount, wgpu::ShaderStage::Fragment, + "[[builtin(position)]] fragCoord: vec4;"); + utils::CreateShaderModule(device, fragmentShader.c_str()); + } + + { + constexpr uint32_t kInterStageShaderComponentCount = kMaxInterStageShaderComponents - 3; + std::string vertexShader = + generateShaderForTest(kInterStageShaderComponentCount, wgpu::ShaderStage::Vertex, ""); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, vertexShader.c_str())); + + std::string fragmentShader = + generateShaderForTest(kInterStageShaderComponentCount, wgpu::ShaderStage::Fragment, + "[[builtin(position)]] fragCoord: vec4;"); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, fragmentShader.c_str())); + } + + // [[front_facing]] should be counted into the maximum inter-stage component count. + { + const char* builtinDeclaration = "[[builtin(front_facing)]] frontFacing : bool;"; + + { + std::string fragmentShader = + generateShaderForTest(kMaxInterStageShaderComponents - 1, + wgpu::ShaderStage::Fragment, builtinDeclaration); + utils::CreateShaderModule(device, fragmentShader.c_str()); + } + + { + std::string fragmentShader = generateShaderForTest( + kMaxInterStageShaderComponents, wgpu::ShaderStage::Fragment, builtinDeclaration); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, fragmentShader.c_str())); + } + } + + // [[sample_index]] should be counted into the maximum inter-stage component count. + { + const char* builtinDeclaration = "[[builtin(sample_index)]] sampleIndex: u32;"; + + { + std::string fragmentShader = + generateShaderForTest(kMaxInterStageShaderComponents - 1, + wgpu::ShaderStage::Fragment, builtinDeclaration); + utils::CreateShaderModule(device, fragmentShader.c_str()); + } + + { + std::string fragmentShader = generateShaderForTest( + kMaxInterStageShaderComponents, wgpu::ShaderStage::Fragment, builtinDeclaration); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, fragmentShader.c_str())); + } + } + + // [[sample_mask]] should be counted into the maximum inter-stage component count. + { + const char* builtinDeclaration = "[[builtin(front_facing)]] frontFacing : bool;"; + + { + std::string fragmentShader = + generateShaderForTest(kMaxInterStageShaderComponents - 1, + wgpu::ShaderStage::Fragment, builtinDeclaration); + utils::CreateShaderModule(device, fragmentShader.c_str()); + } + + { + std::string fragmentShader = generateShaderForTest( + kMaxInterStageShaderComponents, wgpu::ShaderStage::Fragment, builtinDeclaration); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, fragmentShader.c_str())); + } + } +} + // Tests that we validate workgroup size limits. TEST_F(ShaderModuleValidationTest, ComputeWorkgroupSizeLimits) { DAWN_SKIP_TEST_IF(!HasToggleEnabled("use_tint_generator"));