react-native-wgpu 0.1.4 → 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 (167) 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/libwebgpu_dawn.xcframework/xros-arm64 → 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/apple/libwebgpu_dawn.xcframework/Info.plist +44 -0
  100. package/libs/{ios/arm64_iphoneos → apple/libwebgpu_dawn.xcframework/ios-arm64}/libwebgpu_dawn.a +0 -0
  101. package/libs/{ios → apple}/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
  102. package/libs/apple/libwebgpu_dawn_macosx.xcframework/Info.plist +28 -0
  103. package/libs/apple/libwebgpu_dawn_macosx.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
  104. package/libs/{ios/libwebgpu_dawn.xcframework/xros-arm64_x86_64-simulator → apple}/libwebgpu_dawn_visionos.a +0 -0
  105. package/libs/{ios/libwebgpu_dawn.xcframework → apple/libwebgpu_dawn_visionos.xcframework}/Info.plist +0 -31
  106. package/libs/{ios/arm64_xros → apple/libwebgpu_dawn_visionos.xcframework/xros-arm64}/libwebgpu_dawn.a +0 -0
  107. package/libs/{ios → apple/libwebgpu_dawn_visionos.xcframework/xros-arm64_x86_64-simulator}/libwebgpu_dawn_visionos.a +0 -0
  108. package/libs/apple/universal_macosx/libwebgpu_dawn.a +0 -0
  109. package/libs/{ios → apple}/x86_64_iphonesimulator/libwebgpu_dawn.a +0 -0
  110. package/libs/{ios → apple}/x86_64_xrsimulator/libwebgpu_dawn.a +0 -0
  111. package/package.json +24 -24
  112. package/src/Canvas.tsx +4 -2
  113. package/src/__tests__/ArrayBuffer.spec.ts +76 -0
  114. package/src/__tests__/Buffer.spec.ts +357 -0
  115. package/src/__tests__/ComputeShader.spec.ts +375 -0
  116. package/src/__tests__/Constants.spec.ts +91 -0
  117. package/src/__tests__/Device.spec.ts +35 -0
  118. package/src/__tests__/ExternalTexture.spec.ts +284 -0
  119. package/src/__tests__/GPU.spec.ts +229 -0
  120. package/src/__tests__/ImageData.spec.ts +26 -0
  121. package/src/__tests__/Shaders.spec.ts +232 -0
  122. package/src/__tests__/Texture.spec.ts +191 -0
  123. package/src/__tests__/assets/Di-3d.png +0 -0
  124. package/src/__tests__/components/DrawingContext.ts +11 -0
  125. package/src/__tests__/components/Wireframe/Shaders.ts +138 -0
  126. package/src/__tests__/components/Wireframe/models.ts +113 -0
  127. package/src/__tests__/components/Wireframe/utils.ts +22 -0
  128. package/src/__tests__/components/cube.ts +51 -0
  129. package/src/__tests__/components/meshes/mesh.ts +96 -0
  130. package/src/__tests__/components/meshes/sphere.ts +103 -0
  131. package/src/__tests__/components/meshes/stanfordDragon.ts +44 -0
  132. package/src/__tests__/components/meshes/stanfordDragonData.ts +5 -0
  133. package/src/__tests__/components/meshes/teapot.ts +13 -0
  134. package/src/__tests__/components/meshes/utils.ts +235 -0
  135. package/src/__tests__/components/triangle.ts +17 -0
  136. package/src/__tests__/config.ts +2 -0
  137. package/src/__tests__/demos/ABuffer.spec.ts +885 -0
  138. package/src/__tests__/demos/Blur.spec.ts +397 -0
  139. package/src/__tests__/demos/Cube.spec.ts +925 -0
  140. package/src/__tests__/demos/FractalCube.spec.ts +239 -0
  141. package/src/__tests__/demos/OcclusionQuery.spec.ts +376 -0
  142. package/src/__tests__/demos/RenderBundles.spec.ts +579 -0
  143. package/src/__tests__/demos/Triangle.spec.ts +248 -0
  144. package/src/__tests__/demos/Wireframe.spec.ts +190 -0
  145. package/src/__tests__/globalSetup.ts +54 -0
  146. package/src/__tests__/globalTeardown.ts +11 -0
  147. package/src/__tests__/setup.ts +409 -0
  148. package/src/__tests__/snapshots/abuffer.png +0 -0
  149. package/src/__tests__/snapshots/asteroid.png +0 -0
  150. package/src/__tests__/snapshots/blur.png +0 -0
  151. package/src/__tests__/snapshots/buffer.png +0 -0
  152. package/src/__tests__/snapshots/constant-triangle.png +0 -0
  153. package/src/__tests__/snapshots/cube.png +0 -0
  154. package/src/__tests__/snapshots/f.png +0 -0
  155. package/src/__tests__/snapshots/f2.png +0 -0
  156. package/src/__tests__/snapshots/fractal-cubes.png +0 -0
  157. package/src/__tests__/snapshots/instanced-cubes.png +0 -0
  158. package/src/__tests__/snapshots/occlusion-query.png +0 -0
  159. package/src/__tests__/snapshots/ref.png +0 -0
  160. package/src/__tests__/snapshots/texture.png +0 -0
  161. package/src/__tests__/snapshots/textured-cube.png +0 -0
  162. package/src/__tests__/snapshots/triangle-msaa.png +0 -0
  163. package/src/__tests__/snapshots/triangle.png +0 -0
  164. package/src/__tests__/snapshots/two-cube.png +0 -0
  165. package/src/utils.ts +3 -1
  166. package/android/gradle.properties +0 -5
  167. package/react-native-wgpu.podspec +0 -53
@@ -0,0 +1,925 @@
1
+ import type { Mat4 } from "wgpu-matrix";
2
+
3
+ import { checkImage, client, encodeImage } from "../setup";
4
+
5
+ export const basicVert = /*wgsl*/ `struct Uniforms {
6
+ modelViewProjectionMatrix : mat4x4f,
7
+ }
8
+ @binding(0) @group(0) var<uniform> uniforms : Uniforms;
9
+
10
+ struct VertexOutput {
11
+ @builtin(position) Position : vec4f,
12
+ @location(0) fragUV : vec2f,
13
+ @location(1) fragPosition: vec4f,
14
+ }
15
+
16
+ @vertex
17
+ fn main(
18
+ @location(0) position : vec4f,
19
+ @location(1) uv : vec2f
20
+ ) -> VertexOutput {
21
+ var output : VertexOutput;
22
+ output.Position = uniforms.modelViewProjectionMatrix * position;
23
+ output.fragUV = uv;
24
+ output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
25
+ return output;
26
+ }
27
+ `;
28
+
29
+ const vertexPositionColor = /*wgsl*/ `@fragment
30
+ fn main(
31
+ @location(0) fragUV: vec2f,
32
+ @location(1) fragPosition: vec4f
33
+ ) -> @location(0) vec4f {
34
+ return fragPosition;
35
+ }`;
36
+
37
+ const sampleTextureMixColor = /*wgsl*/ `@group(0) @binding(1) var mySampler: sampler;
38
+ @group(0) @binding(2) var myTexture: texture_2d<f32>;
39
+
40
+ @fragment
41
+ fn main(
42
+ @location(0) fragUV: vec2f,
43
+ @location(1) fragPosition: vec4f
44
+ ) -> @location(0) vec4f {
45
+ return textureSample(myTexture, mySampler, fragUV) * fragPosition;
46
+ }
47
+ `;
48
+
49
+ const instancedVert = /*wgsl*/ `struct Uniforms {
50
+ modelViewProjectionMatrix : array<mat4x4f, 16>,
51
+ }
52
+
53
+ @binding(0) @group(0) var<uniform> uniforms : Uniforms;
54
+
55
+ struct VertexOutput {
56
+ @builtin(position) Position : vec4f,
57
+ @location(0) fragUV : vec2f,
58
+ @location(1) fragPosition: vec4f,
59
+ }
60
+
61
+ @vertex
62
+ fn main(
63
+ @builtin(instance_index) instanceIdx : u32,
64
+ @location(0) position : vec4f,
65
+ @location(1) uv : vec2f
66
+ ) -> VertexOutput {
67
+ var output : VertexOutput;
68
+ output.Position = uniforms.modelViewProjectionMatrix[instanceIdx] * position;
69
+ output.fragUV = uv;
70
+ output.fragPosition = 0.5 * (position + vec4(1.0));
71
+ return output;
72
+ }
73
+ `;
74
+
75
+ describe("Cube", () => {
76
+ it("Rotating cube", async () => {
77
+ const result = await client.eval(
78
+ ({
79
+ gpu,
80
+ device,
81
+ assets: { cubeVertexArray },
82
+ ctx,
83
+ mat4,
84
+ vec3,
85
+ basicVertWGSL,
86
+ vertexPositionColorWGSL,
87
+ }) => {
88
+ const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
89
+ const cubePositionOffset = 0;
90
+ const cubeUVOffset = 4 * 8;
91
+ const cubeVertexCount = 36;
92
+
93
+ const presentationFormat = gpu.getPreferredCanvasFormat();
94
+ // Create a vertex buffer from the cube data.
95
+ const verticesBuffer = device.createBuffer({
96
+ size: cubeVertexArray.byteLength,
97
+ usage: GPUBufferUsage.VERTEX,
98
+ mappedAtCreation: true,
99
+ });
100
+ new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
101
+ verticesBuffer.unmap();
102
+
103
+ const pipeline = device.createRenderPipeline({
104
+ label: "cube render pipeline",
105
+ layout: "auto",
106
+ vertex: {
107
+ module: device.createShaderModule({
108
+ code: basicVertWGSL,
109
+ }),
110
+ buffers: [
111
+ {
112
+ arrayStride: cubeVertexSize,
113
+ attributes: [
114
+ {
115
+ // position
116
+ shaderLocation: 0,
117
+ offset: cubePositionOffset,
118
+ format: "float32x4",
119
+ },
120
+ {
121
+ // uv
122
+ shaderLocation: 1,
123
+ offset: cubeUVOffset,
124
+ format: "float32x2",
125
+ },
126
+ ],
127
+ },
128
+ ],
129
+ },
130
+ fragment: {
131
+ module: device.createShaderModule({
132
+ code: vertexPositionColorWGSL,
133
+ }),
134
+ targets: [
135
+ {
136
+ format: presentationFormat,
137
+ },
138
+ ],
139
+ },
140
+ primitive: {
141
+ topology: "triangle-list",
142
+
143
+ // Backface culling since the cube is solid piece of geometry.
144
+ // Faces pointing away from the camera will be occluded by faces
145
+ // pointing toward the camera.
146
+ cullMode: "back",
147
+ },
148
+
149
+ // Enable depth testing so that the fragment closest to the camera
150
+ // is rendered in front.
151
+ depthStencil: {
152
+ depthWriteEnabled: true,
153
+ depthCompare: "less",
154
+ format: "depth24plus",
155
+ },
156
+ });
157
+
158
+ const depthTexture = device.createTexture({
159
+ size: [ctx.width, ctx.height],
160
+ format: "depth24plus",
161
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
162
+ });
163
+
164
+ const uniformBufferSize = 4 * 16; // 4x4 matrix
165
+ const uniformBuffer = device.createBuffer({
166
+ size: uniformBufferSize,
167
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
168
+ });
169
+
170
+ const uniformBindGroup = device.createBindGroup({
171
+ layout: pipeline.getBindGroupLayout(0),
172
+ entries: [
173
+ {
174
+ binding: 0,
175
+ resource: {
176
+ buffer: uniformBuffer,
177
+ },
178
+ },
179
+ ],
180
+ });
181
+
182
+ const renderPassDescriptor: GPURenderPassDescriptor = {
183
+ colorAttachments: [
184
+ {
185
+ view: ctx.getCurrentTexture().createView(), // Assigned later
186
+ clearValue: [0.5, 0.5, 0.5, 1.0],
187
+ loadOp: "clear",
188
+ storeOp: "store",
189
+ },
190
+ ],
191
+ depthStencilAttachment: {
192
+ view: depthTexture.createView(),
193
+ depthClearValue: 1.0,
194
+ depthLoadOp: "clear",
195
+ depthStoreOp: "store",
196
+ },
197
+ };
198
+
199
+ const aspect = ctx.width / ctx.height;
200
+ const projectionMatrix = mat4.perspective(
201
+ (2 * Math.PI) / 5,
202
+ aspect,
203
+ 1,
204
+ 100.0,
205
+ );
206
+ const modelViewProjectionMatrix = mat4.create();
207
+
208
+ function getTransformationMatrix() {
209
+ const viewMatrix = mat4.identity();
210
+ mat4.translate(viewMatrix, vec3.fromValues(0, 0, -4), viewMatrix);
211
+ const now = 1721677648;
212
+ mat4.rotate(
213
+ viewMatrix,
214
+ vec3.fromValues(Math.sin(now), Math.cos(now), 0),
215
+ 1,
216
+ viewMatrix,
217
+ );
218
+
219
+ mat4.multiply(
220
+ projectionMatrix,
221
+ viewMatrix,
222
+ modelViewProjectionMatrix,
223
+ );
224
+
225
+ return modelViewProjectionMatrix;
226
+ }
227
+
228
+ const transformationMatrix = getTransformationMatrix();
229
+ device.queue.writeBuffer(
230
+ uniformBuffer,
231
+ 0,
232
+ transformationMatrix.buffer,
233
+ transformationMatrix.byteOffset,
234
+ transformationMatrix.byteLength,
235
+ );
236
+
237
+ const commandEncoder = device.createCommandEncoder();
238
+ const passEncoder =
239
+ commandEncoder.beginRenderPass(renderPassDescriptor);
240
+ passEncoder.setPipeline(pipeline);
241
+ passEncoder.setBindGroup(0, uniformBindGroup);
242
+ passEncoder.setVertexBuffer(0, verticesBuffer);
243
+ passEncoder.draw(cubeVertexCount);
244
+ passEncoder.end();
245
+ device.queue.submit([commandEncoder.finish()]);
246
+ return ctx.getImageData();
247
+ },
248
+ {
249
+ basicVertWGSL: basicVert,
250
+ vertexPositionColorWGSL: vertexPositionColor,
251
+ },
252
+ );
253
+ const image = encodeImage(result);
254
+ checkImage(image, "snapshots/cube.png");
255
+ });
256
+ it("Two cubes", async () => {
257
+ const result = await client.eval(
258
+ ({
259
+ gpu,
260
+ device,
261
+ assets: { cubeVertexArray },
262
+ ctx,
263
+ mat4,
264
+ vec3,
265
+ basicVertWGSL,
266
+ vertexPositionColorWGSL,
267
+ }) => {
268
+ const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
269
+ const cubePositionOffset = 0;
270
+ const cubeUVOffset = 4 * 8;
271
+ const cubeVertexCount = 36;
272
+ const presentationFormat = gpu.getPreferredCanvasFormat();
273
+ // Create a vertex buffer from the cube data.
274
+ const verticesBuffer = device.createBuffer({
275
+ size: cubeVertexArray.byteLength,
276
+ usage: GPUBufferUsage.VERTEX,
277
+ mappedAtCreation: true,
278
+ });
279
+ new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
280
+ verticesBuffer.unmap();
281
+
282
+ const pipeline = device.createRenderPipeline({
283
+ layout: "auto",
284
+ vertex: {
285
+ module: device.createShaderModule({
286
+ code: basicVertWGSL,
287
+ }),
288
+ buffers: [
289
+ {
290
+ arrayStride: cubeVertexSize,
291
+ attributes: [
292
+ {
293
+ // position
294
+ shaderLocation: 0,
295
+ offset: cubePositionOffset,
296
+ format: "float32x4",
297
+ },
298
+ {
299
+ // uv
300
+ shaderLocation: 1,
301
+ offset: cubeUVOffset,
302
+ format: "float32x2",
303
+ },
304
+ ],
305
+ },
306
+ ],
307
+ },
308
+ fragment: {
309
+ module: device.createShaderModule({
310
+ code: vertexPositionColorWGSL,
311
+ }),
312
+ targets: [
313
+ {
314
+ format: presentationFormat,
315
+ },
316
+ ],
317
+ },
318
+ primitive: {
319
+ topology: "triangle-list",
320
+
321
+ // Backface culling since the cube is solid piece of geometry.
322
+ // Faces pointing away from the camera will be occluded by faces
323
+ // pointing toward the camera.
324
+ cullMode: "back",
325
+ },
326
+
327
+ // Enable depth testing so that the fragment closest to the camera
328
+ // is rendered in front.
329
+ depthStencil: {
330
+ depthWriteEnabled: true,
331
+ depthCompare: "less",
332
+ format: "depth24plus",
333
+ },
334
+ });
335
+
336
+ const depthTexture = device.createTexture({
337
+ size: [ctx.width, ctx.height],
338
+ format: "depth24plus",
339
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
340
+ });
341
+
342
+ const matrixSize = 4 * 16; // 4x4 matrix
343
+ const offset = 256; // uniformBindGroup offset must be 256-byte aligned
344
+ const uniformBufferSize = offset + matrixSize;
345
+
346
+ const uniformBuffer = device.createBuffer({
347
+ size: uniformBufferSize,
348
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
349
+ });
350
+
351
+ const uniformBindGroup1 = device.createBindGroup({
352
+ layout: pipeline.getBindGroupLayout(0),
353
+ entries: [
354
+ {
355
+ binding: 0,
356
+ resource: {
357
+ buffer: uniformBuffer,
358
+ offset: 0,
359
+ size: matrixSize,
360
+ },
361
+ },
362
+ ],
363
+ });
364
+
365
+ const uniformBindGroup2 = device.createBindGroup({
366
+ layout: pipeline.getBindGroupLayout(0),
367
+ entries: [
368
+ {
369
+ binding: 0,
370
+ resource: {
371
+ buffer: uniformBuffer,
372
+ offset: offset,
373
+ size: matrixSize,
374
+ },
375
+ },
376
+ ],
377
+ });
378
+
379
+ const renderPassDescriptor: GPURenderPassDescriptor = {
380
+ colorAttachments: [
381
+ {
382
+ view: ctx.getCurrentTexture().createView(),
383
+
384
+ clearValue: [0.5, 0.5, 0.5, 1.0],
385
+ loadOp: "clear",
386
+ storeOp: "store",
387
+ },
388
+ ],
389
+ depthStencilAttachment: {
390
+ view: depthTexture.createView(),
391
+
392
+ depthClearValue: 1.0,
393
+ depthLoadOp: "clear",
394
+ depthStoreOp: "store",
395
+ },
396
+ };
397
+
398
+ const aspect = ctx.width / ctx.height;
399
+ const projectionMatrix = mat4.perspective(
400
+ (2 * Math.PI) / 5,
401
+ aspect,
402
+ 1,
403
+ 100.0,
404
+ );
405
+
406
+ const modelMatrix1 = mat4.translation(vec3.create(-2, 0, 0));
407
+ const modelMatrix2 = mat4.translation(vec3.create(2, 0, 0));
408
+ const modelViewProjectionMatrix1 = mat4.create();
409
+ const modelViewProjectionMatrix2 = mat4.create();
410
+ const viewMatrix = mat4.translation(vec3.fromValues(0, 0, -7));
411
+
412
+ const tmpMat41 = mat4.create();
413
+ const tmpMat42 = mat4.create();
414
+
415
+ function updateTransformationMatrix() {
416
+ const now = 1721677648;
417
+
418
+ mat4.rotate(
419
+ modelMatrix1,
420
+ vec3.fromValues(Math.sin(now), Math.cos(now), 0),
421
+ 1,
422
+ tmpMat41,
423
+ );
424
+ mat4.rotate(
425
+ modelMatrix2,
426
+ vec3.fromValues(Math.cos(now), Math.sin(now), 0),
427
+ 1,
428
+ tmpMat42,
429
+ );
430
+
431
+ mat4.multiply(viewMatrix, tmpMat41, modelViewProjectionMatrix1);
432
+ mat4.multiply(
433
+ projectionMatrix,
434
+ modelViewProjectionMatrix1,
435
+ modelViewProjectionMatrix1,
436
+ );
437
+ mat4.multiply(viewMatrix, tmpMat42, modelViewProjectionMatrix2);
438
+ mat4.multiply(
439
+ projectionMatrix,
440
+ modelViewProjectionMatrix2,
441
+ modelViewProjectionMatrix2,
442
+ );
443
+ }
444
+
445
+ updateTransformationMatrix();
446
+ device.queue.writeBuffer(
447
+ uniformBuffer,
448
+ 0,
449
+ modelViewProjectionMatrix1.buffer,
450
+ modelViewProjectionMatrix1.byteOffset,
451
+ modelViewProjectionMatrix1.byteLength,
452
+ );
453
+ device.queue.writeBuffer(
454
+ uniformBuffer,
455
+ offset,
456
+ modelViewProjectionMatrix2.buffer,
457
+ modelViewProjectionMatrix2.byteOffset,
458
+ modelViewProjectionMatrix2.byteLength,
459
+ );
460
+
461
+ const commandEncoder = device.createCommandEncoder();
462
+ const passEncoder =
463
+ commandEncoder.beginRenderPass(renderPassDescriptor);
464
+ passEncoder.setPipeline(pipeline);
465
+ passEncoder.setVertexBuffer(0, verticesBuffer);
466
+
467
+ // Bind the bind group (with the transformation matrix) for
468
+ // each cube, and draw.
469
+ passEncoder.setBindGroup(0, uniformBindGroup1);
470
+ passEncoder.draw(cubeVertexCount);
471
+
472
+ passEncoder.setBindGroup(0, uniformBindGroup2);
473
+ passEncoder.draw(cubeVertexCount);
474
+
475
+ passEncoder.end();
476
+ device.queue.submit([commandEncoder.finish()]);
477
+
478
+ return ctx.getImageData();
479
+ },
480
+ {
481
+ basicVertWGSL: basicVert,
482
+ vertexPositionColorWGSL: vertexPositionColor,
483
+ },
484
+ );
485
+ const image = encodeImage(result);
486
+ checkImage(image, "snapshots/two-cube.png");
487
+ });
488
+ it("Textured cube", async () => {
489
+ const result = await client.eval(
490
+ ({
491
+ gpu,
492
+ device,
493
+ assets: { cubeVertexArray, di3D: imageBitmap },
494
+ ctx,
495
+ mat4,
496
+ vec3,
497
+ basicVertWGSL,
498
+ sampleTextureMixColorWGSL,
499
+ }) => {
500
+ const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
501
+ const cubePositionOffset = 0;
502
+ const cubeUVOffset = 4 * 8;
503
+ const cubeVertexCount = 36;
504
+ const presentationFormat = gpu.getPreferredCanvasFormat();
505
+
506
+ // Create a vertex buffer from the cube data.
507
+ const verticesBuffer = device.createBuffer({
508
+ size: cubeVertexArray.byteLength,
509
+ usage: GPUBufferUsage.VERTEX,
510
+ mappedAtCreation: true,
511
+ });
512
+ new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
513
+ verticesBuffer.unmap();
514
+
515
+ const pipeline = device.createRenderPipeline({
516
+ layout: "auto",
517
+ vertex: {
518
+ module: device.createShaderModule({
519
+ code: basicVertWGSL,
520
+ }),
521
+ buffers: [
522
+ {
523
+ arrayStride: cubeVertexSize,
524
+ attributes: [
525
+ {
526
+ // position
527
+ shaderLocation: 0,
528
+ offset: cubePositionOffset,
529
+ format: "float32x4",
530
+ },
531
+ {
532
+ // uv
533
+ shaderLocation: 1,
534
+ offset: cubeUVOffset,
535
+ format: "float32x2",
536
+ },
537
+ ],
538
+ },
539
+ ],
540
+ },
541
+ fragment: {
542
+ module: device.createShaderModule({
543
+ code: sampleTextureMixColorWGSL,
544
+ }),
545
+ targets: [
546
+ {
547
+ format: presentationFormat,
548
+ },
549
+ ],
550
+ },
551
+ primitive: {
552
+ topology: "triangle-list",
553
+
554
+ // Backface culling since the cube is solid piece of geometry.
555
+ // Faces pointing away from the camera will be occluded by faces
556
+ // pointing toward the camera.
557
+ cullMode: "back",
558
+ },
559
+
560
+ // Enable depth testing so that the fragment closest to the camera
561
+ // is rendered in front.
562
+ depthStencil: {
563
+ depthWriteEnabled: true,
564
+ depthCompare: "less",
565
+ format: "depth24plus",
566
+ },
567
+ });
568
+
569
+ const depthTexture = device.createTexture({
570
+ size: [ctx.width, ctx.height],
571
+ format: "depth24plus",
572
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
573
+ });
574
+
575
+ const uniformBufferSize = 4 * 16; // 4x4 matrix
576
+ const uniformBuffer = device.createBuffer({
577
+ size: uniformBufferSize,
578
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
579
+ });
580
+
581
+ const cubeTexture = device.createTexture({
582
+ size: [imageBitmap.width, imageBitmap.height, 1],
583
+ format: "rgba8unorm",
584
+ usage:
585
+ GPUTextureUsage.TEXTURE_BINDING |
586
+ GPUTextureUsage.COPY_DST |
587
+ GPUTextureUsage.RENDER_ATTACHMENT,
588
+ });
589
+
590
+ device.queue.copyExternalImageToTexture(
591
+ { source: imageBitmap },
592
+ { texture: cubeTexture },
593
+ [imageBitmap.width, imageBitmap.height],
594
+ );
595
+
596
+ // Create a sampler with linear filtering for smooth interpolation.
597
+ const sampler = device.createSampler({
598
+ magFilter: "linear",
599
+ minFilter: "linear",
600
+ });
601
+
602
+ const uniformBindGroup = device.createBindGroup({
603
+ layout: pipeline.getBindGroupLayout(0),
604
+ entries: [
605
+ {
606
+ binding: 0,
607
+ resource: {
608
+ buffer: uniformBuffer,
609
+ },
610
+ },
611
+ {
612
+ binding: 1,
613
+ resource: sampler,
614
+ },
615
+ {
616
+ binding: 2,
617
+ resource: cubeTexture.createView(),
618
+ },
619
+ ],
620
+ });
621
+
622
+ const renderPassDescriptor: GPURenderPassDescriptor = {
623
+ colorAttachments: [
624
+ {
625
+ view: ctx.getCurrentTexture().createView(), // Assigned later
626
+
627
+ clearValue: [0.5, 0.5, 0.5, 1.0],
628
+ loadOp: "clear",
629
+ storeOp: "store",
630
+ },
631
+ ],
632
+ depthStencilAttachment: {
633
+ view: depthTexture.createView(),
634
+
635
+ depthClearValue: 1.0,
636
+ depthLoadOp: "clear",
637
+ depthStoreOp: "store",
638
+ },
639
+ };
640
+
641
+ const aspect = ctx.width / ctx.height;
642
+ const projectionMatrix = mat4.perspective(
643
+ (2 * Math.PI) / 5,
644
+ aspect,
645
+ 1,
646
+ 100.0,
647
+ );
648
+ const modelViewProjectionMatrix = mat4.create();
649
+
650
+ function getTransformationMatrix() {
651
+ const viewMatrix = mat4.identity();
652
+ mat4.translate(viewMatrix, vec3.fromValues(0, 0, -4), viewMatrix);
653
+ const now = 1721677648;
654
+ mat4.rotate(
655
+ viewMatrix,
656
+ vec3.fromValues(Math.sin(now), Math.cos(now), 0),
657
+ 1,
658
+ viewMatrix,
659
+ );
660
+
661
+ mat4.multiply(
662
+ projectionMatrix,
663
+ viewMatrix,
664
+ modelViewProjectionMatrix,
665
+ );
666
+
667
+ return modelViewProjectionMatrix;
668
+ }
669
+
670
+ const transformationMatrix = getTransformationMatrix();
671
+ device.queue.writeBuffer(
672
+ uniformBuffer,
673
+ 0,
674
+ transformationMatrix.buffer,
675
+ transformationMatrix.byteOffset,
676
+ transformationMatrix.byteLength,
677
+ );
678
+
679
+ const commandEncoder = device.createCommandEncoder();
680
+ const passEncoder =
681
+ commandEncoder.beginRenderPass(renderPassDescriptor);
682
+ passEncoder.setPipeline(pipeline);
683
+ passEncoder.setBindGroup(0, uniformBindGroup);
684
+ passEncoder.setVertexBuffer(0, verticesBuffer);
685
+ passEncoder.draw(cubeVertexCount);
686
+ passEncoder.end();
687
+ device.queue.submit([commandEncoder.finish()]);
688
+
689
+ return ctx.getImageData();
690
+ },
691
+ {
692
+ basicVertWGSL: basicVert,
693
+ vertexPositionColorWGSL: vertexPositionColor,
694
+ sampleTextureMixColorWGSL: sampleTextureMixColor,
695
+ },
696
+ );
697
+ const image = encodeImage(result);
698
+ checkImage(image, "snapshots/textured-cube.png");
699
+ });
700
+ it("Instanced cubes", async () => {
701
+ const result = await client.eval(
702
+ ({
703
+ gpu,
704
+ device,
705
+ assets: { cubeVertexArray },
706
+ ctx,
707
+ mat4,
708
+ vec3,
709
+ vertexPositionColorWGSL,
710
+ instancedVertWGSL,
711
+ }) => {
712
+ const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
713
+ const cubePositionOffset = 0;
714
+ const cubeUVOffset = 4 * 8;
715
+ const cubeVertexCount = 36;
716
+ const presentationFormat = gpu.getPreferredCanvasFormat();
717
+ // Create a vertex buffer from the cube data.
718
+ const verticesBuffer = device.createBuffer({
719
+ size: cubeVertexArray.byteLength,
720
+ usage: GPUBufferUsage.VERTEX,
721
+ mappedAtCreation: true,
722
+ });
723
+ new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
724
+ verticesBuffer.unmap();
725
+
726
+ const pipeline = device.createRenderPipeline({
727
+ layout: "auto",
728
+ vertex: {
729
+ module: device.createShaderModule({
730
+ code: instancedVertWGSL,
731
+ }),
732
+ buffers: [
733
+ {
734
+ arrayStride: cubeVertexSize,
735
+ attributes: [
736
+ {
737
+ // position
738
+ shaderLocation: 0,
739
+ offset: cubePositionOffset,
740
+ format: "float32x4",
741
+ },
742
+ {
743
+ // uv
744
+ shaderLocation: 1,
745
+ offset: cubeUVOffset,
746
+ format: "float32x2",
747
+ },
748
+ ],
749
+ },
750
+ ],
751
+ },
752
+ fragment: {
753
+ module: device.createShaderModule({
754
+ code: vertexPositionColorWGSL,
755
+ }),
756
+ targets: [
757
+ {
758
+ format: presentationFormat,
759
+ },
760
+ ],
761
+ },
762
+ primitive: {
763
+ topology: "triangle-list",
764
+
765
+ // Backface culling since the cube is solid piece of geometry.
766
+ // Faces pointing away from the camera will be occluded by faces
767
+ // pointing toward the camera.
768
+ cullMode: "back",
769
+ },
770
+
771
+ // Enable depth testing so that the fragment closest to the camera
772
+ // is rendered in front.
773
+ depthStencil: {
774
+ depthWriteEnabled: true,
775
+ depthCompare: "less",
776
+ format: "depth24plus",
777
+ },
778
+ });
779
+
780
+ const depthTexture = device.createTexture({
781
+ size: [ctx.width, ctx.height],
782
+ format: "depth24plus",
783
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
784
+ });
785
+
786
+ const xCount = 4;
787
+ const yCount = 4;
788
+ const numInstances = xCount * yCount;
789
+ const matrixFloatCount = 16; // 4x4 matrix
790
+ const matrixSize = 4 * matrixFloatCount;
791
+ const uniformBufferSize = numInstances * matrixSize;
792
+
793
+ // Allocate a buffer large enough to hold transforms for every
794
+ // instance.
795
+ const uniformBuffer = device.createBuffer({
796
+ size: uniformBufferSize,
797
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
798
+ });
799
+
800
+ const uniformBindGroup = device.createBindGroup({
801
+ layout: pipeline.getBindGroupLayout(0),
802
+ entries: [
803
+ {
804
+ binding: 0,
805
+ resource: {
806
+ buffer: uniformBuffer,
807
+ },
808
+ },
809
+ ],
810
+ });
811
+
812
+ const aspect = ctx.width / ctx.height;
813
+ const projectionMatrix = mat4.perspective(
814
+ (2 * Math.PI) / 5,
815
+ aspect,
816
+ 1,
817
+ 100.0,
818
+ );
819
+
820
+ const modelMatrices = new Array<Mat4>(numInstances);
821
+ const mvpMatricesData = new Float32Array(
822
+ matrixFloatCount * numInstances,
823
+ );
824
+
825
+ const step = 4.0;
826
+
827
+ // Initialize the matrix data for every instance.
828
+ let m = 0;
829
+ for (let x = 0; x < xCount; x++) {
830
+ for (let y = 0; y < yCount; y++) {
831
+ modelMatrices[m] = mat4.translation(
832
+ vec3.fromValues(
833
+ step * (x - xCount / 2 + 0.5),
834
+ step * (y - yCount / 2 + 0.5),
835
+ 0,
836
+ ),
837
+ );
838
+ m++;
839
+ }
840
+ }
841
+
842
+ const viewMatrix = mat4.translation(vec3.fromValues(0, 0, -12));
843
+
844
+ const tmpMat4 = mat4.create();
845
+
846
+ // Update the transformation matrix data for each instance.
847
+ function updateTransformationMatrix() {
848
+ const now = 1721677648;
849
+
850
+ // eslint-disable-next-line @typescript-eslint/no-shadow
851
+ let m = 0,
852
+ i = 0;
853
+ for (let x = 0; x < xCount; x++) {
854
+ for (let y = 0; y < yCount; y++) {
855
+ mat4.rotate(
856
+ modelMatrices[i],
857
+ vec3.fromValues(
858
+ Math.sin((x + 0.5) * now),
859
+ Math.cos((y + 0.5) * now),
860
+ 0,
861
+ ),
862
+ 1,
863
+ tmpMat4,
864
+ );
865
+
866
+ mat4.multiply(viewMatrix, tmpMat4, tmpMat4);
867
+ mat4.multiply(projectionMatrix, tmpMat4, tmpMat4);
868
+
869
+ mvpMatricesData.set(tmpMat4, m);
870
+
871
+ i++;
872
+ m += matrixFloatCount;
873
+ }
874
+ }
875
+ }
876
+
877
+ const renderPassDescriptor: GPURenderPassDescriptor = {
878
+ colorAttachments: [
879
+ {
880
+ view: ctx.getCurrentTexture().createView(), // Assigned later
881
+
882
+ clearValue: [0.5, 0.5, 0.5, 1.0],
883
+ loadOp: "clear",
884
+ storeOp: "store",
885
+ },
886
+ ],
887
+ depthStencilAttachment: {
888
+ view: depthTexture.createView(),
889
+
890
+ depthClearValue: 1.0,
891
+ depthLoadOp: "clear",
892
+ depthStoreOp: "store",
893
+ },
894
+ };
895
+
896
+ // Update the matrix data.
897
+ updateTransformationMatrix();
898
+ device.queue.writeBuffer(
899
+ uniformBuffer,
900
+ 0,
901
+ mvpMatricesData.buffer,
902
+ mvpMatricesData.byteOffset,
903
+ mvpMatricesData.byteLength,
904
+ );
905
+
906
+ const commandEncoder = device.createCommandEncoder();
907
+ const passEncoder =
908
+ commandEncoder.beginRenderPass(renderPassDescriptor);
909
+ passEncoder.setPipeline(pipeline);
910
+ passEncoder.setBindGroup(0, uniformBindGroup);
911
+ passEncoder.setVertexBuffer(0, verticesBuffer);
912
+ passEncoder.draw(cubeVertexCount, numInstances, 0, 0);
913
+ passEncoder.end();
914
+ device.queue.submit([commandEncoder.finish()]);
915
+ return ctx.getImageData();
916
+ },
917
+ {
918
+ vertexPositionColorWGSL: vertexPositionColor,
919
+ instancedVertWGSL: instancedVert,
920
+ },
921
+ );
922
+ const image = encodeImage(result);
923
+ checkImage(image, "snapshots/instanced-cubes.png");
924
+ });
925
+ });