@@ -1524,6 +1524,58 @@ impl<W: Write> Writer<W> {
1524
1524
Ok ( ( ) )
1525
1525
}
1526
1526
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
+
1527
1579
/// Emit code for the isign expression.
1528
1580
///
1529
1581
fn put_isign (
@@ -2490,53 +2542,41 @@ impl<W: Write> Writer<W> {
2490
2542
write ! ( self . out, "{fun_name}" ) ?;
2491
2543
self . put_call_parameters ( iter:: once ( arg) , context) ?;
2492
2544
}
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" ) ) ) ?
2525
2552
}
2526
2553
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)" ) ?;
2530
2579
}
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)" ) ?;
2540
2580
}
2541
2581
Mf :: QuantizeToF16 => {
2542
2582
match * context. resolve_type ( arg) {
@@ -3279,14 +3319,20 @@ impl<W: Write> Writer<W> {
3279
3319
self . need_bake_expressions . insert ( arg) ;
3280
3320
self . need_bake_expressions . insert ( arg1. unwrap ( ) ) ;
3281
3321
}
3282
- crate :: MathFunction :: FirstLeadingBit
3283
- | crate :: MathFunction :: Pack4xI8
3322
+ crate :: MathFunction :: FirstLeadingBit => {
3323
+ self . need_bake_expressions . insert ( arg) ;
3324
+ }
3325
+ crate :: MathFunction :: Pack4xI8
3284
3326
| crate :: MathFunction :: Pack4xU8
3285
3327
| crate :: MathFunction :: Pack4xI8Clamp
3286
3328
| crate :: MathFunction :: Pack4xU8Clamp
3287
3329
| crate :: MathFunction :: Unpack4xI8
3288
3330
| 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
+ }
3290
3336
}
3291
3337
crate :: MathFunction :: ExtractBits => {
3292
3338
// Only argument 1 is re-used.
0 commit comments