react-native-wgpu 0.1.5 → 0.1.7
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.
- package/README.md +43 -42
- package/android/cpp/AndroidPlatformContext.h +2 -1
- package/cpp/jsi/RNFJSIConverter.h +10 -0
- package/cpp/rnwgpu/SurfaceRegistry.h +1 -2
- package/cpp/rnwgpu/api/Canvas.h +3 -3
- package/cpp/rnwgpu/api/GPUBuffer.h +1 -1
- package/ios/WebGPUView.mm +14 -1
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/utils.js +2 -2
- package/lib/commonjs/utils.js.map +1 -1
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/utils.js +2 -2
- package/lib/module/utils.js.map +1 -1
- package/lib/typescript/lib/commonjs/utils.d.ts +1 -1
- package/lib/typescript/lib/commonjs/utils.d.ts.map +1 -1
- package/lib/typescript/lib/module/utils.d.ts +1 -1
- package/lib/typescript/lib/module/utils.d.ts.map +1 -1
- package/lib/typescript/src/Canvas.d.ts +3 -2
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/__tests__/ArrayBuffer.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/ArrayBuffer.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/Buffer.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/Buffer.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/ComputeShader.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/ComputeShader.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/Constants.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/Constants.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/Device.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/Device.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/ExternalTexture.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/ExternalTexture.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/GPU.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/GPU.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/ImageData.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/ImageData.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/Shaders.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/Shaders.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/Texture.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/Texture.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/DrawingContext.d.ts +12 -0
- package/lib/typescript/src/__tests__/components/DrawingContext.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/Wireframe/Shaders.d.ts +3 -0
- package/lib/typescript/src/__tests__/components/Wireframe/Shaders.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/Wireframe/models.d.ts +29 -0
- package/lib/typescript/src/__tests__/components/Wireframe/models.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/Wireframe/utils.d.ts +5 -0
- package/lib/typescript/src/__tests__/components/Wireframe/utils.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/cube.d.ts +7 -0
- package/lib/typescript/src/__tests__/components/cube.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/meshes/mesh.d.ts +22 -0
- package/lib/typescript/src/__tests__/components/meshes/mesh.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/meshes/sphere.d.ts +12 -0
- package/lib/typescript/src/__tests__/components/meshes/sphere.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/meshes/stanfordDragon.d.ts +7 -0
- package/lib/typescript/src/__tests__/components/meshes/stanfordDragon.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/meshes/stanfordDragonData.d.ts +6 -0
- package/lib/typescript/src/__tests__/components/meshes/stanfordDragonData.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/meshes/teapot.d.ts +6 -0
- package/lib/typescript/src/__tests__/components/meshes/teapot.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/meshes/utils.d.ts +10 -0
- package/lib/typescript/src/__tests__/components/meshes/utils.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/components/triangle.d.ts +3 -0
- package/lib/typescript/src/__tests__/components/triangle.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/config.d.ts +3 -0
- package/lib/typescript/src/__tests__/config.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/ABuffer.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/ABuffer.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/Blur.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/Blur.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/Cube.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/Cube.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/FractalCube.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/FractalCube.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/OcclusionQuery.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/OcclusionQuery.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/RenderBundles.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/RenderBundles.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/Triangle.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/Triangle.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/demos/Wireframe.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/demos/Wireframe.spec.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/globalSetup.d.ts +10 -0
- package/lib/typescript/src/__tests__/globalSetup.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/globalTeardown.d.ts +3 -0
- package/lib/typescript/src/__tests__/globalTeardown.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/setup.d.ts +62 -0
- package/lib/typescript/src/__tests__/setup.d.ts.map +1 -0
- package/lib/typescript/src/utils.d.ts +2 -1
- package/lib/typescript/src/utils.d.ts.map +1 -1
- package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
- package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
- package/libs/android/x86/libwebgpu_dawn.so +0 -0
- package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
- package/libs/{ios/libwebgpu_dawn.xcframework/ios-arm64 → apple/arm64_iphoneos}/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/arm64_iphonesimulator/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/arm64_xros/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/arm64_xrsimulator/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/libwebgpu_dawn.a +0 -0
- package/libs/{ios/arm64_iphoneos → apple/libwebgpu_dawn.xcframework/ios-arm64}/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn_macosx.xcframework/Info.plist +28 -0
- package/libs/apple/libwebgpu_dawn_macosx.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
- package/libs/{ios/libwebgpu_dawn_visionos.xcframework/xros-arm64_x86_64-simulator → apple}/libwebgpu_dawn_visionos.a +0 -0
- package/libs/{ios → apple}/libwebgpu_dawn_visionos.xcframework/Info.plist +2 -2
- package/libs/{ios/arm64_xros/libwebgpu_dawn_visionos.a → apple/libwebgpu_dawn_visionos.xcframework/xros-arm64/libwebgpu_dawn.a} +0 -0
- package/libs/{ios → apple/libwebgpu_dawn_visionos.xcframework/xros-arm64_x86_64-simulator}/libwebgpu_dawn_visionos.a +0 -0
- package/libs/apple/universal_macosx/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/x86_64_iphonesimulator/libwebgpu_dawn.a +0 -0
- package/libs/{ios → apple}/x86_64_xrsimulator/libwebgpu_dawn.a +0 -0
- package/package.json +23 -22
- package/react-native-wgpu.podspec +2 -2
- package/src/Canvas.tsx +4 -2
- package/src/__tests__/ArrayBuffer.spec.ts +76 -0
- package/src/__tests__/Buffer.spec.ts +357 -0
- package/src/__tests__/ComputeShader.spec.ts +375 -0
- package/src/__tests__/Constants.spec.ts +91 -0
- package/src/__tests__/Device.spec.ts +35 -0
- package/src/__tests__/ExternalTexture.spec.ts +284 -0
- package/src/__tests__/GPU.spec.ts +229 -0
- package/src/__tests__/ImageData.spec.ts +26 -0
- package/src/__tests__/Shaders.spec.ts +232 -0
- package/src/__tests__/Texture.spec.ts +191 -0
- package/src/__tests__/assets/Di-3d.png +0 -0
- package/src/__tests__/components/DrawingContext.ts +11 -0
- package/src/__tests__/components/Wireframe/Shaders.ts +138 -0
- package/src/__tests__/components/Wireframe/models.ts +113 -0
- package/src/__tests__/components/Wireframe/utils.ts +22 -0
- package/src/__tests__/components/cube.ts +51 -0
- package/src/__tests__/components/meshes/mesh.ts +96 -0
- package/src/__tests__/components/meshes/sphere.ts +103 -0
- package/src/__tests__/components/meshes/stanfordDragon.ts +44 -0
- package/src/__tests__/components/meshes/stanfordDragonData.ts +5 -0
- package/src/__tests__/components/meshes/teapot.ts +13 -0
- package/src/__tests__/components/meshes/utils.ts +235 -0
- package/src/__tests__/components/triangle.ts +17 -0
- package/src/__tests__/config.ts +2 -0
- package/src/__tests__/demos/ABuffer.spec.ts +885 -0
- package/src/__tests__/demos/Blur.spec.ts +397 -0
- package/src/__tests__/demos/Cube.spec.ts +925 -0
- package/src/__tests__/demos/FractalCube.spec.ts +239 -0
- package/src/__tests__/demos/OcclusionQuery.spec.ts +376 -0
- package/src/__tests__/demos/RenderBundles.spec.ts +579 -0
- package/src/__tests__/demos/Triangle.spec.ts +248 -0
- package/src/__tests__/demos/Wireframe.spec.ts +190 -0
- package/src/__tests__/globalSetup.ts +54 -0
- package/src/__tests__/globalTeardown.ts +11 -0
- package/src/__tests__/setup.ts +409 -0
- package/src/__tests__/snapshots/abuffer.png +0 -0
- package/src/__tests__/snapshots/asteroid.png +0 -0
- package/src/__tests__/snapshots/blur.png +0 -0
- package/src/__tests__/snapshots/buffer.png +0 -0
- package/src/__tests__/snapshots/constant-triangle.png +0 -0
- package/src/__tests__/snapshots/cube.png +0 -0
- package/src/__tests__/snapshots/f.png +0 -0
- package/src/__tests__/snapshots/f2.png +0 -0
- package/src/__tests__/snapshots/fractal-cubes.png +0 -0
- package/src/__tests__/snapshots/instanced-cubes.png +0 -0
- package/src/__tests__/snapshots/occlusion-query.png +0 -0
- package/src/__tests__/snapshots/ref.png +0 -0
- package/src/__tests__/snapshots/texture.png +0 -0
- package/src/__tests__/snapshots/textured-cube.png +0 -0
- package/src/__tests__/snapshots/triangle-msaa.png +0 -0
- package/src/__tests__/snapshots/triangle.png +0 -0
- package/src/__tests__/snapshots/two-cube.png +0 -0
- package/src/utils.ts +3 -1
- package/android/gradle.properties +0 -5
- package/libs/ios/libwebgpu_dawn_visionos.xcframework/xros-arm64/libwebgpu_dawn_visionos.a +0 -0
- 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
|
+
});
|