@bloopjs/toodle 0.0.103 → 0.1.1

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 (128) hide show
  1. package/dist/Toodle.d.ts +50 -20
  2. package/dist/Toodle.d.ts.map +1 -1
  3. package/dist/backends/IBackendShader.d.ts +48 -0
  4. package/dist/backends/IBackendShader.d.ts.map +1 -0
  5. package/dist/backends/IRenderBackend.d.ts +92 -0
  6. package/dist/backends/IRenderBackend.d.ts.map +1 -0
  7. package/dist/backends/ITextureAtlas.d.ts +34 -0
  8. package/dist/backends/ITextureAtlas.d.ts.map +1 -0
  9. package/dist/backends/detection.d.ts +16 -0
  10. package/dist/backends/detection.d.ts.map +1 -0
  11. package/dist/backends/mod.d.ts +9 -0
  12. package/dist/backends/mod.d.ts.map +1 -0
  13. package/dist/backends/webgl2/WebGLBackend.d.ts +51 -0
  14. package/dist/backends/webgl2/WebGLBackend.d.ts.map +1 -0
  15. package/dist/backends/webgl2/WebGLQuadShader.d.ts +17 -0
  16. package/dist/backends/webgl2/WebGLQuadShader.d.ts.map +1 -0
  17. package/dist/backends/webgl2/glsl/quad.glsl.d.ts +12 -0
  18. package/dist/backends/webgl2/glsl/quad.glsl.d.ts.map +1 -0
  19. package/dist/backends/webgl2/mod.d.ts +3 -0
  20. package/dist/backends/webgl2/mod.d.ts.map +1 -0
  21. package/dist/backends/webgpu/ShaderDescriptor.d.ts.map +1 -0
  22. package/dist/{textures → backends/webgpu}/TextureComputeShader.d.ts +1 -1
  23. package/dist/backends/webgpu/TextureComputeShader.d.ts.map +1 -0
  24. package/dist/backends/webgpu/WebGPUBackend.d.ts +67 -0
  25. package/dist/backends/webgpu/WebGPUBackend.d.ts.map +1 -0
  26. package/dist/backends/webgpu/WebGPUQuadShader.d.ts +18 -0
  27. package/dist/backends/webgpu/WebGPUQuadShader.d.ts.map +1 -0
  28. package/dist/backends/webgpu/mod.d.ts +3 -0
  29. package/dist/backends/webgpu/mod.d.ts.map +1 -0
  30. package/dist/backends/webgpu/parser.d.ts.map +1 -0
  31. package/dist/{shaders → backends/webgpu}/postprocess/blur.d.ts +1 -1
  32. package/dist/backends/webgpu/postprocess/blur.d.ts.map +1 -0
  33. package/dist/{shaders → backends/webgpu}/postprocess/mod.d.ts +1 -1
  34. package/dist/backends/webgpu/postprocess/mod.d.ts.map +1 -0
  35. package/dist/backends/webgpu/samplers.d.ts.map +1 -0
  36. package/dist/backends/webgpu/wgsl/example.wgsl.d.ts.map +1 -0
  37. package/dist/backends/webgpu/wgsl/hello.wgsl.d.ts.map +1 -0
  38. package/dist/backends/webgpu/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
  39. package/dist/backends/webgpu/wgsl/pixel-scraping.wgsl.d.ts.map +1 -0
  40. package/dist/backends/webgpu/wgsl/quad.wgsl.d.ts.map +1 -0
  41. package/dist/coreTypes/EngineUniform.d.ts.map +1 -0
  42. package/dist/mod.d.ts +3 -2
  43. package/dist/mod.d.ts.map +1 -1
  44. package/dist/mod.js +6741 -6151
  45. package/dist/mod.js.map +28 -23
  46. package/dist/scene/Batcher.d.ts +2 -2
  47. package/dist/scene/Batcher.d.ts.map +1 -1
  48. package/dist/scene/QuadNode.d.ts +3 -2
  49. package/dist/scene/QuadNode.d.ts.map +1 -1
  50. package/dist/scene/RenderComponent.d.ts +2 -2
  51. package/dist/scene/RenderComponent.d.ts.map +1 -1
  52. package/dist/scene/SceneNode.d.ts +2 -2
  53. package/dist/scene/SceneNode.d.ts.map +1 -1
  54. package/dist/text/TextShader.d.ts +8 -6
  55. package/dist/text/TextShader.d.ts.map +1 -1
  56. package/dist/textures/AssetManager.d.ts +21 -5
  57. package/dist/textures/AssetManager.d.ts.map +1 -1
  58. package/dist/textures/types.d.ts +4 -2
  59. package/dist/textures/types.d.ts.map +1 -1
  60. package/dist/textures/util.d.ts.map +1 -1
  61. package/dist/utils/boilerplate.d.ts +1 -1
  62. package/dist/utils/boilerplate.d.ts.map +1 -1
  63. package/package.json +1 -1
  64. package/src/Toodle.ts +155 -171
  65. package/src/backends/IBackendShader.ts +52 -0
  66. package/src/backends/IRenderBackend.ts +118 -0
  67. package/src/backends/ITextureAtlas.ts +35 -0
  68. package/src/backends/detection.ts +46 -0
  69. package/src/backends/mod.ts +29 -0
  70. package/src/backends/webgl2/WebGLBackend.ts +256 -0
  71. package/src/backends/webgl2/WebGLQuadShader.ts +278 -0
  72. package/src/backends/webgl2/glsl/quad.glsl.ts +114 -0
  73. package/src/backends/webgl2/mod.ts +2 -0
  74. package/src/{textures → backends/webgpu}/TextureComputeShader.ts +2 -48
  75. package/src/backends/webgpu/WebGPUBackend.ts +350 -0
  76. package/src/{shaders/QuadShader.ts → backends/webgpu/WebGPUQuadShader.ts} +226 -170
  77. package/src/backends/webgpu/mod.ts +2 -0
  78. package/src/{shaders → backends/webgpu}/parser.ts +2 -2
  79. package/src/{shaders → backends/webgpu}/postprocess/blur.ts +2 -2
  80. package/src/{shaders → backends/webgpu}/postprocess/mod.ts +1 -1
  81. package/src/mod.ts +3 -2
  82. package/src/scene/Batcher.ts +3 -3
  83. package/src/scene/QuadNode.ts +7 -6
  84. package/src/scene/RenderComponent.ts +2 -2
  85. package/src/scene/SceneNode.ts +11 -12
  86. package/src/text/TextNode.ts +2 -2
  87. package/src/text/TextShader.ts +17 -11
  88. package/src/textures/AssetManager.ts +119 -94
  89. package/src/textures/types.ts +4 -2
  90. package/src/textures/util.ts +0 -65
  91. package/src/utils/boilerplate.ts +1 -1
  92. package/dist/shaders/EngineUniform.d.ts.map +0 -1
  93. package/dist/shaders/IShader.d.ts +0 -15
  94. package/dist/shaders/IShader.d.ts.map +0 -1
  95. package/dist/shaders/QuadShader.d.ts +0 -18
  96. package/dist/shaders/QuadShader.d.ts.map +0 -1
  97. package/dist/shaders/ShaderDescriptor.d.ts.map +0 -1
  98. package/dist/shaders/mod.d.ts +0 -6
  99. package/dist/shaders/mod.d.ts.map +0 -1
  100. package/dist/shaders/parser.d.ts.map +0 -1
  101. package/dist/shaders/postprocess/blur.d.ts.map +0 -1
  102. package/dist/shaders/postprocess/mod.d.ts.map +0 -1
  103. package/dist/shaders/samplers.d.ts.map +0 -1
  104. package/dist/shaders/wgsl/example.wgsl.d.ts.map +0 -1
  105. package/dist/shaders/wgsl/hello.wgsl.d.ts.map +0 -1
  106. package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +0 -1
  107. package/dist/shaders/wgsl/quad.wgsl.d.ts.map +0 -1
  108. package/dist/textures/TextureComputeShader.d.ts.map +0 -1
  109. package/dist/textures/pixel-scraping.wgsl.d.ts.map +0 -1
  110. package/src/shaders/IShader.ts +0 -20
  111. package/src/shaders/mod.ts +0 -5
  112. /package/dist/{shaders → backends/webgpu}/ShaderDescriptor.d.ts +0 -0
  113. /package/dist/{shaders → backends/webgpu}/parser.d.ts +0 -0
  114. /package/dist/{shaders → backends/webgpu}/samplers.d.ts +0 -0
  115. /package/dist/{shaders → backends/webgpu}/wgsl/example.wgsl.d.ts +0 -0
  116. /package/dist/{shaders → backends/webgpu}/wgsl/hello.wgsl.d.ts +0 -0
  117. /package/dist/{shaders → backends/webgpu}/wgsl/helloInstanced.wgsl.d.ts +0 -0
  118. /package/dist/{textures → backends/webgpu/wgsl}/pixel-scraping.wgsl.d.ts +0 -0
  119. /package/dist/{shaders → backends/webgpu}/wgsl/quad.wgsl.d.ts +0 -0
  120. /package/dist/{shaders → coreTypes}/EngineUniform.d.ts +0 -0
  121. /package/src/{shaders → backends/webgpu}/ShaderDescriptor.ts +0 -0
  122. /package/src/{shaders → backends/webgpu}/samplers.ts +0 -0
  123. /package/src/{shaders → backends/webgpu}/wgsl/example.wgsl.ts +0 -0
  124. /package/src/{shaders → backends/webgpu}/wgsl/hello.wgsl.ts +0 -0
  125. /package/src/{shaders → backends/webgpu}/wgsl/helloInstanced.wgsl.ts +0 -0
  126. /package/src/{textures → backends/webgpu/wgsl}/pixel-scraping.wgsl.ts +0 -0
  127. /package/src/{shaders → backends/webgpu}/wgsl/quad.wgsl.ts +0 -0
  128. /package/src/{shaders → coreTypes}/EngineUniform.ts +0 -0
@@ -4,134 +4,86 @@ import {
4
4
  type StructuredView,
5
5
  } from "webgpu-utils";
6
6
  import { WgslReflect } from "wgsl_reflect";
7
- import type { SceneNode } from "../scene/SceneNode";
8
- import type { AssetManager } from "../textures/AssetManager";
9
- import { assert } from "../utils/assert";
7
+ import type { EngineUniform } from "../../coreTypes/EngineUniform";
8
+ import type { SceneNode } from "../../scene/SceneNode";
9
+ import { assert } from "../../utils/assert";
10
10
  import {
11
- attachVertexInstanceBuffer,
12
11
  createGpuPipeline,
13
12
  getGpuPipelineDescriptor,
14
13
  setVertexInstanceBufferLayout,
15
- } from "../utils/boilerplate";
16
- import type { EngineUniform } from "./EngineUniform";
17
- import type { IShader } from "./IShader";
14
+ } from "../../utils/boilerplate";
15
+ import type { IBackendShader } from "../IBackendShader";
16
+ import type { BlendMode } from "../IRenderBackend";
17
+ import type { ITextureAtlas } from "../ITextureAtlas";
18
18
  import {
19
19
  codeWithLineNumbers,
20
20
  combineShaderCode,
21
21
  struct2BufferLayout,
22
22
  } from "./parser";
23
23
  import { pixelArtSampler, smoothSampler } from "./samplers";
24
+ import type { WebGPUBackend } from "./WebGPUBackend";
24
25
  import quadWgsl from "./wgsl/quad.wgsl";
25
26
 
26
- export type QuadShaderOpts = {
27
- assetManager?: AssetManager;
28
- blendMode?: GPUBlendState;
27
+ type InstanceData = {
28
+ cpuBuffer: Float32Array<ArrayBuffer>;
29
+ gpuBuffer: GPUBuffer;
30
+ bufferLayout: GPUVertexBufferLayout;
29
31
  };
30
32
 
31
- export class QuadShader implements IShader {
32
- label: string;
33
- code: string;
33
+ /**
34
+ * WebGPU implementation of quad shader for instanced rendering.
35
+ */
36
+ export class WebGPUQuadShader implements IBackendShader {
37
+ readonly label: string;
38
+ readonly code: string;
34
39
 
40
+ #backend: WebGPUBackend;
41
+ #atlas: ITextureAtlas;
35
42
  #uniformValues: StructuredView;
36
43
  #instanceData: InstanceData;
37
44
  #instanceIndex = 0;
38
- #instanceCount;
45
+ #instanceCount: number;
39
46
 
40
- #device: GPUDevice;
41
47
  #pipeline: GPURenderPipeline;
42
48
  #bindGroups: GPUBindGroup[] = [];
43
49
  #uniformBuffer: GPUBuffer;
44
50
 
45
- startFrame(device: GPUDevice, uniform: EngineUniform) {
46
- this.#instanceIndex = 0;
47
-
48
- this.#uniformValues.set(uniform);
49
- this.#uniformValues.set({
50
- viewProjection: uniform.viewProjectionMatrix,
51
- resolution: [uniform.resolution.width, uniform.resolution.height],
52
- });
53
-
54
- // Write view projection matrix to uniform buffer
55
- device.queue.writeBuffer(
56
- this.#uniformBuffer,
57
- 0,
58
- this.#uniformValues.arrayBuffer,
59
- );
60
- }
61
-
62
- processBatch(renderPass: GPURenderPassEncoder, nodes: SceneNode[]) {
63
- renderPass.setPipeline(this.#pipeline);
64
- const batchStartInstanceIndex = this.#instanceIndex;
65
-
66
- // Count for the number of instances in the buffer...
67
- let instanceCount = 0;
68
-
69
- if (nodes.length > this.#instanceCount) {
70
- throw new Error(
71
- `ToodleInstanceCap: ${nodes.length} instances enqueued, max is ${this.#instanceCount} for ${this.label} shader`,
72
- );
73
- }
74
-
75
- for (let i = 0; i < nodes.length; i++) {
76
- if (!this.#instanceData) {
77
- continue;
78
- }
79
- const instance = nodes[i];
80
- assert(instance.renderComponent, "instance has no render component");
81
- const floatOffset =
82
- ((batchStartInstanceIndex + instanceCount) *
83
- this.#instanceData.bufferLayout.arrayStride) /
84
- Float32Array.BYTES_PER_ELEMENT;
85
-
86
- instanceCount += instance.renderComponent.writeInstance(
87
- instance,
88
- this.#instanceData.cpuBuffer,
89
- floatOffset,
90
- );
91
- }
92
-
93
- if (this.#instanceData) {
94
- const byteOffset =
95
- batchStartInstanceIndex * this.#instanceData.bufferLayout.arrayStride;
96
- const byteLength =
97
- instanceCount * this.#instanceData.bufferLayout.arrayStride;
98
-
99
- this.#device.queue.writeBuffer(
100
- this.#instanceData.gpuBuffer,
101
- byteOffset,
102
- this.#instanceData.cpuBuffer,
103
- byteOffset / Float32Array.BYTES_PER_ELEMENT,
104
- byteLength / Float32Array.BYTES_PER_ELEMENT,
105
- );
106
-
107
- attachVertexInstanceBuffer(renderPass, this.#instanceData.gpuBuffer);
108
-
109
- for (let i = 0; i < this.#bindGroups.length; i++) {
110
- renderPass.setBindGroup(i, this.#bindGroups[i]);
111
- }
112
- }
113
-
114
- this.#instanceIndex += instanceCount;
115
-
116
- renderPass.draw(4, instanceCount, 0, batchStartInstanceIndex);
117
- return 1;
118
- }
119
-
120
- endFrame() {}
121
-
122
51
  constructor(
123
52
  label: string,
124
- assetManager: AssetManager,
125
- device: GPUDevice,
126
- presentationFormat: GPUTextureFormat,
127
- userCode: string,
53
+ backend: WebGPUBackend,
128
54
  instanceCount: number,
129
- blendMode?: GPUBlendState,
55
+ userCode?: string,
56
+ blendMode?: BlendMode,
57
+ atlasId?: string,
130
58
  ) {
59
+ const atlas = backend.getTextureAtlas(atlasId ?? "default");
60
+ if (!atlas) {
61
+ throw new Error(`Atlas "${atlasId ?? "default"}" not found`);
62
+ }
63
+ this.#atlas = atlas;
131
64
  this.label = label;
65
+ this.#backend = backend;
66
+ this.#instanceCount = instanceCount;
67
+
68
+ const device = backend.device;
69
+ const presentationFormat = backend.presentationFormat;
132
70
 
133
71
  // Combine user code with base quad shader code
134
- const shaderDescriptor = combineShaderCode(label, quadWgsl, userCode);
72
+ const effectiveUserCode =
73
+ userCode ??
74
+ /*wgsl*/ `
75
+ @fragment
76
+ fn frag(vertex: VertexOutput) -> @location(0) vec4f {
77
+ let color = default_fragment_shader(vertex, linearSampler);
78
+ return color;
79
+ }
80
+ `;
81
+
82
+ const shaderDescriptor = combineShaderCode(
83
+ label,
84
+ quadWgsl,
85
+ effectiveUserCode,
86
+ );
135
87
 
136
88
  // Create shader module from combined code
137
89
  let module: GPUShaderModule;
@@ -149,18 +101,20 @@ export class QuadShader implements IShader {
149
101
  this.code = shaderDescriptor.code;
150
102
 
151
103
  // Create blend state
152
- const blend = blendMode ?? {
153
- color: {
154
- srcFactor: "src-alpha",
155
- dstFactor: "one-minus-src-alpha",
156
- operation: "add",
157
- },
158
- alpha: {
159
- srcFactor: "one",
160
- dstFactor: "one-minus-src-alpha",
161
- operation: "add",
162
- },
163
- };
104
+ const blend = blendMode
105
+ ? convertBlendMode(blendMode)
106
+ : {
107
+ color: {
108
+ srcFactor: "src-alpha" as GPUBlendFactor,
109
+ dstFactor: "one-minus-src-alpha" as GPUBlendFactor,
110
+ operation: "add" as GPUBlendOperation,
111
+ },
112
+ alpha: {
113
+ srcFactor: "one" as GPUBlendFactor,
114
+ dstFactor: "one-minus-src-alpha" as GPUBlendFactor,
115
+ operation: "add" as GPUBlendOperation,
116
+ },
117
+ };
164
118
 
165
119
  // Create instance data from shader code
166
120
  const ast = new WgslReflect(shaderDescriptor.code);
@@ -208,12 +162,6 @@ export class QuadShader implements IShader {
208
162
  pipelineDescriptor,
209
163
  );
210
164
 
211
- // Reference texture atlas
212
- const textureAtlas = assetManager.textureAtlas;
213
-
214
- // Store device
215
- this.#device = device;
216
-
217
165
  // Create uniform buffer for engine uniforms (mat3x3 viewProjection)
218
166
  const defs = makeShaderDataDefinitions(this.code);
219
167
  this.#uniformValues = makeStructuredView(defs.uniforms.engineUniform);
@@ -224,65 +172,173 @@ export class QuadShader implements IShader {
224
172
  });
225
173
 
226
174
  // Create bind groups
227
- this.#bindGroups = setQuadBindGroups(
228
- label,
229
- device,
230
- this.#pipeline,
231
- textureAtlas,
175
+ this.#bindGroups = this.#createBindGroups(device);
176
+ }
177
+
178
+ startFrame(uniform: EngineUniform): void {
179
+ this.#instanceIndex = 0;
180
+
181
+ this.#uniformValues.set(uniform);
182
+ this.#uniformValues.set({
183
+ viewProjection: uniform.viewProjectionMatrix,
184
+ resolution: [uniform.resolution.width, uniform.resolution.height],
185
+ });
186
+
187
+ // Write view projection matrix to uniform buffer
188
+ const device = this.#backend.device;
189
+ device.queue.writeBuffer(
232
190
  this.#uniformBuffer,
191
+ 0,
192
+ this.#uniformValues.arrayBuffer,
233
193
  );
194
+ }
234
195
 
235
- this.#instanceCount = instanceCount;
196
+ processBatch(nodes: SceneNode[]): number {
197
+ const device = this.#backend.device;
198
+ const renderPass = this.#backend.renderPass;
199
+
200
+ renderPass.setPipeline(this.#pipeline);
201
+ const batchStartInstanceIndex = this.#instanceIndex;
202
+
203
+ let instanceCount = 0;
204
+
205
+ if (nodes.length > this.#instanceCount) {
206
+ throw new Error(
207
+ `ToodleInstanceCap: ${nodes.length} instances enqueued, max is ${this.#instanceCount} for ${this.label} shader`,
208
+ );
209
+ }
210
+
211
+ for (let i = 0; i < nodes.length; i++) {
212
+ if (!this.#instanceData) {
213
+ continue;
214
+ }
215
+ const instance = nodes[i];
216
+ assert(instance.renderComponent, "instance has no render component");
217
+ const floatOffset =
218
+ ((batchStartInstanceIndex + instanceCount) *
219
+ this.#instanceData.bufferLayout.arrayStride) /
220
+ Float32Array.BYTES_PER_ELEMENT;
221
+
222
+ instanceCount += instance.renderComponent.writeInstance(
223
+ instance,
224
+ this.#instanceData.cpuBuffer,
225
+ floatOffset,
226
+ );
227
+ }
228
+
229
+ if (this.#instanceData) {
230
+ const byteOffset =
231
+ batchStartInstanceIndex * this.#instanceData.bufferLayout.arrayStride;
232
+ const byteLength =
233
+ instanceCount * this.#instanceData.bufferLayout.arrayStride;
234
+
235
+ device.queue.writeBuffer(
236
+ this.#instanceData.gpuBuffer,
237
+ byteOffset,
238
+ this.#instanceData.cpuBuffer,
239
+ byteOffset / Float32Array.BYTES_PER_ELEMENT,
240
+ byteLength / Float32Array.BYTES_PER_ELEMENT,
241
+ );
242
+
243
+ renderPass.setVertexBuffer(0, this.#instanceData.gpuBuffer);
244
+
245
+ for (let i = 0; i < this.#bindGroups.length; i++) {
246
+ renderPass.setBindGroup(i, this.#bindGroups[i]);
247
+ }
248
+ }
249
+
250
+ this.#instanceIndex += instanceCount;
251
+
252
+ renderPass.draw(4, instanceCount, 0, batchStartInstanceIndex);
253
+ return 1;
254
+ }
255
+
256
+ endFrame(): void {
257
+ // Nothing to do
236
258
  }
237
- }
238
259
 
239
- function setQuadBindGroups(
240
- label: string,
241
- device: GPUDevice,
242
- pipeline: GPURenderPipeline,
243
- textureAtlas: GPUTexture,
244
- buffer: GPUBuffer,
245
- ) {
246
- const bindGroup = device.createBindGroup({
247
- label: `${label} engine bind group`,
248
- layout: pipeline.getBindGroupLayout(0),
249
- entries: [
250
- {
251
- binding: 0,
252
- resource: {
253
- buffer,
260
+ #createBindGroups(device: GPUDevice): GPUBindGroup[] {
261
+ const textureAtlas = this.#atlas.handle as GPUTexture;
262
+
263
+ const bindGroup = device.createBindGroup({
264
+ label: `${this.label} engine bind group`,
265
+ layout: this.#pipeline.getBindGroupLayout(0),
266
+ entries: [
267
+ {
268
+ binding: 0,
269
+ resource: {
270
+ buffer: this.#uniformBuffer,
271
+ },
254
272
  },
255
- },
256
- {
257
- binding: 1,
258
- resource: device.createSampler(smoothSampler),
259
- },
260
- {
261
- binding: 2,
262
- resource: device.createSampler(pixelArtSampler),
263
- },
264
- ],
265
- });
266
-
267
- const atlasBindGroup = device.createBindGroup({
268
- label: `${label} atlas bind group`,
269
- layout: pipeline.getBindGroupLayout(1),
270
- entries: [
271
- {
272
- binding: 0,
273
- resource: textureAtlas.createView({
274
- dimension: "2d-array",
275
- arrayLayerCount: textureAtlas.depthOrArrayLayers,
276
- }),
277
- },
278
- ],
279
- });
280
-
281
- return [bindGroup, atlasBindGroup];
273
+ {
274
+ binding: 1,
275
+ resource: device.createSampler(smoothSampler),
276
+ },
277
+ {
278
+ binding: 2,
279
+ resource: device.createSampler(pixelArtSampler),
280
+ },
281
+ ],
282
+ });
283
+
284
+ const atlasBindGroup = device.createBindGroup({
285
+ label: `${this.label} atlas bind group`,
286
+ layout: this.#pipeline.getBindGroupLayout(1),
287
+ entries: [
288
+ {
289
+ binding: 0,
290
+ resource: textureAtlas.createView({
291
+ dimension: "2d-array",
292
+ arrayLayerCount: textureAtlas.depthOrArrayLayers,
293
+ }),
294
+ },
295
+ ],
296
+ });
297
+
298
+ return [bindGroup, atlasBindGroup];
299
+ }
282
300
  }
283
301
 
284
- type InstanceData = {
285
- cpuBuffer: Float32Array<ArrayBuffer>;
286
- gpuBuffer: GPUBuffer;
287
- bufferLayout: GPUVertexBufferLayout;
288
- };
302
+ function convertBlendMode(mode: BlendMode): GPUBlendState {
303
+ const convertFactor = (f: string): GPUBlendFactor => {
304
+ const map: Record<string, GPUBlendFactor> = {
305
+ one: "one",
306
+ zero: "zero",
307
+ "src-alpha": "src-alpha",
308
+ "one-minus-src-alpha": "one-minus-src-alpha",
309
+ "dst-alpha": "dst-alpha",
310
+ "one-minus-dst-alpha": "one-minus-dst-alpha",
311
+ };
312
+ const result = map[f];
313
+ if (!result) {
314
+ throw new Error(`Unknown blend factor: ${f}`);
315
+ }
316
+ return result;
317
+ };
318
+
319
+ const convertOp = (o: string): GPUBlendOperation => {
320
+ const map: Record<string, GPUBlendOperation> = {
321
+ add: "add",
322
+ subtract: "subtract",
323
+ "reverse-subtract": "reverse-subtract",
324
+ };
325
+ const result = map[o];
326
+ if (!result) {
327
+ throw new Error(`Unknown blend operation: ${o}`);
328
+ }
329
+ return result;
330
+ };
331
+
332
+ return {
333
+ color: {
334
+ srcFactor: convertFactor(mode.color.srcFactor),
335
+ dstFactor: convertFactor(mode.color.dstFactor),
336
+ operation: convertOp(mode.color.operation),
337
+ },
338
+ alpha: {
339
+ srcFactor: convertFactor(mode.alpha.srcFactor),
340
+ dstFactor: convertFactor(mode.alpha.dstFactor),
341
+ operation: convertOp(mode.alpha.operation),
342
+ },
343
+ };
344
+ }
@@ -0,0 +1,2 @@
1
+ export { WebGPUBackend, type WebGPUBackendOptions } from "./WebGPUBackend";
2
+ export { WebGPUQuadShader } from "./WebGPUQuadShader";
@@ -120,7 +120,7 @@ function findStartingLocation(struct: StructInfo) {
120
120
  (attr) => attr.name === "location",
121
121
  );
122
122
  if (locationAttr) {
123
- const location = Number.parseInt(locationAttr.value as string);
123
+ const location = Number.parseInt(locationAttr.value as string, 10);
124
124
  startingLocation = Math.max(startingLocation, location);
125
125
  }
126
126
  }
@@ -164,7 +164,7 @@ export function struct2BufferLayout(
164
164
  throw new Error(`Location attribute is an array for member: ${m.name}`);
165
165
  }
166
166
 
167
- const shaderLocation = Number.parseInt(location);
167
+ const shaderLocation = Number.parseInt(location, 10);
168
168
  if (Number.isNaN(shaderLocation)) {
169
169
  throw new Error(
170
170
  `Invalid location attribute: ${location} for member: ${m.name}`,
@@ -1,4 +1,4 @@
1
- import type { Color } from "../../coreTypes/Color";
1
+ import type { Color } from "../../../coreTypes/Color";
2
2
  import { PostProcessDefaults } from "./mod";
3
3
 
4
4
  // example of a blur post-process effect using a two-pass Gaussian blur
@@ -16,7 +16,7 @@ export function blur(
16
16
  const lines = weights.map((w) => w.toFixed(7)).join(", ");
17
17
  const wgslFragment = `let weights = array<f32, ${weights.length}>(${lines});`;
18
18
 
19
- const brightPipeline = device.createRenderPipeline({
19
+ const _brightPipeline = device.createRenderPipeline({
20
20
  ...PostProcessDefaults.pipelineDescriptor(device),
21
21
  label: "toodle post process - brightness pass",
22
22
  fragment: {
@@ -26,7 +26,7 @@ export const PostProcessDefaults = {
26
26
  });
27
27
  },
28
28
 
29
- vertexBufferLayout(device: GPUDevice): GPUVertexBufferLayout {
29
+ vertexBufferLayout(_device: GPUDevice): GPUVertexBufferLayout {
30
30
  return {
31
31
  arrayStride: 4 * 4,
32
32
  attributes: [{ shaderLocation: 0, offset: 0, format: "float32x2" }],
package/src/mod.ts CHANGED
@@ -4,8 +4,9 @@ export type * from "./coreTypes/mod";
4
4
 
5
5
  export type * from "./scene/QuadNode";
6
6
  export type * from "./scene/SceneNode";
7
- export type * from "./shaders/QuadShader";
8
7
  export type * from "./text/TextNode";
8
+ export type * from "./backends/IBackendShader";
9
+ export type * from "./backends/IRenderBackend";
9
10
 
10
11
  export * from "./limits";
11
12
  export * from "./Toodle";
@@ -14,7 +15,7 @@ export * as Colors from "./colors/mod";
14
15
  export * as GfxMath from "./math/mod";
15
16
  export * as Scene from "./scene/mod";
16
17
  export * as Screen from "./screen/mod";
17
- export * as Shaders from "./shaders/mod";
18
+ export * as Backends from "./backends/mod";
18
19
  export * as Text from "./text/mod";
19
20
  export * as Textures from "./textures/mod";
20
21
  export * as Utils from "./utils/mod";
@@ -1,4 +1,4 @@
1
- import type { IShader } from "../shaders/IShader";
1
+ import type { IBackendShader } from "../backends/IBackendShader";
2
2
  import type { SceneNode } from "./SceneNode";
3
3
 
4
4
  type Layer = {
@@ -7,7 +7,7 @@ type Layer = {
7
7
  };
8
8
 
9
9
  export type Pipeline<TNode extends SceneNode = SceneNode> = {
10
- shader: IShader;
10
+ shader: IBackendShader;
11
11
  nodes: TNode[];
12
12
  };
13
13
 
@@ -49,7 +49,7 @@ export class Batcher {
49
49
  return layer;
50
50
  }
51
51
 
52
- #findOrCreatePipeline(layer: Layer, shader: IShader) {
52
+ #findOrCreatePipeline(layer: Layer, shader: IBackendShader) {
53
53
  let pipeline = layer.pipelines.find((p) => p.shader === shader);
54
54
  if (!pipeline) {
55
55
  pipeline = { shader, nodes: [] };
@@ -1,8 +1,8 @@
1
1
  import { type Mat3, mat3 } from "wgpu-matrix";
2
+ import type { IBackendShader } from "../backends/IBackendShader";
2
3
  import type { Color } from "../coreTypes/Color";
3
4
  import type { Size } from "../coreTypes/Size";
4
5
  import type { Vec2 } from "../coreTypes/Vec2";
5
- import type { IShader } from "../shaders/IShader";
6
6
  import type { Toodle } from "../Toodle";
7
7
  import type { AssetManager, TextureId } from "../textures/AssetManager";
8
8
  import type { AtlasCoords, TexelRegion } from "../textures/types";
@@ -57,10 +57,7 @@ export class QuadNode extends SceneNode {
57
57
  options.shader,
58
58
  "QuadNode requires a shader to be explicitly provided",
59
59
  );
60
- assert(
61
- options.idealSize,
62
- "QuadNode requires an ideal size to be explicitly provided",
63
- );
60
+ assert(options.size, "QuadNode requires a size to be explicitly provided");
64
61
 
65
62
  assert(
66
63
  options.atlasCoords,
@@ -147,6 +144,10 @@ export class QuadNode extends SceneNode {
147
144
  return size;
148
145
  }
149
146
 
147
+ set size(val: Size) {
148
+ super.size = val;
149
+ }
150
+
150
151
  /**
151
152
  * This is the final model matrix used to render the quad, which
152
153
  * may differ from the matrix passed down to the node's children.
@@ -305,7 +306,7 @@ export type QuadOptions = NodeOptions & {
305
306
 
306
307
  assetManager?: AssetManager;
307
308
 
308
- shader?: IShader;
309
+ shader?: IBackendShader;
309
310
  writeInstance?: (array: Float32Array, offset: number) => void;
310
311
  color?: Color;
311
312
  /**
@@ -1,8 +1,8 @@
1
- import type { IShader } from "../shaders/IShader";
1
+ import type { IBackendShader } from "../backends/IBackendShader";
2
2
  import type { SceneNode } from "./SceneNode";
3
3
 
4
4
  export type RenderComponent = {
5
- shader: IShader;
5
+ shader: IBackendShader;
6
6
  data?: Float32Array;
7
7
 
8
8
  /**
@@ -27,7 +27,7 @@ export class SceneNode {
27
27
  #transform: Transform;
28
28
  #matrix: Mat3 = mat3.identity();
29
29
  #renderComponent: RenderComponent | null = null;
30
- #idealSize: Size | null = null;
30
+ #size: Size | null = null;
31
31
  #positionProxy: Point;
32
32
  #scaleProxy: Vec2;
33
33
 
@@ -45,7 +45,7 @@ export class SceneNode {
45
45
  this.#transform = {
46
46
  position: opts?.position ?? { x: 0, y: 0 },
47
47
  scale: { x: 1, y: 1 },
48
- size: opts?.idealSize ?? { width: 1, height: 1 },
48
+ size: opts?.size ?? { width: 1, height: 1 },
49
49
  rotation: opts?.rotationRadians ?? 0,
50
50
  };
51
51
  if (opts?.scale) this.scale = opts.scale;
@@ -56,7 +56,7 @@ export class SceneNode {
56
56
  this.#layer = opts?.layer ?? null;
57
57
  this.#isActive = opts?.isActive ?? true;
58
58
  this.label = opts?.label ?? undefined;
59
- this.#idealSize = opts?.idealSize ?? null;
59
+ this.#size = opts?.size ?? null;
60
60
  this.#key = opts?.key ?? null;
61
61
 
62
62
  for (const kid of opts?.kids ?? []) {
@@ -246,17 +246,16 @@ export class SceneNode {
246
246
  this.setDirty();
247
247
  }
248
248
 
249
- set idealSize(value: Size | null) {
250
- this.#idealSize = value;
251
- this.setDirty();
252
- }
253
-
254
249
  /**
255
250
  * The size of the node. See https://toodle.gg/f849595b3ed13fc956fc1459a5cb5f0228f9d259/examples/quad-size-scale.html
256
251
  */
252
+ set size(value: Size | null) {
253
+ this.#size = value;
254
+ this.setDirty();
255
+ }
257
256
 
258
257
  get size() {
259
- return this.#idealSize;
258
+ return this.#size;
260
259
  }
261
260
 
262
261
  /**
@@ -264,13 +263,13 @@ export class SceneNode {
264
263
  * If the node has no defined size, the aspect ratio will be 1.
265
264
  */
266
265
  get aspectRatio() {
267
- if (!this.#idealSize) {
266
+ if (!this.#size) {
268
267
  console.warn(
269
268
  "Attempted to get aspect ratio of a node with no ideal size",
270
269
  );
271
270
  return 1;
272
271
  }
273
- return this.#idealSize.width / this.#idealSize.height;
272
+ return this.#size.width / this.#size.height;
274
273
  }
275
274
 
276
275
  /**
@@ -592,7 +591,7 @@ export type NodeOptions = {
592
591
  /** The scale for the node. */
593
592
  scale?: Vec2 | number;
594
593
  /** The desired size for the node. */
595
- idealSize?: Size;
594
+ size?: Size;
596
595
  /** The active state for the node. */
597
596
  isActive?: boolean;
598
597
  /** The kids for the node. */