@@ -281,18 +281,8 @@ impl WebGpuError for InputError {
281281#[ derive( Clone , Debug , Error ) ]
282282#[ non_exhaustive]
283283pub enum StageError {
284- #[ error(
285- "Shader entry point's workgroup size {current:?} ({current_total} total invocations) must be less or equal to the per-dimension
286- limit `Limits::{per_dimension_limit}` of {limit:?} and the total invocation limit `Limits::{total_limit}` of {total}"
287- ) ]
288- InvalidWorkgroupSize {
289- current : [ u32 ; 3 ] ,
290- current_total : u32 ,
291- limit : [ u32 ; 3 ] ,
292- total : u32 ,
293- per_dimension_limit : & ' static str ,
294- total_limit : & ' static str ,
295- } ,
284+ #[ error( transparent) ]
285+ InvalidWorkgroupSize ( #[ from] InvalidWorkgroupSizeError ) ,
296286 #[ error( "Unable to find entry point '{0}'" ) ]
297287 MissingEntryPoint ( String ) ,
298288 #[ error( "Shader global {0:?} is not available in the pipeline layout" ) ]
@@ -1384,63 +1374,48 @@ impl Interface {
13841374
13851375 // check workgroup size limits
13861376 if shader_stage. to_naga ( ) . compute_like ( ) {
1387- let (
1388- max_workgroup_size_limits,
1389- max_workgroup_size_total,
1390- per_dimension_limit,
1391- total_limit,
1392- ) = match shader_stage. to_naga ( ) {
1393- naga:: ShaderStage :: Compute => (
1394- [
1377+ let total = match shader_stage. to_naga ( ) {
1378+ naga:: ShaderStage :: Compute => check_workgroup_sizes (
1379+ & entry_point. workgroup_size ,
1380+ & [
13951381 self . limits . max_compute_workgroup_size_x ,
13961382 self . limits . max_compute_workgroup_size_y ,
13971383 self . limits . max_compute_workgroup_size_z ,
13981384 ] ,
1399- self . limits . max_compute_invocations_per_workgroup ,
14001385 "max_compute_workgroup_size_*" ,
1386+ self . limits . max_compute_invocations_per_workgroup ,
14011387 "max_compute_invocations_per_workgroup" ,
1402- ) ,
1403- naga:: ShaderStage :: Task => (
1404- [
1388+ ) ?,
1389+ naga:: ShaderStage :: Task => check_workgroup_sizes (
1390+ & entry_point. workgroup_size ,
1391+ & [
14051392 self . limits . max_task_invocations_per_dimension ,
14061393 self . limits . max_task_invocations_per_dimension ,
14071394 self . limits . max_task_invocations_per_dimension ,
14081395 ] ,
1409- self . limits . max_task_invocations_per_workgroup ,
14101396 "max_task_invocations_per_dimension" ,
1397+ self . limits . max_task_invocations_per_workgroup ,
14111398 "max_task_invocations_per_workgroup" ,
1412- ) ,
1413- naga:: ShaderStage :: Mesh => (
1414- [
1399+ ) ?,
1400+ naga:: ShaderStage :: Mesh => check_workgroup_sizes (
1401+ & entry_point. workgroup_size ,
1402+ & [
14151403 self . limits . max_mesh_invocations_per_dimension ,
14161404 self . limits . max_mesh_invocations_per_dimension ,
14171405 self . limits . max_mesh_invocations_per_dimension ,
14181406 ] ,
1419- self . limits . max_mesh_invocations_per_workgroup ,
14201407 "max_mesh_invocations_per_dimension" ,
1408+ self . limits . max_mesh_invocations_per_workgroup ,
14211409 "max_mesh_invocations_per_workgroup" ,
1422- ) ,
1410+ ) ? ,
14231411 _ => unreachable ! ( ) ,
14241412 } ;
1425- let total_invocations = entry_point
1426- . workgroup_size
1427- . iter ( )
1428- . fold ( 1u32 , |total, & dim| total. saturating_mul ( dim) ) ;
1429- let invalid_total_invocations =
1430- total_invocations > max_workgroup_size_total || total_invocations == 0 ;
1431-
1432- let dimension_too_large = entry_point. workgroup_size [ 0 ] > max_workgroup_size_limits[ 0 ]
1433- || entry_point. workgroup_size [ 1 ] > max_workgroup_size_limits[ 1 ]
1434- || entry_point. workgroup_size [ 2 ] > max_workgroup_size_limits[ 2 ] ;
1435- if invalid_total_invocations || dimension_too_large {
1436- return Err ( StageError :: InvalidWorkgroupSize {
1437- current : entry_point. workgroup_size ,
1438- current_total : total_invocations,
1439- limit : max_workgroup_size_limits,
1440- total : max_workgroup_size_total,
1441- per_dimension_limit,
1442- total_limit,
1443- } ) ;
1413+ if total == 0 {
1414+ return Err ( StageError :: InvalidWorkgroupSize (
1415+ InvalidWorkgroupSizeError :: Zero {
1416+ dimensions : entry_point. workgroup_size ,
1417+ } ,
1418+ ) ) ;
14441419 }
14451420 }
14461421
@@ -1810,6 +1785,62 @@ pub fn validate_color_attachment_bytes_per_sample(
18101785 Ok ( ( ) )
18111786}
18121787
1788+ #[ derive( Clone , Debug , Error ) ]
1789+ pub enum InvalidWorkgroupSizeError {
1790+ #[ error(
1791+ "Workgroup size {dimensions:?} ({total} total invocations) must be less or equal to \
1792+ the per-dimension limit `Limits::{per_dimension_limits_desc}` of {per_dimension_limits:?} \
1793+ and the total invocation limit `Limits::{total_limit_desc}` of {total_limit}"
1794+ ) ]
1795+ LimitExceeded {
1796+ dimensions : [ u32 ; 3 ] ,
1797+ per_dimension_limits : [ u32 ; 3 ] ,
1798+ per_dimension_limits_desc : & ' static str ,
1799+ total : u32 ,
1800+ total_limit : u32 ,
1801+ total_limit_desc : & ' static str ,
1802+ } ,
1803+ #[ error( "Workgroup sizes {dimensions:?} must be positive" ) ]
1804+ Zero { dimensions : [ u32 ; 3 ] } ,
1805+ }
1806+
1807+ /// Check X/Y/Z workgroup sizes against per-dimension and overall limits.
1808+ ///
1809+ /// This function does not check that the sizes are non-zero. In a dispatch, it is legal for
1810+ /// the size to be zero. In shader or pipeline creation, it is an error for the size to be
1811+ /// zero, and the caller must check that.
1812+ pub ( crate ) fn check_workgroup_sizes (
1813+ sizes : & [ u32 ; 3 ] ,
1814+ per_dimension_limits : & [ u32 ; 3 ] ,
1815+ per_dimension_limits_desc : & ' static str ,
1816+ total_limit : u32 ,
1817+ total_limit_desc : & ' static str ,
1818+ ) -> Result < u32 , InvalidWorkgroupSizeError > {
1819+ let total = sizes
1820+ . iter ( )
1821+ . fold ( 1u32 , |total, & dim| total. saturating_mul ( dim) ) ;
1822+
1823+ let invalid_total_invocations = total > total_limit;
1824+
1825+ let dimension_too_large = sizes
1826+ . iter ( )
1827+ . zip ( per_dimension_limits. iter ( ) )
1828+ . any ( |( dim, limit) | dim > limit) ;
1829+
1830+ if invalid_total_invocations || dimension_too_large {
1831+ Err ( InvalidWorkgroupSizeError :: LimitExceeded {
1832+ dimensions : * sizes,
1833+ per_dimension_limits : * per_dimension_limits,
1834+ per_dimension_limits_desc,
1835+ total,
1836+ total_limit,
1837+ total_limit_desc,
1838+ } )
1839+ } else {
1840+ Ok ( total)
1841+ }
1842+ }
1843+
18131844pub enum ShaderStageForValidation {
18141845 Vertex {
18151846 topology : wgt:: PrimitiveTopology ,
0 commit comments