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 <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
Jiawei Shao 2021-08-06 20:57:18 +00:00 committed by Dawn LUCI CQ
parent 9eb890dff4
commit 8d8fadbbe4
2 changed files with 182 additions and 1 deletions

View File

@ -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) {

View File

@ -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<f32>;"
<< std::endl;
}
uint32_t lastComponentCount = totalUserDefinedInterStageShaderComponentCount % 4;
if (lastComponentCount > 0) {
stream << "[[location(" << vec4InputLocations << ")]] var" << vec4InputLocations
<< ": ";
if (lastComponentCount == 1) {
stream << "f32;";
} else {
stream << " vec" << lastComponentCount << "<f32>;";
}
stream << std::endl;
}
switch (shaderStage) {
case wgpu::ShaderStage::Vertex: {
stream << R"(
[[builtin(position)]] pos: vec4<f32>;
};
[[stage(vertex)]] fn main() -> ShaderIO {
var shaderIO : ShaderIO;
shaderIO.pos = vec4<f32>(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<f32> {
return vec4<f32>(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<f32>;");
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<f32>;");
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"));