@bloopjs/toodle 0.0.104 → 0.1.2

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 (122) hide show
  1. package/dist/Toodle.d.ts +41 -19
  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 +7247 -6663
  45. package/dist/mod.js.map +27 -22
  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/text/TextShader.d.ts +8 -6
  53. package/dist/text/TextShader.d.ts.map +1 -1
  54. package/dist/textures/AssetManager.d.ts +21 -5
  55. package/dist/textures/AssetManager.d.ts.map +1 -1
  56. package/dist/textures/util.d.ts +0 -4
  57. package/dist/textures/util.d.ts.map +1 -1
  58. package/dist/utils/boilerplate.d.ts +1 -1
  59. package/dist/utils/boilerplate.d.ts.map +1 -1
  60. package/package.json +1 -2
  61. package/src/Toodle.ts +124 -156
  62. package/src/backends/IBackendShader.ts +52 -0
  63. package/src/backends/IRenderBackend.ts +118 -0
  64. package/src/backends/ITextureAtlas.ts +35 -0
  65. package/src/backends/detection.ts +46 -0
  66. package/src/backends/mod.ts +29 -0
  67. package/src/backends/webgl2/WebGLBackend.ts +256 -0
  68. package/src/backends/webgl2/WebGLQuadShader.ts +278 -0
  69. package/src/backends/webgl2/glsl/quad.glsl.ts +114 -0
  70. package/src/backends/webgl2/mod.ts +2 -0
  71. package/src/{textures → backends/webgpu}/TextureComputeShader.ts +2 -48
  72. package/src/backends/webgpu/WebGPUBackend.ts +350 -0
  73. package/src/{shaders/QuadShader.ts → backends/webgpu/WebGPUQuadShader.ts} +226 -170
  74. package/src/backends/webgpu/mod.ts +2 -0
  75. package/src/{shaders → backends/webgpu}/parser.ts +2 -2
  76. package/src/{shaders → backends/webgpu}/postprocess/blur.ts +2 -2
  77. package/src/{shaders → backends/webgpu}/postprocess/mod.ts +1 -1
  78. package/src/mod.ts +3 -2
  79. package/src/scene/Batcher.ts +3 -3
  80. package/src/scene/QuadNode.ts +6 -2
  81. package/src/scene/RenderComponent.ts +2 -2
  82. package/src/text/TextShader.ts +17 -11
  83. package/src/textures/AssetManager.ts +117 -93
  84. package/src/textures/util.ts +0 -92
  85. package/src/utils/boilerplate.ts +1 -1
  86. package/dist/shaders/EngineUniform.d.ts.map +0 -1
  87. package/dist/shaders/IShader.d.ts +0 -15
  88. package/dist/shaders/IShader.d.ts.map +0 -1
  89. package/dist/shaders/QuadShader.d.ts +0 -18
  90. package/dist/shaders/QuadShader.d.ts.map +0 -1
  91. package/dist/shaders/ShaderDescriptor.d.ts.map +0 -1
  92. package/dist/shaders/mod.d.ts +0 -6
  93. package/dist/shaders/mod.d.ts.map +0 -1
  94. package/dist/shaders/parser.d.ts.map +0 -1
  95. package/dist/shaders/postprocess/blur.d.ts.map +0 -1
  96. package/dist/shaders/postprocess/mod.d.ts.map +0 -1
  97. package/dist/shaders/samplers.d.ts.map +0 -1
  98. package/dist/shaders/wgsl/example.wgsl.d.ts.map +0 -1
  99. package/dist/shaders/wgsl/hello.wgsl.d.ts.map +0 -1
  100. package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +0 -1
  101. package/dist/shaders/wgsl/quad.wgsl.d.ts.map +0 -1
  102. package/dist/textures/TextureComputeShader.d.ts.map +0 -1
  103. package/dist/textures/pixel-scraping.wgsl.d.ts.map +0 -1
  104. package/src/shaders/IShader.ts +0 -20
  105. package/src/shaders/mod.ts +0 -5
  106. /package/dist/{shaders → backends/webgpu}/ShaderDescriptor.d.ts +0 -0
  107. /package/dist/{shaders → backends/webgpu}/parser.d.ts +0 -0
  108. /package/dist/{shaders → backends/webgpu}/samplers.d.ts +0 -0
  109. /package/dist/{shaders → backends/webgpu}/wgsl/example.wgsl.d.ts +0 -0
  110. /package/dist/{shaders → backends/webgpu}/wgsl/hello.wgsl.d.ts +0 -0
  111. /package/dist/{shaders → backends/webgpu}/wgsl/helloInstanced.wgsl.d.ts +0 -0
  112. /package/dist/{textures → backends/webgpu/wgsl}/pixel-scraping.wgsl.d.ts +0 -0
  113. /package/dist/{shaders → backends/webgpu}/wgsl/quad.wgsl.d.ts +0 -0
  114. /package/dist/{shaders → coreTypes}/EngineUniform.d.ts +0 -0
  115. /package/src/{shaders → backends/webgpu}/ShaderDescriptor.ts +0 -0
  116. /package/src/{shaders → backends/webgpu}/samplers.ts +0 -0
  117. /package/src/{shaders → backends/webgpu}/wgsl/example.wgsl.ts +0 -0
  118. /package/src/{shaders → backends/webgpu}/wgsl/hello.wgsl.ts +0 -0
  119. /package/src/{shaders → backends/webgpu}/wgsl/helloInstanced.wgsl.ts +0 -0
  120. /package/src/{textures → backends/webgpu/wgsl}/pixel-scraping.wgsl.ts +0 -0
  121. /package/src/{shaders → backends/webgpu}/wgsl/quad.wgsl.ts +0 -0
  122. /package/src/{shaders → coreTypes}/EngineUniform.ts +0 -0
@@ -1,15 +1,15 @@
1
+ import type { IRenderBackend } from "../backends/IRenderBackend";
2
+ import { TextureComputeShader } from "../backends/webgpu/TextureComputeShader";
3
+ import type { WebGPUBackend } from "../backends/webgpu/WebGPUBackend";
1
4
  import type { Size } from "../coreTypes/Size";
2
5
  import type { Vec2 } from "../coreTypes/Vec2";
3
- import type { Limits } from "../limits";
4
6
  import { JumboQuadNode } from "../scene/JumboQuadNode";
5
7
  import { QuadNode } from "../scene/QuadNode";
6
8
  import type { SceneNode } from "../scene/SceneNode";
7
9
  import { FontPipeline } from "../text/FontPipeline";
8
10
  import { MsdfFont } from "../text/MsdfFont";
9
11
  import { TextShader } from "../text/TextShader";
10
- import { assert } from "../utils/mod";
11
12
  import { Bundles } from "./Bundles";
12
- import { TextureComputeShader } from "./TextureComputeShader";
13
13
  import type {
14
14
  AtlasBundleOpts,
15
15
  AtlasCoords,
@@ -26,51 +26,63 @@ export type FontId = string;
26
26
  export type AssetManagerOptions = {
27
27
  /** Existing Bundles instance to use for CPU-side storage. If not provided, a new one is created. */
28
28
  bundles?: Bundles;
29
- /** Texture format (default: "rgba8unorm") */
30
- format?: "rgba8unorm" | "rg8unorm";
29
+ /** Which texture atlas to use (default: "default") */
30
+ atlasId?: string;
31
31
  };
32
32
 
33
33
  export class AssetManager {
34
- readonly textureAtlas: GPUTexture;
35
34
  readonly bundles: Bundles;
36
- #device: GPUDevice;
37
- #presentationFormat: GPUTextureFormat;
35
+ #backend: IRenderBackend;
36
+ #atlasId: string;
38
37
  #fonts: Map<string, TextShader> = new Map();
39
- #cropComputeShader: TextureComputeShader;
40
- #limits: Limits;
38
+ #cropComputeShader: TextureComputeShader | null = null;
41
39
  #availableIndices: Set<number> = new Set();
42
40
 
43
- constructor(
44
- device: GPUDevice,
45
- presentationFormat: GPUTextureFormat,
46
- limits: Limits,
47
- options: AssetManagerOptions = {},
48
- ) {
49
- this.#device = device;
50
- this.#presentationFormat = presentationFormat;
51
- this.#limits = limits;
52
- this.bundles =
53
- options.bundles ?? new Bundles({ atlasSize: limits.textureSize });
54
- const format = options.format ?? "rgba8unorm";
55
- this.textureAtlas = device.createTexture({
56
- label: "Asset Manager Atlas Texture",
57
- size: [
58
- this.#limits.textureSize,
59
- this.#limits.textureSize,
60
- this.#limits.textureArrayLayers,
61
- ],
62
- format,
63
- usage:
64
- GPUTextureUsage.TEXTURE_BINDING |
65
- GPUTextureUsage.COPY_DST |
66
- GPUTextureUsage.RENDER_ATTACHMENT,
67
- });
68
- this.#cropComputeShader = TextureComputeShader.create(device);
41
+ constructor(backend: IRenderBackend, options: AssetManagerOptions = {}) {
42
+ this.#backend = backend;
43
+ this.#atlasId = options.atlasId ?? "default";
44
+
45
+ const atlas = backend.getTextureAtlas(this.#atlasId);
46
+ if (!atlas) {
47
+ throw new Error(`Atlas "${this.#atlasId}" not found`);
48
+ }
49
+
50
+ this.bundles = options.bundles ?? new Bundles({ atlasSize: atlas.size });
51
+
52
+ // Initialize compute shader only for WebGPU backend
53
+ if (backend.type === "webgpu") {
54
+ const device = (backend as WebGPUBackend).device;
55
+ this.#cropComputeShader = TextureComputeShader.create(device);
56
+ }
57
+
69
58
  this.#availableIndices = new Set(
70
- Array.from({ length: limits.textureArrayLayers }, (_, i) => i),
59
+ Array.from({ length: atlas.layers }, (_, i) => i),
71
60
  );
72
61
  }
73
62
 
63
+ /**
64
+ * Get the atlas ID this asset manager uses.
65
+ */
66
+ get atlasId(): string {
67
+ return this.#atlasId;
68
+ }
69
+
70
+ /**
71
+ * Get the GPU texture atlas. For WebGPU, returns GPUTexture.
72
+ * @deprecated Access via backend.getTextureAtlas(atlasId).handle instead
73
+ */
74
+ get textureAtlas(): GPUTexture {
75
+ return this.#backend.getTextureAtlas(this.#atlasId)!.handle as GPUTexture;
76
+ }
77
+
78
+ /**
79
+ * Get the atlas dimensions
80
+ */
81
+ get atlasSize(): Size {
82
+ const atlas = this.#backend.getTextureAtlas(this.#atlasId);
83
+ return { width: atlas!.size, height: atlas!.size };
84
+ }
85
+
74
86
  /**
75
87
  * True dimensions of a loaded texture, prior to any transparent pixel cropping.
76
88
  *
@@ -80,9 +92,10 @@ export class AssetManager {
80
92
  getSize(id: TextureId): Size {
81
93
  const coords = this.extra.getAtlasCoords(id);
82
94
  const originalScale = coords[0].uvScale;
95
+ // Use atlasSize instead of textureAtlas.width/height for WebGL2 compatibility
83
96
  return {
84
- width: originalScale.width * this.textureAtlas.width,
85
- height: originalScale.height * this.textureAtlas.height,
97
+ width: originalScale.width * this.atlasSize.width,
98
+ height: originalScale.height * this.atlasSize.height,
86
99
  };
87
100
  }
88
101
 
@@ -95,9 +108,10 @@ export class AssetManager {
95
108
  getCroppedSize(id: TextureId): Size {
96
109
  const scaledUvs = this.extra.getAtlasCoords(id)[0].uvScaleCropped;
97
110
  if (scaledUvs) {
111
+ // Use atlasSize instead of textureAtlas.width/height for WebGL2 compatibility
98
112
  return {
99
- width: scaledUvs.width * this.textureAtlas.width,
100
- height: scaledUvs.height * this.textureAtlas.height,
113
+ width: scaledUvs.width * this.atlasSize.width,
114
+ height: scaledUvs.height * this.atlasSize.height,
101
115
  };
102
116
  }
103
117
  return this.getSize(id);
@@ -165,12 +179,20 @@ export class AssetManager {
165
179
  *
166
180
  * Note: this will consume one texture atlas per texture.
167
181
  * For more efficient loading of multiple textures, consider {@link loadBundle}
182
+ *
183
+ * @throws Error if using WebGL backend (use registerBundle instead)
168
184
  */
169
185
  async loadTexture(
170
186
  id: TextureId,
171
187
  url: URL | ImageBitmap,
172
188
  options?: Partial<TextureBundleOpts>,
173
189
  ) {
190
+ if (this.#backend.type !== "webgpu") {
191
+ throw new Error(
192
+ "loadTexture is only supported with WebGPU backend. Use registerBundle with prebaked atlases instead.",
193
+ );
194
+ }
195
+
174
196
  const bitmap =
175
197
  url instanceof ImageBitmap ? url : await getBitmapFromUrl(url);
176
198
 
@@ -180,24 +202,25 @@ export class AssetManager {
180
202
  );
181
203
  const atlasIndex = this.extra.nextAvailableAtlasIndex();
182
204
 
183
- if (options?.cropTransparentPixels) {
205
+ if (options?.cropTransparentPixels && this.#cropComputeShader) {
184
206
  textureWrapper =
185
207
  await this.#cropComputeShader.processTexture(textureWrapper);
186
208
  }
187
209
 
188
210
  this.#copyTextureToAtlas(textureWrapper.texture, atlasIndex);
189
211
 
212
+ const { width: atlasWidth, height: atlasHeight } = this.atlasSize;
190
213
  const coords: AtlasCoords = {
191
214
  uvOffset: { x: 0, y: 0 },
192
215
  cropOffset: textureWrapper.cropOffset,
193
216
  uvScale: {
194
- width: textureWrapper.texture.width / this.textureAtlas.width,
195
- height: textureWrapper.texture.height / this.textureAtlas.height,
217
+ width: textureWrapper.texture.width / atlasWidth,
218
+ height: textureWrapper.texture.height / atlasHeight,
196
219
  },
197
220
  originalSize: textureWrapper.originalSize,
198
221
  uvScaleCropped: {
199
- width: textureWrapper.texture.width / this.textureAtlas.width,
200
- height: textureWrapper.texture.height / this.textureAtlas.height,
222
+ width: textureWrapper.texture.width / atlasWidth,
223
+ height: textureWrapper.texture.height / atlasHeight,
201
224
  },
202
225
  atlasIndex,
203
226
  };
@@ -292,23 +315,36 @@ export class AssetManager {
292
315
  * @param id - The id of the font to load
293
316
  * @param url - The url of the font to load
294
317
  * @param fallbackCharacter - The character to use as a fallback if the font does not contain a character to be rendererd
318
+ *
319
+ * @throws Error if using WebGL backend (fonts not supported in WebGL mode)
295
320
  */
296
321
  async loadFont(id: string, url: URL, fallbackCharacter = "_") {
322
+ if (this.#backend.type !== "webgpu") {
323
+ throw new Error(
324
+ "loadFont is only supported with WebGPU backend. Text rendering is not available in WebGL mode.",
325
+ );
326
+ }
327
+
328
+ const webgpuBackend = this.#backend as WebGPUBackend;
329
+ const device = webgpuBackend.device;
330
+ const presentationFormat = webgpuBackend.presentationFormat;
331
+ const limits = this.#backend.limits;
332
+
297
333
  const font = await MsdfFont.create(id, url);
298
334
  font.fallbackCharacter = fallbackCharacter;
299
335
  const fontPipeline = await FontPipeline.create(
300
- this.#device,
336
+ device,
301
337
  font,
302
- this.#presentationFormat,
303
- this.#limits.maxTextLength,
338
+ presentationFormat,
339
+ limits.maxTextLength,
304
340
  );
305
341
 
306
342
  const textShader = new TextShader(
307
- this.#device,
343
+ this.#backend as WebGPUBackend,
308
344
  fontPipeline,
309
345
  font,
310
- this.#presentationFormat,
311
- this.#limits.instanceCount,
346
+ presentationFormat,
347
+ limits.instanceCount,
312
348
  );
313
349
  this.#fonts.set(id, textShader);
314
350
  return id;
@@ -353,7 +389,8 @@ export class AssetManager {
353
389
  * @private
354
390
  */
355
391
  #createTextureFromImageBitmap(bitmap: ImageBitmap, name: string): GPUTexture {
356
- const texture = this.#device.createTexture({
392
+ const device = (this.#backend as WebGPUBackend).device;
393
+ const texture = device.createTexture({
357
394
  label: `${name} Intermediary Texture`,
358
395
  size: [bitmap.width, bitmap.height],
359
396
  format: "rgba8unorm",
@@ -364,7 +401,7 @@ export class AssetManager {
364
401
  GPUTextureUsage.RENDER_ATTACHMENT,
365
402
  });
366
403
 
367
- this.#device.queue.copyExternalImageToTexture(
404
+ device.queue.copyExternalImageToTexture(
368
405
  {
369
406
  source: bitmap,
370
407
  },
@@ -380,20 +417,27 @@ export class AssetManager {
380
417
  bundleId: BundleId,
381
418
  opts: TextureBundleOpts,
382
419
  ) {
420
+ if (this.#backend.type !== "webgpu") {
421
+ throw new Error(
422
+ "Dynamic texture bundle registration is only supported with WebGPU backend. Use prebaked atlases instead.",
423
+ );
424
+ }
425
+
426
+ const device = (this.#backend as WebGPUBackend).device;
383
427
  const images = new Map<string, TextureWithMetadata>();
384
428
 
385
- let networkLoadTime = 0;
429
+ let _networkLoadTime = 0;
386
430
  await Promise.all(
387
431
  Object.entries(opts.textures).map(async ([id, url]) => {
388
432
  const now = performance.now();
389
433
  const bitmap = await getBitmapFromUrl(url);
390
- networkLoadTime += performance.now() - now;
434
+ _networkLoadTime += performance.now() - now;
391
435
  let textureWrapper: TextureWithMetadata = this.#wrapBitmapToTexture(
392
436
  bitmap,
393
437
  id,
394
438
  );
395
439
 
396
- if (opts.cropTransparentPixels) {
440
+ if (opts.cropTransparentPixels && this.#cropComputeShader) {
397
441
  textureWrapper =
398
442
  await this.#cropComputeShader.processTexture(textureWrapper);
399
443
  }
@@ -403,8 +447,8 @@ export class AssetManager {
403
447
 
404
448
  const atlases = await packBitmapsToAtlas(
405
449
  images,
406
- this.#limits.textureSize,
407
- this.#device,
450
+ this.#backend.limits.textureSize,
451
+ device,
408
452
  );
409
453
 
410
454
  this.bundles.registerDynamicBundle(bundleId, atlases);
@@ -467,6 +511,8 @@ export class AssetManager {
467
511
  * @returns Usage stats for texture atlases
468
512
  */
469
513
  getAtlasUsage: () => {
514
+ const atlas = this.#backend.getTextureAtlas(this.#atlasId);
515
+ const totalLayers = atlas?.layers ?? 0;
470
516
  return {
471
517
  /**
472
518
  * The number of texture atlases that are currently unused
@@ -476,11 +522,11 @@ export class AssetManager {
476
522
  /**
477
523
  * The number of texture atlases that are currently in use.
478
524
  */
479
- used: this.#limits.textureArrayLayers - this.#availableIndices.size,
525
+ used: totalLayers - this.#availableIndices.size,
480
526
  /**
481
527
  * The total number of texture atlases that can be loaded.
482
528
  */
483
- total: this.#limits.textureArrayLayers,
529
+ total: totalLayers,
484
530
  };
485
531
  },
486
532
 
@@ -489,7 +535,9 @@ export class AssetManager {
489
535
  *
490
536
  */
491
537
  nextAvailableAtlasIndex: () => {
492
- for (let i = 0; i < this.#limits.textureArrayLayers; i++) {
538
+ const atlas = this.#backend.getTextureAtlas(this.#atlasId);
539
+ const totalLayers = atlas?.layers ?? 0;
540
+ for (let i = 0; i < totalLayers; i++) {
493
541
  if (this.#availableIndices.has(i)) {
494
542
  this.#availableIndices.delete(i);
495
543
  return i;
@@ -507,34 +555,8 @@ export class AssetManager {
507
555
  loadAtlas: async (atlas: CpuTextureAtlas) => {
508
556
  const atlasIndex = this.extra.nextAvailableAtlasIndex();
509
557
 
510
- if (atlas.rg8Bytes) {
511
- const { width: w, height: h } = {
512
- width: this.textureAtlas.width,
513
- height: this.textureAtlas.height,
514
- };
515
-
516
- // WebGPU requires 256-byte bytesPerRow
517
- const rowBytes = w * 2;
518
- assert(rowBytes % 256 === 0, "rowBytes must be a multiple of 256");
519
-
520
- this.#device.queue.writeTexture(
521
- { texture: this.textureAtlas, origin: { x: 0, y: 0, z: atlasIndex } },
522
- atlas.rg8Bytes,
523
- { bytesPerRow: rowBytes, rowsPerImage: h },
524
- { width: w, height: h, depthOrArrayLayers: 1 },
525
- );
526
- } else {
527
- this.#device.queue.copyExternalImageToTexture(
528
- {
529
- source: atlas.texture,
530
- },
531
- {
532
- texture: this.textureAtlas,
533
- origin: [0, 0, atlasIndex],
534
- },
535
- [atlas.texture.width, atlas.texture.height, 1],
536
- );
537
- }
558
+ // Delegate to backend for actual GPU upload
559
+ await this.#backend.uploadAtlas(atlas, atlasIndex, this.#atlasId);
538
560
 
539
561
  for (const [id, region] of atlas.textureRegions) {
540
562
  this.bundles.addTextureEntry(id, { ...region, atlasIndex });
@@ -569,7 +591,8 @@ export class AssetManager {
569
591
  }
570
592
 
571
593
  #copyTextureToAtlas(texture: GPUTexture, atlasIndex: number) {
572
- const copyEncoder: GPUCommandEncoder = this.#device.createCommandEncoder();
594
+ const device = (this.#backend as WebGPUBackend).device;
595
+ const copyEncoder: GPUCommandEncoder = device.createCommandEncoder();
573
596
  copyEncoder.copyTextureToTexture(
574
597
  {
575
598
  texture: texture,
@@ -584,12 +607,13 @@ export class AssetManager {
584
607
  [texture.width, texture.height, 1],
585
608
  );
586
609
 
587
- this.#device.queue.submit([copyEncoder.finish()]);
610
+ device.queue.submit([copyEncoder.finish()]);
588
611
  }
612
+
589
613
  /**
590
614
  * Destroy the texture atlas. This should free up ~4gb of gpu memory (and make all draw calls fail)
591
615
  */
592
616
  destroy() {
593
- this.textureAtlas.destroy();
617
+ this.#backend.destroy();
594
618
  }
595
619
  }
@@ -1,4 +1,3 @@
1
- import { unzipSync } from "fflate";
2
1
  import type {
3
2
  CpuTextureAtlas,
4
3
  TextureRegion,
@@ -16,97 +15,6 @@ export async function getBitmapFromUrl(url: URL): Promise<ImageBitmap> {
16
15
  }
17
16
  }
18
17
 
19
- /**
20
- * Converts an image Blob to ImageData.
21
- *
22
- * @param blob - The Blob containing the image.
23
- * @returns A Promise resolving to the image as ImageData.
24
- */
25
- async function blobToImageData(blob: Blob) {
26
- const imageBitmap: ImageBitmap = await createImageBitmap(blob);
27
-
28
- const canvas = document.createElement("canvas");
29
- const ctx = canvas.getContext("2d");
30
-
31
- if (!ctx) {
32
- throw new Error("Failed to get 2D context from canvas");
33
- }
34
-
35
- canvas.width = imageBitmap.width;
36
- canvas.height = imageBitmap.height;
37
-
38
- ctx.drawImage(imageBitmap, 0, 0);
39
-
40
- const imageData: ImageData = ctx.getImageData(
41
- 0,
42
- 0,
43
- canvas.width,
44
- canvas.height,
45
- );
46
-
47
- imageBitmap.close();
48
-
49
- return imageData;
50
- }
51
-
52
- /**
53
- * Creates a checkerboard pattern ImageData in black and purple.
54
- *
55
- * @param width - Width of the image.
56
- * @param height - Height of the image.
57
- * @param tileSize - Size of each checker tile (default is 8).
58
- * @returns The generated ImageData object.
59
- */
60
- function createCheckerboardImageData(
61
- width: number,
62
- height: number,
63
- tileSize = 8,
64
- ): ImageData {
65
- const data = new Uint8ClampedArray(width * height * 4);
66
-
67
- const purple = [128, 0, 128, 255]; // #800080
68
- const black = [0, 0, 0, 255]; // #000000
69
-
70
- for (let y = 0; y < height; y++) {
71
- for (let x = 0; x < width; x++) {
72
- const usePurple =
73
- (Math.floor(x / tileSize) + Math.floor(y / tileSize)) % 2 === 0;
74
- const color = usePurple ? purple : black;
75
- const idx = (y * width + x) * 4;
76
-
77
- data.set(color, idx);
78
- }
79
- }
80
-
81
- return new ImageData(data, width, height);
82
- }
83
-
84
- export async function loadZip(
85
- zipUrl: URL,
86
- ): Promise<{ path: string; bitmap: ImageBitmap }[]> {
87
- console.time("fetch zip");
88
- const zip = await fetch(zipUrl);
89
- const zipBlob = await zip.blob();
90
- const zipUint8Array = await zipBlob.arrayBuffer();
91
- console.timeEnd("fetch zip");
92
-
93
- console.time("unzip");
94
- const files = unzipSync(new Uint8Array(zipUint8Array));
95
- console.timeEnd("unzip");
96
-
97
- console.time("create bitmaps");
98
- const validFiles = Object.entries(files).filter(
99
- ([path]) => !path.match("__MACOS") && path.endsWith(".png"),
100
- );
101
-
102
- return await Promise.all(
103
- validFiles.map(async ([path, file]) => {
104
- const bitmap = await createImageBitmap(new Blob([file]));
105
- return { path, bitmap };
106
- }),
107
- );
108
- }
109
-
110
18
  export async function packBitmapsToAtlas(
111
19
  images: Map<string, TextureWithMetadata>,
112
20
  textureSize: number,
@@ -2,7 +2,7 @@ import {
2
2
  makeBindGroupLayoutDescriptors,
3
3
  makeShaderDataDefinitions,
4
4
  } from "webgpu-utils";
5
- import type { ShaderDescriptor } from "../shaders/ShaderDescriptor";
5
+ import type { ShaderDescriptor } from "../backends/webgpu/ShaderDescriptor";
6
6
  import { assert } from "./assert";
7
7
 
8
8
  // convenience functions to wrap verbose webgpu apis - not part of public api
@@ -1 +0,0 @@
1
- {"version":3,"file":"EngineUniform.d.ts","sourceRoot":"","sources":["../../src/shaders/EngineUniform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,IAAI,CAAC;CAG5B,CAAC"}
@@ -1,15 +0,0 @@
1
- import type { SceneNode } from "../scene/SceneNode";
2
- import type { EngineUniform } from "./EngineUniform";
3
- export interface IShader {
4
- startFrame: (device: GPUDevice, uniform: EngineUniform) => void;
5
- /**
6
- * Process a batch of nodes.
7
- *
8
- * @param renderPass - The render pass to use.
9
- * @param nodes - The nodes to process.
10
- * @returns The number of draw calls made.
11
- */
12
- processBatch: (renderPass: GPURenderPassEncoder, nodes: SceneNode[]) => number;
13
- endFrame: () => void;
14
- }
15
- //# sourceMappingURL=IShader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"IShader.d.ts","sourceRoot":"","sources":["../../src/shaders/IShader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAEhE;;;;;;OAMG;IACH,YAAY,EAAE,CACZ,UAAU,EAAE,oBAAoB,EAChC,KAAK,EAAE,SAAS,EAAE,KACf,MAAM,CAAC;IAEZ,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB"}
@@ -1,18 +0,0 @@
1
- import type { SceneNode } from "../scene/SceneNode";
2
- import type { AssetManager } from "../textures/AssetManager";
3
- import type { EngineUniform } from "./EngineUniform";
4
- import type { IShader } from "./IShader";
5
- export type QuadShaderOpts = {
6
- assetManager?: AssetManager;
7
- blendMode?: GPUBlendState;
8
- };
9
- export declare class QuadShader implements IShader {
10
- #private;
11
- label: string;
12
- code: string;
13
- startFrame(device: GPUDevice, uniform: EngineUniform): void;
14
- processBatch(renderPass: GPURenderPassEncoder, nodes: SceneNode[]): number;
15
- endFrame(): void;
16
- constructor(label: string, assetManager: AssetManager, device: GPUDevice, presentationFormat: GPUTextureFormat, userCode: string, instanceCount: number, blendMode?: GPUBlendState);
17
- }
18
- //# sourceMappingURL=QuadShader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"QuadShader.d.ts","sourceRoot":"","sources":["../../src/shaders/QuadShader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAQ7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B,CAAC;AAEF,qBAAa,UAAW,YAAW,OAAO;;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IAYb,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa;IAiBpD,YAAY,CAAC,UAAU,EAAE,oBAAoB,EAAE,KAAK,EAAE,SAAS,EAAE;IA0DjE,QAAQ;gBAGN,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,SAAS,EACjB,kBAAkB,EAAE,gBAAgB,EACpC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,aAAa;CA4G5B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ShaderDescriptor.d.ts","sourceRoot":"","sources":["../../src/shaders/ShaderDescriptor.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC"}
@@ -1,6 +0,0 @@
1
- export * from "./EngineUniform";
2
- export * from "./IShader";
3
- export * from "./postprocess/mod";
4
- export * from "./ShaderDescriptor";
5
- export * from "./samplers";
6
- //# sourceMappingURL=mod.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/shaders/mod.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/shaders/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAe,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,UAK/C;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,gBAAgB,CA4ClB;AAuED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,QAAQ,GAAE,iBAA8B,GACvC,qBAAqB,CAkDvB;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,QAaxD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAqB9D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"blur.d.ts","sourceRoot":"","sources":["../../../src/shaders/postprocess/blur.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,wBAAgB,IAAI,CAClB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,KAAK,EACjB,kBAAkB,EAAE,gBAAgB,EACpC,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,QA2InC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../src/shaders/postprocess/mod.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;OAMG;IACH,OAAO,CACL,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,EAClC,MAAM,EAAE,UAAU,GACjB,IAAI,CAAC;CACT,CAAC;AAEF,eAAO,MAAM,mBAAmB;+BACd,SAAS,KAAG,UAAU;0CAWX,SAAS,KAAG,qBAAqB;oCAOvC,SAAS,KAAG,eAAe;0CAuBrB,SAAS,KAAG,2BAA2B;CAY1D,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"samplers.d.ts","sourceRoot":"","sources":["../../src/shaders/samplers.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,EAAE,oBAK7B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,oBAK3B,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"example.wgsl.d.ts","sourceRoot":"","sources":["../../../src/shaders/wgsl/example.wgsl.ts"],"names":[],"mappings":";AAEA,wBAqBE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"hello.wgsl.d.ts","sourceRoot":"","sources":["../../../src/shaders/wgsl/hello.wgsl.ts"],"names":[],"mappings":";AAEA,wBA2DE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"helloInstanced.wgsl.d.ts","sourceRoot":"","sources":["../../../src/shaders/wgsl/helloInstanced.wgsl.ts"],"names":[],"mappings":";AAEA,wBA2CE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"quad.wgsl.d.ts","sourceRoot":"","sources":["../../../src/shaders/wgsl/quad.wgsl.ts"],"names":[],"mappings":";AAAA,wBA2IE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"TextureComputeShader.d.ts","sourceRoot":"","sources":["../../src/textures/TextureComputeShader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAwBnD;;;;;GAKG;AACH,qBAAa,oBAAoB;;gBAQ7B,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,kBAAkB,EAChC,aAAa,EAAE,kBAAkB,EACjC,sBAAsB,EAAE,kBAAkB;IAiB5C;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS;IAU/B;;;OAGG;IACG,cAAc,CAClB,cAAc,EAAE,mBAAmB,GAClC,OAAO,CAAC,mBAAmB,CAAC;CAwQhC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pixel-scraping.wgsl.d.ts","sourceRoot":"","sources":["../../src/textures/pixel-scraping.wgsl.ts"],"names":[],"mappings":";AAAA,wBAkIE"}
@@ -1,20 +0,0 @@
1
- import type { SceneNode } from "../scene/SceneNode";
2
- import type { EngineUniform } from "./EngineUniform";
3
-
4
- export interface IShader {
5
- startFrame: (device: GPUDevice, uniform: EngineUniform) => void;
6
-
7
- /**
8
- * Process a batch of nodes.
9
- *
10
- * @param renderPass - The render pass to use.
11
- * @param nodes - The nodes to process.
12
- * @returns The number of draw calls made.
13
- */
14
- processBatch: (
15
- renderPass: GPURenderPassEncoder,
16
- nodes: SceneNode[],
17
- ) => number;
18
-
19
- endFrame: () => void;
20
- }
@@ -1,5 +0,0 @@
1
- export * from "./EngineUniform";
2
- export * from "./IShader";
3
- export * from "./postprocess/mod";
4
- export * from "./ShaderDescriptor";
5
- export * from "./samplers";
File without changes
File without changes
File without changes
File without changes