@@ -328,6 +328,25 @@ pub enum StageError {
328328 MultipleEntryPointsFound ,
329329 #[ error( transparent) ]
330330 InvalidResource ( #[ from] InvalidResourceError ) ,
331+ #[ error(
332+ "Location[{location}]: {var}'s vertex shader output index exceeds the \
333+ `max_inter_stage_shader_variables` limit ({limit}); note that some " // TODO
334+ ) ]
335+ VertexOutputLocationTooLarge {
336+ location : u32 ,
337+ var : InterfaceVar ,
338+ limit : u32 ,
339+ deductions : Vec < MaxInterStageShaderVariablesDeduction > ,
340+ } ,
341+ #[ error(
342+ "found {num_found} user-defined vertex shader output variables, which exceeds the \
343+ `max_inter_stage_shader_variables` limit ({limit}); note that some " // TODO
344+ ) ]
345+ TooManyUserDefinedVertexOutputs {
346+ num_found : u32 ,
347+ limit : u32 ,
348+ deductions : Vec < MaxInterStageShaderVariablesDeduction > ,
349+ } ,
331350 #[ error(
332351 "Location[{location}] {var}'s index exceeds the `max_color_attachments` limit ({limit})"
333352 ) ]
@@ -375,6 +394,8 @@ impl WebGpuError for StageError {
375394 | Self :: MissingEntryPoint ( ..)
376395 | Self :: NoEntryPointFound
377396 | Self :: MultipleEntryPointsFound
397+ | Self :: VertexOutputLocationTooLarge { .. }
398+ | Self :: TooManyUserDefinedVertexOutputs { .. }
378399 | Self :: ColorAttachmentLocationTooLarge { .. }
379400 | Self :: TooManyMeshVertices { .. }
380401 | Self :: TooManyMeshPrimitives { .. }
@@ -388,6 +409,42 @@ impl WebGpuError for StageError {
388409 }
389410}
390411
412+ #[ derive( Clone , Copy ) ]
413+ pub struct MaxInterStageShaderVariablesDeduction ( MaxInterStageShaderVariablesDeductionImpl ) ;
414+
415+ impl fmt:: Debug for MaxInterStageShaderVariablesDeduction {
416+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
417+ let Self ( inner) = self ;
418+ fmt:: Debug :: fmt ( inner, f)
419+ }
420+ }
421+
422+ #[ derive( Clone , Copy , Debug ) ]
423+ enum MaxInterStageShaderVariablesDeductionImpl {
424+ PointLinePrimitiveTopology ,
425+ ClipDistance { count : u32 } ,
426+ }
427+
428+ impl MaxInterStageShaderVariablesDeductionImpl {
429+ fn variables_from_clip_distance_slot ( num_slots : u32 ) -> u32 {
430+ num_slots. div_ceil ( 4 )
431+ }
432+
433+ pub fn for_variables ( self ) -> u32 {
434+ match self {
435+ Self :: PointLinePrimitiveTopology => 1 ,
436+ Self :: ClipDistance { count } => Self :: variables_from_clip_distance_slot ( count) ,
437+ }
438+ }
439+
440+ pub fn for_location ( self ) -> u32 {
441+ match self {
442+ Self :: PointLinePrimitiveTopology => 0 ,
443+ Self :: ClipDistance { count } => Self :: variables_from_clip_distance_slot ( count) ,
444+ }
445+ }
446+ }
447+
391448pub fn map_storage_format_to_naga ( format : wgt:: TextureFormat ) -> Option < naga:: StorageFormat > {
392449 use naga:: StorageFormat as Sf ;
393450 use wgt:: TextureFormat as Tf ;
@@ -1445,13 +1502,60 @@ impl Interface {
14451502
14461503 match shader_stage {
14471504 ShaderStageForValidation :: Vertex {
1505+ topology,
14481506 compare_function,
14491507 } => {
1508+ let mut max_vertex_shader_output_variables =
1509+ self . limits . max_inter_stage_shader_variables ;
1510+ let mut max_vertex_shader_output_location = max_vertex_shader_output_variables - 1 ;
1511+
1512+ let point_list_deduction = if topology == wgt:: PrimitiveTopology :: PointList {
1513+ Some ( MaxInterStageShaderVariablesDeductionImpl :: PointLinePrimitiveTopology )
1514+ } else {
1515+ None
1516+ } ;
1517+
1518+ let clip_distance_deductions = entry_point. outputs . iter ( ) . filter_map ( |output| {
1519+ if let Varying :: BuiltIn ( naga:: BuiltIn :: ClipDistance ) = output {
1520+ Some ( MaxInterStageShaderVariablesDeductionImpl :: ClipDistance {
1521+ // NOTE: `clip_distances`' max array size is currently 8, so at most we deduct 2.
1522+ count : todo ! ( "get size of `clip_distances` binding" ) ,
1523+ } )
1524+ } else {
1525+ None
1526+ }
1527+ } ) ;
1528+
1529+ let deductions = point_list_deduction
1530+ . into_iter ( )
1531+ . chain ( clip_distance_deductions) ;
1532+
1533+ for deduction in deductions. clone ( ) {
1534+ // NOTE: We assume that these will never get to 0.
1535+ max_vertex_shader_output_variables -= deduction. for_variables ( ) ;
1536+ max_vertex_shader_output_location -= deduction. for_location ( ) ;
1537+ }
1538+
1539+ let mut num_user_defined_outputs = 0 ;
1540+
14501541 for output in entry_point. outputs . iter ( ) {
1451- //TODO: count builtins towards the limit?
1452- inter_stage_components += match * output {
1453- Varying :: Local { ref iv, .. } => iv. ty . dim . num_components ( ) ,
1454- Varying :: BuiltIn ( _) => 0 ,
1542+ match * output {
1543+ Varying :: Local { ref iv, location } => {
1544+ if location > max_vertex_shader_output_location {
1545+ // TODO: add diagnostics context for limit deductions
1546+ return Err ( StageError :: VertexOutputLocationTooLarge {
1547+ location,
1548+ var : iv. clone ( ) ,
1549+ limit : self . limits . max_inter_stage_shader_variables ,
1550+ deductions : deductions
1551+ . map ( MaxInterStageShaderVariablesDeduction )
1552+ . collect ( ) ,
1553+ } ) ;
1554+ }
1555+ num_user_defined_outputs += 1 ;
1556+ inter_stage_components += iv. ty . dim . num_components ( )
1557+ }
1558+ Varying :: BuiltIn ( _) => { }
14551559 } ;
14561560
14571561 if let Some (
@@ -1478,6 +1582,16 @@ impl Interface {
14781582 }
14791583 }
14801584 }
1585+
1586+ if num_user_defined_outputs > max_vertex_shader_output_variables {
1587+ return Err ( StageError :: TooManyUserDefinedVertexOutputs {
1588+ num_found : num_user_defined_outputs,
1589+ limit : self . limits . max_inter_stage_shader_variables - 1 ,
1590+ deductions : deductions
1591+ . map ( MaxInterStageShaderVariablesDeduction )
1592+ . collect ( ) ,
1593+ } ) ;
1594+ }
14811595 }
14821596 ShaderStageForValidation :: Fragment => {
14831597 for output in & entry_point. outputs {
@@ -1617,12 +1731,14 @@ pub fn validate_color_attachment_bytes_per_sample(
16171731
16181732pub enum ShaderStageForValidation {
16191733 Vertex {
1734+ topology : wgt:: PrimitiveTopology ,
16201735 compare_function : Option < wgt:: CompareFunction > ,
16211736 } ,
16221737 Mesh ,
16231738 Fragment ,
16241739 Compute ,
16251740 Task ,
1741+ // TODO: preserve ordering?
16261742}
16271743
16281744impl ShaderStageForValidation {
0 commit comments