@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.
- 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 +0 -4
- 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 -2
- 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 -92
- 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
|
@@ -4,134 +4,86 @@ import {
|
|
|
4
4
|
type StructuredView,
|
|
5
5
|
} from "webgpu-utils";
|
|
6
6
|
import { WgslReflect } from "wgsl_reflect";
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
9
|
-
import { assert } from "
|
|
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 "
|
|
16
|
-
import type {
|
|
17
|
-
import type {
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
type InstanceData = {
|
|
28
|
+
cpuBuffer: Float32Array<ArrayBuffer>;
|
|
29
|
+
gpuBuffer: GPUBuffer;
|
|
30
|
+
bufferLayout: GPUVertexBufferLayout;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
125
|
-
device: GPUDevice,
|
|
126
|
-
presentationFormat: GPUTextureFormat,
|
|
127
|
-
userCode: string,
|
|
53
|
+
backend: WebGPUBackend,
|
|
128
54
|
instanceCount: number,
|
|
129
|
-
|
|
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
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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 =
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
+
}
|
|
@@ -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 "
|
|
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
|
|
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(
|
|
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
|
|
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";
|
package/src/scene/Batcher.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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:
|
|
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:
|
|
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: [] };
|
package/src/scene/QuadNode.ts
CHANGED
|
@@ -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";
|
|
@@ -144,6 +144,10 @@ export class QuadNode extends SceneNode {
|
|
|
144
144
|
return size;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
set size(val: Size) {
|
|
148
|
+
super.size = val;
|
|
149
|
+
}
|
|
150
|
+
|
|
147
151
|
/**
|
|
148
152
|
* This is the final model matrix used to render the quad, which
|
|
149
153
|
* may differ from the matrix passed down to the node's children.
|
|
@@ -302,7 +306,7 @@ export type QuadOptions = NodeOptions & {
|
|
|
302
306
|
|
|
303
307
|
assetManager?: AssetManager;
|
|
304
308
|
|
|
305
|
-
shader?:
|
|
309
|
+
shader?: IBackendShader;
|
|
306
310
|
writeInstance?: (array: Float32Array, offset: number) => void;
|
|
307
311
|
color?: Color;
|
|
308
312
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IBackendShader } from "../backends/IBackendShader";
|
|
2
2
|
import type { SceneNode } from "./SceneNode";
|
|
3
3
|
|
|
4
4
|
export type RenderComponent = {
|
|
5
|
-
shader:
|
|
5
|
+
shader: IBackendShader;
|
|
6
6
|
data?: Float32Array;
|
|
7
7
|
|
|
8
8
|
/**
|
package/src/text/TextShader.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { WgslReflect } from "wgsl_reflect";
|
|
2
|
+
import type { IBackendShader } from "../backends/IBackendShader";
|
|
3
|
+
import type { WebGPUBackend } from "../backends/webgpu/WebGPUBackend";
|
|
4
|
+
import type { EngineUniform } from "../coreTypes/EngineUniform";
|
|
2
5
|
import type { SceneNode } from "../scene/SceneNode";
|
|
3
|
-
import type { EngineUniform } from "../shaders/EngineUniform";
|
|
4
|
-
import type { IShader } from "../shaders/IShader";
|
|
5
6
|
import type { FontPipeline } from "./FontPipeline";
|
|
6
7
|
import type { MsdfFont } from "./MsdfFont";
|
|
7
8
|
import { findLargestFontSize, measureText, shapeText } from "./shaping";
|
|
@@ -15,8 +16,10 @@ if (!struct) {
|
|
|
15
16
|
}
|
|
16
17
|
const textDescriptorInstanceSize = struct.size;
|
|
17
18
|
|
|
18
|
-
export class TextShader implements
|
|
19
|
-
|
|
19
|
+
export class TextShader implements IBackendShader {
|
|
20
|
+
readonly label = "text";
|
|
21
|
+
|
|
22
|
+
#backend: WebGPUBackend;
|
|
20
23
|
#pipeline: GPURenderPipeline;
|
|
21
24
|
#bindGroups: GPUBindGroup[] = [];
|
|
22
25
|
#font: MsdfFont;
|
|
@@ -30,13 +33,14 @@ export class TextShader implements IShader {
|
|
|
30
33
|
#textBlockOffset = 0;
|
|
31
34
|
|
|
32
35
|
constructor(
|
|
33
|
-
|
|
36
|
+
backend: WebGPUBackend,
|
|
34
37
|
pipeline: FontPipeline,
|
|
35
38
|
font: MsdfFont,
|
|
36
|
-
|
|
39
|
+
_colorFormat: GPUTextureFormat,
|
|
37
40
|
instanceCount: number,
|
|
38
41
|
) {
|
|
39
|
-
this.#
|
|
42
|
+
this.#backend = backend;
|
|
43
|
+
const device = backend.device;
|
|
40
44
|
this.#font = font;
|
|
41
45
|
this.#pipeline = pipeline.pipeline;
|
|
42
46
|
this.#maxCharCount = pipeline.maxCharCount;
|
|
@@ -102,7 +106,8 @@ export class TextShader implements IShader {
|
|
|
102
106
|
this.#bindGroups.push(engineUniformsBindGroup);
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
startFrame(
|
|
109
|
+
startFrame(uniform: EngineUniform): void {
|
|
110
|
+
const device = this.#backend.device;
|
|
106
111
|
device.queue.writeBuffer(
|
|
107
112
|
this.#engineUniformsBuffer,
|
|
108
113
|
0,
|
|
@@ -112,9 +117,10 @@ export class TextShader implements IShader {
|
|
|
112
117
|
this.#textBlockOffset = 0;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
processBatch(
|
|
120
|
+
processBatch(nodes: SceneNode[]): number {
|
|
116
121
|
if (nodes.length === 0) return 0;
|
|
117
122
|
|
|
123
|
+
const renderPass = this.#backend.renderPass;
|
|
118
124
|
renderPass.setPipeline(this.#pipeline);
|
|
119
125
|
for (let i = 0; i < this.#bindGroups.length; i++) {
|
|
120
126
|
renderPass.setBindGroup(i, this.#bindGroups[i]);
|
|
@@ -178,7 +184,7 @@ export class TextShader implements IShader {
|
|
|
178
184
|
);
|
|
179
185
|
|
|
180
186
|
// Write instance data
|
|
181
|
-
this.#device.queue.writeBuffer(
|
|
187
|
+
this.#backend.device.queue.writeBuffer(
|
|
182
188
|
this.#descriptorBuffer,
|
|
183
189
|
textDescriptorOffset * Float32Array.BYTES_PER_ELEMENT,
|
|
184
190
|
this.#cpuDescriptorBuffer,
|
|
@@ -186,7 +192,7 @@ export class TextShader implements IShader {
|
|
|
186
192
|
textDescriptorInstanceSize / Float32Array.BYTES_PER_ELEMENT,
|
|
187
193
|
);
|
|
188
194
|
|
|
189
|
-
this.#device.queue.writeBuffer(
|
|
195
|
+
this.#backend.device.queue.writeBuffer(
|
|
190
196
|
this.#textBlockBuffer,
|
|
191
197
|
this.#textBlockOffset * Float32Array.BYTES_PER_ELEMENT,
|
|
192
198
|
this.#cpuTextBlockBuffer,
|