@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
|
@@ -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";
|
|
@@ -57,10 +57,7 @@ export class QuadNode extends SceneNode {
|
|
|
57
57
|
options.shader,
|
|
58
58
|
"QuadNode requires a shader to be explicitly provided",
|
|
59
59
|
);
|
|
60
|
-
assert(
|
|
61
|
-
options.idealSize,
|
|
62
|
-
"QuadNode requires an ideal size to be explicitly provided",
|
|
63
|
-
);
|
|
60
|
+
assert(options.size, "QuadNode requires a size to be explicitly provided");
|
|
64
61
|
|
|
65
62
|
assert(
|
|
66
63
|
options.atlasCoords,
|
|
@@ -147,6 +144,10 @@ export class QuadNode extends SceneNode {
|
|
|
147
144
|
return size;
|
|
148
145
|
}
|
|
149
146
|
|
|
147
|
+
set size(val: Size) {
|
|
148
|
+
super.size = val;
|
|
149
|
+
}
|
|
150
|
+
|
|
150
151
|
/**
|
|
151
152
|
* This is the final model matrix used to render the quad, which
|
|
152
153
|
* may differ from the matrix passed down to the node's children.
|
|
@@ -305,7 +306,7 @@ export type QuadOptions = NodeOptions & {
|
|
|
305
306
|
|
|
306
307
|
assetManager?: AssetManager;
|
|
307
308
|
|
|
308
|
-
shader?:
|
|
309
|
+
shader?: IBackendShader;
|
|
309
310
|
writeInstance?: (array: Float32Array, offset: number) => void;
|
|
310
311
|
color?: Color;
|
|
311
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/scene/SceneNode.ts
CHANGED
|
@@ -27,7 +27,7 @@ export class SceneNode {
|
|
|
27
27
|
#transform: Transform;
|
|
28
28
|
#matrix: Mat3 = mat3.identity();
|
|
29
29
|
#renderComponent: RenderComponent | null = null;
|
|
30
|
-
#
|
|
30
|
+
#size: Size | null = null;
|
|
31
31
|
#positionProxy: Point;
|
|
32
32
|
#scaleProxy: Vec2;
|
|
33
33
|
|
|
@@ -45,7 +45,7 @@ export class SceneNode {
|
|
|
45
45
|
this.#transform = {
|
|
46
46
|
position: opts?.position ?? { x: 0, y: 0 },
|
|
47
47
|
scale: { x: 1, y: 1 },
|
|
48
|
-
size: opts?.
|
|
48
|
+
size: opts?.size ?? { width: 1, height: 1 },
|
|
49
49
|
rotation: opts?.rotationRadians ?? 0,
|
|
50
50
|
};
|
|
51
51
|
if (opts?.scale) this.scale = opts.scale;
|
|
@@ -56,7 +56,7 @@ export class SceneNode {
|
|
|
56
56
|
this.#layer = opts?.layer ?? null;
|
|
57
57
|
this.#isActive = opts?.isActive ?? true;
|
|
58
58
|
this.label = opts?.label ?? undefined;
|
|
59
|
-
this.#
|
|
59
|
+
this.#size = opts?.size ?? null;
|
|
60
60
|
this.#key = opts?.key ?? null;
|
|
61
61
|
|
|
62
62
|
for (const kid of opts?.kids ?? []) {
|
|
@@ -246,17 +246,16 @@ export class SceneNode {
|
|
|
246
246
|
this.setDirty();
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
set idealSize(value: Size | null) {
|
|
250
|
-
this.#idealSize = value;
|
|
251
|
-
this.setDirty();
|
|
252
|
-
}
|
|
253
|
-
|
|
254
249
|
/**
|
|
255
250
|
* The size of the node. See https://toodle.gg/f849595b3ed13fc956fc1459a5cb5f0228f9d259/examples/quad-size-scale.html
|
|
256
251
|
*/
|
|
252
|
+
set size(value: Size | null) {
|
|
253
|
+
this.#size = value;
|
|
254
|
+
this.setDirty();
|
|
255
|
+
}
|
|
257
256
|
|
|
258
257
|
get size() {
|
|
259
|
-
return this.#
|
|
258
|
+
return this.#size;
|
|
260
259
|
}
|
|
261
260
|
|
|
262
261
|
/**
|
|
@@ -264,13 +263,13 @@ export class SceneNode {
|
|
|
264
263
|
* If the node has no defined size, the aspect ratio will be 1.
|
|
265
264
|
*/
|
|
266
265
|
get aspectRatio() {
|
|
267
|
-
if (!this.#
|
|
266
|
+
if (!this.#size) {
|
|
268
267
|
console.warn(
|
|
269
268
|
"Attempted to get aspect ratio of a node with no ideal size",
|
|
270
269
|
);
|
|
271
270
|
return 1;
|
|
272
271
|
}
|
|
273
|
-
return this.#
|
|
272
|
+
return this.#size.width / this.#size.height;
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
/**
|
|
@@ -592,7 +591,7 @@ export type NodeOptions = {
|
|
|
592
591
|
/** The scale for the node. */
|
|
593
592
|
scale?: Vec2 | number;
|
|
594
593
|
/** The desired size for the node. */
|
|
595
|
-
|
|
594
|
+
size?: Size;
|
|
596
595
|
/** The active state for the node. */
|
|
597
596
|
isActive?: boolean;
|
|
598
597
|
/** The kids for the node. */
|