Skip to content

Commit 718698e

Browse files
committed
Introduce a PENDING state to make accumulation still work, even though it's slower
1 parent d26e804 commit 718698e

1 file changed

Lines changed: 69 additions & 30 deletions

File tree

src/webgpu/p5.RendererWebGPU.js

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import noiseWGSL from './shaders/functions/noise3DWGSL';
1212
import { baseFilterVertexShader, baseFilterFragmentShader } from './shaders/filters/base';
1313
import { imageLightVertexShader, imageLightDiffusedFragmentShader, imageLightSpecularFragmentShader } from './shaders/imageLight';
1414

15+
const FRAME_STATE = {
16+
PENDING: 0,
17+
UNPROMOTED: 1,
18+
PROMOTED: 2
19+
};
20+
1521
function rendererWebGPU(p5, fn) {
1622
const { lineDefs } = getStrokeDefs((n, v, t) => `const ${n}: ${t} = ${v};\n`);
1723

@@ -65,7 +71,7 @@ function rendererWebGPU(p5, fn) {
6571
this._pixelReadCanvas = null;
6672
this._pixelReadCtx = null;
6773
this.mainFramebuffer = null;
68-
this._framePromotedToFramebuffer = false;
74+
this._frameState = FRAME_STATE.PENDING;
6975

7076
this.finalCamera = new Camera(this);
7177
this.finalCamera._computeCameraDefaultSettings();
@@ -204,6 +210,11 @@ function rendererWebGPU(p5, fn) {
204210
const _b = args[2] || 0;
205211
const _a = args[3] || 0;
206212

213+
// If PENDING and no custom framebuffer, clear means stay UNPROMOTED
214+
if (this._frameState === FRAME_STATE.PENDING && !this.activeFramebuffer()) {
215+
this._frameState = FRAME_STATE.UNPROMOTED;
216+
}
217+
207218
const commandEncoder = this.device.createCommandEncoder();
208219

209220
// Use framebuffer texture if active, otherwise use canvas texture
@@ -808,34 +819,37 @@ function rendererWebGPU(p5, fn) {
808819
}
809820

810821
_resetBuffersBeforeDraw() {
811-
// Only use mainFramebuffer if promotion has occurred
812-
if (!this.activeFramebuffer() && this._framePromotedToFramebuffer) {
813-
this.mainFramebuffer.begin();
814-
}
822+
// Set state to PENDING - we'll decide on first draw
823+
this._frameState = FRAME_STATE.PENDING;
824+
825+
// Clear depth buffer but DON'T start any render pass yet
826+
const activeFramebuffer = this.activeFramebuffer();
815827
const commandEncoder = this.device.createCommandEncoder();
816828

817-
const depthTextureView = this.depthTextureView;
818-
const depthAttachment = depthTextureView
819-
? {
829+
const depthTextureView = activeFramebuffer
830+
? (activeFramebuffer.aaDepthTexture
831+
? activeFramebuffer.aaDepthTextureView
832+
: activeFramebuffer.depthTextureView)
833+
: this.depthTextureView;
834+
835+
if (depthTextureView) {
836+
const depthAttachment = {
820837
view: depthTextureView,
821838
depthClearValue: 1.0,
822839
depthLoadOp: 'clear',
823840
depthStoreOp: 'store',
824841
stencilLoadOp: 'load',
825-
stencilStoreOp: "store",
826-
}
827-
: undefined;
828-
829-
const renderPassDescriptor = {
830-
colorAttachments: [],
831-
...(depthAttachment ? { depthStencilAttachment: depthAttachment } : {}),
832-
};
833-
834-
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
835-
passEncoder.end();
836-
837-
this._pendingCommandEncoders.push(commandEncoder.finish());
838-
this._hasPendingDraws = true;
842+
stencilStoreOp: 'store',
843+
};
844+
const renderPassDescriptor = {
845+
colorAttachments: [],
846+
depthStencilAttachment: depthAttachment,
847+
};
848+
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
849+
passEncoder.end();
850+
this._pendingCommandEncoders.push(commandEncoder.finish());
851+
this._hasPendingDraws = true;
852+
}
839853
}
840854

841855
/**
@@ -845,7 +859,7 @@ function rendererWebGPU(p5, fn) {
845859
*/
846860
_promoteToFramebuffer() {
847861
// Already promoted this frame
848-
if (this._framePromotedToFramebuffer) {
862+
if (this._frameState === FRAME_STATE.PROMOTED) {
849863
return;
850864
}
851865

@@ -858,7 +872,7 @@ function rendererWebGPU(p5, fn) {
858872
this.flushDraw();
859873

860874
// Mark as promoted
861-
this._framePromotedToFramebuffer = true;
875+
this._frameState = FRAME_STATE.PROMOTED;
862876

863877
// Get current canvas texture
864878
const canvasTexture = this.drawingContext.getCurrentTexture();
@@ -924,6 +938,29 @@ function rendererWebGPU(p5, fn) {
924938
this.states.uModelMatrix.set(savedModelMatrix);
925939
}
926940

941+
_promoteToFramebufferWithoutCopy() {
942+
// Ensure mainFramebuffer matches canvas size
943+
if (this.mainFramebuffer.width !== this.width ||
944+
this.mainFramebuffer.height !== this.height) {
945+
this.mainFramebuffer.resize(this.width, this.height);
946+
}
947+
948+
// Mark as promoted WITHOUT copying canvas content
949+
this._frameState = FRAME_STATE.PROMOTED;
950+
951+
// Flush any pending draws first
952+
this.flushDraw();
953+
954+
// Preserve transformation state
955+
const savedModelMatrix = this.states.uModelMatrix.copy();
956+
this.mainFramebuffer.defaultCamera.set(this.states.curCamera);
957+
958+
// Begin rendering to mainFramebuffer
959+
this.mainFramebuffer.begin();
960+
961+
this.states.uModelMatrix.set(savedModelMatrix);
962+
}
963+
927964
//////////////////////////////////////////////
928965
// Geometry buffer pool management
929966
//////////////////////////////////////////////
@@ -1134,7 +1171,7 @@ function rendererWebGPU(p5, fn) {
11341171
const states = [];
11351172

11361173
// Only blit if we promoted to framebuffer this frame
1137-
if (this._framePromotedToFramebuffer) {
1174+
if (this._frameState === FRAME_STATE.PROMOTED) {
11381175
while (this.activeFramebuffers.length > 0) {
11391176
const fbo = this.activeFramebuffers.pop();
11401177
states.unshift({ fbo, diff: { ...this.states } });
@@ -1173,19 +1210,16 @@ function rendererWebGPU(p5, fn) {
11731210
}
11741211
this._retiredBuffers = [];
11751212

1176-
if (this._framePromotedToFramebuffer) {
1213+
if (this._frameState === FRAME_STATE.PROMOTED) {
11771214
for (const { fbo, diff } of states) {
1178-
if (fbo !== this.mainFramebuffer || !this._framePromotedToFramebuffer) {
1215+
if (fbo !== this.mainFramebuffer || this._frameState !== FRAME_STATE.PROMOTED) {
11791216
fbo.begin();
11801217
}
11811218
for (const key in diff) {
11821219
this.states.setValue(key, diff[key]);
11831220
}
11841221
}
11851222
}
1186-
1187-
// Reset promotion state for next frame
1188-
this._framePromotedToFramebuffer = false;
11891223
}
11901224

11911225
//////////////////////////////////////////////
@@ -1196,6 +1230,11 @@ function rendererWebGPU(p5, fn) {
11961230
const buffers = this.geometryBufferCache.getCached(geometry);
11971231
if (!buffers) return;
11981232

1233+
// If PENDING and no custom framebuffer, regular draw means PROMOTE
1234+
if (this._frameState === FRAME_STATE.PENDING && !this.activeFramebuffer()) {
1235+
this._promoteToFramebufferWithoutCopy();
1236+
}
1237+
11991238
const commandEncoder = this.device.createCommandEncoder();
12001239

12011240
// Use framebuffer texture if active, otherwise use canvas texture

0 commit comments

Comments
 (0)