11use alloc:: {
22 boxed:: Box ,
33 string:: { String , ToString as _} ,
4- sync:: Arc ,
54 vec:: Vec ,
65} ;
76use 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+
353409pub 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 ,
0 commit comments