Skip to content

Commit 40c5180

Browse files
WIP: fix(core): add max_inter_stage_shader_variables validation
- add `StageError::VertexOutputLocationTooLarge` - add `StageError::TooManyVertexOutputs` - add `MaxInterStageShaderVariablesDeductions`
1 parent 0bb7f3a commit 40c5180

File tree

12 files changed

+146
-12
lines changed

12 files changed

+146
-12
lines changed

deno_webgpu/01_webgpu.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,7 @@ ObjectDefineProperty(GPUSupportedLimitsPrototype, privateCustomInspect, {
273273
"maxBufferSize",
274274
"maxVertexAttributes",
275275
"maxVertexBufferArrayStride",
276-
// TODO(@crowlKats): support max_inter_stage_shader_variables
277-
// "maxInterStageShaderVariables",
276+
"maxInterStageShaderVariables",
278277
"maxColorAttachments",
279278
"maxColorAttachmentBytesPerSample",
280279
"maxComputeWorkgroupStorageSize",

deno_webgpu/adapter.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,10 @@ impl GPUSupportedLimits {
344344
self.0.max_vertex_buffer_array_stride
345345
}
346346

347-
// TODO(@crowlKats): support max_inter_stage_shader_variables
347+
#[getter]
348+
fn maxInterStageShaderVariables(&self) -> u32 {
349+
self.0.max_inter_stage_shader_variables
350+
}
348351

349352
#[getter]
350353
fn maxColorAttachments(&self) -> u32 {

wgpu-core/src/device/resource.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4225,7 +4225,7 @@ impl Device {
42254225
vertex_stage = {
42264226
let stage_desc = &vertex.stage;
42274227
let stage = validation::ShaderStageForValidation::Vertex {
4228-
state: vertex,
4228+
topology: desc.primitive.topology,
42294229
compare_function: desc.depth_stencil.as_ref().map(|d| d.depth_compare),
42304230
};
42314231
let stage_bit = stage.to_wgt_bit();

wgpu-core/src/validation.rs

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use alloc::{
22
boxed::Box,
33
string::{String, ToString as _},
4-
sync::Arc,
54
vec::Vec,
65
};
76
use core::fmt;
@@ -314,6 +313,25 @@ pub enum StageError {
314313
MultipleEntryPointsFound,
315314
#[error(transparent)]
316315
InvalidResource(#[from] InvalidResourceError),
316+
#[error(
317+
"Location[{location}]: {var}'s index exceeds the `max_inter_stage_shader_variables` limit \
318+
({limit}); note that some " // TODO
319+
)]
320+
VertexOutputLocationTooLarge {
321+
location: u32,
322+
var: InterfaceVar,
323+
limit: u32,
324+
deductions: Vec<MaxInterStageShaderVariablesDeduction>,
325+
},
326+
#[error(
327+
"found {num_found} user-defined vertex shader output variables, which exceeds the \
328+
`max_inter_stage_shader_variables` limit ({limit}); note that some " // TODO
329+
)]
330+
TooManyUserDefinedVertexOutputs {
331+
num_found: u32,
332+
limit: u32,
333+
deductions: Vec<MaxInterStageShaderVariablesDeduction>,
334+
},
317335
#[error(
318336
"Location[{location}] {var}'s index exceeds the `max_color_attachments` limit ({limit})"
319337
)]
@@ -344,12 +362,50 @@ impl WebGpuError for StageError {
344362
| Self::MissingEntryPoint(..)
345363
| Self::NoEntryPointFound
346364
| Self::MultipleEntryPointsFound
365+
| Self::VertexOutputLocationTooLarge { .. }
366+
| Self::TooManyUserDefinedVertexOutputs { .. }
347367
| Self::ColorAttachmentLocationTooLarge { .. } => return ErrorType::Validation,
348368
};
349369
e.webgpu_error_type()
350370
}
351371
}
352372

373+
#[derive(Clone, Copy)]
374+
pub struct MaxInterStageShaderVariablesDeduction(MaxInterStageShaderVariablesDeductionImpl);
375+
376+
impl fmt::Debug for MaxInterStageShaderVariablesDeduction {
377+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378+
let Self(inner) = self;
379+
fmt::Debug::fmt(inner, f)
380+
}
381+
}
382+
383+
#[derive(Clone, Copy, Debug)]
384+
enum MaxInterStageShaderVariablesDeductionImpl {
385+
PointLinePrimitiveTopology,
386+
ClipDistance { count: u32 },
387+
}
388+
389+
impl MaxInterStageShaderVariablesDeductionImpl {
390+
fn variables_from_clip_distance_slot(num_slots: u32) -> u32 {
391+
num_slots.div_ceil(4)
392+
}
393+
394+
pub fn for_variables(self) -> u32 {
395+
match self {
396+
Self::PointLinePrimitiveTopology => 1,
397+
Self::ClipDistance { count } => Self::variables_from_clip_distance_slot(count),
398+
}
399+
}
400+
401+
pub fn for_location(self) -> u32 {
402+
match self {
403+
Self::PointLinePrimitiveTopology => 0,
404+
Self::ClipDistance { count } => Self::variables_from_clip_distance_slot(count),
405+
}
406+
}
407+
}
408+
353409
pub fn map_storage_format_to_naga(format: wgt::TextureFormat) -> Option<naga::StorageFormat> {
354410
use naga::StorageFormat as Sf;
355411
use wgt::TextureFormat as Tf;
@@ -1095,7 +1151,7 @@ impl Interface {
10951151
layouts: &mut BindingLayoutSource<'_>,
10961152
shader_binding_sizes: &mut FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
10971153
entry_point_name: &str,
1098-
shader_stage: ShaderStageForValidation<'_, '_>,
1154+
shader_stage: ShaderStageForValidation,
10991155
inputs: StageIo,
11001156
) -> Result<StageIo, StageError> {
11011157
// Since a shader module can have multiple entry points with the same name,
@@ -1320,13 +1376,60 @@ impl Interface {
13201376

13211377
match shader_stage {
13221378
ShaderStageForValidation::Vertex {
1379+
topology,
13231380
compare_function,
13241381
} => {
1382+
let mut max_vertex_shader_output_variables =
1383+
self.limits.max_inter_stage_shader_variables;
1384+
let mut max_vertex_shader_output_location = max_vertex_shader_output_variables - 1;
1385+
1386+
let point_list_deduction = if topology == wgt::PrimitiveTopology::PointList {
1387+
Some(MaxInterStageShaderVariablesDeductionImpl::PointLinePrimitiveTopology)
1388+
} else {
1389+
None
1390+
};
1391+
1392+
let clip_distance_deductions = entry_point.outputs.iter().filter_map(|output| {
1393+
if let Varying::BuiltIn(naga::BuiltIn::ClipDistance) = output {
1394+
Some(MaxInterStageShaderVariablesDeductionImpl::ClipDistance {
1395+
// NOTE: `clip_distances`' max array size is currently 8, so at most we deduct 2.
1396+
count: todo!("get size of `clip_distances` binding"),
1397+
})
1398+
} else {
1399+
None
1400+
}
1401+
});
1402+
1403+
let deductions = point_list_deduction
1404+
.into_iter()
1405+
.chain(clip_distance_deductions);
1406+
1407+
for deduction in deductions.clone() {
1408+
// NOTE: We assume that these will never get to 0.
1409+
max_vertex_shader_output_variables -= deduction.for_variables();
1410+
max_vertex_shader_output_location -= deduction.for_location();
1411+
}
1412+
1413+
let mut num_user_defined_outputs = 0;
1414+
13251415
for output in entry_point.outputs.iter() {
1326-
//TODO: count builtins towards the limit?
1327-
inter_stage_components += match *output {
1328-
Varying::Local { ref iv, .. } => iv.ty.dim.num_components(),
1329-
Varying::BuiltIn(_) => 0,
1416+
match *output {
1417+
Varying::Local { ref iv, location } => {
1418+
if location > max_vertex_shader_output_location {
1419+
// TODO: add diagnostics context for limit deductions
1420+
return Err(StageError::VertexOutputLocationTooLarge {
1421+
location,
1422+
var: iv.clone(),
1423+
limit: self.limits.max_inter_stage_shader_variables,
1424+
deductions: deductions
1425+
.map(MaxInterStageShaderVariablesDeduction)
1426+
.collect(),
1427+
});
1428+
}
1429+
num_user_defined_outputs += 1;
1430+
inter_stage_components += iv.ty.dim.num_components()
1431+
}
1432+
Varying::BuiltIn(_) => {}
13301433
};
13311434

13321435
if let Some(
@@ -1353,6 +1456,16 @@ impl Interface {
13531456
}
13541457
}
13551458
}
1459+
1460+
if num_user_defined_outputs > max_vertex_shader_output_variables {
1461+
return Err(StageError::TooManyUserDefinedVertexOutputs {
1462+
num_found: num_user_defined_outputs,
1463+
limit: self.limits.max_inter_stage_shader_variables - 1,
1464+
deductions: deductions
1465+
.map(MaxInterStageShaderVariablesDeduction)
1466+
.collect(),
1467+
});
1468+
}
13561469
}
13571470
ShaderStageForValidation::Fragment => {
13581471
for output in &entry_point.outputs {
@@ -1427,17 +1540,19 @@ pub fn validate_color_attachment_bytes_per_sample(
14271540
Ok(())
14281541
}
14291542

1430-
pub enum ShaderStageForValidation<'a, 'vertex_state> {
1543+
pub enum ShaderStageForValidation {
14311544
Vertex {
1545+
topology: wgt::PrimitiveTopology,
14321546
compare_function: Option<wgt::CompareFunction>,
14331547
},
14341548
Mesh,
14351549
Fragment,
14361550
Compute,
14371551
Task,
1552+
// TODO: preserve ordering?
14381553
}
14391554

1440-
impl ShaderStageForValidation<'_, '_> {
1555+
impl ShaderStageForValidation {
14411556
pub fn to_naga(&self) -> naga::ShaderStage {
14421557
match self {
14431558
Self::Vertex { .. } => naga::ShaderStage::Vertex,

wgpu-hal/src/dx12/adapter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ impl super::Adapter {
717717
//
718718
// Source: https://learn.microsoft.com/en-us/windows/win32/direct3d12/root-signature-limits#memory-limits-and-costs
719719
max_immediate_size: 128,
720+
max_inter_stage_shader_variables: 16,
720721
min_uniform_buffer_offset_alignment:
721722
Direct3D12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT,
722723
min_storage_buffer_offset_alignment: 4,

wgpu-hal/src/gles/adapter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ impl super::Adapter {
754754
min_subgroup_size: 0,
755755
max_subgroup_size: 0,
756756
max_immediate_size: super::MAX_IMMEDIATES as u32 * 4,
757+
max_inter_stage_shader_variables: 16,
757758
min_uniform_buffer_offset_alignment,
758759
min_storage_buffer_offset_alignment,
759760
max_inter_stage_shader_components: {

wgpu-hal/src/metal/adapter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ impl super::PrivateCapabilities {
11031103
min_subgroup_size: 4,
11041104
max_subgroup_size: 64,
11051105
max_immediate_size: 0x1000,
1106+
max_inter_stage_shader_variables: 16,
11061107
min_uniform_buffer_offset_alignment: self.buffer_alignment as u32,
11071108
min_storage_buffer_offset_alignment: self.buffer_alignment as u32,
11081109
max_inter_stage_shader_components: self.max_varying_components,

wgpu-hal/src/noop/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub const CAPABILITIES: crate::Capabilities = {
178178
max_buffer_size: ALLOC_MAX_U32 as u64,
179179
max_vertex_attributes: ALLOC_MAX_U32,
180180
max_vertex_buffer_array_stride: ALLOC_MAX_U32,
181+
max_inter_stage_shader_variables: ALLOC_MAX_U32,
181182
min_uniform_buffer_offset_alignment: 1,
182183
min_storage_buffer_offset_alignment: 1,
183184
max_inter_stage_shader_components: ALLOC_MAX_U32,

wgpu-hal/src/vulkan/adapter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ impl PhysicalDeviceProperties {
13401340
.map(|subgroup_size| subgroup_size.max_subgroup_size)
13411341
.unwrap_or(0),
13421342
max_immediate_size: limits.max_push_constants_size,
1343+
max_inter_stage_shader_variables: 16,
13431344
min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment as u32,
13441345
min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment as u32,
13451346
max_inter_stage_shader_components: limits

wgpu-info/src/human.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize
146146
max_buffer_size,
147147
max_vertex_attributes,
148148
max_vertex_buffer_array_stride,
149+
max_inter_stage_shader_variables,
149150
min_uniform_buffer_offset_alignment,
150151
min_storage_buffer_offset_alignment,
151152
max_inter_stage_shader_components,
@@ -198,6 +199,7 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize
198199
writeln!(output, "\t\t Min Subgroup Size: {min_subgroup_size}")?;
199200
writeln!(output, "\t\t Max Subgroup Size: {max_subgroup_size}")?;
200201
writeln!(output, "\t\t Max Immediate data Size: {max_immediate_size}")?;
202+
writeln!(output, "\t\t Max Inter-stage Shader Variables: {max_inter_stage_shader_variables}")?;
201203
writeln!(output, "\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}")?;
202204
writeln!(output, "\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}")?;
203205
writeln!(output, "\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}")?;

0 commit comments

Comments
 (0)