From 304f612a2ebece5ecb5088eb95ba38f4ea643340 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 5 May 2024 23:59:55 +0900 Subject: [PATCH 1/9] Copy to async implementation from #24466 --- examples/gi_test.html | 286 ++++++++++++++++++++++ examples/webgl_interactive_cubes_gpu.html | 28 ++- src/renderers/WebGLRenderer.js | 147 ++++++++++- src/utils.js | 29 ++- 4 files changed, 475 insertions(+), 15 deletions(-) create mode 100644 examples/gi_test.html diff --git a/examples/gi_test.html b/examples/gi_test.html new file mode 100644 index 00000000000000..e4b6679cfc39e3 --- /dev/null +++ b/examples/gi_test.html @@ -0,0 +1,286 @@ + + + + three.js webgl - simple global illumination + + + + + + +
+ three.js - simple global illumination (article) +
+ + + + + + + + + + + diff --git a/examples/webgl_interactive_cubes_gpu.html b/examples/webgl_interactive_cubes_gpu.html index 013f82acaf0ff8..813ce836f6c628 100644 --- a/examples/webgl_interactive_cubes_gpu.html +++ b/examples/webgl_interactive_cubes_gpu.html @@ -261,23 +261,27 @@ const pixelBuffer = new Int32Array( 4 ); // read the pixel - renderer.readRenderTargetPixels( pickingTexture, 0, 0, 1, 1, pixelBuffer ); + renderer + .readRenderTargetPixelsAsync( pickingTexture, 0, 0, 1, 1, pixelBuffer ) + .then( () => { - const id = pixelBuffer[ 0 ]; - if ( id !== - 1 ) { + const id = pixelBuffer[ 0 ]; + if ( id !== - 1 ) { - // move our highlightBox so that it surrounds the picked object - const data = pickingData[ id ]; - highlightBox.position.copy( data.position ); - highlightBox.rotation.copy( data.rotation ); - highlightBox.scale.copy( data.scale ).add( offset ); - highlightBox.visible = true; + // move our highlightBox so that it surrounds the picked object + const data = pickingData[ id ]; + highlightBox.position.copy( data.position ); + highlightBox.rotation.copy( data.rotation ); + highlightBox.scale.copy( data.scale ).add( offset ); + highlightBox.visible = true; - } else { + } else { - highlightBox.visible = false; + highlightBox.visible = false; - } + } + + } ); } diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 6fe5a52fa19f08..9b37c7d36473f4 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -19,7 +19,9 @@ import { UnsignedShort5551Type, WebGLCoordinateSystem, DisplayP3ColorSpace, - LinearDisplayP3ColorSpace + LinearDisplayP3ColorSpace, + RGBAFormat, + FloatType } from '../constants.js'; import { Color } from '../math/Color.js'; import { Frustum } from '../math/Frustum.js'; @@ -54,7 +56,7 @@ import { WebGLUtils } from './webgl/WebGLUtils.js'; import { WebXRManager } from './webxr/WebXRManager.js'; import { WebGLMaterials } from './webgl/WebGLMaterials.js'; import { WebGLUniformsGroups } from './webgl/WebGLUniformsGroups.js'; -import { createCanvasElement } from '../utils.js'; +import { createCanvasElement, probeAsync } from '../utils.js'; import { ColorManagement } from '../math/ColorManagement.js'; class WebGLRenderer { @@ -2357,6 +2359,147 @@ class WebGLRenderer { }; + this.createReadbackBuffer = function ( byteLength ) { + + const glBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); + _gl.bufferData( _gl.PIXEL_PACK_BUFFER, byteLength, _gl.STREAM_READ ); + + return glBuffer; + + }; + + this.readbackPixels = function ( glBuffer, typedarray ) { + + _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); + _gl.getBufferSubData( _gl.PIXEL_PACK_BUFFER, 0, typedarray ); + + }; + + this.disposeReadbackBuffer = function ( glBuffer ) { + + if ( ! glBuffer || ! Number.isInteger( glBuffer ) ) { + + console.error( 'THREE.WebGLRenderer.disposePixelBuffer: invalid glBuffer.' ); + return; + + } + + _gl.deleteBuffer( glBuffer ); + + }; + + this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex, options ) { + + return new Promise( ( resolve, reject ) => { + + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + reject(); + + } + + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + + framebuffer = framebuffer[ activeCubeFaceIndex ]; + + } + + if ( framebuffer ) { + + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + + try { + + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; + + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + reject(); + + } + + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); + + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + reject(); + + } + + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + + const asyncOptions = options || {}; + + const glBuffer = ( asyncOptions.glBuffer != undefined ) ? asyncOptions.glBuffer : _gl.createBuffer(); + const byteOffset = ( asyncOptions.byteOffset != undefined ) ? asyncOptions.byteOffset : 0; + const interval = ( asyncOptions.interval != undefined ) ? asyncOptions.interval : 8; + const shouldSync = ( asyncOptions.sync != undefined ) ? asyncOptions.sync : true; + const shouldReadback = ( asyncOptions.readback != undefined ) ? asyncOptions.readback : true; + + _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); + + if ( ! asyncOptions.glBuffer ) + _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); + + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), byteOffset ); + _gl.flush(); + + if ( shouldSync ) { + + const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); + + probeAsync( _gl, sync, interval ).then( () => { + + if ( shouldReadback ) + this.readbackPixels( glBuffer, buffer ); + + resolve( buffer || true ); + + } ).finally( () => { + + if ( ! asyncOptions.glBuffer ) + _gl.deleteBuffer( glBuffer ); + + _gl.deleteSync( sync ); + + } ).catch( () => reject() ); + + } else { + + resolve( false ); + + } + + } + + } finally { + + // restore framebuffer of current render target if necessary + + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + + } + + } + + } ); + + }; + this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { const levelScale = Math.pow( 2, - level ); diff --git a/src/utils.js b/src/utils.js index 6fd13ca9dee638..494a83aacfcb0e 100644 --- a/src/utils.js +++ b/src/utils.js @@ -88,4 +88,31 @@ function warnOnce( message ) { } -export { arrayMin, arrayMax, arrayNeedsUint32, getTypedArray, createElementNS, createCanvasElement, warnOnce }; +async function probeAsync( gl, sync, interval ) { + + return new Promise( function ( resolveProbe, rejectProbe ) { + + function probe() { + + switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) { + + case gl.WAIT_FAILED: + rejectProbe(); break; + + case gl.TIMEOUT_EXPIRED: + setTimeout( probe, interval ); break; + + default: + resolveProbe(); + + } + + } + + setTimeout( probe ); + + } ); + +} + +export { arrayMin, arrayMax, arrayNeedsUint32, getTypedArray, createElementNS, createCanvasElement, warnOnce, probeAsync }; From 78bec30adcc65acb11777073f0bd570fb5f134ac Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 00:11:26 +0900 Subject: [PATCH 2/9] probeSync cleanup --- src/utils.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/utils.js b/src/utils.js index 494a83aacfcb0e..3c333e5085f402 100644 --- a/src/utils.js +++ b/src/utils.js @@ -88,28 +88,30 @@ function warnOnce( message ) { } -async function probeAsync( gl, sync, interval ) { +function probeAsync( gl, sync, interval ) { - return new Promise( function ( resolveProbe, rejectProbe ) { + return new Promise( function ( resolve, reject ) { function probe() { switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) { case gl.WAIT_FAILED: - rejectProbe(); break; + reject(); + break; case gl.TIMEOUT_EXPIRED: - setTimeout( probe, interval ); break; + setTimeout( probe, interval ); + break; default: - resolveProbe(); + resolve(); } } - setTimeout( probe ); + setTimeout( probe, interval ); } ); From 7be074f01a757fab08d89e62b505481009620942 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 00:22:14 +0900 Subject: [PATCH 3/9] More simplification --- src/renderers/WebGLRenderer.js | 49 +++++++++++----------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 9b37c7d36473f4..f33fb242c19d83 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -2390,7 +2390,7 @@ class WebGLRenderer { }; - this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex, options ) { + this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { return new Promise( ( resolve, reject ) => { @@ -2441,47 +2441,30 @@ class WebGLRenderer { if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - const asyncOptions = options || {}; - - const glBuffer = ( asyncOptions.glBuffer != undefined ) ? asyncOptions.glBuffer : _gl.createBuffer(); - const byteOffset = ( asyncOptions.byteOffset != undefined ) ? asyncOptions.byteOffset : 0; - const interval = ( asyncOptions.interval != undefined ) ? asyncOptions.interval : 8; - const shouldSync = ( asyncOptions.sync != undefined ) ? asyncOptions.sync : true; - const shouldReadback = ( asyncOptions.readback != undefined ) ? asyncOptions.readback : true; - + const interval = 8; + const glBuffer = _gl.createBuffer(); _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); + _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); - if ( ! asyncOptions.glBuffer ) - _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); - - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), byteOffset ); - _gl.flush(); - - if ( shouldSync ) { - - const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); + // _gl.flush(); - probeAsync( _gl, sync, interval ).then( () => { + const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); + probeAsync( _gl, sync, interval ).then( () => { - if ( shouldReadback ) - this.readbackPixels( glBuffer, buffer ); + this.readbackPixels( glBuffer, buffer ); + resolve( buffer ); - resolve( buffer || true ); + } ).finally( () => { - } ).finally( () => { + _gl.deleteBuffer( glBuffer ); + _gl.deleteSync( sync ); - if ( ! asyncOptions.glBuffer ) - _gl.deleteBuffer( glBuffer ); + } ).catch( () => { - _gl.deleteSync( sync ); + reject(); - } ).catch( () => reject() ); - - } else { - - resolve( false ); - - } + } ); } From f7c5cd2bcf02a21e4d7724312a30a4b842ae51bd Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 00:31:44 +0900 Subject: [PATCH 4/9] Simplification --- src/renderers/WebGLRenderer.js | 105 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index f33fb242c19d83..b2ef797fc21b17 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -2392,94 +2392,91 @@ class WebGLRenderer { this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - return new Promise( ( resolve, reject ) => { - - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - reject(); - - } - - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + // reject(); + throw new Error(); - framebuffer = framebuffer[ activeCubeFaceIndex ]; + } - } + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - if ( framebuffer ) { + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + framebuffer = framebuffer[ activeCubeFaceIndex ]; - try { + } - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; + if ( framebuffer ) { - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - reject(); + try { - } + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + // reject(); + throw new Error(); - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - reject(); + } - } + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + // reject(); + throw new Error(); - const interval = 8; - const glBuffer = _gl.createBuffer(); - _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); - _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); + } - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); - // _gl.flush(); + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); - probeAsync( _gl, sync, interval ).then( () => { + const interval = 8; + const glBuffer = _gl.createBuffer(); + _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); + _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); - this.readbackPixels( glBuffer, buffer ); - resolve( buffer ); + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); + // _gl.flush(); - } ).finally( () => { + const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); + await probeAsync( _gl, sync, interval ) - _gl.deleteBuffer( glBuffer ); - _gl.deleteSync( sync ); + try { - } ).catch( () => { + this.readbackPixels( glBuffer, buffer ); - reject(); + } finally { - } ); + _gl.deleteBuffer( glBuffer ); + _gl.deleteSync( sync ); } - } finally { + return buffer; - // restore framebuffer of current render target if necessary + } - const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; - state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + } finally { - } + // restore framebuffer of current render target if necessary + + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); } - } ); + } }; From 5b8255b8e01d973afeb1e3e2ee4aca34f20c67db Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 00:35:53 +0900 Subject: [PATCH 5/9] Remove tangential functions --- src/renderers/WebGLRenderer.js | 53 +++++----------------------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index b2ef797fc21b17..51413bbd248e37 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -2359,49 +2359,16 @@ class WebGLRenderer { }; - this.createReadbackBuffer = function ( byteLength ) { - - const glBuffer = _gl.createBuffer(); - - _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); - _gl.bufferData( _gl.PIXEL_PACK_BUFFER, byteLength, _gl.STREAM_READ ); - - return glBuffer; - - }; - - this.readbackPixels = function ( glBuffer, typedarray ) { - - _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); - _gl.getBufferSubData( _gl.PIXEL_PACK_BUFFER, 0, typedarray ); - - }; - - this.disposeReadbackBuffer = function ( glBuffer ) { - - if ( ! glBuffer || ! Number.isInteger( glBuffer ) ) { - - console.error( 'THREE.WebGLRenderer.disposePixelBuffer: invalid glBuffer.' ); - return; - - } - - _gl.deleteBuffer( glBuffer ); - - }; - this.readRenderTargetPixelsAsync = async function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - // reject(); throw new Error(); } let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { framebuffer = framebuffer[ activeCubeFaceIndex ]; @@ -2418,22 +2385,16 @@ class WebGLRenderer { const textureFormat = texture.format; const textureType = texture.type; - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + if ( ! capabilities.textureFormatReadable( textureFormat ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - // reject(); + console.error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.' ); throw new Error(); } - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { + if ( ! capabilities.textureTypeReadable( textureType ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - // reject(); + console.error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.' ); throw new Error(); } @@ -2445,16 +2406,16 @@ class WebGLRenderer { const glBuffer = _gl.createBuffer(); _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); // _gl.flush(); const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); - await probeAsync( _gl, sync, interval ) + await probeAsync( _gl, sync, interval ); try { - this.readbackPixels( glBuffer, buffer ); + _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); + _gl.getBufferSubData( _gl.PIXEL_PACK_BUFFER, 0, buffer ); } finally { From af065bc275efc42358934dd57ca4af52db8c2dc7 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 00:41:54 +0900 Subject: [PATCH 6/9] More simplification --- src/renderers/WebGLRenderer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 51413bbd248e37..80c670bfc3190f 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -2402,15 +2402,16 @@ class WebGLRenderer { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - const interval = 8; const glBuffer = _gl.createBuffer(); _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); + // TODO: is this needed? // _gl.flush(); + // check if the commands have finished every 8 ms const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); - await probeAsync( _gl, sync, interval ); + await probeAsync( _gl, sync, 8 ); try { From 73ae9b9b7dc2221fe80970115fbb2ef760354ebd Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 13:51:20 +0900 Subject: [PATCH 7/9] Convert to thrown errors --- examples/gi_test.html | 286 --------------------------------- src/renderers/WebGLRenderer.js | 14 +- 2 files changed, 4 insertions(+), 296 deletions(-) delete mode 100644 examples/gi_test.html diff --git a/examples/gi_test.html b/examples/gi_test.html deleted file mode 100644 index e4b6679cfc39e3..00000000000000 --- a/examples/gi_test.html +++ /dev/null @@ -1,286 +0,0 @@ - - - - three.js webgl - simple global illumination - - - - - - -
- three.js - simple global illumination (article) -
- - - - - - - - - - - diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 80c670bfc3190f..76cd311f596b5c 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -20,8 +20,6 @@ import { WebGLCoordinateSystem, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, - RGBAFormat, - FloatType } from '../constants.js'; import { Color } from '../math/Color.js'; import { Frustum } from '../math/Frustum.js'; @@ -2363,8 +2361,7 @@ class WebGLRenderer { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - throw new Error(); + throw new Error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); } @@ -2387,15 +2384,13 @@ class WebGLRenderer { if ( ! capabilities.textureFormatReadable( textureFormat ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.' ); - throw new Error(); + throw new Error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.' ); } if ( ! capabilities.textureTypeReadable( textureType ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.' ); - throw new Error(); + throw new Error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.' ); } @@ -2406,8 +2401,7 @@ class WebGLRenderer { _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); - // TODO: is this needed? - // _gl.flush(); + _gl.flush(); // check if the commands have finished every 8 ms const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); From d47524532a6a8003e1c6695ae24852d291057b2d Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 6 May 2024 13:53:48 +0900 Subject: [PATCH 8/9] Remove comma --- src/renderers/WebGLRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 76cd311f596b5c..719c73540993ef 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -19,7 +19,7 @@ import { UnsignedShort5551Type, WebGLCoordinateSystem, DisplayP3ColorSpace, - LinearDisplayP3ColorSpace, + LinearDisplayP3ColorSpace } from '../constants.js'; import { Color } from '../math/Color.js'; import { Frustum } from '../math/Frustum.js'; From 6ddb37abe3be8d313aab09cd7dc0e2819ec485bb Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 7 May 2024 12:29:23 +0900 Subject: [PATCH 9/9] Update docs, probe frequency --- docs/api/en/renderers/WebGLRenderer.html | 15 +++++++++++---- src/renderers/WebGLRenderer.js | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/api/en/renderers/WebGLRenderer.html b/docs/api/en/renderers/WebGLRenderer.html index c80c7eeafcb4bd..3b5617a160cc02 100644 --- a/docs/api/en/renderers/WebGLRenderer.html +++ b/docs/api/en/renderers/WebGLRenderer.html @@ -511,16 +511,23 @@

This is a wrapper around [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/readPixels WebGLRenderingContext.readPixels]().

-

- See the [example:webgl_interactive_cubes_gpu interactive / cubes / gpu] - example. -

For reading out a [page:WebGLCubeRenderTarget WebGLCubeRenderTarget] use the optional parameter activeCubeFaceIndex to determine which face should be read.

+

+ [method:Promise readRenderTargetPixelsAsync]( [param:WebGLRenderTarget renderTarget], [param:Float x], [param:Float y], [param:Float width], [param:Float height], [param:TypedArray buffer], [param:Integer activeCubeFaceIndex] ) +

+

+ Asynchronous, non-blocking version of [page:WebGLRenderer.readRenderTargetPixels .readRenderTargetPixels]. The + returned promise resolves once the buffer data is ready to be used. +

+

+ See the [example:webgl_interactive_cubes_gpu interactive / cubes / gpu] example. +

+

[method:undefined render]( [param:Object3D scene], [param:Camera camera] )

diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 719c73540993ef..7227d7c00553c8 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -2405,7 +2405,7 @@ class WebGLRenderer { // check if the commands have finished every 8 ms const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); - await probeAsync( _gl, sync, 8 ); + await probeAsync( _gl, sync, 4 ); try {