@@ -1524,6 +1524,58 @@ impl<W: Write> Writer<W> {
15241524 Ok ( ( ) )
15251525 }
15261526
1527+ /// Emit code for the WGSL functions `pack4x{I, U}8[Clamp]`.
1528+ fn put_pack4x8 (
1529+ & mut self ,
1530+ arg : Handle < crate :: Expression > ,
1531+ context : & ExpressionContext < ' _ > ,
1532+ was_signed : bool ,
1533+ clamp_bounds : Option < ( & str , & str ) > ,
1534+ ) -> Result < ( ) , Error > {
1535+ let write_arg = |this : & mut Self | -> BackendResult {
1536+ if let Some ( ( min, max) ) = clamp_bounds {
1537+ // Clamping with scalar bounds works (component-wise) even for packed_[u]char4.
1538+ write ! ( this. out, "{NAMESPACE}::clamp(" ) ?;
1539+ this. put_expression ( arg, context, true ) ?;
1540+ write ! ( this. out, ", {min}, {max})" ) ?;
1541+ } else {
1542+ this. put_expression ( arg, context, true ) ?;
1543+ }
1544+ Ok ( ( ) )
1545+ } ;
1546+
1547+ if context. lang_version >= ( 2 , 1 ) {
1548+ let packed_type = if was_signed {
1549+ "packed_char4"
1550+ } else {
1551+ "packed_uchar4"
1552+ } ;
1553+ // Metal uses little endian byte order, which matches what WGSL expects here.
1554+ write ! ( self . out, "as_type<uint>({packed_type}(" ) ?;
1555+ write_arg ( self ) ?;
1556+ write ! ( self . out, "))" ) ?;
1557+ } else {
1558+ // MSL < 2.1 doesn't support `as_type` casting between packed chars and scalars.
1559+ if was_signed {
1560+ write ! ( self . out, "uint(" ) ?;
1561+ }
1562+ write ! ( self . out, "(" ) ?;
1563+ write_arg ( self ) ?;
1564+ write ! ( self . out, "[0] & 0xFF) | ((" ) ?;
1565+ write_arg ( self ) ?;
1566+ write ! ( self . out, "[1] & 0xFF) << 8) | ((" ) ?;
1567+ write_arg ( self ) ?;
1568+ write ! ( self . out, "[2] & 0xFF) << 16) | ((" ) ?;
1569+ write_arg ( self ) ?;
1570+ write ! ( self . out, "[3] & 0xFF) << 24)" ) ?;
1571+ if was_signed {
1572+ write ! ( self . out, ")" ) ?;
1573+ }
1574+ }
1575+
1576+ Ok ( ( ) )
1577+ }
1578+
15271579 /// Emit code for the isign expression.
15281580 ///
15291581 fn put_isign (
@@ -2490,53 +2542,41 @@ impl<W: Write> Writer<W> {
24902542 write ! ( self . out, "{fun_name}" ) ?;
24912543 self . put_call_parameters ( iter:: once ( arg) , context) ?;
24922544 }
2493- fun @ ( Mf :: Pack4xI8 | Mf :: Pack4xU8 | Mf :: Pack4xI8Clamp | Mf :: Pack4xU8Clamp ) => {
2494- let was_signed = matches ! ( fun, Mf :: Pack4xI8 | Mf :: Pack4xI8Clamp ) ;
2495- let clamp_bounds = match fun {
2496- Mf :: Pack4xI8Clamp => Some ( ( "-128" , "127" ) ) ,
2497- Mf :: Pack4xU8Clamp => Some ( ( "0" , "255" ) ) ,
2498- _ => None ,
2499- } ;
2500- if was_signed {
2501- write ! ( self . out, "uint(" ) ?;
2502- }
2503- let write_arg = |this : & mut Self | -> BackendResult {
2504- if let Some ( ( min, max) ) = clamp_bounds {
2505- write ! ( this. out, "{NAMESPACE}::clamp(" ) ?;
2506- this. put_expression ( arg, context, true ) ?;
2507- write ! ( this. out, ", {min}, {max})" ) ?;
2508- } else {
2509- this. put_expression ( arg, context, true ) ?;
2510- }
2511- Ok ( ( ) )
2512- } ;
2513- write ! ( self . out, "(" ) ?;
2514- write_arg ( self ) ?;
2515- write ! ( self . out, "[0] & 0xFF) | ((" ) ?;
2516- write_arg ( self ) ?;
2517- write ! ( self . out, "[1] & 0xFF) << 8) | ((" ) ?;
2518- write_arg ( self ) ?;
2519- write ! ( self . out, "[2] & 0xFF) << 16) | ((" ) ?;
2520- write_arg ( self ) ?;
2521- write ! ( self . out, "[3] & 0xFF) << 24)" ) ?;
2522- if was_signed {
2523- write ! ( self . out, ")" ) ?;
2524- }
2545+ Mf :: Pack4xI8 => self . put_pack4x8 ( arg, context, true , None ) ?,
2546+ Mf :: Pack4xU8 => self . put_pack4x8 ( arg, context, false , None ) ?,
2547+ Mf :: Pack4xI8Clamp => {
2548+ self . put_pack4x8 ( arg, context, true , Some ( ( "-128" , "127" ) ) ) ?
2549+ }
2550+ Mf :: Pack4xU8Clamp => {
2551+ self . put_pack4x8 ( arg, context, false , Some ( ( "0" , "255" ) ) ) ?
25252552 }
25262553 fun @ ( Mf :: Unpack4xI8 | Mf :: Unpack4xU8 ) => {
2527- write ! ( self . out, "(" ) ?;
2528- if matches ! ( fun, Mf :: Unpack4xU8 ) {
2529- write ! ( self . out, "u" ) ?;
2554+ let sign_prefix = if matches ! ( fun, Mf :: Unpack4xU8 ) {
2555+ "u"
2556+ } else {
2557+ ""
2558+ } ;
2559+
2560+ if context. lang_version >= ( 2 , 1 ) {
2561+ // Metal uses little endian byte order, which matches what WGSL expects here.
2562+ write ! (
2563+ self . out,
2564+ "{sign_prefix}int4(as_type<packed_{sign_prefix}char4>("
2565+ ) ?;
2566+ self . put_expression ( arg, context, true ) ?;
2567+ write ! ( self . out, "))" ) ?;
2568+ } else {
2569+ // MSL < 2.1 doesn't support `as_type` casting between packed chars and scalars.
2570+ write ! ( self . out, "({sign_prefix}int4(" ) ?;
2571+ self . put_expression ( arg, context, true ) ?;
2572+ write ! ( self . out, ", " ) ?;
2573+ self . put_expression ( arg, context, true ) ?;
2574+ write ! ( self . out, " >> 8, " ) ?;
2575+ self . put_expression ( arg, context, true ) ?;
2576+ write ! ( self . out, " >> 16, " ) ?;
2577+ self . put_expression ( arg, context, true ) ?;
2578+ write ! ( self . out, " >> 24) << 24 >> 24)" ) ?;
25302579 }
2531- write ! ( self . out, "int4(" ) ?;
2532- self . put_expression ( arg, context, true ) ?;
2533- write ! ( self . out, ", " ) ?;
2534- self . put_expression ( arg, context, true ) ?;
2535- write ! ( self . out, " >> 8, " ) ?;
2536- self . put_expression ( arg, context, true ) ?;
2537- write ! ( self . out, " >> 16, " ) ?;
2538- self . put_expression ( arg, context, true ) ?;
2539- write ! ( self . out, " >> 24) << 24 >> 24)" ) ?;
25402580 }
25412581 Mf :: QuantizeToF16 => {
25422582 match * context. resolve_type ( arg) {
@@ -3279,14 +3319,20 @@ impl<W: Write> Writer<W> {
32793319 self . need_bake_expressions . insert ( arg) ;
32803320 self . need_bake_expressions . insert ( arg1. unwrap ( ) ) ;
32813321 }
3282- crate :: MathFunction :: FirstLeadingBit
3283- | crate :: MathFunction :: Pack4xI8
3322+ crate :: MathFunction :: FirstLeadingBit => {
3323+ self . need_bake_expressions . insert ( arg) ;
3324+ }
3325+ crate :: MathFunction :: Pack4xI8
32843326 | crate :: MathFunction :: Pack4xU8
32853327 | crate :: MathFunction :: Pack4xI8Clamp
32863328 | crate :: MathFunction :: Pack4xU8Clamp
32873329 | crate :: MathFunction :: Unpack4xI8
32883330 | crate :: MathFunction :: Unpack4xU8 => {
3289- self . need_bake_expressions . insert ( arg) ;
3331+ // On MSL < 2.1, we emit a polyfill for these functions that uses the
3332+ // argument multiple times. This is no longer necessary on MSL >= 2.1.
3333+ if context. lang_version < ( 2 , 1 ) {
3334+ self . need_bake_expressions . insert ( arg) ;
3335+ }
32903336 }
32913337 crate :: MathFunction :: ExtractBits => {
32923338 // Only argument 1 is re-used.
0 commit comments