@bloopjs/toodle 0.0.104 → 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.
- package/dist/Toodle.d.ts +41 -19
- package/dist/Toodle.d.ts.map +1 -1
- package/dist/backends/IBackendShader.d.ts +48 -0
- package/dist/backends/IBackendShader.d.ts.map +1 -0
- package/dist/backends/IRenderBackend.d.ts +92 -0
- package/dist/backends/IRenderBackend.d.ts.map +1 -0
- package/dist/backends/ITextureAtlas.d.ts +34 -0
- package/dist/backends/ITextureAtlas.d.ts.map +1 -0
- package/dist/backends/detection.d.ts +16 -0
- package/dist/backends/detection.d.ts.map +1 -0
- package/dist/backends/mod.d.ts +9 -0
- package/dist/backends/mod.d.ts.map +1 -0
- package/dist/backends/webgl2/WebGLBackend.d.ts +51 -0
- package/dist/backends/webgl2/WebGLBackend.d.ts.map +1 -0
- package/dist/backends/webgl2/WebGLQuadShader.d.ts +17 -0
- package/dist/backends/webgl2/WebGLQuadShader.d.ts.map +1 -0
- package/dist/backends/webgl2/glsl/quad.glsl.d.ts +12 -0
- package/dist/backends/webgl2/glsl/quad.glsl.d.ts.map +1 -0
- package/dist/backends/webgl2/mod.d.ts +3 -0
- package/dist/backends/webgl2/mod.d.ts.map +1 -0
- package/dist/backends/webgpu/ShaderDescriptor.d.ts.map +1 -0
- package/dist/{textures → backends/webgpu}/TextureComputeShader.d.ts +1 -1
- package/dist/backends/webgpu/TextureComputeShader.d.ts.map +1 -0
- package/dist/backends/webgpu/WebGPUBackend.d.ts +67 -0
- package/dist/backends/webgpu/WebGPUBackend.d.ts.map +1 -0
- package/dist/backends/webgpu/WebGPUQuadShader.d.ts +18 -0
- package/dist/backends/webgpu/WebGPUQuadShader.d.ts.map +1 -0
- package/dist/backends/webgpu/mod.d.ts +3 -0
- package/dist/backends/webgpu/mod.d.ts.map +1 -0
- package/dist/backends/webgpu/parser.d.ts.map +1 -0
- package/dist/{shaders → backends/webgpu}/postprocess/blur.d.ts +1 -1
- package/dist/backends/webgpu/postprocess/blur.d.ts.map +1 -0
- package/dist/{shaders → backends/webgpu}/postprocess/mod.d.ts +1 -1
- package/dist/backends/webgpu/postprocess/mod.d.ts.map +1 -0
- package/dist/backends/webgpu/samplers.d.ts.map +1 -0
- package/dist/backends/webgpu/wgsl/example.wgsl.d.ts.map +1 -0
- package/dist/backends/webgpu/wgsl/hello.wgsl.d.ts.map +1 -0
- package/dist/backends/webgpu/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
- package/dist/backends/webgpu/wgsl/pixel-scraping.wgsl.d.ts.map +1 -0
- package/dist/backends/webgpu/wgsl/quad.wgsl.d.ts.map +1 -0
- package/dist/coreTypes/EngineUniform.d.ts.map +1 -0
- package/dist/mod.d.ts +3 -2
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +7247 -6663
- package/dist/mod.js.map +27 -22
- package/dist/scene/Batcher.d.ts +2 -2
- package/dist/scene/Batcher.d.ts.map +1 -1
- package/dist/scene/QuadNode.d.ts +3 -2
- package/dist/scene/QuadNode.d.ts.map +1 -1
- package/dist/scene/RenderComponent.d.ts +2 -2
- package/dist/scene/RenderComponent.d.ts.map +1 -1
- package/dist/text/TextShader.d.ts +8 -6
- package/dist/text/TextShader.d.ts.map +1 -1
- package/dist/textures/AssetManager.d.ts +21 -5
- package/dist/textures/AssetManager.d.ts.map +1 -1
- package/dist/textures/util.d.ts.map +1 -1
- package/dist/utils/boilerplate.d.ts +1 -1
- package/dist/utils/boilerplate.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Toodle.ts +124 -156
- package/src/backends/IBackendShader.ts +52 -0
- package/src/backends/IRenderBackend.ts +118 -0
- package/src/backends/ITextureAtlas.ts +35 -0
- package/src/backends/detection.ts +46 -0
- package/src/backends/mod.ts +29 -0
- package/src/backends/webgl2/WebGLBackend.ts +256 -0
- package/src/backends/webgl2/WebGLQuadShader.ts +278 -0
- package/src/backends/webgl2/glsl/quad.glsl.ts +114 -0
- package/src/backends/webgl2/mod.ts +2 -0
- package/src/{textures → backends/webgpu}/TextureComputeShader.ts +2 -48
- package/src/backends/webgpu/WebGPUBackend.ts +350 -0
- package/src/{shaders/QuadShader.ts → backends/webgpu/WebGPUQuadShader.ts} +226 -170
- package/src/backends/webgpu/mod.ts +2 -0
- package/src/{shaders → backends/webgpu}/parser.ts +2 -2
- package/src/{shaders → backends/webgpu}/postprocess/blur.ts +2 -2
- package/src/{shaders → backends/webgpu}/postprocess/mod.ts +1 -1
- package/src/mod.ts +3 -2
- package/src/scene/Batcher.ts +3 -3
- package/src/scene/QuadNode.ts +6 -2
- package/src/scene/RenderComponent.ts +2 -2
- package/src/text/TextShader.ts +17 -11
- package/src/textures/AssetManager.ts +117 -93
- package/src/textures/util.ts +0 -65
- package/src/utils/boilerplate.ts +1 -1
- package/dist/shaders/EngineUniform.d.ts.map +0 -1
- package/dist/shaders/IShader.d.ts +0 -15
- package/dist/shaders/IShader.d.ts.map +0 -1
- package/dist/shaders/QuadShader.d.ts +0 -18
- package/dist/shaders/QuadShader.d.ts.map +0 -1
- package/dist/shaders/ShaderDescriptor.d.ts.map +0 -1
- package/dist/shaders/mod.d.ts +0 -6
- package/dist/shaders/mod.d.ts.map +0 -1
- package/dist/shaders/parser.d.ts.map +0 -1
- package/dist/shaders/postprocess/blur.d.ts.map +0 -1
- package/dist/shaders/postprocess/mod.d.ts.map +0 -1
- package/dist/shaders/samplers.d.ts.map +0 -1
- package/dist/shaders/wgsl/example.wgsl.d.ts.map +0 -1
- package/dist/shaders/wgsl/hello.wgsl.d.ts.map +0 -1
- package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +0 -1
- package/dist/shaders/wgsl/quad.wgsl.d.ts.map +0 -1
- package/dist/textures/TextureComputeShader.d.ts.map +0 -1
- package/dist/textures/pixel-scraping.wgsl.d.ts.map +0 -1
- package/src/shaders/IShader.ts +0 -20
- package/src/shaders/mod.ts +0 -5
- /package/dist/{shaders → backends/webgpu}/ShaderDescriptor.d.ts +0 -0
- /package/dist/{shaders → backends/webgpu}/parser.d.ts +0 -0
- /package/dist/{shaders → backends/webgpu}/samplers.d.ts +0 -0
- /package/dist/{shaders → backends/webgpu}/wgsl/example.wgsl.d.ts +0 -0
- /package/dist/{shaders → backends/webgpu}/wgsl/hello.wgsl.d.ts +0 -0
- /package/dist/{shaders → backends/webgpu}/wgsl/helloInstanced.wgsl.d.ts +0 -0
- /package/dist/{textures → backends/webgpu/wgsl}/pixel-scraping.wgsl.d.ts +0 -0
- /package/dist/{shaders → backends/webgpu}/wgsl/quad.wgsl.d.ts +0 -0
- /package/dist/{shaders → coreTypes}/EngineUniform.d.ts +0 -0
- /package/src/{shaders → backends/webgpu}/ShaderDescriptor.ts +0 -0
- /package/src/{shaders → backends/webgpu}/samplers.ts +0 -0
- /package/src/{shaders → backends/webgpu}/wgsl/example.wgsl.ts +0 -0
- /package/src/{shaders → backends/webgpu}/wgsl/hello.wgsl.ts +0 -0
- /package/src/{shaders → backends/webgpu}/wgsl/helloInstanced.wgsl.ts +0 -0
- /package/src/{textures → backends/webgpu/wgsl}/pixel-scraping.wgsl.ts +0 -0
- /package/src/{shaders → backends/webgpu}/wgsl/quad.wgsl.ts +0 -0
- /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
|
-
/**
|
|
30
|
-
|
|
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
|
-
#
|
|
37
|
-
#
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.bundles =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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:
|
|
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.
|
|
85
|
-
height: originalScale.height * this.
|
|
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.
|
|
100
|
-
height: scaledUvs.height * this.
|
|
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 /
|
|
195
|
-
height: textureWrapper.texture.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 /
|
|
200
|
-
height: textureWrapper.texture.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
|
-
|
|
336
|
+
device,
|
|
301
337
|
font,
|
|
302
|
-
|
|
303
|
-
|
|
338
|
+
presentationFormat,
|
|
339
|
+
limits.maxTextLength,
|
|
304
340
|
);
|
|
305
341
|
|
|
306
342
|
const textShader = new TextShader(
|
|
307
|
-
this.#
|
|
343
|
+
this.#backend as WebGPUBackend,
|
|
308
344
|
fontPipeline,
|
|
309
345
|
font,
|
|
310
|
-
|
|
311
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
525
|
+
used: totalLayers - this.#availableIndices.size,
|
|
480
526
|
/**
|
|
481
527
|
* The total number of texture atlases that can be loaded.
|
|
482
528
|
*/
|
|
483
|
-
total:
|
|
529
|
+
total: totalLayers,
|
|
484
530
|
};
|
|
485
531
|
},
|
|
486
532
|
|
|
@@ -489,7 +535,9 @@ export class AssetManager {
|
|
|
489
535
|
*
|
|
490
536
|
*/
|
|
491
537
|
nextAvailableAtlasIndex: () => {
|
|
492
|
-
|
|
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
|
-
|
|
511
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
617
|
+
this.#backend.destroy();
|
|
594
618
|
}
|
|
595
619
|
}
|
package/src/textures/util.ts
CHANGED
|
@@ -16,71 +16,6 @@ export async function getBitmapFromUrl(url: URL): Promise<ImageBitmap> {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
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
19
|
export async function loadZip(
|
|
85
20
|
zipUrl: URL,
|
|
86
21
|
): Promise<{ path: string; bitmap: ImageBitmap }[]> {
|
package/src/utils/boilerplate.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
makeBindGroupLayoutDescriptors,
|
|
3
3
|
makeShaderDataDefinitions,
|
|
4
4
|
} from "webgpu-utils";
|
|
5
|
-
import type { ShaderDescriptor } from "../
|
|
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"}
|
package/dist/shaders/mod.d.ts
DELETED
|
@@ -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"}
|
package/src/shaders/IShader.ts
DELETED
|
@@ -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
|
-
}
|
package/src/shaders/mod.ts
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|