@@ -1497,6 +1497,63 @@ impl<W: Write> Writer<W> {
1497
1497
Ok ( ( ) )
1498
1498
}
1499
1499
1500
+ /// Emit code for the WGSL functions `pack4x{I, U}8[Clamp]`.
1501
+ fn put_pack4x8 (
1502
+ & mut self ,
1503
+ arg : Handle < crate :: Expression > ,
1504
+ context : & ExpressionContext < ' _ > ,
1505
+ was_signed : bool ,
1506
+ clamp_bounds : Option < ( & str , & str ) > ,
1507
+ ) -> Result < ( ) , Error > {
1508
+ if context. lang_version >= ( 2 , 1 ) {
1509
+ let packed_type = if was_signed {
1510
+ "packed_char4"
1511
+ } else {
1512
+ "packed_uchar4"
1513
+ } ;
1514
+ // Metal uses little endian byte order, which matches what WGSL expects here.
1515
+ write ! ( self . out, "as_type<uint>({packed_type}(" ) ?;
1516
+ if let Some ( ( min, max) ) = clamp_bounds {
1517
+ // Clamping a vector to scalar bounds works and operates component-wise.
1518
+ write ! ( self . out, "{NAMESPACE}::clamp(" ) ?;
1519
+ self . put_expression ( arg, context, true ) ?;
1520
+ write ! ( self . out, ", {min}, {max})" ) ?;
1521
+ } else {
1522
+ self . put_expression ( arg, context, true ) ?;
1523
+ }
1524
+ write ! ( self . out, "))" ) ?;
1525
+ } else {
1526
+ // MSL < 2.1 doesn't support `as_type` casting between packed chars and scalars.
1527
+ if was_signed {
1528
+ write ! ( self . out, "uint(" ) ?;
1529
+ }
1530
+ let write_arg = |this : & mut Self | -> BackendResult {
1531
+ if let Some ( ( min, max) ) = clamp_bounds {
1532
+ write ! ( this. out, "{NAMESPACE}::clamp(" ) ?;
1533
+ this. put_expression ( arg, context, true ) ?;
1534
+ write ! ( this. out, ", {min}, {max})" ) ?;
1535
+ } else {
1536
+ this. put_expression ( arg, context, true ) ?;
1537
+ }
1538
+ Ok ( ( ) )
1539
+ } ;
1540
+ write ! ( self . out, "(" ) ?;
1541
+ write_arg ( self ) ?;
1542
+ write ! ( self . out, "[0] & 0xFF) | ((" ) ?;
1543
+ write_arg ( self ) ?;
1544
+ write ! ( self . out, "[1] & 0xFF) << 8) | ((" ) ?;
1545
+ write_arg ( self ) ?;
1546
+ write ! ( self . out, "[2] & 0xFF) << 16) | ((" ) ?;
1547
+ write_arg ( self ) ?;
1548
+ write ! ( self . out, "[3] & 0xFF) << 24)" ) ?;
1549
+ if was_signed {
1550
+ write ! ( self . out, ")" ) ?;
1551
+ }
1552
+ }
1553
+
1554
+ Ok ( ( ) )
1555
+ }
1556
+
1500
1557
/// Emit code for the isign expression.
1501
1558
///
1502
1559
fn put_isign (
@@ -2437,53 +2494,41 @@ impl<W: Write> Writer<W> {
2437
2494
write ! ( self . out, "{fun_name}" ) ?;
2438
2495
self . put_call_parameters ( iter:: once ( arg) , context) ?;
2439
2496
}
2440
- fun @ ( Mf :: Pack4xI8 | Mf :: Pack4xU8 | Mf :: Pack4xI8Clamp | Mf :: Pack4xU8Clamp ) => {
2441
- let was_signed = matches ! ( fun, Mf :: Pack4xI8 | Mf :: Pack4xI8Clamp ) ;
2442
- let clamp_bounds = match fun {
2443
- Mf :: Pack4xI8Clamp => Some ( ( "-128" , "127" ) ) ,
2444
- Mf :: Pack4xU8Clamp => Some ( ( "0" , "255" ) ) ,
2445
- _ => None ,
2446
- } ;
2447
- if was_signed {
2448
- write ! ( self . out, "uint(" ) ?;
2449
- }
2450
- let write_arg = |this : & mut Self | -> BackendResult {
2451
- if let Some ( ( min, max) ) = clamp_bounds {
2452
- write ! ( this. out, "{NAMESPACE}::clamp(" ) ?;
2453
- this. put_expression ( arg, context, true ) ?;
2454
- write ! ( this. out, ", {min}, {max})" ) ?;
2455
- } else {
2456
- this. put_expression ( arg, context, true ) ?;
2457
- }
2458
- Ok ( ( ) )
2459
- } ;
2460
- write ! ( self . out, "(" ) ?;
2461
- write_arg ( self ) ?;
2462
- write ! ( self . out, "[0] & 0xFF) | ((" ) ?;
2463
- write_arg ( self ) ?;
2464
- write ! ( self . out, "[1] & 0xFF) << 8) | ((" ) ?;
2465
- write_arg ( self ) ?;
2466
- write ! ( self . out, "[2] & 0xFF) << 16) | ((" ) ?;
2467
- write_arg ( self ) ?;
2468
- write ! ( self . out, "[3] & 0xFF) << 24)" ) ?;
2469
- if was_signed {
2470
- write ! ( self . out, ")" ) ?;
2471
- }
2497
+ Mf :: Pack4xI8 => self . put_pack4x8 ( arg, context, true , None ) ?,
2498
+ Mf :: Pack4xU8 => self . put_pack4x8 ( arg, context, false , None ) ?,
2499
+ Mf :: Pack4xI8Clamp => {
2500
+ self . put_pack4x8 ( arg, context, true , Some ( ( "-128" , "127" ) ) ) ?
2501
+ }
2502
+ Mf :: Pack4xU8Clamp => {
2503
+ self . put_pack4x8 ( arg, context, false , Some ( ( "0" , "255" ) ) ) ?
2472
2504
}
2473
2505
fun @ ( Mf :: Unpack4xI8 | Mf :: Unpack4xU8 ) => {
2474
- write ! ( self . out, "(" ) ?;
2475
- if matches ! ( fun, Mf :: Unpack4xU8 ) {
2476
- write ! ( self . out, "u" ) ?;
2506
+ let sign_prefix = if matches ! ( fun, Mf :: Unpack4xU8 ) {
2507
+ "u"
2508
+ } else {
2509
+ ""
2510
+ } ;
2511
+
2512
+ if context. lang_version >= ( 2 , 1 ) {
2513
+ // Metal uses little endian byte order, which matches what WGSL expects here.
2514
+ write ! (
2515
+ self . out,
2516
+ "{sign_prefix}int4(as_type<packed_{sign_prefix}char4>("
2517
+ ) ?;
2518
+ self . put_expression ( arg, context, true ) ?;
2519
+ write ! ( self . out, "))" ) ?;
2520
+ } else {
2521
+ // MSL < 2.1 doesn't support `as_type` casting between packed chars and scalars.
2522
+ write ! ( self . out, "({sign_prefix}int4(" ) ?;
2523
+ self . put_expression ( arg, context, true ) ?;
2524
+ write ! ( self . out, ", " ) ?;
2525
+ self . put_expression ( arg, context, true ) ?;
2526
+ write ! ( self . out, " >> 8, " ) ?;
2527
+ self . put_expression ( arg, context, true ) ?;
2528
+ write ! ( self . out, " >> 16, " ) ?;
2529
+ self . put_expression ( arg, context, true ) ?;
2530
+ write ! ( self . out, " >> 24) << 24 >> 24)" ) ?;
2477
2531
}
2478
- write ! ( self . out, "int4(" ) ?;
2479
- self . put_expression ( arg, context, true ) ?;
2480
- write ! ( self . out, ", " ) ?;
2481
- self . put_expression ( arg, context, true ) ?;
2482
- write ! ( self . out, " >> 8, " ) ?;
2483
- self . put_expression ( arg, context, true ) ?;
2484
- write ! ( self . out, " >> 16, " ) ?;
2485
- self . put_expression ( arg, context, true ) ?;
2486
- write ! ( self . out, " >> 24) << 24 >> 24)" ) ?;
2487
2532
}
2488
2533
Mf :: QuantizeToF16 => {
2489
2534
match * context. resolve_type ( arg) {
@@ -3226,14 +3271,20 @@ impl<W: Write> Writer<W> {
3226
3271
self . need_bake_expressions . insert ( arg) ;
3227
3272
self . need_bake_expressions . insert ( arg1. unwrap ( ) ) ;
3228
3273
}
3229
- crate :: MathFunction :: FirstLeadingBit
3230
- | crate :: MathFunction :: Pack4xI8
3274
+ crate :: MathFunction :: FirstLeadingBit => {
3275
+ self . need_bake_expressions . insert ( arg) ;
3276
+ }
3277
+ crate :: MathFunction :: Pack4xI8
3231
3278
| crate :: MathFunction :: Pack4xU8
3232
3279
| crate :: MathFunction :: Pack4xI8Clamp
3233
3280
| crate :: MathFunction :: Pack4xU8Clamp
3234
3281
| crate :: MathFunction :: Unpack4xI8
3235
3282
| crate :: MathFunction :: Unpack4xU8 => {
3236
- self . need_bake_expressions . insert ( arg) ;
3283
+ // On MSL < 2.1, we emit a polyfill for these functions that uses the
3284
+ // argument multiple times. This is no longer necessary on MSL >= 2.1.
3285
+ if context. lang_version < ( 2 , 1 ) {
3286
+ self . need_bake_expressions . insert ( arg) ;
3287
+ }
3237
3288
}
3238
3289
crate :: MathFunction :: ExtractBits => {
3239
3290
// Only argument 1 is re-used.
0 commit comments