Make the validation on inter-stage shader variables match latest WebGPU SPEC

This patch updates the validations on the inter-stage shader variables to
match the latest WebGPU SPEC (in chapter "validating-inter-stage-interfaces").

With this patch the below validation tests in WebGPU CTS will pass:
- render_pipeline,inter_stage:max_shader_variable_location:*
- render_pipeline,inter_stage:max_components_count,*

Fixed: dawn:1448
Test: dawn_unittests
Change-Id: I3e4d98f03ec18e5d1642a4d7ecd3eed1b7ae04d0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/102104
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Jiawei Shao 2022-09-16 00:30:38 +00:00 committed by Dawn LUCI CQ
parent 43655314ee
commit 233d64066c
6 changed files with 79 additions and 58 deletions

View File

@ -25,7 +25,7 @@ static constexpr uint32_t kNumStages = 3;
static constexpr uint8_t kMaxColorAttachments = 8u;
static constexpr uint32_t kTextureBytesPerRowAlignment = 256u;
static constexpr uint32_t kMaxInterStageShaderComponents = 60u;
static constexpr uint32_t kMaxInterStageShaderVariables = kMaxInterStageShaderComponents / 4;
static constexpr uint32_t kMaxInterStageShaderVariables = 16u;
// Per stage limits
static constexpr uint32_t kMaxSampledTexturesPerShaderStage = 16;

View File

@ -115,12 +115,15 @@ MaybeError ValidateVertexBufferLayout(
MaybeError ValidateVertexState(DeviceBase* device,
const VertexState* descriptor,
const PipelineLayoutBase* layout) {
const PipelineLayoutBase* layout,
wgpu::PrimitiveTopology primitiveTopology) {
DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
DAWN_INVALID_IF(descriptor->bufferCount > kMaxVertexBuffers,
const CombinedLimits& limits = device->GetLimits();
DAWN_INVALID_IF(descriptor->bufferCount > limits.v1.maxVertexBuffers,
"Vertex buffer count (%u) exceeds the maximum number of vertex buffers (%u).",
descriptor->bufferCount, kMaxVertexBuffers);
descriptor->bufferCount, limits.v1.maxVertexBuffers);
DAWN_TRY_CONTEXT(ValidateProgrammableStage(device, descriptor->module, descriptor->entryPoint,
descriptor->constantCount, descriptor->constants,
@ -129,6 +132,15 @@ MaybeError ValidateVertexState(DeviceBase* device,
descriptor->entryPoint);
const EntryPointMetadata& vertexMetadata =
descriptor->module->GetEntryPoint(descriptor->entryPoint);
if (primitiveTopology == wgpu::PrimitiveTopology::PointList) {
DAWN_INVALID_IF(
vertexMetadata.totalInterStageShaderComponents + 1 >
limits.v1.maxInterStageShaderComponents,
"Total vertex output components count (%u) exceeds the maximum (%u) when primitive "
"topology is %s as another component is implicitly used for the point size.",
vertexMetadata.totalInterStageShaderComponents,
limits.v1.maxInterStageShaderComponents - 1, primitiveTopology);
}
ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes> attributesSetMask;
uint32_t totalAttributesNum = 0;
@ -433,7 +445,8 @@ MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
DAWN_TRY(device->ValidateObject(descriptor->layout));
}
DAWN_TRY_CONTEXT(ValidateVertexState(device, &descriptor->vertex, descriptor->layout),
DAWN_TRY_CONTEXT(ValidateVertexState(device, &descriptor->vertex, descriptor->layout,
descriptor->primitive.topology),
"validating vertex state.");
DAWN_TRY_CONTEXT(ValidatePrimitiveState(device, &descriptor->primitive),

View File

@ -510,8 +510,6 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
const DeviceBase* device,
tint::inspector::Inspector* inspector,
const tint::inspector::EntryPoint& entryPoint) {
constexpr uint32_t kMaxInterStageShaderLocation = kMaxInterStageShaderVariables - 1;
std::unique_ptr<EntryPointMetadata> metadata = std::make_unique<EntryPointMetadata>();
// Returns the invalid argument, and if it is true additionally store the formatted
@ -572,13 +570,17 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
metadata->usesNumWorkgroups = entryPoint.num_workgroups_used;
}
const CombinedLimits& limits = device->GetLimits();
const uint32_t maxVertexAttributes = limits.v1.maxVertexAttributes;
const uint32_t maxInterStageShaderVariables = limits.v1.maxInterStageShaderVariables;
const uint32_t maxInterStageShaderComponents = limits.v1.maxInterStageShaderComponents;
if (metadata->stage == SingleShaderStage::Vertex) {
for (const auto& inputVar : entryPoint.input_variables) {
uint32_t unsanitizedLocation = inputVar.location_decoration;
if (DelayedInvalidIf(unsanitizedLocation >= kMaxVertexAttributes,
if (DelayedInvalidIf(unsanitizedLocation >= maxVertexAttributes,
"Vertex input variable \"%s\" has a location (%u) that "
"exceeds the maximum (%u)",
inputVar.name, unsanitizedLocation, kMaxVertexAttributes)) {
inputVar.name, unsanitizedLocation, maxVertexAttributes)) {
continue;
}
@ -588,9 +590,7 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
metadata->usedVertexInputs.set(location);
}
// [[position]] must be declared in a vertex shader but is not exposed as an
// output variable by Tint so we directly add its components to the total.
uint32_t totalInterStageShaderComponents = 4;
uint32_t totalInterStageShaderComponents = 0;
for (const auto& outputVar : entryPoint.output_variables) {
EntryPointMetadata::InterStageVariableInfo variable;
DAWN_TRY_ASSIGN(variable.baseType,
@ -605,10 +605,10 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
totalInterStageShaderComponents += variable.componentCount;
uint32_t location = outputVar.location_decoration;
if (DelayedInvalidIf(location > kMaxInterStageShaderLocation,
if (DelayedInvalidIf(location >= maxInterStageShaderVariables,
"Vertex output variable \"%s\" has a location (%u) that "
"exceeds the maximum (%u).",
outputVar.name, location, kMaxInterStageShaderLocation)) {
"is greater than or equal to (%u).",
outputVar.name, location, maxInterStageShaderVariables)) {
continue;
}
@ -616,9 +616,10 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
metadata->interStageVariables[location] = variable;
}
DelayedInvalidIf(totalInterStageShaderComponents > kMaxInterStageShaderComponents,
metadata->totalInterStageShaderComponents = totalInterStageShaderComponents;
DelayedInvalidIf(totalInterStageShaderComponents > maxInterStageShaderComponents,
"Total vertex output components count (%u) exceeds the maximum (%u).",
totalInterStageShaderComponents, kMaxInterStageShaderComponents);
totalInterStageShaderComponents, maxInterStageShaderComponents);
}
if (metadata->stage == SingleShaderStage::Fragment) {
@ -637,10 +638,10 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
totalInterStageShaderComponents += variable.componentCount;
uint32_t location = inputVar.location_decoration;
if (DelayedInvalidIf(location > kMaxInterStageShaderLocation,
if (DelayedInvalidIf(location >= maxInterStageShaderVariables,
"Fragment input variable \"%s\" has a location (%u) that "
"exceeds the maximum (%u).",
inputVar.name, location, kMaxInterStageShaderLocation)) {
"is greater than or equal to (%u).",
inputVar.name, location, maxInterStageShaderVariables)) {
continue;
}
@ -658,15 +659,13 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
if (entryPoint.sample_index_used) {
totalInterStageShaderComponents += 1;
}
if (entryPoint.input_position_used) {
totalInterStageShaderComponents += 4;
}
DelayedInvalidIf(totalInterStageShaderComponents > kMaxInterStageShaderComponents,
metadata->totalInterStageShaderComponents = totalInterStageShaderComponents;
DelayedInvalidIf(totalInterStageShaderComponents > maxInterStageShaderComponents,
"Total fragment input components count (%u) exceeds the maximum (%u).",
totalInterStageShaderComponents, kMaxInterStageShaderComponents);
totalInterStageShaderComponents, maxInterStageShaderComponents);
uint32_t maxColorAttachments = device->GetLimits().v1.maxColorAttachments;
uint32_t maxColorAttachments = limits.v1.maxColorAttachments;
for (const auto& outputVar : entryPoint.output_variables) {
EntryPointMetadata::FragmentOutputVariableInfo variable;
DAWN_TRY_ASSIGN(variable.baseType,

View File

@ -210,6 +210,7 @@ struct EntryPointMetadata {
// inputs and outputs in one shader stage.
std::bitset<kMaxInterStageShaderVariables> usedInterStageVariables;
std::array<InterStageVariableInfo, kMaxInterStageShaderVariables> interStageVariables;
uint32_t totalInterStageShaderComponents;
// The shader stage for this binding.
SingleShaderStage stage;

View File

@ -288,21 +288,21 @@ TEST_F(ShaderModuleValidationTest, MaximumShaderIOLocations) {
}
};
constexpr uint32_t kMaxInterShaderIOLocation = kMaxInterStageShaderComponents / 4 - 1;
// It is allowed to create a shader module with the maximum active vertex output location ==
// (kMaxInterStageShaderVariables - 1);
CheckTestPipeline(true, kMaxInterStageShaderVariables - 1, wgpu::ShaderStage::Vertex);
// It is allowed to create a shader module with the maximum active vertex output location == 14;
CheckTestPipeline(true, kMaxInterShaderIOLocation, wgpu::ShaderStage::Vertex);
// It isn't allowed to create a shader module with the maximum active vertex output location >
// 14;
CheckTestPipeline(false, kMaxInterShaderIOLocation + 1, wgpu::ShaderStage::Vertex);
// It isn't allowed to create a shader module with the maximum active vertex output location ==
// kMaxInterStageShaderVariables;
CheckTestPipeline(false, kMaxInterStageShaderVariables, wgpu::ShaderStage::Vertex);
// It is allowed to create a shader module with the maximum active fragment input location ==
// 14;
CheckTestPipeline(true, kMaxInterShaderIOLocation, wgpu::ShaderStage::Fragment);
// (kMaxInterStageShaderVariables - 1);
CheckTestPipeline(true, kMaxInterStageShaderVariables - 1, wgpu::ShaderStage::Fragment);
// It is allowed to create a shader module with the maximum active vertex output location > 14;
CheckTestPipeline(false, kMaxInterShaderIOLocation + 1, wgpu::ShaderStage::Fragment);
// It isn't allowed to create a shader module with the maximum active vertex output location ==
// kMaxInterStageShaderVariables;
CheckTestPipeline(false, kMaxInterStageShaderVariables, wgpu::ShaderStage::Fragment);
}
// Validate the maximum number of total inter-stage user-defined variable component count and
@ -311,7 +311,8 @@ TEST_F(ShaderModuleValidationTest, MaximumInterStageShaderComponents) {
auto CheckTestPipeline = [&](bool success,
uint32_t totalUserDefinedInterStageShaderComponentCount,
wgpu::ShaderStage failingShaderStage,
const char* extraBuiltInDeclarations = "") {
const char* extraBuiltInDeclarations = "",
bool usePointListAsPrimitiveType = false) {
// Build the ShaderIO struct containing totalUserDefinedInterStageShaderComponentCount
// components. Components are added in two parts, a bunch of vec4s, then one additional
// variable for the remaining components.
@ -347,11 +348,20 @@ TEST_F(ShaderModuleValidationTest, MaximumInterStageShaderComponents) {
// string "failingVertex" or "failingFragment" in the error message.
utils::ComboRenderPipelineDescriptor pDesc;
pDesc.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
if (usePointListAsPrimitiveType) {
pDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
} else {
pDesc.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
}
const char* errorMatcher = nullptr;
switch (failingShaderStage) {
case wgpu::ShaderStage::Vertex: {
errorMatcher = "failingVertex";
if (usePointListAsPrimitiveType) {
errorMatcher = "PointList";
} else {
errorMatcher = "failingVertex";
}
pDesc.vertex.entryPoint = "failingVertex";
pDesc.vertex.module = utils::CreateShaderModule(device, (ioStruct + R"(
@vertex fn failingVertex() -> ShaderIO {
@ -408,20 +418,28 @@ TEST_F(ShaderModuleValidationTest, MaximumInterStageShaderComponents) {
CheckTestPipeline(false, kMaxInterStageShaderComponents + 1, wgpu::ShaderStage::Fragment);
}
// @builtin(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().
// Verify the total user-defined vertex output component count must be less than
// kMaxInterStageShaderComponents.
{
CheckTestPipeline(true, kMaxInterStageShaderComponents - 4, wgpu::ShaderStage::Vertex);
CheckTestPipeline(false, kMaxInterStageShaderComponents - 3, wgpu::ShaderStage::Vertex);
CheckTestPipeline(true, kMaxInterStageShaderComponents, wgpu::ShaderStage::Vertex);
CheckTestPipeline(false, kMaxInterStageShaderComponents + 1, wgpu::ShaderStage::Vertex);
}
// @builtin(position) in fragment shaders should be counted into the maximum inter-stage
// Verify the total user-defined vertex output component count must be less than
// (kMaxInterStageShaderComponents - 1) when the primitive topology is PointList.
{
constexpr bool kUsePointListAsPrimitiveTopology = true;
const char* kExtraBuiltins = "";
CheckTestPipeline(true, kMaxInterStageShaderComponents - 1, wgpu::ShaderStage::Vertex,
kExtraBuiltins, kUsePointListAsPrimitiveTopology);
CheckTestPipeline(false, kMaxInterStageShaderComponents, wgpu::ShaderStage::Vertex,
kExtraBuiltins, kUsePointListAsPrimitiveTopology);
}
// @builtin(position) in fragment shaders shouldn't be counted into the maximum inter-stage
// component count.
{
CheckTestPipeline(true, kMaxInterStageShaderComponents - 4, wgpu::ShaderStage::Fragment,
"@builtin(position) fragCoord : vec4<f32>,");
CheckTestPipeline(false, kMaxInterStageShaderComponents - 3, wgpu::ShaderStage::Fragment,
CheckTestPipeline(true, kMaxInterStageShaderComponents, wgpu::ShaderStage::Fragment,
"@builtin(position) fragCoord : vec4<f32>,");
}

View File

@ -542,16 +542,6 @@ crbug.com/tint/0000 webgpu:shader,validation,parse,identifiers:identifiers:* [ F
################################################################################
# API validation failures
################################################################################
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,input:isAsync=false;numScalarDelta=-3;useExtraBuiltinInputs=true [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,input:isAsync=false;numScalarDelta=0;useExtraBuiltinInputs=false [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,input:isAsync=true;numScalarDelta=-3;useExtraBuiltinInputs=true [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,input:isAsync=true;numScalarDelta=0;useExtraBuiltinInputs=false [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,output:isAsync=false;numScalarDelta=-1;topology="point-list" [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,output:isAsync=false;numScalarDelta=0;topology="triangle-list" [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,output:isAsync=true;numScalarDelta=-1;topology="point-list" [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_components_count,output:isAsync=true;numScalarDelta=0;topology="triangle-list" [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_shader_variable_location:isAsync=false;locationDelta=-1 [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,render_pipeline,inter_stage:max_shader_variable_location:isAsync=true;locationDelta=-1 [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,shader_module,entry_point:compute:isAsync=false;shaderModuleEntryPoint="main";stageEntryPoint="main%5Cu0000" [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,shader_module,entry_point:compute:isAsync=false;shaderModuleEntryPoint="main";stageEntryPoint="main%5Cu0000a" [ Failure ]
crbug.com/dawn/0000 webgpu:api,validation,shader_module,entry_point:compute:isAsync=true;shaderModuleEntryPoint="main";stageEntryPoint="main%5Cu0000" [ Failure ]