react-native-wgpu 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +43 -42
  2. package/android/cpp/AndroidPlatformContext.h +2 -1
  3. package/cpp/jsi/RNFJSIConverter.h +10 -0
  4. package/cpp/rnwgpu/SurfaceRegistry.h +1 -2
  5. package/cpp/rnwgpu/api/Canvas.h +3 -3
  6. package/cpp/rnwgpu/api/GPUBuffer.h +1 -1
  7. package/ios/WebGPUView.mm +14 -1
  8. package/lib/commonjs/Canvas.js.map +1 -1
  9. package/lib/commonjs/utils.js +2 -2
  10. package/lib/commonjs/utils.js.map +1 -1
  11. package/lib/module/Canvas.js.map +1 -1
  12. package/lib/module/utils.js +2 -2
  13. package/lib/module/utils.js.map +1 -1
  14. package/lib/typescript/lib/commonjs/utils.d.ts +1 -1
  15. package/lib/typescript/lib/commonjs/utils.d.ts.map +1 -1
  16. package/lib/typescript/lib/module/utils.d.ts +1 -1
  17. package/lib/typescript/lib/module/utils.d.ts.map +1 -1
  18. package/lib/typescript/src/Canvas.d.ts +3 -2
  19. package/lib/typescript/src/Canvas.d.ts.map +1 -1
  20. package/lib/typescript/src/__tests__/ArrayBuffer.spec.d.ts +2 -0
  21. package/lib/typescript/src/__tests__/ArrayBuffer.spec.d.ts.map +1 -0
  22. package/lib/typescript/src/__tests__/Buffer.spec.d.ts +2 -0
  23. package/lib/typescript/src/__tests__/Buffer.spec.d.ts.map +1 -0
  24. package/lib/typescript/src/__tests__/ComputeShader.spec.d.ts +2 -0
  25. package/lib/typescript/src/__tests__/ComputeShader.spec.d.ts.map +1 -0
  26. package/lib/typescript/src/__tests__/Constants.spec.d.ts +2 -0
  27. package/lib/typescript/src/__tests__/Constants.spec.d.ts.map +1 -0
  28. package/lib/typescript/src/__tests__/Device.spec.d.ts +2 -0
  29. package/lib/typescript/src/__tests__/Device.spec.d.ts.map +1 -0
  30. package/lib/typescript/src/__tests__/ExternalTexture.spec.d.ts +2 -0
  31. package/lib/typescript/src/__tests__/ExternalTexture.spec.d.ts.map +1 -0
  32. package/lib/typescript/src/__tests__/GPU.spec.d.ts +2 -0
  33. package/lib/typescript/src/__tests__/GPU.spec.d.ts.map +1 -0
  34. package/lib/typescript/src/__tests__/ImageData.spec.d.ts +2 -0
  35. package/lib/typescript/src/__tests__/ImageData.spec.d.ts.map +1 -0
  36. package/lib/typescript/src/__tests__/Shaders.spec.d.ts +2 -0
  37. package/lib/typescript/src/__tests__/Shaders.spec.d.ts.map +1 -0
  38. package/lib/typescript/src/__tests__/Texture.spec.d.ts +2 -0
  39. package/lib/typescript/src/__tests__/Texture.spec.d.ts.map +1 -0
  40. package/lib/typescript/src/__tests__/components/DrawingContext.d.ts +12 -0
  41. package/lib/typescript/src/__tests__/components/DrawingContext.d.ts.map +1 -0
  42. package/lib/typescript/src/__tests__/components/Wireframe/Shaders.d.ts +3 -0
  43. package/lib/typescript/src/__tests__/components/Wireframe/Shaders.d.ts.map +1 -0
  44. package/lib/typescript/src/__tests__/components/Wireframe/models.d.ts +29 -0
  45. package/lib/typescript/src/__tests__/components/Wireframe/models.d.ts.map +1 -0
  46. package/lib/typescript/src/__tests__/components/Wireframe/utils.d.ts +5 -0
  47. package/lib/typescript/src/__tests__/components/Wireframe/utils.d.ts.map +1 -0
  48. package/lib/typescript/src/__tests__/components/cube.d.ts +7 -0
  49. package/lib/typescript/src/__tests__/components/cube.d.ts.map +1 -0
  50. package/lib/typescript/src/__tests__/components/meshes/mesh.d.ts +22 -0
  51. package/lib/typescript/src/__tests__/components/meshes/mesh.d.ts.map +1 -0
  52. package/lib/typescript/src/__tests__/components/meshes/sphere.d.ts +12 -0
  53. package/lib/typescript/src/__tests__/components/meshes/sphere.d.ts.map +1 -0
  54. package/lib/typescript/src/__tests__/components/meshes/stanfordDragon.d.ts +7 -0
  55. package/lib/typescript/src/__tests__/components/meshes/stanfordDragon.d.ts.map +1 -0
  56. package/lib/typescript/src/__tests__/components/meshes/stanfordDragonData.d.ts +6 -0
  57. package/lib/typescript/src/__tests__/components/meshes/stanfordDragonData.d.ts.map +1 -0
  58. package/lib/typescript/src/__tests__/components/meshes/teapot.d.ts +6 -0
  59. package/lib/typescript/src/__tests__/components/meshes/teapot.d.ts.map +1 -0
  60. package/lib/typescript/src/__tests__/components/meshes/utils.d.ts +10 -0
  61. package/lib/typescript/src/__tests__/components/meshes/utils.d.ts.map +1 -0
  62. package/lib/typescript/src/__tests__/components/triangle.d.ts +3 -0
  63. package/lib/typescript/src/__tests__/components/triangle.d.ts.map +1 -0
  64. package/lib/typescript/src/__tests__/config.d.ts +3 -0
  65. package/lib/typescript/src/__tests__/config.d.ts.map +1 -0
  66. package/lib/typescript/src/__tests__/demos/ABuffer.spec.d.ts +2 -0
  67. package/lib/typescript/src/__tests__/demos/ABuffer.spec.d.ts.map +1 -0
  68. package/lib/typescript/src/__tests__/demos/Blur.spec.d.ts +2 -0
  69. package/lib/typescript/src/__tests__/demos/Blur.spec.d.ts.map +1 -0
  70. package/lib/typescript/src/__tests__/demos/Cube.spec.d.ts +2 -0
  71. package/lib/typescript/src/__tests__/demos/Cube.spec.d.ts.map +1 -0
  72. package/lib/typescript/src/__tests__/demos/FractalCube.spec.d.ts +2 -0
  73. package/lib/typescript/src/__tests__/demos/FractalCube.spec.d.ts.map +1 -0
  74. package/lib/typescript/src/__tests__/demos/OcclusionQuery.spec.d.ts +2 -0
  75. package/lib/typescript/src/__tests__/demos/OcclusionQuery.spec.d.ts.map +1 -0
  76. package/lib/typescript/src/__tests__/demos/RenderBundles.spec.d.ts +2 -0
  77. package/lib/typescript/src/__tests__/demos/RenderBundles.spec.d.ts.map +1 -0
  78. package/lib/typescript/src/__tests__/demos/Triangle.spec.d.ts +2 -0
  79. package/lib/typescript/src/__tests__/demos/Triangle.spec.d.ts.map +1 -0
  80. package/lib/typescript/src/__tests__/demos/Wireframe.spec.d.ts +2 -0
  81. package/lib/typescript/src/__tests__/demos/Wireframe.spec.d.ts.map +1 -0
  82. package/lib/typescript/src/__tests__/globalSetup.d.ts +10 -0
  83. package/lib/typescript/src/__tests__/globalSetup.d.ts.map +1 -0
  84. package/lib/typescript/src/__tests__/globalTeardown.d.ts +3 -0
  85. package/lib/typescript/src/__tests__/globalTeardown.d.ts.map +1 -0
  86. package/lib/typescript/src/__tests__/setup.d.ts +62 -0
  87. package/lib/typescript/src/__tests__/setup.d.ts.map +1 -0
  88. package/lib/typescript/src/utils.d.ts +2 -1
  89. package/lib/typescript/src/utils.d.ts.map +1 -1
  90. package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
  91. package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
  92. package/libs/android/x86/libwebgpu_dawn.so +0 -0
  93. package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
  94. package/libs/{ios/libwebgpu_dawn.xcframework/ios-arm64 → apple/arm64_iphoneos}/libwebgpu_dawn.a +0 -0
  95. package/libs/{ios → apple}/arm64_iphonesimulator/libwebgpu_dawn.a +0 -0
  96. package/libs/{ios/arm64_xros/libwebgpu_dawn_visionos.a → apple/arm64_xros/libwebgpu_dawn.a} +0 -0
  97. package/libs/{ios → apple}/arm64_xrsimulator/libwebgpu_dawn.a +0 -0
  98. package/libs/{ios → apple}/libwebgpu_dawn.a +0 -0
  99. package/libs/{ios/arm64_iphoneos → apple/libwebgpu_dawn.xcframework/ios-arm64}/libwebgpu_dawn.a +0 -0
  100. package/libs/{ios → apple}/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
  101. package/libs/apple/libwebgpu_dawn_macosx.xcframework/Info.plist +28 -0
  102. package/libs/apple/libwebgpu_dawn_macosx.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
  103. package/libs/{ios/libwebgpu_dawn_visionos.xcframework/xros-arm64_x86_64-simulator → apple}/libwebgpu_dawn_visionos.a +0 -0
  104. package/libs/{ios → apple}/libwebgpu_dawn_visionos.xcframework/Info.plist +7 -7
  105. package/libs/{ios/libwebgpu_dawn_visionos.xcframework/xros-arm64/libwebgpu_dawn_visionos.a → apple/libwebgpu_dawn_visionos.xcframework/xros-arm64/libwebgpu_dawn.a} +0 -0
  106. package/libs/{ios → apple/libwebgpu_dawn_visionos.xcframework/xros-arm64_x86_64-simulator}/libwebgpu_dawn_visionos.a +0 -0
  107. package/libs/apple/universal_macosx/libwebgpu_dawn.a +0 -0
  108. package/libs/{ios → apple}/x86_64_iphonesimulator/libwebgpu_dawn.a +0 -0
  109. package/libs/{ios → apple}/x86_64_xrsimulator/libwebgpu_dawn.a +0 -0
  110. package/package.json +23 -23
  111. package/src/Canvas.tsx +4 -2
  112. package/src/__tests__/ArrayBuffer.spec.ts +76 -0
  113. package/src/__tests__/Buffer.spec.ts +357 -0
  114. package/src/__tests__/ComputeShader.spec.ts +375 -0
  115. package/src/__tests__/Constants.spec.ts +91 -0
  116. package/src/__tests__/Device.spec.ts +35 -0
  117. package/src/__tests__/ExternalTexture.spec.ts +284 -0
  118. package/src/__tests__/GPU.spec.ts +229 -0
  119. package/src/__tests__/ImageData.spec.ts +26 -0
  120. package/src/__tests__/Shaders.spec.ts +232 -0
  121. package/src/__tests__/Texture.spec.ts +191 -0
  122. package/src/__tests__/assets/Di-3d.png +0 -0
  123. package/src/__tests__/components/DrawingContext.ts +11 -0
  124. package/src/__tests__/components/Wireframe/Shaders.ts +138 -0
  125. package/src/__tests__/components/Wireframe/models.ts +113 -0
  126. package/src/__tests__/components/Wireframe/utils.ts +22 -0
  127. package/src/__tests__/components/cube.ts +51 -0
  128. package/src/__tests__/components/meshes/mesh.ts +96 -0
  129. package/src/__tests__/components/meshes/sphere.ts +103 -0
  130. package/src/__tests__/components/meshes/stanfordDragon.ts +44 -0
  131. package/src/__tests__/components/meshes/stanfordDragonData.ts +5 -0
  132. package/src/__tests__/components/meshes/teapot.ts +13 -0
  133. package/src/__tests__/components/meshes/utils.ts +235 -0
  134. package/src/__tests__/components/triangle.ts +17 -0
  135. package/src/__tests__/config.ts +2 -0
  136. package/src/__tests__/demos/ABuffer.spec.ts +885 -0
  137. package/src/__tests__/demos/Blur.spec.ts +397 -0
  138. package/src/__tests__/demos/Cube.spec.ts +925 -0
  139. package/src/__tests__/demos/FractalCube.spec.ts +239 -0
  140. package/src/__tests__/demos/OcclusionQuery.spec.ts +376 -0
  141. package/src/__tests__/demos/RenderBundles.spec.ts +579 -0
  142. package/src/__tests__/demos/Triangle.spec.ts +248 -0
  143. package/src/__tests__/demos/Wireframe.spec.ts +190 -0
  144. package/src/__tests__/globalSetup.ts +54 -0
  145. package/src/__tests__/globalTeardown.ts +11 -0
  146. package/src/__tests__/setup.ts +409 -0
  147. package/src/__tests__/snapshots/abuffer.png +0 -0
  148. package/src/__tests__/snapshots/asteroid.png +0 -0
  149. package/src/__tests__/snapshots/blur.png +0 -0
  150. package/src/__tests__/snapshots/buffer.png +0 -0
  151. package/src/__tests__/snapshots/constant-triangle.png +0 -0
  152. package/src/__tests__/snapshots/cube.png +0 -0
  153. package/src/__tests__/snapshots/f.png +0 -0
  154. package/src/__tests__/snapshots/f2.png +0 -0
  155. package/src/__tests__/snapshots/fractal-cubes.png +0 -0
  156. package/src/__tests__/snapshots/instanced-cubes.png +0 -0
  157. package/src/__tests__/snapshots/occlusion-query.png +0 -0
  158. package/src/__tests__/snapshots/ref.png +0 -0
  159. package/src/__tests__/snapshots/texture.png +0 -0
  160. package/src/__tests__/snapshots/textured-cube.png +0 -0
  161. package/src/__tests__/snapshots/triangle-msaa.png +0 -0
  162. package/src/__tests__/snapshots/triangle.png +0 -0
  163. package/src/__tests__/snapshots/two-cube.png +0 -0
  164. package/src/utils.ts +3 -1
  165. package/android/gradle.properties +0 -5
  166. package/libs/ios/arm64_xros/libwebgpu_dawn.a +0 -0
  167. package/react-native-wgpu.podspec +0 -57
  168. package/libs/{ios → apple}/libwebgpu_dawn.xcframework/Info.plist +5 -5
@@ -0,0 +1,375 @@
1
+ /* eslint-disable prefer-destructuring */
2
+ import { client } from "./setup";
3
+
4
+ const multiplyMatrices = (m1: number[], m2: number[]) => {
5
+ const rows1 = m1[0],
6
+ cols1 = m1[1];
7
+ const cols2 = m2[1];
8
+ const result: number[] = new Array(2 + rows1 * cols2);
9
+ result[0] = rows1;
10
+ result[1] = cols2;
11
+
12
+ for (let i = 0; i < rows1; i++) {
13
+ for (let j = 0; j < cols2; j++) {
14
+ let sum = 0;
15
+ for (let k = 0; k < cols1; k++) {
16
+ sum += m1[2 + i * cols1 + k] * m2[2 + k * cols2 + j];
17
+ }
18
+ result[2 + i * cols2 + j] = sum;
19
+ }
20
+ }
21
+ return result;
22
+ };
23
+
24
+ describe("Computer Shader", () => {
25
+ it("matrix multiplication", async () => {
26
+ // First Matrix
27
+ const rows = 16;
28
+ const columns = 16;
29
+ const m1: number[] = new Array(rows * columns + 2);
30
+ m1[0] = rows;
31
+ m1[1] = columns;
32
+ for (let i = 2; i < m1.length; i++) {
33
+ m1[i] = Math.random();
34
+ }
35
+
36
+ // Second Matrix
37
+ const m2: number[] = new Array(rows * columns + 2);
38
+ m2[0] = rows;
39
+ m2[1] = columns;
40
+ for (let i = 2; i < m2.length; i++) {
41
+ m2[i] = Math.random();
42
+ }
43
+
44
+ const result = await client.eval(
45
+ ({ device, firstMatrixRaw, secondMatrixRaw, rows1, columns1 }) => {
46
+ const firstMatrix = new Float32Array(firstMatrixRaw);
47
+ const secondMatrix = new Float32Array(secondMatrixRaw);
48
+ const gpuBufferFirstMatrix = device.createBuffer({
49
+ mappedAtCreation: true,
50
+ size: firstMatrix.byteLength,
51
+ usage: GPUBufferUsage.STORAGE,
52
+ });
53
+ const arrayBufferFirstMatrix = gpuBufferFirstMatrix.getMappedRange();
54
+ new Float32Array(arrayBufferFirstMatrix).set(firstMatrix);
55
+ gpuBufferFirstMatrix.unmap();
56
+
57
+ const gpuBufferSecondMatrix = device.createBuffer({
58
+ mappedAtCreation: true,
59
+ size: secondMatrix.byteLength,
60
+ usage: GPUBufferUsage.STORAGE,
61
+ });
62
+ const arrayBufferSecondMatrix = gpuBufferSecondMatrix.getMappedRange();
63
+ new Float32Array(arrayBufferSecondMatrix).set(secondMatrix);
64
+ gpuBufferSecondMatrix.unmap();
65
+
66
+ // Result Matrix
67
+ const resultMatrixBufferSize =
68
+ Float32Array.BYTES_PER_ELEMENT *
69
+ (2 + firstMatrix[0] * secondMatrix[1]);
70
+ const resultMatrixBuffer = device.createBuffer({
71
+ size: resultMatrixBufferSize,
72
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
73
+ });
74
+
75
+ // Compute shader code
76
+ const shaderModule = device.createShaderModule({
77
+ code: `
78
+ struct Matrix {
79
+ size : vec2<f32>,
80
+ numbers: array<f32>,
81
+ }
82
+
83
+ @group(0) @binding(0) var<storage, read> firstMatrix : Matrix;
84
+ @group(0) @binding(1) var<storage, read> secondMatrix : Matrix;
85
+ @group(0) @binding(2) var<storage, read_write> resultMatrix : Matrix;
86
+
87
+ @compute @workgroup_size(8, 8)
88
+ fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {
89
+ // Guard against out-of-bounds work group sizes
90
+ if (global_id.x >= u32(firstMatrix.size.x) || global_id.y >= u32(secondMatrix.size.y)) {
91
+ return;
92
+ }
93
+
94
+ resultMatrix.size = vec2(firstMatrix.size.x, secondMatrix.size.y);
95
+
96
+ let resultCell = vec2(global_id.x, global_id.y);
97
+ var result = 0.0;
98
+ for (var i = 0u; i < u32(firstMatrix.size.y); i = i + 1u) {
99
+ let a = i + resultCell.x * u32(firstMatrix.size.y);
100
+ let b = resultCell.y + i * u32(secondMatrix.size.y);
101
+ result = result + firstMatrix.numbers[a] * secondMatrix.numbers[b];
102
+ }
103
+
104
+ let index = resultCell.y + resultCell.x * u32(secondMatrix.size.y);
105
+ resultMatrix.numbers[index] = result;
106
+ }
107
+ `,
108
+ });
109
+
110
+ // Pipeline setup
111
+ const computePipeline = device.createComputePipeline({
112
+ layout: "auto",
113
+ compute: {
114
+ module: shaderModule,
115
+ entryPoint: "main",
116
+ },
117
+ });
118
+
119
+ // Bind group
120
+ const bindGroup = device.createBindGroup({
121
+ layout: computePipeline.getBindGroupLayout(0 /* index */),
122
+ entries: [
123
+ {
124
+ binding: 0,
125
+ resource: {
126
+ buffer: gpuBufferFirstMatrix,
127
+ },
128
+ },
129
+ {
130
+ binding: 1,
131
+ resource: {
132
+ buffer: gpuBufferSecondMatrix,
133
+ },
134
+ },
135
+ {
136
+ binding: 2,
137
+ resource: {
138
+ buffer: resultMatrixBuffer,
139
+ },
140
+ },
141
+ ],
142
+ });
143
+
144
+ // Commands submission
145
+ const commandEncoder = device.createCommandEncoder();
146
+
147
+ const passEncoder = commandEncoder.beginComputePass();
148
+ passEncoder.setPipeline(computePipeline);
149
+ passEncoder.setBindGroup(0, bindGroup);
150
+ const workgroupCountX = Math.ceil(rows1 / 8);
151
+ const workgroupCountY = Math.ceil(columns1 / 8);
152
+ passEncoder.dispatchWorkgroups(workgroupCountX, workgroupCountY);
153
+ passEncoder.end();
154
+
155
+ // Get a GPU buffer for reading in an unmapped state.
156
+ const gpuReadBuffer = device.createBuffer({
157
+ size: resultMatrixBufferSize,
158
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
159
+ });
160
+
161
+ // Encode commands for copying buffer to buffer.
162
+ commandEncoder.copyBufferToBuffer(
163
+ resultMatrixBuffer /* source buffer */,
164
+ 0 /* source offset */,
165
+ gpuReadBuffer /* destination buffer */,
166
+ 0 /* destination offset */,
167
+ resultMatrixBufferSize /* size */,
168
+ );
169
+
170
+ // Submit GPU commands.
171
+ const gpuCommands = commandEncoder.finish();
172
+ device.queue.submit([gpuCommands]);
173
+
174
+ // Read buffer.
175
+ return gpuReadBuffer.mapAsync(GPUMapMode.READ).then(() => {
176
+ const arrayBuffer = gpuReadBuffer.getMappedRange();
177
+ const uint8Array = new Float32Array(arrayBuffer);
178
+ // At this point, uint8Array contains the pixel data of the rendered image
179
+ // You can process it further, save it, or send it somewhere else
180
+ const r = Array.from(uint8Array);
181
+ gpuReadBuffer.unmap();
182
+ return r;
183
+ });
184
+ },
185
+ {
186
+ firstMatrixRaw: m1,
187
+ secondMatrixRaw: m2,
188
+ rows1: rows,
189
+ columns1: columns,
190
+ },
191
+ );
192
+ expect(result.length).toBe(16 * 16 + 2);
193
+ expect(result.some((x) => x !== 0)).toBe(true);
194
+ const referenceResult = multiplyMatrices(m1, m2);
195
+ for (let i = 0; i < result.length; i++) {
196
+ expect(result[i]).toBeCloseTo(referenceResult[i], 5); // Using 5 decimal places for floating-point comparison
197
+ }
198
+ });
199
+ it("async matrix multiplication", async () => {
200
+ // First Matrix
201
+ const rows = 16;
202
+ const columns = 16;
203
+ const m1: number[] = new Array(rows * columns + 2);
204
+ m1[0] = rows;
205
+ m1[1] = columns;
206
+ for (let i = 2; i < m1.length; i++) {
207
+ m1[i] = Math.random();
208
+ }
209
+
210
+ // Second Matrix
211
+ const m2: number[] = new Array(rows * columns + 2);
212
+ m2[0] = rows;
213
+ m2[1] = columns;
214
+ for (let i = 2; i < m2.length; i++) {
215
+ m2[i] = Math.random();
216
+ }
217
+
218
+ const result = await client.eval(
219
+ ({ device, firstMatrixRaw, secondMatrixRaw, rows1, columns1 }) => {
220
+ const firstMatrix = new Float32Array(firstMatrixRaw);
221
+ const secondMatrix = new Float32Array(secondMatrixRaw);
222
+ const gpuBufferFirstMatrix = device.createBuffer({
223
+ mappedAtCreation: true,
224
+ size: firstMatrix.byteLength,
225
+ usage: GPUBufferUsage.STORAGE,
226
+ });
227
+ const arrayBufferFirstMatrix = gpuBufferFirstMatrix.getMappedRange();
228
+ new Float32Array(arrayBufferFirstMatrix).set(firstMatrix);
229
+ gpuBufferFirstMatrix.unmap();
230
+
231
+ const gpuBufferSecondMatrix = device.createBuffer({
232
+ mappedAtCreation: true,
233
+ size: secondMatrix.byteLength,
234
+ usage: GPUBufferUsage.STORAGE,
235
+ });
236
+ const arrayBufferSecondMatrix = gpuBufferSecondMatrix.getMappedRange();
237
+ new Float32Array(arrayBufferSecondMatrix).set(secondMatrix);
238
+ gpuBufferSecondMatrix.unmap();
239
+
240
+ // Result Matrix
241
+ const resultMatrixBufferSize =
242
+ Float32Array.BYTES_PER_ELEMENT *
243
+ (2 + firstMatrix[0] * secondMatrix[1]);
244
+ const resultMatrixBuffer = device.createBuffer({
245
+ size: resultMatrixBufferSize,
246
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
247
+ });
248
+
249
+ // Compute shader code
250
+ const shaderModule = device.createShaderModule({
251
+ code: `
252
+ struct Matrix {
253
+ size : vec2<f32>,
254
+ numbers: array<f32>,
255
+ }
256
+
257
+ @group(0) @binding(0) var<storage, read> firstMatrix : Matrix;
258
+ @group(0) @binding(1) var<storage, read> secondMatrix : Matrix;
259
+ @group(0) @binding(2) var<storage, read_write> resultMatrix : Matrix;
260
+
261
+ @compute @workgroup_size(8, 8)
262
+ fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {
263
+ // Guard against out-of-bounds work group sizes
264
+ if (global_id.x >= u32(firstMatrix.size.x) || global_id.y >= u32(secondMatrix.size.y)) {
265
+ return;
266
+ }
267
+
268
+ resultMatrix.size = vec2(firstMatrix.size.x, secondMatrix.size.y);
269
+
270
+ let resultCell = vec2(global_id.x, global_id.y);
271
+ var result = 0.0;
272
+ for (var i = 0u; i < u32(firstMatrix.size.y); i = i + 1u) {
273
+ let a = i + resultCell.x * u32(firstMatrix.size.y);
274
+ let b = resultCell.y + i * u32(secondMatrix.size.y);
275
+ result = result + firstMatrix.numbers[a] * secondMatrix.numbers[b];
276
+ }
277
+
278
+ let index = resultCell.y + resultCell.x * u32(secondMatrix.size.y);
279
+ resultMatrix.numbers[index] = result;
280
+ }
281
+ `,
282
+ });
283
+
284
+ // Pipeline setup
285
+ return device
286
+ .createComputePipelineAsync({
287
+ layout: "auto",
288
+ compute: {
289
+ module: shaderModule,
290
+ entryPoint: "main",
291
+ },
292
+ })
293
+ .then((computePipeline) => {
294
+ // Bind group
295
+ const bindGroup = device.createBindGroup({
296
+ layout: computePipeline.getBindGroupLayout(0 /* index */),
297
+ entries: [
298
+ {
299
+ binding: 0,
300
+ resource: {
301
+ buffer: gpuBufferFirstMatrix,
302
+ },
303
+ },
304
+ {
305
+ binding: 1,
306
+ resource: {
307
+ buffer: gpuBufferSecondMatrix,
308
+ },
309
+ },
310
+ {
311
+ binding: 2,
312
+ resource: {
313
+ buffer: resultMatrixBuffer,
314
+ },
315
+ },
316
+ ],
317
+ });
318
+
319
+ // Commands submission
320
+ const commandEncoder = device.createCommandEncoder();
321
+
322
+ const passEncoder = commandEncoder.beginComputePass();
323
+ passEncoder.setPipeline(computePipeline);
324
+ passEncoder.setBindGroup(0, bindGroup);
325
+ const workgroupCountX = Math.ceil(rows1 / 8);
326
+ const workgroupCountY = Math.ceil(columns1 / 8);
327
+ passEncoder.dispatchWorkgroups(workgroupCountX, workgroupCountY);
328
+ passEncoder.end();
329
+
330
+ // Get a GPU buffer for reading in an unmapped state.
331
+ const gpuReadBuffer = device.createBuffer({
332
+ size: resultMatrixBufferSize,
333
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
334
+ });
335
+
336
+ // Encode commands for copying buffer to buffer.
337
+ commandEncoder.copyBufferToBuffer(
338
+ resultMatrixBuffer /* source buffer */,
339
+ 0 /* source offset */,
340
+ gpuReadBuffer /* destination buffer */,
341
+ 0 /* destination offset */,
342
+ resultMatrixBufferSize /* size */,
343
+ );
344
+
345
+ // Submit GPU commands.
346
+ const gpuCommands = commandEncoder.finish();
347
+ device.queue.submit([gpuCommands]);
348
+
349
+ // Read buffer.
350
+ return gpuReadBuffer.mapAsync(GPUMapMode.READ).then(() => {
351
+ const arrayBuffer = gpuReadBuffer.getMappedRange();
352
+ const uint8Array = new Float32Array(arrayBuffer);
353
+ // At this point, uint8Array contains the pixel data of the rendered image
354
+ // You can process it further, save it, or send it somewhere else
355
+ const r = Array.from(uint8Array);
356
+ gpuReadBuffer.unmap();
357
+ return r;
358
+ });
359
+ });
360
+ },
361
+ {
362
+ firstMatrixRaw: m1,
363
+ secondMatrixRaw: m2,
364
+ rows1: rows,
365
+ columns1: columns,
366
+ },
367
+ );
368
+ expect(result.length).toBe(16 * 16 + 2);
369
+ expect(result.some((x) => x !== 0)).toBe(true);
370
+ const referenceResult = multiplyMatrices(m1, m2);
371
+ for (let i = 0; i < result.length; i++) {
372
+ expect(result[i]).toBeCloseTo(referenceResult[i], 5); // Using 5 decimal places for floating-point comparison
373
+ }
374
+ });
375
+ });
@@ -0,0 +1,91 @@
1
+ import { client } from "./setup";
2
+
3
+ describe("WebGPUConstants", () => {
4
+ it("GPUBufferUsage", async () => {
5
+ const result = await client.eval(() => {
6
+ return GPUBufferUsage;
7
+ });
8
+ expect(result).toEqual({
9
+ MAP_READ: 1,
10
+ MAP_WRITE: 2,
11
+ COPY_SRC: 4,
12
+ COPY_DST: 8,
13
+ INDEX: 16,
14
+ VERTEX: 32,
15
+ UNIFORM: 64,
16
+ STORAGE: 128,
17
+ INDIRECT: 256,
18
+ QUERY_RESOLVE: 512,
19
+ });
20
+ });
21
+ it("GPUColorWrite", async () => {
22
+ const result = await client.eval(() => {
23
+ return GPUColorWrite;
24
+ });
25
+ expect(result).toEqual({
26
+ RED: 1,
27
+ GREEN: 2,
28
+ BLUE: 4,
29
+ ALPHA: 8,
30
+ ALL: 15,
31
+ });
32
+ });
33
+ it("GPUMapMode", async () => {
34
+ const result = await client.eval(() => {
35
+ return GPUMapMode;
36
+ });
37
+ expect(result).toEqual({
38
+ READ: 1,
39
+ WRITE: 2,
40
+ });
41
+ });
42
+ it("GPUShaderStage", async () => {
43
+ const result = await client.eval(() => {
44
+ return GPUShaderStage;
45
+ });
46
+ expect(result).toEqual({
47
+ VERTEX: 1,
48
+ FRAGMENT: 2,
49
+ COMPUTE: 4,
50
+ });
51
+ });
52
+ it("GPUTextureUsage", async () => {
53
+ const result = await client.eval(() => {
54
+ return GPUTextureUsage;
55
+ });
56
+ expect(result).toEqual({
57
+ COPY_SRC: 1,
58
+ COPY_DST: 2,
59
+ TEXTURE_BINDING: 4,
60
+ STORAGE_BINDING: 8,
61
+ RENDER_ATTACHMENT: 16,
62
+ });
63
+ });
64
+ it("GPUAdapter", async () => {
65
+ const result = await client.eval(({ adapter, device, gpu }) => {
66
+ return [
67
+ gpu instanceof GPU,
68
+ adapter instanceof GPUAdapter,
69
+ device instanceof GPUDevice,
70
+ adapter instanceof GPUDevice,
71
+ device instanceof GPUAdapter,
72
+ ];
73
+ });
74
+ expect(result).toEqual([true, true, true, false, false]);
75
+ });
76
+ it("instanceof", async () => {
77
+ const result = await client.eval(
78
+ ({ device, shaders: { triangleVertWGSL } }) => {
79
+ const buffer = device.createBuffer({
80
+ size: 16,
81
+ usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
82
+ });
83
+ const module = device.createShaderModule({
84
+ code: triangleVertWGSL,
85
+ });
86
+ return [buffer instanceof GPUBuffer, module instanceof GPUShaderModule];
87
+ },
88
+ );
89
+ expect(result).toEqual([true, true]);
90
+ });
91
+ });
@@ -0,0 +1,35 @@
1
+ import { client } from "./setup";
2
+
3
+ describe("Device", () => {
4
+ it("request device (1)", async () => {
5
+ const result = await client.eval(({ adapter }) =>
6
+ adapter.requestDevice().then((device) => device !== null),
7
+ );
8
+ expect(result).toBe(true);
9
+ });
10
+
11
+ it("request device (2)", async () => {
12
+ const result = await client.eval(({ adapter }) =>
13
+ adapter.requestDevice(undefined).then((device) => device.label),
14
+ );
15
+ expect(result).toBe("");
16
+ });
17
+ it("request device (3)", async () => {
18
+ const result = await client.eval(({ adapter }) =>
19
+ adapter.requestDevice({ label: "MyGPU" }).then((device) => device.label),
20
+ );
21
+ expect(result).toBe("MyGPU");
22
+ });
23
+ it("destroy device (3)", async () => {
24
+ const result = await client.eval(({ adapter }) =>
25
+ adapter.requestDevice({ label: "MyGPU" }).then((device) => {
26
+ device.destroy();
27
+ return device.lost.then((r) => ({
28
+ reason: r.reason,
29
+ message: r.message,
30
+ }));
31
+ }),
32
+ );
33
+ expect(["unknown", "destroyed"].includes(result.reason)).toBe(true);
34
+ });
35
+ });