wgsl-renderer 0.0.1 → 0.0.3

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/esm/index.js CHANGED
@@ -6,8 +6,12 @@ var RenderPass = class {
6
6
  vertexBuffer;
7
7
  clearColor;
8
8
  blendMode;
9
- hasOutputTexture = false;
9
+ view;
10
+ format;
11
+ passResources = [];
12
+ device;
10
13
  constructor(descriptor, device, format, layout) {
14
+ this.device = device;
11
15
  this.name = descriptor.name;
12
16
  this.clearColor = descriptor.clearColor || {
13
17
  r: 0,
@@ -15,9 +19,12 @@ var RenderPass = class {
15
19
  b: 0,
16
20
  a: 1
17
21
  };
18
- this.blendMode = descriptor.blendMode || "alpha";
19
- const module = device.createShaderModule({ code: descriptor.shaderCode });
20
- this.vertexBuffer = device.createBuffer({
22
+ this.blendMode = descriptor.blendMode || "none";
23
+ this.view = descriptor.view;
24
+ this.format = descriptor.format;
25
+ const actualFormat = descriptor.format || format;
26
+ const module = this.device.createShaderModule({ code: descriptor.shaderCode });
27
+ this.vertexBuffer = this.device.createBuffer({
21
28
  size: 36,
22
29
  usage: GPUBufferUsage.VERTEX,
23
30
  mappedAtCreation: true
@@ -34,11 +41,13 @@ var RenderPass = class {
34
41
  0
35
42
  ]);
36
43
  this.vertexBuffer.unmap();
37
- this.pipeline = device.createRenderPipeline({
44
+ const vertexEntryPoint = descriptor.entryPoints?.vertex || "vs_main";
45
+ const fragmentEntryPoint = descriptor.entryPoints?.fragment || "fs_main";
46
+ this.pipeline = this.device.createRenderPipeline({
38
47
  layout,
39
48
  vertex: {
40
49
  module,
41
- entryPoint: "vs_main",
50
+ entryPoint: vertexEntryPoint,
42
51
  buffers: [{
43
52
  arrayStride: 12,
44
53
  attributes: [{
@@ -50,18 +59,24 @@ var RenderPass = class {
50
59
  },
51
60
  fragment: {
52
61
  module,
53
- entryPoint: "fs_main",
62
+ entryPoint: fragmentEntryPoint,
54
63
  targets: [{
55
- format,
64
+ format: actualFormat,
56
65
  blend: this.getBlendState()
57
66
  }]
58
67
  },
59
68
  primitive: { topology: "triangle-list" }
60
69
  });
70
+ this.bindGroup = null;
71
+ }
72
+ /**
73
+ * Update bind group with new entries (e.g., after texture resize)
74
+ */
75
+ updateBindGroup(newEntries) {
61
76
  const bindGroupLayout = this.pipeline.getBindGroupLayout(0);
62
- this.bindGroup = device.createBindGroup({
77
+ this.bindGroup = this.device.createBindGroup({
63
78
  layout: bindGroupLayout,
64
- entries: descriptor.bindGroupEntries || []
79
+ entries: newEntries
65
80
  });
66
81
  }
67
82
  getBlendState() {
@@ -125,7 +140,7 @@ var TextureManager = class {
125
140
  const texture = this.device.createTexture({
126
141
  size: [this.width, this.height],
127
142
  format: format || "bgra8unorm",
128
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
143
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
129
144
  });
130
145
  this.textures.set(name, texture);
131
146
  return texture;
@@ -135,10 +150,12 @@ var TextureManager = class {
135
150
  }
136
151
  resize(width, height) {
137
152
  if (width === this.width && height === this.height) return;
138
- this.textures.forEach((texture) => texture.destroy());
139
- this.textures.clear();
140
153
  this.width = width;
141
154
  this.height = height;
155
+ this.textures.forEach((texture) => {
156
+ texture.destroy();
157
+ });
158
+ this.textures.clear();
142
159
  }
143
160
  destroy() {
144
161
  this.textures.forEach((texture) => texture.destroy());
@@ -152,6 +169,30 @@ var TextureManager = class {
152
169
  }
153
170
  };
154
171
 
172
+ //#endregion
173
+ //#region src/PassTextureRef.ts
174
+ const PASS_TEXTURE_REF_SYMBOL = Symbol("PassTextureRef");
175
+ var PassTextureRef = class PassTextureRef {
176
+ [PASS_TEXTURE_REF_SYMBOL] = true;
177
+ passName;
178
+ constructor(passName) {
179
+ this.passName = passName;
180
+ }
181
+ static is(obj) {
182
+ return obj && typeof obj === "object" && PASS_TEXTURE_REF_SYMBOL in obj;
183
+ }
184
+ static fromGPUBindingResource(resource) {
185
+ if (this.is(resource)) return resource;
186
+ return null;
187
+ }
188
+ static create(passName) {
189
+ return new PassTextureRef(passName);
190
+ }
191
+ };
192
+ function isPassTextureRef(obj) {
193
+ return PassTextureRef.is(obj);
194
+ }
195
+
155
196
  //#endregion
156
197
  //#region src/index.ts
157
198
  var WGSLRenderer = class {
@@ -160,145 +201,122 @@ var WGSLRenderer = class {
160
201
  format;
161
202
  passes = [];
162
203
  textureManager;
163
- backgroundPassAdded = false;
164
- backgroundColor = {
165
- r: .1,
166
- g: .1,
167
- b: .1,
168
- a: 1
169
- };
170
- uniforms = /* @__PURE__ */ new Map();
171
204
  animationFrameId = null;
205
+ isResizing = false;
172
206
  constructor(canvas, options) {
173
207
  this.canvas = canvas;
208
+ this.options = options;
174
209
  if (!navigator.gpu) throw new Error("WebGPU is not supported in this browser.");
175
210
  this.ctx = canvas.getContext("webgpu");
176
- switch (typeof options?.backgroundColor) {
177
- case "number":
178
- const hex = options.backgroundColor;
179
- this.backgroundColor = {
180
- r: (hex >> 16 & 255) / 255,
181
- g: (hex >> 8 & 255) / 255,
182
- b: (hex & 255) / 255,
183
- a: 1
184
- };
185
- break;
186
- case "string":
187
- const m = options.backgroundColor.match(/^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);
188
- if (m) this.backgroundColor = {
189
- r: Number.parseInt(m[1], 16) / 255,
190
- g: Number.parseInt(m[2], 16) / 255,
191
- b: Number.parseInt(m[3], 16) / 255,
192
- a: 1
193
- };
194
- break;
195
- case "object":
196
- Object.assign(this.backgroundColor, options.backgroundColor);
197
- break;
198
- }
199
211
  }
200
212
  async init() {
201
213
  this.device = await (await navigator.gpu.requestAdapter()).requestDevice();
202
214
  this.format = navigator.gpu.getPreferredCanvasFormat();
203
- this.ctx.configure({
215
+ const config = Object.assign({
204
216
  device: this.device,
205
217
  format: this.format,
206
218
  alphaMode: "opaque"
207
- });
219
+ }, this.options?.config);
220
+ this.ctx.configure(config);
208
221
  const canvasWidth = this.canvas.width || this.canvas.clientWidth;
209
222
  const canvasHeight = this.canvas.height || this.canvas.clientHeight;
210
223
  this.textureManager = new TextureManager(this.device, canvasWidth, canvasHeight);
211
- this.ensureBackgroundPass();
224
+ }
225
+ async resize(width, height) {
226
+ if (this.isResizing) return;
227
+ if (this.canvas.width === width && this.canvas.height === height) return;
228
+ this.isResizing = true;
229
+ this.canvas.width = width;
230
+ this.canvas.height = height;
231
+ const future = this.device.queue.onSubmittedWorkDone();
232
+ future.catch(() => {
233
+ console.warn("GPU work submission failed during resize.");
234
+ this.isResizing = false;
235
+ });
236
+ await future;
237
+ this.textureManager.resize(width, height);
238
+ this.isResizing = false;
239
+ }
240
+ getContext() {
241
+ return this.ctx;
242
+ }
243
+ getDevice() {
244
+ return this.device;
212
245
  }
213
246
  /**
214
- * Ensure background pass is added
247
+ * Get texture reference by pass name
248
+ * Returns a PassTextureRef that will resolve to the actual texture at render time
215
249
  */
216
- ensureBackgroundPass() {
217
- if (!this.backgroundPassAdded) {
218
- const backgroundPass = new RenderPass({
219
- name: "builtin_background",
220
- shaderCode: `
221
- @vertex
222
- fn vs_main(@location(0) p: vec3<f32>) -> @builtin(position) vec4<f32> {
223
- return vec4<f32>(p, 1.0);
224
- }
225
-
226
- @fragment
227
- fn fs_main() -> @location(0) vec4<f32> {
228
- return vec4<f32>(${this.backgroundColor.r}, ${this.backgroundColor.g}, ${this.backgroundColor.b}, ${this.backgroundColor.a});
229
- }
230
- `,
231
- blendMode: "none",
232
- clearColor: this.backgroundColor,
233
- bindGroupEntries: []
234
- }, this.device, this.format, "auto");
235
- this.passes.unshift(backgroundPass);
236
- this.backgroundPassAdded = true;
237
- this.textureManager.createTexture("pass_0_output", this.format);
238
- }
250
+ getPassTexture(passName) {
251
+ if (!this.passes.find((pass) => pass.name === passName)) throw new Error(`Cannot find pass named '${passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
252
+ return PassTextureRef.create(passName);
253
+ }
254
+ /**
255
+ * Resolve a PassTextureRef to actual GPUTextureView with validation
256
+ */
257
+ resolveTextureRef(ref) {
258
+ const targetPassIndex = this.passes.findIndex((pass) => pass.name === ref.passName);
259
+ if (targetPassIndex === -1) throw new Error(`Cannot find pass named '${ref.passName}'. Available passes: [${this.passes.map((p) => p.name).join(", ")}]`);
260
+ const textureName = `pass_${targetPassIndex}_output`;
261
+ let texture = this.textureManager.getTexture(textureName);
262
+ if (!texture) texture = this.textureManager.createTexture(textureName, this.format);
263
+ return texture.createView();
264
+ }
265
+ /**
266
+ * Get pass by name
267
+ */
268
+ getPassByName(passName) {
269
+ return this.passes.find((pass) => pass.name === passName);
239
270
  }
240
271
  /**
241
272
  * Add a render pass to the multi-pass pipeline
242
273
  */
243
274
  addPass(descriptor) {
244
275
  const finalBindGroupEntries = [];
245
- if (this.passes.length > 0) {
246
- const previousOutput = this.getPassOutput(this.passes.length - 1);
247
- if (previousOutput) finalBindGroupEntries.push({
248
- binding: 0,
249
- resource: previousOutput.createView()
250
- });
251
- }
252
- descriptor.resources.forEach((resource, index) => {
276
+ descriptor.resources?.forEach((resource, index) => {
253
277
  finalBindGroupEntries.push({
254
- binding: index + 1,
278
+ binding: index,
255
279
  resource
256
280
  });
257
281
  });
258
- const pass = new RenderPass({
282
+ const internalDescriptor = {
259
283
  name: descriptor.name,
260
284
  shaderCode: descriptor.shaderCode,
285
+ entryPoints: descriptor.entryPoints,
261
286
  clearColor: descriptor.clearColor,
262
287
  blendMode: descriptor.blendMode,
263
- bindGroupEntries: finalBindGroupEntries
264
- }, this.device, this.format, "auto");
265
- this.passes.push(pass);
266
- const currentPassIndex = this.passes.length - 1;
267
- const textureName = `pass_${currentPassIndex}_output`;
268
- this.textureManager.createTexture(textureName, this.format);
269
- this.passes[currentPassIndex].hasOutputTexture = true;
270
- }
271
- /**
272
- * Set background color
273
- */
274
- setBackgroundColor(r, g, b, a = 1) {
275
- this.backgroundColor = {
276
- r,
277
- g,
278
- b,
279
- a
288
+ bindGroupEntries: finalBindGroupEntries,
289
+ view: descriptor.view,
290
+ format: descriptor.format
280
291
  };
292
+ const pipelineFormat = descriptor.format || this.format;
293
+ const pass = new RenderPass(internalDescriptor, this.device, pipelineFormat, "auto");
294
+ pass.passResources = descriptor.resources ?? [];
295
+ this.passes.push(pass);
281
296
  }
282
297
  /**
283
- * Force create output texture for a specific pass
284
- * This is useful when you need the output texture immediately after adding a pass
298
+ * Resolve resource to actual GPU binding resource
299
+ * Handles PassTextureRef by getting the current texture view with validation
285
300
  */
286
- createPassOutput(passIndex) {
287
- if (passIndex < 0 || passIndex >= this.passes.length) return;
288
- if (passIndex === this.passes.length - 1) return;
289
- const textureName = `pass_${passIndex}_output`;
290
- return this.textureManager.createTexture(textureName, this.format);
301
+ resolveResource(resource) {
302
+ if (isPassTextureRef(resource)) return this.resolveTextureRef(resource);
303
+ return resource;
291
304
  }
292
305
  /**
293
- * Get the output texture of a specific pass
306
+ * Update bind groups to resolve current texture references
307
+ * Call this before rendering to ensure all PassTextureRef are resolved
294
308
  */
295
- getPassOutput(passIndex) {
296
- if (passIndex < 0 || passIndex >= this.passes.length) return;
297
- if (passIndex !== 0 && passIndex === this.passes.length - 1) {
298
- if (!this.passes[passIndex]?.hasOutputTexture) return;
299
- }
300
- const textureName = `pass_${passIndex}_output`;
301
- return this.textureManager.getTexture(textureName);
309
+ updateBindGroups() {
310
+ this.passes.forEach((pass) => {
311
+ const finalBindGroupEntries = [];
312
+ pass.passResources.forEach((resource, index) => {
313
+ finalBindGroupEntries.push({
314
+ binding: index,
315
+ resource: this.resolveResource(resource)
316
+ });
317
+ });
318
+ pass.updateBindGroup(finalBindGroupEntries);
319
+ });
302
320
  }
303
321
  /**
304
322
  * Create a uniforms
@@ -311,20 +329,13 @@ var WGSLRenderer = class {
311
329
  size: values.byteLength,
312
330
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
313
331
  });
314
- const uniformID = Symbol();
315
- const uniforms = {
316
- id: uniformID,
332
+ return {
317
333
  values,
318
334
  apply: () => {
319
335
  this.device.queue.writeBuffer(buffer, 0, values.buffer, values.byteOffset, values.byteLength);
320
336
  },
321
337
  getBuffer: () => buffer
322
338
  };
323
- this.uniforms.set(uniformID, uniforms);
324
- return uniforms;
325
- }
326
- getUniformsByID(id) {
327
- return this.uniforms.get(id);
328
339
  }
329
340
  /**
330
341
  * Create a sampler
@@ -337,25 +348,7 @@ var WGSLRenderer = class {
337
348
  addressModeV: "clamp-to-edge"
338
349
  }, options));
339
350
  }
340
- /**
341
- * Create a bind group entry for texture
342
- */
343
- createTextureBinding(texture) {
344
- return texture.createView();
345
- }
346
- /**
347
- * Configure multi-pass rendering
348
- */
349
- setupMultiPass(descriptor) {
350
- this.passes = [];
351
- this.textureManager.destroy();
352
- const canvasWidth = this.canvas.width || this.canvas.clientWidth;
353
- const canvasHeight = this.canvas.height || this.canvas.clientHeight;
354
- this.textureManager = new TextureManager(this.device, canvasWidth, canvasHeight);
355
- descriptor.passes.forEach((passDesc) => this.addPass(passDesc));
356
- if (descriptor.output?.texture && !descriptor.output.writeToCanvas) this.textureManager.createTexture("final_output");
357
- }
358
- async loadTexture(url) {
351
+ async loadImageTexture(url) {
359
352
  const resp = fetch(url);
360
353
  resp.catch((err) => {
361
354
  console.error("Failed to load texture:", err);
@@ -380,25 +373,25 @@ var WGSLRenderer = class {
380
373
  }
381
374
  renderFrame() {
382
375
  if (this.passes.length === 0) return;
376
+ this.updateBindGroups();
383
377
  const commandEncoder = this.device.createCommandEncoder();
384
- const canvasWidth = this.canvas.width || this.canvas.clientWidth;
385
- const canvasHeight = this.canvas.height || this.canvas.clientHeight;
386
- this.textureManager.resize(canvasWidth, canvasHeight);
387
378
  for (let i = 0; i < this.passes.length; i++) {
388
379
  const pass = this.passes[i];
380
+ let loadOp = "load";
381
+ const isLast = i === this.passes.length - 1;
382
+ if (isLast) loadOp = "clear";
389
383
  let renderTarget;
390
- let loadOp = "clear";
391
- if (i === this.passes.length - 1) renderTarget = this.ctx.getCurrentTexture().createView();
384
+ if (pass.view) renderTarget = pass.view;
385
+ else if (isLast) renderTarget = this.ctx.getCurrentTexture().createView();
392
386
  else {
393
387
  const textureName = `pass_${i}_output`;
394
- const texture = this.textureManager.getTexture(textureName);
395
- if (!texture) continue;
388
+ let texture = this.textureManager.getTexture(textureName);
389
+ if (!texture) texture = this.textureManager.createTexture(textureName, "rgba16float");
396
390
  renderTarget = texture.createView();
397
- loadOp = "load";
398
391
  }
399
392
  const renderPass = commandEncoder.beginRenderPass({ colorAttachments: [{
400
393
  view: renderTarget,
401
- loadOp: i === 0 ? "clear" : loadOp,
394
+ loadOp,
402
395
  storeOp: "store",
403
396
  clearValue: pass.clearColor
404
397
  }] });
@@ -411,9 +404,11 @@ var WGSLRenderer = class {
411
404
  this.device.queue.submit([commandEncoder.finish()]);
412
405
  }
413
406
  loopRender(cb) {
414
- cb?.();
415
- this.renderFrame();
416
- this.animationFrameId = requestAnimationFrame(() => this.loopRender(cb));
407
+ this.animationFrameId = requestAnimationFrame((t) => {
408
+ cb?.(t);
409
+ this.renderFrame();
410
+ this.loopRender(cb);
411
+ });
417
412
  }
418
413
  stopLoop() {
419
414
  if (this.animationFrameId !== null) {
@@ -421,11 +416,6 @@ var WGSLRenderer = class {
421
416
  this.animationFrameId = null;
422
417
  }
423
418
  }
424
- resize(width, height) {
425
- this.canvas.width = width;
426
- this.canvas.height = height;
427
- this.textureManager.resize(width, height);
428
- }
429
419
  };
430
420
  async function createWGSLRenderer(cvs, options) {
431
421
  const renderer = new WGSLRenderer(cvs, options);
@@ -1,7 +1,11 @@
1
1
  //#region src/RenderPass.d.ts
2
- interface RenderPassDescriptor {
2
+ interface RenderPassOptions {
3
3
  name: string;
4
4
  shaderCode: string;
5
+ entryPoints?: {
6
+ vertex?: string;
7
+ fragment?: string;
8
+ };
5
9
  clearColor?: {
6
10
  r: number;
7
11
  g: number;
@@ -10,10 +14,16 @@ interface RenderPassDescriptor {
10
14
  };
11
15
  blendMode?: 'additive' | 'alpha' | 'multiply' | 'none';
12
16
  resources: GPUBindingResource[];
17
+ view?: GPUTextureView;
18
+ format?: GPUTextureFormat;
13
19
  }
14
20
  interface InternalRenderPassDescriptor {
15
21
  name: string;
16
22
  shaderCode: string;
23
+ entryPoints?: {
24
+ vertex?: string;
25
+ fragment?: string;
26
+ };
17
27
  clearColor?: {
18
28
  r: number;
19
29
  g: number;
@@ -22,15 +32,13 @@ interface InternalRenderPassDescriptor {
22
32
  };
23
33
  blendMode?: 'additive' | 'alpha' | 'multiply' | 'none';
24
34
  bindGroupEntries: GPUBindGroupEntry[];
25
- }
26
- interface RenderPassOutput {
27
- texture?: GPUTexture;
28
- writeToCanvas?: boolean;
35
+ view?: GPUTextureView;
36
+ format?: GPUTextureFormat;
29
37
  }
30
38
  declare class RenderPass {
31
39
  name: string;
32
40
  pipeline: GPURenderPipeline;
33
- bindGroup: GPUBindGroup;
41
+ bindGroup: GPUBindGroup | null;
34
42
  vertexBuffer: GPUBuffer;
35
43
  clearColor: {
36
44
  r: number;
@@ -39,101 +47,102 @@ declare class RenderPass {
39
47
  a: number;
40
48
  };
41
49
  blendMode: 'additive' | 'alpha' | 'multiply' | 'none';
42
- hasOutputTexture: boolean;
50
+ view?: GPUTextureView;
51
+ format?: GPUTextureFormat;
52
+ passResources: GPUBindingResource[];
53
+ private device;
43
54
  constructor(descriptor: InternalRenderPassDescriptor, device: GPUDevice, format: GPUTextureFormat, layout: GPUPipelineLayout | 'auto');
55
+ /**
56
+ * Update bind group with new entries (e.g., after texture resize)
57
+ */
58
+ updateBindGroup(newEntries: GPUBindGroupEntry[]): void;
44
59
  private getBlendState;
45
60
  }
46
61
  //#endregion
62
+ //#region src/PassTextureRef.d.ts
63
+ declare const PASS_TEXTURE_REF_SYMBOL: unique symbol;
64
+ declare class PassTextureRef {
65
+ readonly [PASS_TEXTURE_REF_SYMBOL] = true;
66
+ readonly passName: string;
67
+ constructor(passName: string);
68
+ static is(obj: any): obj is PassTextureRef;
69
+ static fromGPUBindingResource(resource: GPUBindingResource): PassTextureRef | null;
70
+ static create(passName: string): PassTextureRef;
71
+ }
72
+ //#endregion
47
73
  //#region src/index.d.ts
48
74
  interface MultiPassDescriptor {
49
- passes: RenderPassDescriptor[];
50
- output?: RenderPassOutput;
75
+ passes: RenderPassOptions[];
51
76
  }
52
77
  interface WGSLRendererOptions {
53
- backgroundColor?: number | string | {
54
- r: number;
55
- g: number;
56
- b: number;
57
- };
78
+ config?: GPUCanvasConfiguration;
58
79
  }
59
80
  declare class WGSLRenderer {
60
81
  canvas: HTMLCanvasElement;
61
- ctx: GPUCanvasContext;
62
- device: GPUDevice;
63
- format: GPUTextureFormat;
64
- passes: RenderPass[];
82
+ options?: WGSLRendererOptions | undefined;
83
+ private ctx;
84
+ private device;
85
+ private format;
86
+ private passes;
65
87
  private textureManager;
66
- private backgroundPassAdded;
67
- private backgroundColor;
68
- private uniforms;
69
88
  private animationFrameId;
70
- constructor(canvas: HTMLCanvasElement, options?: WGSLRendererOptions);
89
+ private isResizing;
90
+ constructor(canvas: HTMLCanvasElement, options?: WGSLRendererOptions | undefined);
71
91
  init(): Promise<void>;
92
+ resize(width: number, height: number): Promise<void>;
93
+ getContext(): GPUCanvasContext;
94
+ getDevice(): GPUDevice;
72
95
  /**
73
- * Ensure background pass is added
96
+ * Get texture reference by pass name
97
+ * Returns a PassTextureRef that will resolve to the actual texture at render time
74
98
  */
75
- private ensureBackgroundPass;
99
+ getPassTexture(passName: string): PassTextureRef;
76
100
  /**
77
- * Add a render pass to the multi-pass pipeline
101
+ * Resolve a PassTextureRef to actual GPUTextureView with validation
78
102
  */
79
- addPass(descriptor: RenderPassDescriptor): void;
103
+ private resolveTextureRef;
80
104
  /**
81
- * Set background color
105
+ * Get pass by name
106
+ */
107
+ getPassByName(passName: string): RenderPass | undefined;
108
+ /**
109
+ * Add a render pass to the multi-pass pipeline
82
110
  */
83
- setBackgroundColor(r: number, g: number, b: number, a?: number): void;
111
+ addPass(descriptor: RenderPassOptions): void;
84
112
  /**
85
- * Force create output texture for a specific pass
86
- * This is useful when you need the output texture immediately after adding a pass
113
+ * Resolve resource to actual GPU binding resource
114
+ * Handles PassTextureRef by getting the current texture view with validation
87
115
  */
88
- createPassOutput(passIndex: number): GPUTexture | undefined;
116
+ private resolveResource;
89
117
  /**
90
- * Get the output texture of a specific pass
118
+ * Update bind groups to resolve current texture references
119
+ * Call this before rendering to ensure all PassTextureRef are resolved
91
120
  */
92
- getPassOutput(passIndex: number): GPUTexture | undefined;
121
+ private updateBindGroups;
93
122
  /**
94
123
  * Create a uniforms
95
124
  * @param length The length of the uniform buffer in number of floats
96
125
  * @return The uniform object containing the buffer and data array
97
126
  */
98
127
  createUniforms(length: number): {
99
- id: symbol;
100
128
  values: Float32Array<ArrayBuffer>;
101
129
  apply: () => void;
102
130
  getBuffer: () => GPUBuffer;
103
131
  };
104
- getUniformsByID(id: symbol): {
105
- id: symbol;
106
- values: Float32Array;
107
- apply: {
108
- (): void;
109
- };
110
- getBuffer: {
111
- (): GPUBuffer;
112
- };
113
- } | undefined;
114
132
  /**
115
133
  * Create a sampler
116
134
  */
117
135
  createSampler(options?: GPUSamplerDescriptor): GPUSampler;
118
- /**
119
- * Create a bind group entry for texture
120
- */
121
- createTextureBinding(texture: GPUTexture): GPUTextureView;
122
- /**
123
- * Configure multi-pass rendering
124
- */
125
- setupMultiPass(descriptor: MultiPassDescriptor): void;
126
- loadTexture(url: string): Promise<{
136
+ loadImageTexture(url: string): Promise<{
127
137
  texture: GPUTexture;
128
138
  width: number;
129
139
  height: number;
130
140
  }>;
131
141
  renderFrame(): void;
132
142
  loopRender(cb?: {
133
- (): void;
143
+ (t?: number): void;
134
144
  }): void;
135
145
  stopLoop(): void;
136
- resize(width: number, height: number): void;
137
146
  }
138
147
  declare function createWGSLRenderer(cvs: HTMLCanvasElement, options?: WGSLRendererOptions): Promise<WGSLRenderer>;
139
148
  //#endregion