@@ -62,6 +62,7 @@ function rendererWebGPU(p5, fn) {
6262 this . _pixelReadCanvas = null ;
6363 this . _pixelReadCtx = null ;
6464 this . mainFramebuffer = null ;
65+ this . _framePromotedToFramebuffer = false ;
6566
6667 this . finalCamera = new Camera ( this ) ;
6768 this . finalCamera . _computeCameraDefaultSettings ( ) ;
@@ -107,7 +108,7 @@ function rendererWebGPU(p5, fn) {
107108
108109 // TODO disablable stencil
109110 this . depthFormat = 'depth24plus-stencil8' ;
110- this . mainFramebuffer = this . createFramebuffer ( ) ;
111+ this . mainFramebuffer = this . createFramebuffer ( { _useCanvasFormat : true } ) ;
111112 this . _updateSize ( ) ;
112113 this . _update ( ) ;
113114 }
@@ -801,7 +802,8 @@ function rendererWebGPU(p5, fn) {
801802 }
802803
803804 _resetBuffersBeforeDraw ( ) {
804- if ( ! this . activeFramebuffer ( ) ) {
805+ // Only use mainFramebuffer if promotion has occurred
806+ if ( ! this . activeFramebuffer ( ) && this . _framePromotedToFramebuffer ) {
805807 this . mainFramebuffer . begin ( ) ;
806808 }
807809 const commandEncoder = this . device . createCommandEncoder ( ) ;
@@ -830,6 +832,64 @@ function rendererWebGPU(p5, fn) {
830832 this . _hasPendingDraws = true ;
831833 }
832834
835+ /**
836+ * Promotes the current frame to use mainFramebuffer.
837+ * Copies current canvas content to mainFramebuffer, then switches to rendering there.
838+ * @private
839+ */
840+ _promoteToFramebuffer ( ) {
841+ // Already promoted this frame
842+ if ( this . _framePromotedToFramebuffer ) {
843+ return ;
844+ }
845+
846+ // Already drawing to a custom framebuffer, no promotion needed
847+ if ( this . activeFramebuffer ( ) ) {
848+ return ;
849+ }
850+
851+ // Flush any pending draws to canvas first
852+ this . flushDraw ( ) ;
853+
854+ // Mark as promoted
855+ this . _framePromotedToFramebuffer = true ;
856+
857+ // Get current canvas texture
858+ const canvasTexture = this . drawingContext . getCurrentTexture ( ) ;
859+
860+ // Ensure mainFramebuffer matches canvas size
861+ if ( this . mainFramebuffer . width !== this . width ||
862+ this . mainFramebuffer . height !== this . height ) {
863+ this . mainFramebuffer . resize ( this . width , this . height ) ;
864+ }
865+
866+ // Copy canvas texture to mainFramebuffer
867+ const commandEncoder = this . device . createCommandEncoder ( ) ;
868+ commandEncoder . copyTextureToTexture (
869+ {
870+ texture : canvasTexture ,
871+ origin : { x : 0 , y : 0 , z : 0 } ,
872+ mipLevel : 0 ,
873+ } ,
874+ {
875+ texture : this . mainFramebuffer . colorTexture ,
876+ origin : { x : 0 , y : 0 , z : 0 } ,
877+ mipLevel : 0 ,
878+ } ,
879+ {
880+ width : Math . ceil ( this . width * this . _pixelDensity ) ,
881+ height : Math . ceil ( this . height * this . _pixelDensity ) ,
882+ depthOrArrayLayers : 1 ,
883+ }
884+ ) ;
885+
886+ this . _pendingCommandEncoders . push ( commandEncoder . finish ( ) ) ;
887+ this . _hasPendingDraws = true ;
888+
889+ // Activate mainFramebuffer for subsequent draws
890+ this . mainFramebuffer . begin ( ) ;
891+ }
892+
833893 //////////////////////////////////////////////
834894 // Geometry buffer pool management
835895 //////////////////////////////////////////////
@@ -1025,24 +1085,29 @@ function rendererWebGPU(p5, fn) {
10251085
10261086 async finishDraw ( ) {
10271087 this . flushDraw ( ) ;
1088+
10281089 const states = [ ] ;
1029- while ( this . activeFramebuffers . length > 0 ) {
1030- const fbo = this . activeFramebuffers . pop ( ) ;
1031- states . unshift ( { fbo, diff : { ...this . states } } ) ;
1032- }
1033- this . flushDraw ( ) ;
10341090
1035- // this._pInst.background('red');
1036- this . _pInst . push ( ) ;
1037- this . states . setValue ( 'enableLighting' , false ) ;
1038- this . states . setValue ( 'activeImageLight' , null ) ;
1039- this . _pInst . setCamera ( this . finalCamera ) ;
1040- this . _pInst . shader ( this . _getBlitShader ( ) ) ;
1041- this . _pInst . resetMatrix ( ) ;
1042- this . _pInst . imageMode ( this . _pInst . CENTER ) ;
1043- this . _pInst . image ( this . mainFramebuffer , 0 , 0 ) ;
1044- this . _pInst . pop ( ) ;
1045- this . flushDraw ( ) ;
1091+ // Only blit if we promoted to framebuffer this frame
1092+ if ( this . _framePromotedToFramebuffer ) {
1093+ while ( this . activeFramebuffers . length > 0 ) {
1094+ const fbo = this . activeFramebuffers . pop ( ) ;
1095+ states . unshift ( { fbo, diff : { ...this . states } } ) ;
1096+ }
1097+ this . flushDraw ( ) ;
1098+
1099+ // this._pInst.background('red');
1100+ this . _pInst . push ( ) ;
1101+ this . states . setValue ( 'enableLighting' , false ) ;
1102+ this . states . setValue ( 'activeImageLight' , null ) ;
1103+ this . _pInst . setCamera ( this . finalCamera ) ;
1104+ this . _pInst . shader ( this . _getBlitShader ( ) ) ;
1105+ this . _pInst . resetMatrix ( ) ;
1106+ this . _pInst . imageMode ( this . _pInst . CENTER ) ;
1107+ this . _pInst . image ( this . mainFramebuffer , 0 , 0 ) ;
1108+ this . _pInst . pop ( ) ;
1109+ this . flushDraw ( ) ;
1110+ }
10461111
10471112 // Return all uniform buffers to their pools
10481113 this . _returnUniformBuffersToPool ( ) ;
@@ -1063,12 +1128,19 @@ function rendererWebGPU(p5, fn) {
10631128 }
10641129 this . _retiredBuffers = [ ] ;
10651130
1066- for ( const { fbo, diff } of states ) {
1067- fbo . begin ( ) ;
1068- for ( const key in diff ) {
1069- this . states . setValue ( key , diff [ key ] ) ;
1131+ if ( this . _framePromotedToFramebuffer ) {
1132+ for ( const { fbo, diff } of states ) {
1133+ if ( fbo !== this . mainFramebuffer || ! this . _framePromotedToFramebuffer ) {
1134+ fbo . begin ( ) ;
1135+ }
1136+ for ( const key in diff ) {
1137+ this . states . setValue ( key , diff [ key ] ) ;
1138+ }
10701139 }
10711140 }
1141+
1142+ // Reset promotion state for next frame
1143+ this . _framePromotedToFramebuffer = false ;
10721144 }
10731145
10741146 //////////////////////////////////////////////
@@ -2203,7 +2275,10 @@ function rendererWebGPU(p5, fn) {
22032275 // Create non-multisampled texture for texture binding (always needed)
22042276 const colorTextureDescriptor = {
22052277 ...baseDescriptor ,
2206- usage : GPUTextureUsage . RENDER_ATTACHMENT | GPUTextureUsage . TEXTURE_BINDING | GPUTextureUsage . COPY_SRC ,
2278+ usage : GPUTextureUsage . RENDER_ATTACHMENT |
2279+ GPUTextureUsage . TEXTURE_BINDING |
2280+ GPUTextureUsage . COPY_SRC |
2281+ ( framebuffer . _useCanvasFormat ? GPUTextureUsage . COPY_DST : 0 ) ,
22072282 sampleCount : 1 ,
22082283 } ;
22092284 framebuffer . colorTexture = this . device . createTexture ( colorTextureDescriptor ) ;
@@ -2324,6 +2399,11 @@ function rendererWebGPU(p5, fn) {
23242399 } else if ( framebuffer . format === constants . HALF_FLOAT ) {
23252400 return framebuffer . channels === RGBA ? 'rgba16float' : 'rgba16float' ;
23262401 } else {
2402+ // Framebuffer with _useCanvasFormat should match canvas presentation format
2403+ if ( framebuffer . _useCanvasFormat ) {
2404+ return this . presentationFormat ;
2405+ }
2406+ // Other framebuffers use standard RGBA format
23272407 return framebuffer . channels === RGBA ? 'rgba8unorm' : 'rgba8unorm' ;
23282408 }
23292409 }
@@ -2413,22 +2493,28 @@ function rendererWebGPU(p5, fn) {
24132493 const mappedRange = stagingBuffer . getMappedRange ( 0 , bufferSize ) ;
24142494
24152495 // If alignment was needed, extract the actual pixel data
2496+ let result ;
24162497 if ( alignedBytesPerRow === unalignedBytesPerRow ) {
2417- const result = new Uint8Array ( mappedRange . slice ( 0 , width * height * bytesPerPixel ) ) ;
2498+ result = new Uint8Array ( mappedRange . slice ( 0 , width * height * bytesPerPixel ) ) ;
24182499 stagingBuffer . unmap ( ) ;
2419- return result ;
24202500 } else {
24212501 // Need to extract pixel data from aligned buffer
2422- const result = new Uint8Array ( width * height * bytesPerPixel ) ;
2502+ result = new Uint8Array ( width * height * bytesPerPixel ) ;
24232503 const mappedData = new Uint8Array ( mappedRange ) ;
24242504 for ( let y = 0 ; y < height ; y ++ ) {
24252505 const srcOffset = y * alignedBytesPerRow ;
24262506 const dstOffset = y * unalignedBytesPerRow ;
24272507 result . set ( mappedData . subarray ( srcOffset , srcOffset + unalignedBytesPerRow ) , dstOffset ) ;
24282508 }
24292509 stagingBuffer . unmap ( ) ;
2430- return result ;
24312510 }
2511+
2512+ // Convert BGRA to RGBA if reading from canvas-format framebuffer on BGRA systems
2513+ if ( framebuffer . _useCanvasFormat && this . presentationFormat === 'bgra8unorm' ) {
2514+ this . _convertBGRtoRGB ( result ) ;
2515+ }
2516+
2517+ return result ;
24322518 }
24332519
24342520 async readFramebufferPixel ( framebuffer , x , y ) {
@@ -2556,25 +2642,34 @@ function rendererWebGPU(p5, fn) {
25562642 //////////////////////////////////////////////
25572643
25582644 _convertBGRtoRGB ( pixelData ) {
2559- // Convert BGR to RGB by swapping red and blue channels
25602645 for ( let i = 0 ; i < pixelData . length ; i += 4 ) {
2561- const temp = pixelData [ i ] ; // Store red
2562- pixelData [ i ] = pixelData [ i + 2 ] ; // Red = Blue
2563- pixelData [ i + 2 ] = temp ; // Blue = Red
2564- // Green (i + 1) and Alpha (i + 3) stay the same
2646+ const temp = pixelData [ i ] ;
2647+ pixelData [ i ] = pixelData [ i + 2 ] ;
2648+ pixelData [ i + 2 ] = temp ;
25652649 }
25662650 return pixelData ;
25672651 }
25682652
25692653 async loadPixels ( ) {
2654+ this . _promoteToFramebuffer ( ) ;
25702655 await this . mainFramebuffer . loadPixels ( ) ;
25712656 this . pixels = this . mainFramebuffer . pixels . slice ( ) ;
25722657 }
25732658
25742659 async get ( x , y , w , h ) {
2660+ this . _promoteToFramebuffer ( ) ;
25752661 return this . mainFramebuffer . get ( x , y , w , h ) ;
25762662 }
25772663
2664+ filter ( ...args ) {
2665+ // If no custom framebuffer is active, promote to mainFramebuffer
2666+ if ( ! this . activeFramebuffer ( ) ) {
2667+ this . _promoteToFramebuffer ( ) ;
2668+ }
2669+
2670+ return super . filter ( ...args ) ;
2671+ }
2672+
25782673 getNoiseShaderSnippet ( ) {
25792674 return noiseWGSL ;
25802675 }
0 commit comments