@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.
- package/dist/Toodle.d.ts +50 -20
- 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 +6741 -6151
- package/dist/mod.js.map +28 -23
- 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/scene/SceneNode.d.ts +2 -2
- package/dist/scene/SceneNode.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/types.d.ts +4 -2
- package/dist/textures/types.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 +155 -171
- 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 +7 -6
- package/src/scene/RenderComponent.ts +2 -2
- package/src/scene/SceneNode.ts +11 -12
- package/src/text/TextNode.ts +2 -2
- package/src/text/TextShader.ts +17 -11
- package/src/textures/AssetManager.ts +119 -94
- package/src/textures/types.ts +4 -2
- 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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Color } from "../coreTypes/Color";
|
|
2
|
+
import type { EngineUniform } from "../coreTypes/EngineUniform";
|
|
3
|
+
import type { Size } from "../coreTypes/Size";
|
|
4
|
+
import type { Limits } from "../limits";
|
|
5
|
+
import type { CpuTextureAtlas } from "../textures/types";
|
|
6
|
+
import type { IBackendShader, QuadShaderCreationOpts } from "./IBackendShader";
|
|
7
|
+
import type { ITextureAtlas, TextureAtlasOptions } from "./ITextureAtlas";
|
|
8
|
+
|
|
9
|
+
export type BackendType = "webgpu" | "webgl2";
|
|
10
|
+
|
|
11
|
+
export type BlendFactor =
|
|
12
|
+
| "one"
|
|
13
|
+
| "zero"
|
|
14
|
+
| "src-alpha"
|
|
15
|
+
| "one-minus-src-alpha"
|
|
16
|
+
| "dst-alpha"
|
|
17
|
+
| "one-minus-dst-alpha";
|
|
18
|
+
export type BlendOperation = "add" | "subtract" | "reverse-subtract";
|
|
19
|
+
|
|
20
|
+
export type BlendMode = {
|
|
21
|
+
color: {
|
|
22
|
+
srcFactor: BlendFactor;
|
|
23
|
+
dstFactor: BlendFactor;
|
|
24
|
+
operation: BlendOperation;
|
|
25
|
+
};
|
|
26
|
+
alpha: {
|
|
27
|
+
srcFactor: BlendFactor;
|
|
28
|
+
dstFactor: BlendFactor;
|
|
29
|
+
operation: BlendOperation;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The render backend interface abstracts WebGPU and WebGL differences.
|
|
35
|
+
*
|
|
36
|
+
* Implementations handle GPU-specific operations like texture management,
|
|
37
|
+
* shader creation, and frame lifecycle.
|
|
38
|
+
*/
|
|
39
|
+
export interface IRenderBackend {
|
|
40
|
+
/** The type of backend ("webgpu" or "webgl2") */
|
|
41
|
+
readonly type: BackendType;
|
|
42
|
+
|
|
43
|
+
/** Engine limits (texture size, instance count, etc.) */
|
|
44
|
+
readonly limits: Limits;
|
|
45
|
+
|
|
46
|
+
/** Size of the default texture atlas */
|
|
47
|
+
readonly atlasSize: Size;
|
|
48
|
+
|
|
49
|
+
/** Default atlas ID (always "default") */
|
|
50
|
+
readonly defaultAtlasId: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Begin a new frame.
|
|
54
|
+
* WebGPU: Creates command encoder and render pass
|
|
55
|
+
* WebGL: Clears the canvas if loadOp is "clear"
|
|
56
|
+
*/
|
|
57
|
+
startFrame(clearColor: Color, loadOp: "clear" | "load"): void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* End the current frame and submit to GPU.
|
|
61
|
+
* WebGPU: Ends render pass and submits command buffer
|
|
62
|
+
* WebGL: Flushes pending operations
|
|
63
|
+
*/
|
|
64
|
+
endFrame(): void;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Update engine uniforms (view-projection matrix, resolution).
|
|
68
|
+
* Called once per frame before shader processing.
|
|
69
|
+
*/
|
|
70
|
+
updateEngineUniform(uniform: EngineUniform): void;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Upload a CPU texture atlas to a GPU texture array layer.
|
|
74
|
+
* @param atlas - The CPU-side atlas data to upload
|
|
75
|
+
* @param layerIndex - Which layer in the texture array to upload to
|
|
76
|
+
* @param atlasId - Which atlas to upload to (default: "default")
|
|
77
|
+
*/
|
|
78
|
+
uploadAtlas(
|
|
79
|
+
atlas: CpuTextureAtlas,
|
|
80
|
+
layerIndex: number,
|
|
81
|
+
atlasId?: string,
|
|
82
|
+
): Promise<void>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a new texture atlas.
|
|
86
|
+
* @param id - Unique identifier for this atlas
|
|
87
|
+
* @param options - Atlas configuration (format, layers, size)
|
|
88
|
+
*/
|
|
89
|
+
createTextureAtlas(id: string, options?: TextureAtlasOptions): ITextureAtlas;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get a texture atlas by ID.
|
|
93
|
+
* @param id - Atlas identifier or defaults to "default"
|
|
94
|
+
* @returns The atlas, or null if not found
|
|
95
|
+
*/
|
|
96
|
+
getTextureAtlas(id?: string): ITextureAtlas | null;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Destroy a texture atlas and free GPU resources.
|
|
100
|
+
* @param id - Atlas identifier
|
|
101
|
+
*/
|
|
102
|
+
destroyTextureAtlas(id: string): void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a quad shader for instanced rendering.
|
|
106
|
+
*/
|
|
107
|
+
createQuadShader(opts: QuadShaderCreationOpts): IBackendShader;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Handle canvas resize.
|
|
111
|
+
*/
|
|
112
|
+
resize(width: number, height: number): void;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Clean up GPU resources.
|
|
116
|
+
*/
|
|
117
|
+
destroy(): void;
|
|
118
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported texture atlas formats.
|
|
3
|
+
*/
|
|
4
|
+
export type TextureAtlasFormat = "rgba8unorm" | "rg8unorm";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a texture atlas.
|
|
8
|
+
*/
|
|
9
|
+
export type TextureAtlasOptions = {
|
|
10
|
+
/** Texture format (default: "rgba8unorm") */
|
|
11
|
+
format?: TextureAtlasFormat;
|
|
12
|
+
/** Number of array layers (default: limits.textureArrayLayers) */
|
|
13
|
+
layers?: number;
|
|
14
|
+
/** Atlas size in pixels (default: limits.textureSize) */
|
|
15
|
+
size?: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Backend-agnostic texture atlas interface.
|
|
20
|
+
*
|
|
21
|
+
* Texture atlases are GPU texture arrays that store multiple textures.
|
|
22
|
+
* Each backend manages its atlases and provides this common interface.
|
|
23
|
+
*/
|
|
24
|
+
export interface ITextureAtlas {
|
|
25
|
+
/** Unique identifier for this atlas */
|
|
26
|
+
readonly id: string;
|
|
27
|
+
/** Texture format */
|
|
28
|
+
readonly format: TextureAtlasFormat;
|
|
29
|
+
/** Number of array layers */
|
|
30
|
+
readonly layers: number;
|
|
31
|
+
/** Size in pixels (width = height) */
|
|
32
|
+
readonly size: number;
|
|
33
|
+
/** Underlying GPU handle (GPUTexture for WebGPU, WebGLTexture for WebGL) */
|
|
34
|
+
readonly handle: unknown;
|
|
35
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BackendType } from "./IRenderBackend";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Detect the best available rendering backend.
|
|
5
|
+
*
|
|
6
|
+
* Tries WebGPU first (better performance), falls back to WebGL 2.
|
|
7
|
+
*/
|
|
8
|
+
export async function detectBackend(): Promise<BackendType> {
|
|
9
|
+
// Check for WebGPU support
|
|
10
|
+
if (typeof navigator !== "undefined" && "gpu" in navigator) {
|
|
11
|
+
try {
|
|
12
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
13
|
+
if (adapter) {
|
|
14
|
+
return "webgpu";
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
// WebGPU initialization failed, fall back to WebGL
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return "webgl2";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if WebGPU is available in the current environment.
|
|
26
|
+
*/
|
|
27
|
+
export function isWebGPUAvailable(): boolean {
|
|
28
|
+
return typeof navigator !== "undefined" && "gpu" in navigator;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if WebGL 2 is available in the current environment.
|
|
33
|
+
*/
|
|
34
|
+
export function isWebGL2Available(): boolean {
|
|
35
|
+
if (typeof document === "undefined") {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const canvas = document.createElement("canvas");
|
|
41
|
+
const gl = canvas.getContext("webgl2");
|
|
42
|
+
return gl !== null;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Backend abstraction layer
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
detectBackend,
|
|
5
|
+
isWebGL2Available,
|
|
6
|
+
isWebGPUAvailable,
|
|
7
|
+
} from "./detection";
|
|
8
|
+
export type { IBackendShader, QuadShaderCreationOpts } from "./IBackendShader";
|
|
9
|
+
export type {
|
|
10
|
+
BackendType,
|
|
11
|
+
BlendFactor,
|
|
12
|
+
BlendMode,
|
|
13
|
+
BlendOperation,
|
|
14
|
+
IRenderBackend,
|
|
15
|
+
} from "./IRenderBackend";
|
|
16
|
+
export type {
|
|
17
|
+
ITextureAtlas,
|
|
18
|
+
TextureAtlasFormat,
|
|
19
|
+
TextureAtlasOptions,
|
|
20
|
+
} from "./ITextureAtlas";
|
|
21
|
+
export { defaultFragmentShader as defaultGLSLFragmentShader } from "./webgl2/glsl/quad.glsl";
|
|
22
|
+
export { WebGLBackend } from "./webgl2/WebGLBackend";
|
|
23
|
+
// WebGPU-specific postprocess utilities
|
|
24
|
+
export {
|
|
25
|
+
type PostProcess,
|
|
26
|
+
PostProcessDefaults,
|
|
27
|
+
} from "./webgpu/postprocess/mod";
|
|
28
|
+
// Concrete backend implementations
|
|
29
|
+
export { WebGPUBackend } from "./webgpu/WebGPUBackend";
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type { Color } from "../../coreTypes/Color";
|
|
2
|
+
import type { EngineUniform } from "../../coreTypes/EngineUniform";
|
|
3
|
+
import type { Size } from "../../coreTypes/Size";
|
|
4
|
+
import type { Limits, LimitsOptions } from "../../limits";
|
|
5
|
+
import { DEFAULT_LIMITS } from "../../limits";
|
|
6
|
+
import type { CpuTextureAtlas } from "../../textures/types";
|
|
7
|
+
import { assert } from "../../utils/assert";
|
|
8
|
+
import type { IBackendShader, QuadShaderCreationOpts } from "../IBackendShader";
|
|
9
|
+
import type { IRenderBackend } from "../IRenderBackend";
|
|
10
|
+
import type {
|
|
11
|
+
ITextureAtlas,
|
|
12
|
+
TextureAtlasFormat,
|
|
13
|
+
TextureAtlasOptions,
|
|
14
|
+
} from "../ITextureAtlas";
|
|
15
|
+
import { WebGLQuadShader } from "./WebGLQuadShader";
|
|
16
|
+
|
|
17
|
+
export type WebGLBackendOptions = {
|
|
18
|
+
limits?: LimitsOptions;
|
|
19
|
+
format?: "rgba8unorm" | "rg8unorm";
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* WebGL 2 implementation of the render backend.
|
|
24
|
+
*/
|
|
25
|
+
export class WebGLBackend implements IRenderBackend {
|
|
26
|
+
readonly type = "webgl2" as const;
|
|
27
|
+
readonly limits: Limits;
|
|
28
|
+
readonly atlasSize: Size;
|
|
29
|
+
readonly defaultAtlasId = "default";
|
|
30
|
+
|
|
31
|
+
#atlases = new Map<string, ITextureAtlas>();
|
|
32
|
+
#gl: WebGL2RenderingContext;
|
|
33
|
+
#canvas: HTMLCanvasElement;
|
|
34
|
+
|
|
35
|
+
private constructor(
|
|
36
|
+
gl: WebGL2RenderingContext,
|
|
37
|
+
canvas: HTMLCanvasElement,
|
|
38
|
+
limits: Limits,
|
|
39
|
+
) {
|
|
40
|
+
this.#gl = gl;
|
|
41
|
+
this.#canvas = canvas;
|
|
42
|
+
this.limits = limits;
|
|
43
|
+
this.atlasSize = {
|
|
44
|
+
width: limits.textureSize,
|
|
45
|
+
height: limits.textureSize,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create a WebGL 2 backend attached to a canvas.
|
|
51
|
+
*/
|
|
52
|
+
static async create(
|
|
53
|
+
canvas: HTMLCanvasElement,
|
|
54
|
+
options: WebGLBackendOptions = {},
|
|
55
|
+
): Promise<WebGLBackend> {
|
|
56
|
+
const gl = canvas.getContext("webgl2", {
|
|
57
|
+
alpha: true,
|
|
58
|
+
antialias: false,
|
|
59
|
+
premultipliedAlpha: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!gl) {
|
|
63
|
+
throw new Error("WebGL 2 not supported");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const limits: Limits = {
|
|
67
|
+
...DEFAULT_LIMITS,
|
|
68
|
+
...options.limits,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const backend = new WebGLBackend(gl, canvas, limits);
|
|
72
|
+
|
|
73
|
+
// Create the default texture atlas
|
|
74
|
+
backend.createTextureAtlas("default", {
|
|
75
|
+
format: options.format ?? "rgba8unorm",
|
|
76
|
+
layers: limits.textureArrayLayers,
|
|
77
|
+
size: limits.textureSize,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return backend;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
startFrame(clearColor: Color, loadOp: "clear" | "load"): void {
|
|
84
|
+
const gl = this.#gl;
|
|
85
|
+
|
|
86
|
+
// Set viewport to canvas size
|
|
87
|
+
gl.viewport(0, 0, this.#canvas.width, this.#canvas.height);
|
|
88
|
+
|
|
89
|
+
if (loadOp === "clear") {
|
|
90
|
+
gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
|
91
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Enable blending
|
|
95
|
+
gl.enable(gl.BLEND);
|
|
96
|
+
gl.blendFuncSeparate(
|
|
97
|
+
gl.SRC_ALPHA,
|
|
98
|
+
gl.ONE_MINUS_SRC_ALPHA,
|
|
99
|
+
gl.ONE,
|
|
100
|
+
gl.ONE_MINUS_SRC_ALPHA,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
endFrame(): void {
|
|
105
|
+
const gl = this.#gl;
|
|
106
|
+
gl.flush();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
updateEngineUniform(_uniform: EngineUniform): void {
|
|
110
|
+
// Uniforms are updated per-shader in WebGL, not at the backend level
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async uploadAtlas(
|
|
114
|
+
atlas: CpuTextureAtlas,
|
|
115
|
+
layerIndex: number,
|
|
116
|
+
atlasId?: string,
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
const gl = this.#gl;
|
|
119
|
+
const targetAtlas = this.getTextureAtlas(atlasId ?? "default");
|
|
120
|
+
assert(targetAtlas, `Atlas "${atlasId ?? "default"}" not found`);
|
|
121
|
+
const texture = targetAtlas.handle as WebGLTexture;
|
|
122
|
+
|
|
123
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
124
|
+
|
|
125
|
+
if (atlas.rg8Bytes) {
|
|
126
|
+
// Upload raw bytes for RG8 format
|
|
127
|
+
gl.texSubImage3D(
|
|
128
|
+
gl.TEXTURE_2D_ARRAY,
|
|
129
|
+
0, // mip level
|
|
130
|
+
0,
|
|
131
|
+
0,
|
|
132
|
+
layerIndex, // x, y, z offset
|
|
133
|
+
targetAtlas.size,
|
|
134
|
+
targetAtlas.size,
|
|
135
|
+
1, // width, height, depth
|
|
136
|
+
gl.RG,
|
|
137
|
+
gl.UNSIGNED_BYTE,
|
|
138
|
+
atlas.rg8Bytes,
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
// Upload ImageBitmap for RGBA format
|
|
142
|
+
gl.texSubImage3D(
|
|
143
|
+
gl.TEXTURE_2D_ARRAY,
|
|
144
|
+
0,
|
|
145
|
+
0,
|
|
146
|
+
0,
|
|
147
|
+
layerIndex,
|
|
148
|
+
atlas.texture.width,
|
|
149
|
+
atlas.texture.height,
|
|
150
|
+
1,
|
|
151
|
+
gl.RGBA,
|
|
152
|
+
gl.UNSIGNED_BYTE,
|
|
153
|
+
atlas.texture,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
createQuadShader(opts: QuadShaderCreationOpts): IBackendShader {
|
|
161
|
+
return new WebGLQuadShader(
|
|
162
|
+
opts.label,
|
|
163
|
+
this,
|
|
164
|
+
opts.instanceCount,
|
|
165
|
+
opts.userCode,
|
|
166
|
+
opts.atlasId,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
createTextureAtlas(id: string, options?: TextureAtlasOptions): ITextureAtlas {
|
|
171
|
+
if (this.#atlases.has(id)) {
|
|
172
|
+
throw new Error(`Atlas "${id}" already exists`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const gl = this.#gl;
|
|
176
|
+
const format: TextureAtlasFormat = options?.format ?? "rgba8unorm";
|
|
177
|
+
const layers = options?.layers ?? this.limits.textureArrayLayers;
|
|
178
|
+
const size = options?.size ?? this.limits.textureSize;
|
|
179
|
+
|
|
180
|
+
const texture = gl.createTexture();
|
|
181
|
+
assert(texture, "Failed to create WebGL texture");
|
|
182
|
+
|
|
183
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
184
|
+
|
|
185
|
+
// Configure texture parameters
|
|
186
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
187
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
188
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
189
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
190
|
+
|
|
191
|
+
// Allocate storage for texture array
|
|
192
|
+
const internalFormat = format === "rg8unorm" ? gl.RG8 : gl.RGBA8;
|
|
193
|
+
|
|
194
|
+
gl.texStorage3D(
|
|
195
|
+
gl.TEXTURE_2D_ARRAY,
|
|
196
|
+
1, // mip levels
|
|
197
|
+
internalFormat,
|
|
198
|
+
size,
|
|
199
|
+
size,
|
|
200
|
+
layers,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
204
|
+
|
|
205
|
+
const atlas: ITextureAtlas = { id, format, layers, size, handle: texture };
|
|
206
|
+
this.#atlases.set(id, atlas);
|
|
207
|
+
return atlas;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getTextureAtlas(id: string): ITextureAtlas | null {
|
|
211
|
+
return this.#atlases.get(id) ?? null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
destroyTextureAtlas(id: string): void {
|
|
215
|
+
const atlas = this.#atlases.get(id);
|
|
216
|
+
if (atlas) {
|
|
217
|
+
this.#gl.deleteTexture(atlas.handle as WebGLTexture);
|
|
218
|
+
this.#atlases.delete(id);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get the default texture atlas handle.
|
|
224
|
+
* @deprecated Use getTextureAtlas("default").handle instead
|
|
225
|
+
*/
|
|
226
|
+
get textureArrayHandle(): WebGLTexture {
|
|
227
|
+
return this.getTextureAtlas("default")!.handle as WebGLTexture;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
resize(_width: number, _height: number): void {
|
|
231
|
+
// Canvas resize is handled by the application
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
destroy(): void {
|
|
235
|
+
const gl = this.#gl;
|
|
236
|
+
// Destroy all atlases
|
|
237
|
+
for (const atlas of this.#atlases.values()) {
|
|
238
|
+
gl.deleteTexture(atlas.handle as WebGLTexture);
|
|
239
|
+
}
|
|
240
|
+
this.#atlases.clear();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get the WebGL 2 rendering context.
|
|
245
|
+
*/
|
|
246
|
+
get gl(): WebGL2RenderingContext {
|
|
247
|
+
return this.#gl;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get the presentation format (of the default atlas).
|
|
252
|
+
*/
|
|
253
|
+
get presentationFormat(): TextureAtlasFormat {
|
|
254
|
+
return this.getTextureAtlas("default")?.format ?? "rgba8unorm";
|
|
255
|
+
}
|
|
256
|
+
}
|