wgsl-renderer 0.1.2 → 0.1.4
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/README.md +2 -1
- package/README.zh-CN.md +2 -1
- package/dist/cjs/index.js +172 -28
- package/dist/esm/index.d.ts +15 -8
- package/dist/esm/index.js +172 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -206,7 +206,8 @@ interface RenderPassOptions {
|
|
|
206
206
|
blendMode?: 'additive' | 'alpha' | 'multiply' | 'none';
|
|
207
207
|
resources?: GPUBindingResource[];
|
|
208
208
|
bindGroupSets?: { [setName: string]: GPUBindingResource[] }; // Multiple bind group sets
|
|
209
|
-
|
|
209
|
+
renderToCanvas?: boolean; // Optional render current pass to canvas, default is false and the lastest pass always true.
|
|
210
|
+
view?: GPUTextureView; // Optional custom view for this pass, invalid when rederToCanvas is ture.
|
|
210
211
|
format?: GPUTextureFormat; // Optional format for the view (required when using custom view with different format)
|
|
211
212
|
}
|
|
212
213
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -223,7 +223,8 @@ interface RenderPassOptions {
|
|
|
223
223
|
blendMode?: 'additive' | 'alpha' | 'multiply' | 'none';
|
|
224
224
|
resources?: GPUBindingResource[];
|
|
225
225
|
bindGroupSets?: { [setName: string]: GPUBindingResource[] }; // 可选的设置多个绑定组,用于动态切换
|
|
226
|
-
|
|
226
|
+
renderToCanvas?: boolean; // 可选的将当前通道输出到canvas,默认是false,最后一个通道始终是true
|
|
227
|
+
view?: GPUTextureView; // 可选的自定义View,renderToCanvas为true时无效。
|
|
227
228
|
format?: GPUTextureFormat; // 可选的自定义格式(使用自定义View时需要指定格式一致)
|
|
228
229
|
}
|
|
229
230
|
```
|
package/dist/cjs/index.js
CHANGED
|
@@ -9,13 +9,14 @@ var RenderPass = class {
|
|
|
9
9
|
blendMode;
|
|
10
10
|
view;
|
|
11
11
|
format;
|
|
12
|
+
renderToCanvas;
|
|
12
13
|
passResources = [];
|
|
13
14
|
bindGroups = {};
|
|
14
15
|
activeBindGroupSet = "default";
|
|
15
16
|
device;
|
|
16
17
|
descriptor;
|
|
17
18
|
enabled = true;
|
|
18
|
-
constructor(descriptor, device, format, layout) {
|
|
19
|
+
constructor(descriptor, device, format, layout = "auto") {
|
|
19
20
|
this.device = device;
|
|
20
21
|
this.descriptor = descriptor;
|
|
21
22
|
this.name = descriptor.name;
|
|
@@ -28,6 +29,7 @@ var RenderPass = class {
|
|
|
28
29
|
this.blendMode = descriptor.blendMode || "none";
|
|
29
30
|
this.view = descriptor.view;
|
|
30
31
|
this.format = descriptor.format;
|
|
32
|
+
this.renderToCanvas = descriptor.renderToCanvas;
|
|
31
33
|
const actualFormat = descriptor.format || format;
|
|
32
34
|
const module$1 = this.device.createShaderModule({
|
|
33
35
|
code: descriptor.shaderCode,
|
|
@@ -146,38 +148,158 @@ var RenderPass = class {
|
|
|
146
148
|
case "none": return;
|
|
147
149
|
case "alpha": return {
|
|
148
150
|
color: {
|
|
151
|
+
operation: "add",
|
|
149
152
|
srcFactor: "src-alpha",
|
|
150
|
-
dstFactor: "one-minus-src-alpha"
|
|
151
|
-
operation: "add"
|
|
153
|
+
dstFactor: "one-minus-src-alpha"
|
|
152
154
|
},
|
|
153
155
|
alpha: {
|
|
156
|
+
operation: "add",
|
|
154
157
|
srcFactor: "one",
|
|
155
|
-
dstFactor: "one-minus-src-alpha"
|
|
156
|
-
operation: "add"
|
|
158
|
+
dstFactor: "one-minus-src-alpha"
|
|
157
159
|
}
|
|
158
160
|
};
|
|
159
161
|
case "additive": return {
|
|
160
162
|
color: {
|
|
163
|
+
operation: "add",
|
|
161
164
|
srcFactor: "src-alpha",
|
|
162
|
-
dstFactor: "one"
|
|
163
|
-
operation: "add"
|
|
165
|
+
dstFactor: "one"
|
|
164
166
|
},
|
|
165
167
|
alpha: {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
operation: "add",
|
|
169
|
+
srcFactor: "src-alpha",
|
|
170
|
+
dstFactor: "one"
|
|
169
171
|
}
|
|
170
172
|
};
|
|
171
173
|
case "multiply": return {
|
|
172
174
|
color: {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
operation: "add",
|
|
176
|
+
srcFactor: "zero",
|
|
177
|
+
dstFactor: "src"
|
|
178
|
+
},
|
|
179
|
+
alpha: {
|
|
180
|
+
operation: "add",
|
|
181
|
+
srcFactor: "zero",
|
|
182
|
+
dstFactor: "src-alpha"
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
case "screen": return {
|
|
186
|
+
color: {
|
|
187
|
+
operation: "add",
|
|
188
|
+
srcFactor: "one",
|
|
189
|
+
dstFactor: "one-minus-src"
|
|
190
|
+
},
|
|
191
|
+
alpha: {
|
|
192
|
+
operation: "add",
|
|
193
|
+
srcFactor: "one",
|
|
194
|
+
dstFactor: "one-minus-src-alpha"
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
case "subtract": return {
|
|
198
|
+
color: {
|
|
199
|
+
operation: "reverse-subtract",
|
|
200
|
+
srcFactor: "src-alpha",
|
|
201
|
+
dstFactor: "one"
|
|
202
|
+
},
|
|
203
|
+
alpha: {
|
|
204
|
+
operation: "reverse-subtract",
|
|
205
|
+
srcFactor: "src-alpha",
|
|
206
|
+
dstFactor: "one"
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
case "min": return {
|
|
210
|
+
color: {
|
|
211
|
+
operation: "min",
|
|
212
|
+
srcFactor: "one",
|
|
213
|
+
dstFactor: "one"
|
|
214
|
+
},
|
|
215
|
+
alpha: {
|
|
216
|
+
operation: "min",
|
|
217
|
+
srcFactor: "one",
|
|
218
|
+
dstFactor: "one"
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
case "max": return {
|
|
222
|
+
color: {
|
|
223
|
+
operation: "max",
|
|
224
|
+
srcFactor: "one",
|
|
225
|
+
dstFactor: "one"
|
|
226
|
+
},
|
|
227
|
+
alpha: {
|
|
228
|
+
operation: "max",
|
|
229
|
+
srcFactor: "one",
|
|
230
|
+
dstFactor: "one"
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
case "darken": return {
|
|
234
|
+
color: {
|
|
235
|
+
operation: "min",
|
|
236
|
+
srcFactor: "one",
|
|
237
|
+
dstFactor: "one"
|
|
238
|
+
},
|
|
239
|
+
alpha: {
|
|
240
|
+
operation: "add",
|
|
241
|
+
srcFactor: "src-alpha",
|
|
242
|
+
dstFactor: "one-minus-src-alpha"
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
case "lighten": return {
|
|
246
|
+
color: {
|
|
247
|
+
operation: "max",
|
|
248
|
+
srcFactor: "one",
|
|
249
|
+
dstFactor: "one"
|
|
176
250
|
},
|
|
177
251
|
alpha: {
|
|
252
|
+
operation: "add",
|
|
253
|
+
srcFactor: "src-alpha",
|
|
254
|
+
dstFactor: "one-minus-src-alpha"
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
case "linear-dodge": return {
|
|
258
|
+
color: {
|
|
259
|
+
operation: "add",
|
|
178
260
|
srcFactor: "one",
|
|
179
|
-
dstFactor: "one
|
|
180
|
-
|
|
261
|
+
dstFactor: "one"
|
|
262
|
+
},
|
|
263
|
+
alpha: {
|
|
264
|
+
operation: "add",
|
|
265
|
+
srcFactor: "src-alpha",
|
|
266
|
+
dstFactor: "one"
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
case "linear-burn": return {
|
|
270
|
+
color: {
|
|
271
|
+
operation: "reverse-subtract",
|
|
272
|
+
srcFactor: "one",
|
|
273
|
+
dstFactor: "one"
|
|
274
|
+
},
|
|
275
|
+
alpha: {
|
|
276
|
+
operation: "add",
|
|
277
|
+
srcFactor: "src-alpha",
|
|
278
|
+
dstFactor: "one-minus-src-alpha"
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
case "difference": return {
|
|
282
|
+
color: {
|
|
283
|
+
operation: "reverse-subtract",
|
|
284
|
+
srcFactor: "one",
|
|
285
|
+
dstFactor: "one"
|
|
286
|
+
},
|
|
287
|
+
alpha: {
|
|
288
|
+
operation: "add",
|
|
289
|
+
srcFactor: "src-alpha",
|
|
290
|
+
dstFactor: "one-minus-src-alpha"
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
case "exclusion": return {
|
|
294
|
+
color: {
|
|
295
|
+
operation: "add",
|
|
296
|
+
srcFactor: "zero",
|
|
297
|
+
dstFactor: "one"
|
|
298
|
+
},
|
|
299
|
+
alpha: {
|
|
300
|
+
operation: "add",
|
|
301
|
+
srcFactor: "src-alpha",
|
|
302
|
+
dstFactor: "one-minus-src-alpha"
|
|
181
303
|
}
|
|
182
304
|
};
|
|
183
305
|
default: return;
|
|
@@ -290,6 +412,8 @@ var WGSLRenderer = class {
|
|
|
290
412
|
this.isResizing = true;
|
|
291
413
|
this.canvas.width = width;
|
|
292
414
|
this.canvas.height = height;
|
|
415
|
+
this.canvas.style.width = `${width / (window.devicePixelRatio || 1)}px`;
|
|
416
|
+
this.canvas.style.height = `${height / (window.devicePixelRatio || 1)}px`;
|
|
293
417
|
const future = this.device.queue.onSubmittedWorkDone();
|
|
294
418
|
future.catch(() => {
|
|
295
419
|
console.warn("GPU work submission failed during resize.");
|
|
@@ -317,9 +441,10 @@ var WGSLRenderer = class {
|
|
|
317
441
|
* Resolve a PassTextureRef to actual GPUTextureView with validation
|
|
318
442
|
*/
|
|
319
443
|
resolveTextureRef(ref) {
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
|
|
444
|
+
const targetPass = this.passes.find((pass) => pass.name === ref.passName);
|
|
445
|
+
if (!targetPass) throw new Error(`Cannot find pass named '${ref.passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
|
|
446
|
+
if (targetPass.view) return targetPass.view;
|
|
447
|
+
const textureName = `pass_${this.passes.indexOf(targetPass)}_output`;
|
|
323
448
|
let texture = this.textureManager.getTexture(textureName);
|
|
324
449
|
if (!texture) texture = this.textureManager.createTexture(textureName, this.format);
|
|
325
450
|
return texture.createView();
|
|
@@ -378,6 +503,12 @@ var WGSLRenderer = class {
|
|
|
378
503
|
return this.passes.filter((pass) => pass.enabled);
|
|
379
504
|
}
|
|
380
505
|
/**
|
|
506
|
+
* Set the entire passes array (replaces existing passes)
|
|
507
|
+
*/
|
|
508
|
+
setPasses(passes) {
|
|
509
|
+
this.passes = passes;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
381
512
|
* Switch bind group set for a specific pass
|
|
382
513
|
*/
|
|
383
514
|
switchBindGroupSet(passName, setName) {
|
|
@@ -398,10 +529,7 @@ var WGSLRenderer = class {
|
|
|
398
529
|
});
|
|
399
530
|
pass.updateBindGroupSetResources(setName, resolvedResources);
|
|
400
531
|
}
|
|
401
|
-
|
|
402
|
-
* Add a render pass to the multi-pass pipeline
|
|
403
|
-
*/
|
|
404
|
-
addPass(descriptor) {
|
|
532
|
+
createPass(descriptor) {
|
|
405
533
|
const finalBindGroupEntries = [];
|
|
406
534
|
descriptor.resources?.forEach((resource, index) => {
|
|
407
535
|
finalBindGroupEntries.push({
|
|
@@ -423,13 +551,30 @@ var WGSLRenderer = class {
|
|
|
423
551
|
bindGroupEntries: finalBindGroupEntries,
|
|
424
552
|
bindGroupSets: bindGroupSetsCopy,
|
|
425
553
|
view: descriptor.view,
|
|
426
|
-
format: descriptor.format
|
|
554
|
+
format: descriptor.format,
|
|
555
|
+
renderToCanvas: descriptor.renderToCanvas
|
|
427
556
|
};
|
|
428
557
|
const pipelineFormat = descriptor.format || this.format;
|
|
429
|
-
|
|
558
|
+
return new RenderPass(internalDescriptor, this.device, pipelineFormat);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Add a render pass to the multi-pass pipeline
|
|
562
|
+
*/
|
|
563
|
+
addPass(descriptor) {
|
|
564
|
+
const pass = this.createPass(descriptor);
|
|
430
565
|
pass.passResources = descriptor.resources ?? [];
|
|
431
566
|
this.passes.push(pass);
|
|
432
567
|
}
|
|
568
|
+
insertPassesTo(passName, descriptors) {
|
|
569
|
+
const i = this.passes.findIndex((p) => p.name === passName);
|
|
570
|
+
if (i === -1) throw new Error(`Cannot find pass named '${passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
|
|
571
|
+
const newPasses = descriptors.map((desc) => {
|
|
572
|
+
const pass = this.createPass(desc);
|
|
573
|
+
pass.passResources = desc.resources ?? [];
|
|
574
|
+
return pass;
|
|
575
|
+
});
|
|
576
|
+
this.passes.splice(i, 0, ...newPasses);
|
|
577
|
+
}
|
|
433
578
|
/**
|
|
434
579
|
* Resolve resource to actual GPU binding resource
|
|
435
580
|
* Handles PassTextureRef by getting the current texture view with validation
|
|
@@ -543,11 +688,10 @@ var WGSLRenderer = class {
|
|
|
543
688
|
for (let i = 0; i < enabledPasses.length; i++) {
|
|
544
689
|
const pass = enabledPasses[i];
|
|
545
690
|
let loadOp = "load";
|
|
546
|
-
|
|
547
|
-
if (isLast) loadOp = "clear";
|
|
691
|
+
if (i === 0) loadOp = "clear";
|
|
548
692
|
let renderTarget;
|
|
549
|
-
if (pass.
|
|
550
|
-
else if (
|
|
693
|
+
if (pass.renderToCanvas || i === enabledPasses.length - 1) renderTarget = this.ctx.getCurrentTexture().createView();
|
|
694
|
+
else if (pass.view) renderTarget = pass.view;
|
|
551
695
|
else {
|
|
552
696
|
const textureName = `pass_${i}_output`;
|
|
553
697
|
let texture = this.textureManager.getTexture(textureName);
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ type BindingEntry = {
|
|
|
15
15
|
binding: number;
|
|
16
16
|
resource: BandingResource;
|
|
17
17
|
};
|
|
18
|
+
type BlendMode = 'additive' | 'alpha' | 'darken' | 'difference' | 'exclusion' | 'lighten' | 'linear-burn' | 'linear-dodge' | 'max' | 'min' | 'multiply' | 'none' | 'screen' | 'subtract';
|
|
18
19
|
interface RenderPassOptions {
|
|
19
20
|
name: string;
|
|
20
21
|
shaderCode: string;
|
|
@@ -28,13 +29,14 @@ interface RenderPassOptions {
|
|
|
28
29
|
b: number;
|
|
29
30
|
a: number;
|
|
30
31
|
};
|
|
31
|
-
blendMode?:
|
|
32
|
+
blendMode?: BlendMode;
|
|
32
33
|
resources?: BandingResource[];
|
|
33
34
|
bindGroupSets?: {
|
|
34
35
|
[setName: string]: BandingResource[];
|
|
35
36
|
};
|
|
36
37
|
view?: GPUTextureView;
|
|
37
38
|
format?: GPUTextureFormat;
|
|
39
|
+
renderToCanvas?: boolean;
|
|
38
40
|
}
|
|
39
41
|
interface InternalRenderPassDescriptor {
|
|
40
42
|
name: string;
|
|
@@ -49,13 +51,14 @@ interface InternalRenderPassDescriptor {
|
|
|
49
51
|
b: number;
|
|
50
52
|
a: number;
|
|
51
53
|
};
|
|
52
|
-
blendMode?:
|
|
54
|
+
blendMode?: BlendMode;
|
|
53
55
|
bindGroupEntries: BindingEntry[];
|
|
54
56
|
bindGroupSets?: {
|
|
55
57
|
[setName: string]: BandingResource[];
|
|
56
58
|
};
|
|
57
59
|
view?: GPUTextureView;
|
|
58
60
|
format?: GPUTextureFormat;
|
|
61
|
+
renderToCanvas?: boolean;
|
|
59
62
|
}
|
|
60
63
|
declare class RenderPass {
|
|
61
64
|
name: string;
|
|
@@ -68,9 +71,10 @@ declare class RenderPass {
|
|
|
68
71
|
b: number;
|
|
69
72
|
a: number;
|
|
70
73
|
};
|
|
71
|
-
blendMode:
|
|
74
|
+
blendMode: BlendMode;
|
|
72
75
|
view?: GPUTextureView;
|
|
73
76
|
format?: GPUTextureFormat;
|
|
77
|
+
renderToCanvas?: boolean;
|
|
74
78
|
passResources: BandingResource[];
|
|
75
79
|
bindGroups: {
|
|
76
80
|
[setName: string]: GPUBindGroup;
|
|
@@ -79,7 +83,7 @@ declare class RenderPass {
|
|
|
79
83
|
private device;
|
|
80
84
|
descriptor: InternalRenderPassDescriptor;
|
|
81
85
|
enabled: boolean;
|
|
82
|
-
constructor(descriptor: InternalRenderPassDescriptor, device: GPUDevice, format: GPUTextureFormat, layout
|
|
86
|
+
constructor(descriptor: InternalRenderPassDescriptor, device: GPUDevice, format: GPUTextureFormat, layout?: GPUPipelineLayout | 'auto');
|
|
83
87
|
/**
|
|
84
88
|
* Update bind group with new entries (e.g., after texture resize)
|
|
85
89
|
*/
|
|
@@ -115,9 +119,6 @@ declare class RenderPass {
|
|
|
115
119
|
}
|
|
116
120
|
//#endregion
|
|
117
121
|
//#region src/index.d.ts
|
|
118
|
-
interface MultiPassDescriptor {
|
|
119
|
-
passes: RenderPassOptions[];
|
|
120
|
-
}
|
|
121
122
|
interface WGSLRendererOptions {
|
|
122
123
|
config?: GPUCanvasConfiguration;
|
|
123
124
|
}
|
|
@@ -173,6 +174,10 @@ declare class WGSLRenderer {
|
|
|
173
174
|
* Get only enabled passes
|
|
174
175
|
*/
|
|
175
176
|
getEnabledPasses(): RenderPass[];
|
|
177
|
+
/**
|
|
178
|
+
* Set the entire passes array (replaces existing passes)
|
|
179
|
+
*/
|
|
180
|
+
setPasses(passes: RenderPass[]): void;
|
|
176
181
|
/**
|
|
177
182
|
* Switch bind group set for a specific pass
|
|
178
183
|
*/
|
|
@@ -182,10 +187,12 @@ declare class WGSLRenderer {
|
|
|
182
187
|
* This allows dynamic modification of bind groups at runtime
|
|
183
188
|
*/
|
|
184
189
|
updateBindGroupSetResources(passName: string, setName: string, resources: BandingResource[]): void;
|
|
190
|
+
private createPass;
|
|
185
191
|
/**
|
|
186
192
|
* Add a render pass to the multi-pass pipeline
|
|
187
193
|
*/
|
|
188
194
|
addPass(descriptor: RenderPassOptions): void;
|
|
195
|
+
insertPassesTo(passName: string, descriptors: RenderPassOptions[]): void;
|
|
189
196
|
/**
|
|
190
197
|
* Resolve resource to actual GPU binding resource
|
|
191
198
|
* Handles PassTextureRef by getting the current texture view with validation
|
|
@@ -224,4 +231,4 @@ declare class WGSLRenderer {
|
|
|
224
231
|
}
|
|
225
232
|
declare function createWGSLRenderer(cvs: HTMLCanvasElement, options?: WGSLRendererOptions): Promise<WGSLRenderer>;
|
|
226
233
|
//#endregion
|
|
227
|
-
export {
|
|
234
|
+
export { type BandingResource, type RenderPassOptions, type WGSLRenderer, createWGSLRenderer };
|
package/dist/esm/index.js
CHANGED
|
@@ -8,13 +8,14 @@ var RenderPass = class {
|
|
|
8
8
|
blendMode;
|
|
9
9
|
view;
|
|
10
10
|
format;
|
|
11
|
+
renderToCanvas;
|
|
11
12
|
passResources = [];
|
|
12
13
|
bindGroups = {};
|
|
13
14
|
activeBindGroupSet = "default";
|
|
14
15
|
device;
|
|
15
16
|
descriptor;
|
|
16
17
|
enabled = true;
|
|
17
|
-
constructor(descriptor, device, format, layout) {
|
|
18
|
+
constructor(descriptor, device, format, layout = "auto") {
|
|
18
19
|
this.device = device;
|
|
19
20
|
this.descriptor = descriptor;
|
|
20
21
|
this.name = descriptor.name;
|
|
@@ -27,6 +28,7 @@ var RenderPass = class {
|
|
|
27
28
|
this.blendMode = descriptor.blendMode || "none";
|
|
28
29
|
this.view = descriptor.view;
|
|
29
30
|
this.format = descriptor.format;
|
|
31
|
+
this.renderToCanvas = descriptor.renderToCanvas;
|
|
30
32
|
const actualFormat = descriptor.format || format;
|
|
31
33
|
const module = this.device.createShaderModule({
|
|
32
34
|
code: descriptor.shaderCode,
|
|
@@ -145,38 +147,158 @@ var RenderPass = class {
|
|
|
145
147
|
case "none": return;
|
|
146
148
|
case "alpha": return {
|
|
147
149
|
color: {
|
|
150
|
+
operation: "add",
|
|
148
151
|
srcFactor: "src-alpha",
|
|
149
|
-
dstFactor: "one-minus-src-alpha"
|
|
150
|
-
operation: "add"
|
|
152
|
+
dstFactor: "one-minus-src-alpha"
|
|
151
153
|
},
|
|
152
154
|
alpha: {
|
|
155
|
+
operation: "add",
|
|
153
156
|
srcFactor: "one",
|
|
154
|
-
dstFactor: "one-minus-src-alpha"
|
|
155
|
-
operation: "add"
|
|
157
|
+
dstFactor: "one-minus-src-alpha"
|
|
156
158
|
}
|
|
157
159
|
};
|
|
158
160
|
case "additive": return {
|
|
159
161
|
color: {
|
|
162
|
+
operation: "add",
|
|
160
163
|
srcFactor: "src-alpha",
|
|
161
|
-
dstFactor: "one"
|
|
162
|
-
operation: "add"
|
|
164
|
+
dstFactor: "one"
|
|
163
165
|
},
|
|
164
166
|
alpha: {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
operation: "add",
|
|
168
|
+
srcFactor: "src-alpha",
|
|
169
|
+
dstFactor: "one"
|
|
168
170
|
}
|
|
169
171
|
};
|
|
170
172
|
case "multiply": return {
|
|
171
173
|
color: {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
operation: "add",
|
|
175
|
+
srcFactor: "zero",
|
|
176
|
+
dstFactor: "src"
|
|
177
|
+
},
|
|
178
|
+
alpha: {
|
|
179
|
+
operation: "add",
|
|
180
|
+
srcFactor: "zero",
|
|
181
|
+
dstFactor: "src-alpha"
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
case "screen": return {
|
|
185
|
+
color: {
|
|
186
|
+
operation: "add",
|
|
187
|
+
srcFactor: "one",
|
|
188
|
+
dstFactor: "one-minus-src"
|
|
189
|
+
},
|
|
190
|
+
alpha: {
|
|
191
|
+
operation: "add",
|
|
192
|
+
srcFactor: "one",
|
|
193
|
+
dstFactor: "one-minus-src-alpha"
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
case "subtract": return {
|
|
197
|
+
color: {
|
|
198
|
+
operation: "reverse-subtract",
|
|
199
|
+
srcFactor: "src-alpha",
|
|
200
|
+
dstFactor: "one"
|
|
201
|
+
},
|
|
202
|
+
alpha: {
|
|
203
|
+
operation: "reverse-subtract",
|
|
204
|
+
srcFactor: "src-alpha",
|
|
205
|
+
dstFactor: "one"
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
case "min": return {
|
|
209
|
+
color: {
|
|
210
|
+
operation: "min",
|
|
211
|
+
srcFactor: "one",
|
|
212
|
+
dstFactor: "one"
|
|
213
|
+
},
|
|
214
|
+
alpha: {
|
|
215
|
+
operation: "min",
|
|
216
|
+
srcFactor: "one",
|
|
217
|
+
dstFactor: "one"
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
case "max": return {
|
|
221
|
+
color: {
|
|
222
|
+
operation: "max",
|
|
223
|
+
srcFactor: "one",
|
|
224
|
+
dstFactor: "one"
|
|
225
|
+
},
|
|
226
|
+
alpha: {
|
|
227
|
+
operation: "max",
|
|
228
|
+
srcFactor: "one",
|
|
229
|
+
dstFactor: "one"
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
case "darken": return {
|
|
233
|
+
color: {
|
|
234
|
+
operation: "min",
|
|
235
|
+
srcFactor: "one",
|
|
236
|
+
dstFactor: "one"
|
|
237
|
+
},
|
|
238
|
+
alpha: {
|
|
239
|
+
operation: "add",
|
|
240
|
+
srcFactor: "src-alpha",
|
|
241
|
+
dstFactor: "one-minus-src-alpha"
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
case "lighten": return {
|
|
245
|
+
color: {
|
|
246
|
+
operation: "max",
|
|
247
|
+
srcFactor: "one",
|
|
248
|
+
dstFactor: "one"
|
|
175
249
|
},
|
|
176
250
|
alpha: {
|
|
251
|
+
operation: "add",
|
|
252
|
+
srcFactor: "src-alpha",
|
|
253
|
+
dstFactor: "one-minus-src-alpha"
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
case "linear-dodge": return {
|
|
257
|
+
color: {
|
|
258
|
+
operation: "add",
|
|
177
259
|
srcFactor: "one",
|
|
178
|
-
dstFactor: "one
|
|
179
|
-
|
|
260
|
+
dstFactor: "one"
|
|
261
|
+
},
|
|
262
|
+
alpha: {
|
|
263
|
+
operation: "add",
|
|
264
|
+
srcFactor: "src-alpha",
|
|
265
|
+
dstFactor: "one"
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
case "linear-burn": return {
|
|
269
|
+
color: {
|
|
270
|
+
operation: "reverse-subtract",
|
|
271
|
+
srcFactor: "one",
|
|
272
|
+
dstFactor: "one"
|
|
273
|
+
},
|
|
274
|
+
alpha: {
|
|
275
|
+
operation: "add",
|
|
276
|
+
srcFactor: "src-alpha",
|
|
277
|
+
dstFactor: "one-minus-src-alpha"
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
case "difference": return {
|
|
281
|
+
color: {
|
|
282
|
+
operation: "reverse-subtract",
|
|
283
|
+
srcFactor: "one",
|
|
284
|
+
dstFactor: "one"
|
|
285
|
+
},
|
|
286
|
+
alpha: {
|
|
287
|
+
operation: "add",
|
|
288
|
+
srcFactor: "src-alpha",
|
|
289
|
+
dstFactor: "one-minus-src-alpha"
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
case "exclusion": return {
|
|
293
|
+
color: {
|
|
294
|
+
operation: "add",
|
|
295
|
+
srcFactor: "zero",
|
|
296
|
+
dstFactor: "one"
|
|
297
|
+
},
|
|
298
|
+
alpha: {
|
|
299
|
+
operation: "add",
|
|
300
|
+
srcFactor: "src-alpha",
|
|
301
|
+
dstFactor: "one-minus-src-alpha"
|
|
180
302
|
}
|
|
181
303
|
};
|
|
182
304
|
default: return;
|
|
@@ -289,6 +411,8 @@ var WGSLRenderer = class {
|
|
|
289
411
|
this.isResizing = true;
|
|
290
412
|
this.canvas.width = width;
|
|
291
413
|
this.canvas.height = height;
|
|
414
|
+
this.canvas.style.width = `${width / (window.devicePixelRatio || 1)}px`;
|
|
415
|
+
this.canvas.style.height = `${height / (window.devicePixelRatio || 1)}px`;
|
|
292
416
|
const future = this.device.queue.onSubmittedWorkDone();
|
|
293
417
|
future.catch(() => {
|
|
294
418
|
console.warn("GPU work submission failed during resize.");
|
|
@@ -316,9 +440,10 @@ var WGSLRenderer = class {
|
|
|
316
440
|
* Resolve a PassTextureRef to actual GPUTextureView with validation
|
|
317
441
|
*/
|
|
318
442
|
resolveTextureRef(ref) {
|
|
319
|
-
const
|
|
320
|
-
if (
|
|
321
|
-
|
|
443
|
+
const targetPass = this.passes.find((pass) => pass.name === ref.passName);
|
|
444
|
+
if (!targetPass) throw new Error(`Cannot find pass named '${ref.passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
|
|
445
|
+
if (targetPass.view) return targetPass.view;
|
|
446
|
+
const textureName = `pass_${this.passes.indexOf(targetPass)}_output`;
|
|
322
447
|
let texture = this.textureManager.getTexture(textureName);
|
|
323
448
|
if (!texture) texture = this.textureManager.createTexture(textureName, this.format);
|
|
324
449
|
return texture.createView();
|
|
@@ -377,6 +502,12 @@ var WGSLRenderer = class {
|
|
|
377
502
|
return this.passes.filter((pass) => pass.enabled);
|
|
378
503
|
}
|
|
379
504
|
/**
|
|
505
|
+
* Set the entire passes array (replaces existing passes)
|
|
506
|
+
*/
|
|
507
|
+
setPasses(passes) {
|
|
508
|
+
this.passes = passes;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
380
511
|
* Switch bind group set for a specific pass
|
|
381
512
|
*/
|
|
382
513
|
switchBindGroupSet(passName, setName) {
|
|
@@ -397,10 +528,7 @@ var WGSLRenderer = class {
|
|
|
397
528
|
});
|
|
398
529
|
pass.updateBindGroupSetResources(setName, resolvedResources);
|
|
399
530
|
}
|
|
400
|
-
|
|
401
|
-
* Add a render pass to the multi-pass pipeline
|
|
402
|
-
*/
|
|
403
|
-
addPass(descriptor) {
|
|
531
|
+
createPass(descriptor) {
|
|
404
532
|
const finalBindGroupEntries = [];
|
|
405
533
|
descriptor.resources?.forEach((resource, index) => {
|
|
406
534
|
finalBindGroupEntries.push({
|
|
@@ -422,13 +550,30 @@ var WGSLRenderer = class {
|
|
|
422
550
|
bindGroupEntries: finalBindGroupEntries,
|
|
423
551
|
bindGroupSets: bindGroupSetsCopy,
|
|
424
552
|
view: descriptor.view,
|
|
425
|
-
format: descriptor.format
|
|
553
|
+
format: descriptor.format,
|
|
554
|
+
renderToCanvas: descriptor.renderToCanvas
|
|
426
555
|
};
|
|
427
556
|
const pipelineFormat = descriptor.format || this.format;
|
|
428
|
-
|
|
557
|
+
return new RenderPass(internalDescriptor, this.device, pipelineFormat);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Add a render pass to the multi-pass pipeline
|
|
561
|
+
*/
|
|
562
|
+
addPass(descriptor) {
|
|
563
|
+
const pass = this.createPass(descriptor);
|
|
429
564
|
pass.passResources = descriptor.resources ?? [];
|
|
430
565
|
this.passes.push(pass);
|
|
431
566
|
}
|
|
567
|
+
insertPassesTo(passName, descriptors) {
|
|
568
|
+
const i = this.passes.findIndex((p) => p.name === passName);
|
|
569
|
+
if (i === -1) throw new Error(`Cannot find pass named '${passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
|
|
570
|
+
const newPasses = descriptors.map((desc) => {
|
|
571
|
+
const pass = this.createPass(desc);
|
|
572
|
+
pass.passResources = desc.resources ?? [];
|
|
573
|
+
return pass;
|
|
574
|
+
});
|
|
575
|
+
this.passes.splice(i, 0, ...newPasses);
|
|
576
|
+
}
|
|
432
577
|
/**
|
|
433
578
|
* Resolve resource to actual GPU binding resource
|
|
434
579
|
* Handles PassTextureRef by getting the current texture view with validation
|
|
@@ -542,11 +687,10 @@ var WGSLRenderer = class {
|
|
|
542
687
|
for (let i = 0; i < enabledPasses.length; i++) {
|
|
543
688
|
const pass = enabledPasses[i];
|
|
544
689
|
let loadOp = "load";
|
|
545
|
-
|
|
546
|
-
if (isLast) loadOp = "clear";
|
|
690
|
+
if (i === 0) loadOp = "clear";
|
|
547
691
|
let renderTarget;
|
|
548
|
-
if (pass.
|
|
549
|
-
else if (
|
|
692
|
+
if (pass.renderToCanvas || i === enabledPasses.length - 1) renderTarget = this.ctx.getCurrentTexture().createView();
|
|
693
|
+
else if (pass.view) renderTarget = pass.view;
|
|
550
694
|
else {
|
|
551
695
|
const textureName = `pass_${i}_output`;
|
|
552
696
|
let texture = this.textureManager.getTexture(textureName);
|