diff --git a/examples/webgpu_lights_custom.html b/examples/webgpu_lights_custom.html index afbe5b5dfe5fc1..398695edc4e75a 100644 --- a/examples/webgpu_lights_custom.html +++ b/examples/webgpu_lights_custom.html @@ -113,6 +113,7 @@ renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); + renderer.toneMapping = THREE.LinearToneMapping; document.body.appendChild( renderer.domElement ); // controls diff --git a/src/renderers/common/Backend.js b/src/renderers/common/Backend.js index 028b74463d26eb..8b42632d2997a0 100644 --- a/src/renderers/common/Backend.js +++ b/src/renderers/common/Backend.js @@ -4,7 +4,7 @@ let _color4 = null; import Color4 from './Color4.js'; import { Vector2 } from '../../math/Vector2.js'; import { createCanvasElement, warnOnce } from '../../utils.js'; -import { REVISION } from '../../constants.js'; +import { HalfFloatType, REVISION } from '../../constants.js'; /** * Most of the rendering related logic is implemented in the @@ -588,6 +588,17 @@ class Backend { } + /** + * Returns `true` if the backend requires a framebuffer target with type `HalfFloat`. + * + * @return {Boolean} Whether the backend requires a framebuffer target with type `HalfFloat` or not. + */ + needsHalfFloatFrameBufferTarget() { + + return ( this.parameters.outputType === HalfFloatType ); + + } + /** * Sets a dictionary for the given object into the * internal data structure. diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 6463696ff0c625..b8a36ef1ea8fbc 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -26,7 +26,7 @@ import { Matrix4 } from '../../math/Matrix4.js'; import { Vector2 } from '../../math/Vector2.js'; import { Vector4 } from '../../math/Vector4.js'; import { RenderTarget } from '../../core/RenderTarget.js'; -import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, HalfFloatType, RGBAFormat, PCFShadowMap } from '../../constants.js'; +import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, HalfFloatType, RGBAFormat, PCFShadowMap, UnsignedByteType } from '../../constants.js'; /** @module Renderer **/ @@ -445,13 +445,13 @@ class Renderer { this._transparentSort = null; /** - * The framebuffer target. + * A map that holds a framebuffer target for each render + * target type. * * @private - * @type {RenderTarget?} - * @default null + * @type {Map} */ - this._frameBufferTarget = null; + this._frameBufferTargetMap = new Map(); const alphaClear = this.alpha === true ? 0 : 1; @@ -1121,14 +1121,19 @@ class Renderer { const { width, height } = this.getDrawingBufferSize( _drawingBufferSize ); const { depth, stencil } = this; - let frameBufferTarget = this._frameBufferTarget; + // to improve performance we only want to use HalfFloatType if necessary + // HalfFloatType is required when a) tone mapping is used or b) the backend specifically requests it + + const type = ( useToneMapping || this.backend.needsHalfFloatFrameBufferTarget() ) ? HalfFloatType : UnsignedByteType; - if ( frameBufferTarget === null ) { + let frameBufferTarget = this._frameBufferTargetMap.get( type ); + + if ( frameBufferTarget === undefined ) { frameBufferTarget = new RenderTarget( width, height, { depthBuffer: depth, stencilBuffer: stencil, - type: HalfFloatType, // FloatType + type: type, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, generateMipmaps: false, @@ -1139,7 +1144,7 @@ class Renderer { frameBufferTarget.isPostProcessingRenderTarget = true; - this._frameBufferTarget = frameBufferTarget; + this._frameBufferTargetMap.set( type, frameBufferTarget ); } @@ -2026,6 +2031,14 @@ class Renderer { this._renderContexts.dispose(); this._textures.dispose(); + for ( const renderTarget of this._frameBufferTargetMap.values() ) { + + renderTarget.dispose(); + + } + + this._frameBufferTargetMap.clear(); + Object.values( this.backend.timestampQueryPool ).forEach( queryPool => { if ( queryPool !== null ) queryPool.dispose();