wgsl-renderer 0.1.3 → 0.1.5

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 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
- view?: GPUTextureView; // Optional custom view for this pass
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
- view?: GPUTextureView; // 可选的自定义View
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
- srcFactor: "one",
167
- dstFactor: "one",
168
- operation: "add"
168
+ operation: "add",
169
+ srcFactor: "src-alpha",
170
+ dstFactor: "one"
169
171
  }
170
172
  };
171
173
  case "multiply": return {
172
174
  color: {
173
- srcFactor: "src",
174
- dstFactor: "dst",
175
- operation: "add"
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-minus-src-alpha",
180
- operation: "add"
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 targetPassIndex = this.passes.findIndex((pass) => pass.name === ref.passName);
321
- if (targetPassIndex === -1) throw new Error(`Cannot find pass named '${ref.passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
322
- const textureName = `pass_${targetPassIndex}_output`;
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
- const pass = new RenderPass(internalDescriptor, this.device, pipelineFormat, "auto");
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
- const isLast = i === enabledPasses.length - 1;
547
- if (isLast) loadOp = "clear";
691
+ if (i === 0) loadOp = "clear";
548
692
  let renderTarget;
549
- if (pass.view) renderTarget = pass.view;
550
- else if (isLast) renderTarget = this.ctx.getCurrentTexture().createView();
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);
@@ -10,11 +10,12 @@ declare class PassTextureRef {
10
10
  }
11
11
  //#endregion
12
12
  //#region src/RenderPass.d.ts
13
- type BandingResource = GPUBindingResource | PassTextureRef;
13
+ type BindingResource = GPUBindingResource | PassTextureRef;
14
14
  type BindingEntry = {
15
15
  binding: number;
16
- resource: BandingResource;
16
+ resource: BindingResource;
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?: 'additive' | 'alpha' | 'multiply' | 'none';
32
- resources?: BandingResource[];
32
+ blendMode?: BlendMode;
33
+ resources?: BindingResource[];
33
34
  bindGroupSets?: {
34
- [setName: string]: BandingResource[];
35
+ [setName: string]: BindingResource[];
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?: 'additive' | 'alpha' | 'multiply' | 'none';
54
+ blendMode?: BlendMode;
53
55
  bindGroupEntries: BindingEntry[];
54
56
  bindGroupSets?: {
55
- [setName: string]: BandingResource[];
57
+ [setName: string]: BindingResource[];
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,10 +71,11 @@ declare class RenderPass {
68
71
  b: number;
69
72
  a: number;
70
73
  };
71
- blendMode: 'additive' | 'alpha' | 'multiply' | 'none';
74
+ blendMode: BlendMode;
72
75
  view?: GPUTextureView;
73
76
  format?: GPUTextureFormat;
74
- passResources: BandingResource[];
77
+ renderToCanvas?: boolean;
78
+ passResources: BindingResource[];
75
79
  bindGroups: {
76
80
  [setName: string]: GPUBindGroup;
77
81
  };
@@ -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: GPUPipelineLayout | 'auto');
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
  */
@@ -110,7 +114,7 @@ declare class RenderPass {
110
114
  * Update or add a bind group set with new resources
111
115
  * This allows dynamic modification of bind groups at runtime
112
116
  */
113
- updateBindGroupSetResources(setName: string, resources: BandingResource[]): void;
117
+ updateBindGroupSetResources(setName: string, resources: BindingResource[]): void;
114
118
  private getBlendState;
115
119
  }
116
120
  //#endregion
@@ -170,6 +174,10 @@ declare class WGSLRenderer {
170
174
  * Get only enabled passes
171
175
  */
172
176
  getEnabledPasses(): RenderPass[];
177
+ /**
178
+ * Set the entire passes array (replaces existing passes)
179
+ */
180
+ setPasses(passes: RenderPass[]): void;
173
181
  /**
174
182
  * Switch bind group set for a specific pass
175
183
  */
@@ -178,11 +186,13 @@ declare class WGSLRenderer {
178
186
  * Update bind group set resources for a specific pass
179
187
  * This allows dynamic modification of bind groups at runtime
180
188
  */
181
- updateBindGroupSetResources(passName: string, setName: string, resources: BandingResource[]): void;
189
+ updateBindGroupSetResources(passName: string, setName: string, resources: BindingResource[]): void;
190
+ private createPass;
182
191
  /**
183
192
  * Add a render pass to the multi-pass pipeline
184
193
  */
185
194
  addPass(descriptor: RenderPassOptions): void;
195
+ insertPassesTo(passName: string, descriptors: RenderPassOptions[]): void;
186
196
  /**
187
197
  * Resolve resource to actual GPU binding resource
188
198
  * Handles PassTextureRef by getting the current texture view with validation
@@ -221,4 +231,4 @@ declare class WGSLRenderer {
221
231
  }
222
232
  declare function createWGSLRenderer(cvs: HTMLCanvasElement, options?: WGSLRendererOptions): Promise<WGSLRenderer>;
223
233
  //#endregion
224
- export { type BandingResource, type RenderPassOptions, type WGSLRenderer, createWGSLRenderer };
234
+ export { type BindingResource, type PassTextureRef, 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
- srcFactor: "one",
166
- dstFactor: "one",
167
- operation: "add"
167
+ operation: "add",
168
+ srcFactor: "src-alpha",
169
+ dstFactor: "one"
168
170
  }
169
171
  };
170
172
  case "multiply": return {
171
173
  color: {
172
- srcFactor: "src",
173
- dstFactor: "dst",
174
- operation: "add"
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-minus-src-alpha",
179
- operation: "add"
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 targetPassIndex = this.passes.findIndex((pass) => pass.name === ref.passName);
320
- if (targetPassIndex === -1) throw new Error(`Cannot find pass named '${ref.passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
321
- const textureName = `pass_${targetPassIndex}_output`;
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
- const pass = new RenderPass(internalDescriptor, this.device, pipelineFormat, "auto");
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
- const isLast = i === enabledPasses.length - 1;
546
- if (isLast) loadOp = "clear";
690
+ if (i === 0) loadOp = "clear";
547
691
  let renderTarget;
548
- if (pass.view) renderTarget = pass.view;
549
- else if (isLast) renderTarget = this.ctx.getCurrentTexture().createView();
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wgsl-renderer",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "A multi-pass renderer based on WebGPU and WGSL.",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",