@@ -8,7 +8,17 @@ const kSampleCases = [
88 { lhs : [ 1 , 2 , 3 , 4 ] , rhs : [ 4 , 3 , 2 , 1 ] } ,
99] as const ;
1010
11- const kWorkgroupSize = 64 ; // Same as in packed.wgsl
11+ const kWorkgroupSize = 64 ; // Must match packed.wgsl
12+
13+ type vec4i = readonly [ number , number , number , number ] ;
14+ // Pack four signed 8-bit components into a u32, low byte first.
15+ function pack4xI8 ( [ x , y , z , w ] : vec4i ) : number {
16+ // `&` operator applies sign extension to i32 before operating.
17+ // `>>> 0` converts the final i32 to u32.
18+ return (
19+ ( x & 0xff ) | ( ( y & 0xff ) << 8 ) | ( ( z & 0xff ) << 16 ) | ( ( w & 0xff ) << 24 )
20+ ) >>> 0 ;
21+ }
1222
1323const result = document . querySelector ( '#result' ) as HTMLElement ;
1424if (
2333 const device = await adapter ?. requestDevice ( ) ;
2434 quitIfWebGPUNotAvailableOrMissingFeatures ( adapter , device ) ;
2535
26- function createInputBuffer (
27- vectors : ReadonlyArray < readonly [ number , number , number , number ] >
28- ) {
29- // Pack four signed 8-bit components into each u32, low byte first.
30- // Masking preserves the two's-complement representation of negative values.
31- const packed = new Uint32Array (
32- vectors . map (
33- ( [ x , y , z , w ] ) =>
34- ( x & 0xff ) |
35- ( ( y & 0xff ) << 8 ) |
36- ( ( z & 0xff ) << 16 ) |
37- ( ( w & 0xff ) << 24 )
38- )
39- ) ;
36+ function createInputBuffer ( vectors : vec4i [ ] ) {
37+ const packed = new Uint32Array ( vectors . map ( pack4xI8 ) ) ;
4038 const buffer = device . createBuffer ( {
4139 size : packed . byteLength ,
4240 usage : GPUBufferUsage . STORAGE ,
8583 readbackBuffer . unmap ( ) ;
8684
8785 for ( const [ i , sample ] of kSampleCases . entries ( ) ) {
86+ // Result should be the same in JS, show that for comparison.
8887 const expected =
8988 sample . lhs [ 0 ] * sample . rhs [ 0 ] +
9089 sample . lhs [ 1 ] * sample . rhs [ 1 ] +
9190 sample . lhs [ 2 ] * sample . rhs [ 2 ] +
9291 sample . lhs [ 3 ] * sample . rhs [ 3 ] ;
93- const lhs = sample . lhs . map ( ( x ) => x . toString ( ) . padStart ( 4 ) ) . join ( ', ' ) ;
94- const rhs = sample . rhs . map ( ( x ) => x . toString ( ) . padStart ( 4 ) ) . join ( ', ' ) ;
92+
93+ const lhs = `[${ sample . lhs
94+ . map ( ( x ) => x . toString ( ) . padStart ( 4 ) )
95+ . join ( ', ' ) } ] (0x${ pack4xI8 ( sample . lhs ) . toString ( 16 ) . padStart ( 8 , '0' ) } )`;
96+ const rhs = `[${ sample . rhs
97+ . map ( ( x ) => x . toString ( ) . padStart ( 4 ) )
98+ . join ( ', ' ) } ] (0x${ pack4xI8 ( sample . rhs ) . toString ( 16 ) . padStart ( 8 , '0' ) } )`;
9599 const out = results [ i ] . toString ( ) . padStart ( 6 ) ;
96100 const exp = expected . toString ( ) . padStart ( 6 ) ;
97- result . textContent += `\ndot4I8Packed of [${ lhs } ] by [${ rhs } ] gave ${ out } (expecting ${ exp } )` ;
101+ result . textContent += `
102+
103+ WGSL dot4I8Packed of ${ lhs }
104+ by ${ rhs } gave ${ out } (JS gave ${ exp } )` ;
98105 }
99106}
0 commit comments