Skip to content

Commit 710ed0c

Browse files
Jiawei-ShaoDawn LUCI CQ
authored andcommitted
Add validations on clip_distances and maximum inter-stage location
This patch adds the validation on the use of `clip_distances` and the maximum location of the inter-stage shader variables according to the latest WebGPU SPEC. `clip_distances` will not only consume inter-stage shader variable slots, but also consume the maximum location of inter-stage shader variables, which is required by Vulkan. According to the Vulkan validation layer, the total number of inter-stage shader components is computed by the maximum location of all the inter-stage shader variables, so we should reserve the locations for `clip_distances`. For example, suppose the maximum location of the vertex outputs is 30, and there is builtin position (4) and clip_distances with size 1, so the Vulkan validation layer thinks the total vertex shader output components is 129 (31 * 4 + 4 + 1) which is over the maximum number of the total vertex shader output components on Intel GPUs. Bug: 358408571 Test: dawn_unittests, dawn_end2end_tests Change-Id: If89cb10b63011dd34df71389411072b4a5cc5d18 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/206354 Reviewed-by: Corentin Wallez <[email protected]> Reviewed-by: Kai Ninomiya <[email protected]> Commit-Queue: Kai Ninomiya <[email protected]>
1 parent 9b4b1f8 commit 710ed0c

File tree

3 files changed

+133
-18
lines changed

3 files changed

+133
-18
lines changed

src/dawn/native/ShaderModule.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,12 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
697697
metadata->usedVertexInputs.set(location);
698698
}
699699

700-
// Vertex ouput (inter-stage variables) reflection.
700+
// Vertex output (inter-stage variables) reflection.
701+
uint32_t clipDistancesSlots = 0;
702+
if (entryPoint.clip_distances_size.has_value()) {
703+
clipDistancesSlots = RoundUp(*entryPoint.clip_distances_size, 4) / 4;
704+
}
705+
uint32_t minInvalidLocation = maxInterStageShaderVariables - clipDistancesSlots;
701706
for (const auto& outputVar : entryPoint.output_variables) {
702707
EntryPointMetadata::InterStageVariableInfo variable;
703708
variable.name = outputVar.variable_name;
@@ -712,10 +717,19 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
712717
outputVar.interpolation_sampling));
713718

714719
uint32_t location = outputVar.attributes.location.value();
715-
if (DelayedInvalidIf(location >= maxInterStageShaderVariables,
716-
"Vertex output variable \"%s\" has a location (%u) that "
717-
"is greater than or equal to (%u).",
718-
outputVar.name, location, maxInterStageShaderVariables)) {
720+
if (location >= minInvalidLocation) {
721+
if (clipDistancesSlots > 0) {
722+
metadata->infringedLimitErrors.push_back(absl::StrFormat(
723+
"Vertex output variable \"%s\" has a location (%u) that "
724+
"is too large. It should be less than (%u = %u - %u (clip_distances)).",
725+
outputVar.name, location, minInvalidLocation, maxInterStageShaderVariables,
726+
clipDistancesSlots));
727+
} else {
728+
metadata->infringedLimitErrors.push_back(
729+
absl::StrFormat("Vertex output variable \"%s\" has a location (%u) that "
730+
"is too large. It should be less than (%u).",
731+
outputVar.name, location, minInvalidLocation));
732+
}
719733
continue;
720734
}
721735

@@ -724,12 +738,8 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
724738
}
725739

726740
// Other vertex metadata.
727-
metadata->totalInterStageShaderVariables = entryPoint.output_variables.size();
728-
if (entryPoint.clip_distances_size.has_value()) {
729-
metadata->totalInterStageShaderVariables +=
730-
RoundUp(*entryPoint.clip_distances_size, 4) / 4;
731-
}
732-
741+
metadata->totalInterStageShaderVariables =
742+
entryPoint.output_variables.size() + clipDistancesSlots;
733743
if (metadata->totalInterStageShaderVariables > maxInterStageShaderVariables) {
734744
size_t userDefinedOutputVariables = entryPoint.output_variables.size();
735745

src/dawn/tests/end2end/MaxLimitTests.cpp

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,7 @@ class MaxInterStageShaderVariablesLimitTests : public MaxLimitTests {
886886
DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode() &&
887887
(spec.hasSampleIndex || spec.hasSampleMask));
888888

889-
wgpu::RenderPipeline pipeline = CreateRenderPipeline(spec);
890-
EXPECT_NE(nullptr, pipeline.Get());
889+
CreateRenderPipeline(spec);
891890
}
892891

893892
protected:
@@ -916,7 +915,7 @@ class MaxInterStageShaderVariablesLimitTests : public MaxLimitTests {
916915
++builtinVariableCount;
917916
}
918917
if (spec.clipDistancesSize.has_value()) {
919-
builtinVariableCount += dawn::RoundUp(*spec.clipDistancesSize, 4) / 4;
918+
builtinVariableCount += RoundUp(*spec.clipDistancesSize, 4) / 4;
920919
}
921920
return baseLimits.maxInterStageShaderVariables - builtinVariableCount;
922921
}
@@ -1137,6 +1136,59 @@ TEST_P(MaxInterStageShaderVariablesLimitTests, RenderPointList_ClipDistances) {
11371136
}
11381137
}
11391138

1139+
// Tests that using @builtin(clip_distances) will decrease the maximum location of the inter-stage
1140+
// shader variable, while the PointList primitive topology doesn't affect the maximum location of
1141+
// the inter-stage shader variable.
1142+
TEST_P(MaxInterStageShaderVariablesLimitTests, MaxLocation_ClipDistances) {
1143+
DAWN_TEST_UNSUPPORTED_IF(!mSupportsClipDistances);
1144+
1145+
wgpu::Limits baseLimits = GetAdapterLimits().limits;
1146+
1147+
constexpr std::array<wgpu::PrimitiveTopology, 2> kPrimitives = {
1148+
{wgpu::PrimitiveTopology::TriangleList, wgpu::PrimitiveTopology::PointList}};
1149+
for (wgpu::PrimitiveTopology primitive : kPrimitives) {
1150+
for (uint32_t clipDistanceSize = 1; clipDistanceSize <= 8; ++clipDistanceSize) {
1151+
uint32_t colorLocation =
1152+
baseLimits.maxInterStageShaderVariables - 1 - RoundUp(clipDistanceSize, 4) / 4;
1153+
std::stringstream stream;
1154+
stream << R"(
1155+
enable clip_distances;
1156+
struct VertexOut {
1157+
@location()"
1158+
<< colorLocation << ") color : vec4f,\n"
1159+
<< R"(
1160+
@builtin(clip_distances) clipDistances : array<f32, )"
1161+
<< clipDistanceSize << ">,\n"
1162+
<< R"(
1163+
@builtin(position) pos : vec4f,
1164+
}
1165+
struct FragmentIn {
1166+
@location()"
1167+
<< colorLocation << ") color : vec4f,\n"
1168+
<< R"(
1169+
@builtin(position) pos : vec4f,
1170+
}
1171+
@vertex fn vsMain() -> VertexOut {
1172+
var vout : VertexOut;
1173+
return vout;
1174+
}
1175+
@fragment fn fsMain(fragIn : FragmentIn) -> @location(0) vec4f {
1176+
return fragIn.pos;
1177+
})";
1178+
1179+
wgpu::ShaderModule shaderModule = utils::CreateShaderModule(device, stream.str());
1180+
utils::ComboRenderPipelineDescriptor descriptor;
1181+
descriptor.vertex.module = shaderModule;
1182+
descriptor.cFragment.module = shaderModule;
1183+
descriptor.vertex.bufferCount = 0;
1184+
descriptor.cBuffers[0].attributeCount = 0;
1185+
descriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
1186+
descriptor.primitive.topology = primitive;
1187+
device.CreateRenderPipeline(&descriptor);
1188+
}
1189+
}
1190+
}
1191+
11401192
DAWN_INSTANTIATE_TEST(MaxInterStageShaderVariablesLimitTests,
11411193
D3D11Backend(),
11421194
D3D12Backend({}, {"use_dxc"}),
@@ -1154,10 +1206,7 @@ class MaxVertexAttributesPipelineCreationTests : public MaxLimitTests {
11541206
bool hasInstanceIndex;
11551207
};
11561208

1157-
void DoTest(const TestSpec& spec) {
1158-
wgpu::RenderPipeline pipeline = CreateRenderPipeline(spec);
1159-
EXPECT_NE(nullptr, pipeline.Get());
1160-
}
1209+
void DoTest(const TestSpec& spec) { CreateRenderPipeline(spec); }
11611210

11621211
private:
11631212
wgpu::RenderPipeline CreateRenderPipeline(const TestSpec& spec) {

src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3409,5 +3409,61 @@ TEST_F(ClipDistancesValidationTest, ClipDistancesAgainstMaxInterStageShaderVaria
34093409
}
34103410
}
34113411

3412+
// Tests that using @builtin(clip_distances) will decrease the maximum location of the inter-stage
3413+
// shader variable, while the PointList primitive topology doesn't affect the maximum location of
3414+
// the inter-stage shader variable.
3415+
TEST_F(ClipDistancesValidationTest, ClipDistancesAgainstMaxInterStageLocation) {
3416+
constexpr std::array<wgpu::PrimitiveTopology, 2> kPrimitives = {
3417+
{wgpu::PrimitiveTopology::TriangleList, wgpu::PrimitiveTopology::PointList}};
3418+
for (wgpu::PrimitiveTopology primitive : kPrimitives) {
3419+
for (uint32_t clipDistancesSize = 1; clipDistancesSize <= 8; ++clipDistancesSize) {
3420+
for (uint32_t location = kMaxInterStageShaderVariables - 3u;
3421+
location < kMaxInterStageShaderVariables; ++location) {
3422+
std::stringstream stream;
3423+
stream << R"(
3424+
enable clip_distances;
3425+
struct VertexOut {
3426+
@location()"
3427+
<< location << ") color : vec4f,\n"
3428+
<< R"(
3429+
@builtin(clip_distances) clipDistances : array<f32, )"
3430+
<< clipDistancesSize << ">,\n"
3431+
<< R"(
3432+
@builtin(position) pos : vec4f,
3433+
}
3434+
struct FragmentIn {
3435+
@location()"
3436+
<< location << ") color : vec4f,\n"
3437+
<< R"(
3438+
@builtin(position) pos : vec4f,
3439+
}
3440+
@vertex
3441+
fn vsMain() -> VertexOut {
3442+
var vout : VertexOut;
3443+
return vout;
3444+
}
3445+
@fragment
3446+
fn fsMain(fragIn : FragmentIn) -> @location(0) vec4f {
3447+
return fragIn.pos;
3448+
})";
3449+
3450+
wgpu::ShaderModule shaderModule = utils::CreateShaderModule(device, stream.str());
3451+
utils::ComboRenderPipelineDescriptor descriptor;
3452+
descriptor.vertex.module = shaderModule;
3453+
descriptor.cFragment.module = shaderModule;
3454+
descriptor.primitive.topology = primitive;
3455+
3456+
uint32_t slotsForClipDistances = Align(clipDistancesSize, 4u) / 4;
3457+
uint32_t maxLocation = kMaxInterStageShaderVariables - 1 - slotsForClipDistances;
3458+
if (location > maxLocation) {
3459+
ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
3460+
} else {
3461+
device.CreateRenderPipeline(&descriptor);
3462+
}
3463+
}
3464+
}
3465+
}
3466+
}
3467+
34123468
} // anonymous namespace
34133469
} // namespace dawn

0 commit comments

Comments
 (0)