@@ -12,6 +12,12 @@ import noiseWGSL from './shaders/functions/noise3DWGSL';
1212import { baseFilterVertexShader , baseFilterFragmentShader } from './shaders/filters/base' ;
1313import { imageLightVertexShader , imageLightDiffusedFragmentShader , imageLightSpecularFragmentShader } from './shaders/imageLight' ;
1414
15+ const FRAME_STATE = {
16+ PENDING : 0 ,
17+ UNPROMOTED : 1 ,
18+ PROMOTED : 2
19+ } ;
20+
1521function 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