diff --git a/examples/files.json b/examples/files.json index ac769186754ed8..4eae01448a21dc 100644 --- a/examples/files.json +++ b/examples/files.json @@ -359,6 +359,7 @@ "webgpu_morphtargets_face", "webgpu_mrt", "webgpu_mrt_mask", + "webgpu_multiple_canvases", "webgpu_multiple_rendertargets", "webgpu_multiple_rendertargets_readback", "webgpu_multisampled_renderbuffers", diff --git a/examples/screenshots/webgpu_multiple_canvases.jpg b/examples/screenshots/webgpu_multiple_canvases.jpg new file mode 100644 index 00000000000000..4354412626a022 Binary files /dev/null and b/examples/screenshots/webgpu_multiple_canvases.jpg differ diff --git a/examples/webgpu_multiple_canvases.html b/examples/webgpu_multiple_canvases.html new file mode 100644 index 00000000000000..651dd2a8ce3e2d --- /dev/null +++ b/examples/webgpu_multiple_canvases.html @@ -0,0 +1,258 @@ + + + + three.js webgpu - Multiple Canvases + + + + + + +
+ threejs webgpu - Multiple Canvases +
+ + + + +
+ + diff --git a/src/Three.WebGPU.js b/src/Three.WebGPU.js index 7a4234b99edf0d..7a51552a3f6cd6 100644 --- a/src/Three.WebGPU.js +++ b/src/Three.WebGPU.js @@ -161,6 +161,7 @@ export * from './constants.js'; export * from './Three.Legacy.js'; export { default as WebGPURenderer } from './renderers/webgpu/WebGPURenderer.js'; +export { default as CanvasRenderTarget } from './renderers/common/CanvasRenderTarget.js'; export { default as QuadMesh } from './renderers/common/QuadMesh.js'; export { default as PMREMGenerator } from './renderers/common/extras/PMREMGenerator.js'; export { default as PostProcessing } from './renderers/common/PostProcessing.js'; diff --git a/src/nodes/display/PassNode.js b/src/nodes/display/PassNode.js index 59bc3b4ac1a4a9..8bde10e77b28ad 100644 --- a/src/nodes/display/PassNode.js +++ b/src/nodes/display/PassNode.js @@ -223,9 +223,10 @@ class PassNode extends TempNode { const { renderer } = frame; const { scene, camera } = this; - this._pixelRatio = renderer.getPixelRatio(); + const canvasRenderTarget = renderer.getActiveCanvasRenderTarget(); + this._pixelRatio = canvasRenderTarget.getPixelRatio(); - const size = renderer.getSize( _size ); + const size = canvasRenderTarget.getSize( _size ); this.setSize( size.width, size.height ); diff --git a/src/nodes/display/ViewportNode.js b/src/nodes/display/ViewportNode.js index b7ae46a0bf163b..ba2339306729e0 100644 --- a/src/nodes/display/ViewportNode.js +++ b/src/nodes/display/ViewportNode.js @@ -48,11 +48,11 @@ class ViewportNode extends Node { if ( this.scope === ViewportNode.VIEWPORT ) { - renderer.getViewport( viewportResult ); + renderer.getActiveCanvasRenderTarget().getViewport( viewportResult ); } else { - renderer.getDrawingBufferSize( resolution ); + renderer.getActiveCanvasRenderTarget().getDrawingBufferSize( resolution ); } diff --git a/src/nodes/display/ViewportTextureNode.js b/src/nodes/display/ViewportTextureNode.js index 88ca05ce37b777..52af5968af854b 100644 --- a/src/nodes/display/ViewportTextureNode.js +++ b/src/nodes/display/ViewportTextureNode.js @@ -34,7 +34,7 @@ class ViewportTextureNode extends TextureNode { updateBefore( frame ) { const renderer = frame.renderer; - renderer.getDrawingBufferSize( _size ); + renderer.getActiveCanvasRenderTarget().getDrawingBufferSize( _size ); // diff --git a/src/nodes/utils/ReflectorNode.js b/src/nodes/utils/ReflectorNode.js index 68c13fd68477c6..7607054c195a07 100644 --- a/src/nodes/utils/ReflectorNode.js +++ b/src/nodes/utils/ReflectorNode.js @@ -63,7 +63,7 @@ class ReflectorNode extends TextureNode { const resolution = this.resolution; - renderer.getDrawingBufferSize( _size ); + renderer.getActiveCanvasRenderTarget().getDrawingBufferSize( _size ); renderTarget.setSize( Math.round( _size.width * resolution ), Math.round( _size.height * resolution ) ); @@ -134,7 +134,7 @@ class ReflectorNode extends TextureNode { const virtualCamera = this.getVirtualCamera( camera ); const renderTarget = this.getRenderTarget( virtualCamera ); - renderer.getDrawingBufferSize( _size ); + renderer.getActiveCanvasRenderTarget().getDrawingBufferSize( _size ); this._updateResolution( renderTarget, renderer ); diff --git a/src/renderers/common/Backend.js b/src/renderers/common/Backend.js index d0c5ae4a1f3ca2..a18e742888126b 100644 --- a/src/renderers/common/Backend.js +++ b/src/renderers/common/Backend.js @@ -1,10 +1,6 @@ -let vector2 = null; -let vector4 = null; let color4 = null; import Color4 from './Color4.js'; -import { Vector2 } from '../../math/Vector2.js'; -import { Vector4 } from '../../math/Vector4.js'; import { createCanvasElement } from '../../utils.js'; import { REVISION } from '../../constants.js'; @@ -89,8 +85,6 @@ class Backend { getContext() { } - updateSize() { } - // utils resolveTimestampAsync( /*renderContext, type*/ ) { } @@ -107,23 +101,7 @@ class Backend { } - getDrawingBufferSize() { - - vector2 = vector2 || new Vector2(); - - return this.renderer.getDrawingBufferSize( vector2 ); - - } - - getScissor() { - - vector4 = vector4 || new Vector4(); - - return this.renderer.getScissor( vector4 ); - - } - - setScissorTest( /*boolean*/ ) { } + setScissorTest( /* boolean */ ) { } getClearColor() { diff --git a/src/renderers/common/CanvasRenderTarget.js b/src/renderers/common/CanvasRenderTarget.js new file mode 100644 index 00000000000000..7e230086063916 --- /dev/null +++ b/src/renderers/common/CanvasRenderTarget.js @@ -0,0 +1,228 @@ +import { createCanvasElement } from '../../utils.js'; +import { REVISION, SRGBColorSpace } from '../../constants'; +import { EventDispatcher } from '../../core/EventDispatcher'; +import { Vector4 } from '../../math/Vector4.js'; + +class CanvasRenderTarget extends EventDispatcher { + + constructor( parameters ) { + + super(); + + this.isCanvasRenderTarget = true; + + this.canvas = parameters.canvas; + this.context = parameters.context; + this._domElement = parameters.domElement; + this.alpha = ( parameters.alpha === undefined ) ? true : parameters.alpha; + + this.antialias = ( parameters.antialias === true ); + + if ( this.antialias === true ) { + + this.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount; + + } else { + + this.sampleCount = 1; + + } + + this.outputColorSpace = SRGBColorSpace; + + this.depth = true; + this.stencil = false; + + this._width = 0; + this._height = 0; + this.pixelRatio = 1; + + this.viewport = new Vector4( 0, 0, this._width, this._height ); + this.scissor = new Vector4( 0, 0, this._width, this._height ); + this.scissorTest = false; + + this.version = 0; + + } + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + } + + get domElement() { + + let domElement = this._domElement; + + if ( ! domElement ) { + + domElement = ( this.canvas !== undefined ) ? this.canvas : createCanvasElement(); + + // OffscreenCanvas does not have setAttribute, see #22811 + if ( 'setAttribute' in domElement ) domElement.setAttribute( 'data-engine', `three.js r${REVISION} webgpu` ); + + this._domElement = domElement; + + } + + return domElement; + + } + + get samples() { + + return this.sampleCount; + + } + + get depthBuffer() { + + return this.depth; + + } + + get stencilBuffer() { + + return this.stencil; + + } + + getPixelRatio() { + + return this.pixelRatio; + + } + + getDrawingBufferSize( target ) { + + return target.set( this._width * this.pixelRatio, this._height * this.pixelRatio ).floor(); + + } + + getSize( target ) { + + return target.set( this._width, this._height ); + + } + + setPixelRatio( value = 1 ) { + + this.pixelRatio = value; + + this.setSize( this._width, this._height, false ); + + } + + setDrawingBufferSize( width, height, pixelRatio ) { + + this._width = width; + this._height = height; + + this.pixelRatio = pixelRatio; + + this.domElement.width = Math.floor( width * pixelRatio ); + this.domElement.height = Math.floor( height * pixelRatio ); + + this.setViewport( 0, 0, width, height ); + + this.needsUpdate = true; + + } + + setSize( width, height, updateStyle = true ) { + + this._width = width; + this._height = height; + + this.domElement.width = Math.floor( width * this.pixelRatio ); + this.domElement.height = Math.floor( height * this.pixelRatio ); + + if ( updateStyle === true ) { + + this.domElement.style.width = width + 'px'; + this.domElement.style.height = height + 'px'; + + } + + this.setViewport( 0, 0, width, height ); + + this.needsUpdate = true; + + } + + getScissor( target ) { + + const scissor = this.scissor; + + target.x = scissor.x; + target.y = scissor.y; + target.width = scissor.width; + target.height = scissor.height; + + return target; + + } + + setScissor( x, y, width, height ) { + + const scissor = this.scissor; + + if ( x.isVector4 ) { + + scissor.copy( x ); + + } else { + + scissor.set( x, y, width, height ); + + } + + } + + getScissorTest() { + + return this.scissorTest; + + } + + setScissorTest( value ) { + + this.scissorTest = value; + + } + + getViewport( target ) { + + return target.copy( this.viewport ); + + } + + setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) { + + const viewport = this.viewport; + + if ( x.isVector4 ) { + + viewport.copy( x ); + + } else { + + viewport.set( x, y, width, height ); + + } + + viewport.minDepth = minDepth; + viewport.maxDepth = maxDepth; + + } + + dispose() { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +} + +export default CanvasRenderTarget; diff --git a/src/renderers/common/RenderContexts.js b/src/renderers/common/RenderContexts.js index da21e6d9a0fed9..ee6d5e3c05e686 100644 --- a/src/renderers/common/RenderContexts.js +++ b/src/renderers/common/RenderContexts.js @@ -9,15 +9,15 @@ class RenderContexts { } - get( scene, camera, renderTarget = null ) { + get( scene, camera, renderTarget ) { const chainKey = [ scene, camera ]; let attachmentState; - if ( renderTarget === null ) { + if ( renderTarget.isCanvasRenderTarget ) { - attachmentState = 'default'; + attachmentState = `${ renderTarget.samples }:${ renderTarget.depth }:${ renderTarget.stencil }`; } else { @@ -38,9 +38,11 @@ class RenderContexts { chainMap.set( chainKey, renderState ); - } + renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples; + renderState.depth = renderTarget.depthBuffer; + renderState.stencil = renderTarget.stencilBuffer; - if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples; + } return renderState; diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 97f26a2d53af6e..349506e9dac0c6 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -14,6 +14,7 @@ import Color4 from './Color4.js'; import ClippingContext from './ClippingContext.js'; import QuadMesh from './QuadMesh.js'; import RenderBundles from './RenderBundles.js'; +import CanvasRenderTarget from './CanvasRenderTarget.js'; import { NodeMaterial } from '../../nodes/Nodes.js'; @@ -24,7 +25,7 @@ import { Vector2 } from '../../math/Vector2.js'; import { Vector3 } from '../../math/Vector3.js'; import { Vector4 } from '../../math/Vector4.js'; import { RenderTarget } from '../../core/RenderTarget.js'; -import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, HalfFloatType, RGBAFormat } from '../../constants.js'; +import { DoubleSide, BackSide, FrontSide, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, HalfFloatType, RGBAFormat } from '../../constants.js'; const _scene = new Scene(); const _drawingBufferSize = new Vector2(); @@ -64,30 +65,17 @@ class Renderer { this.logarithmicDepthBuffer = logarithmicDepthBuffer; - this.outputColorSpace = SRGBColorSpace; - this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; this.sortObjects = true; - this.depth = true; - this.stencil = false; - this.clippingPlanes = []; this.info = new Info(); // internals - this._pixelRatio = 1; - this._width = this.domElement.width; - this._height = this.domElement.height; - - this._viewport = new Vector4( 0, 0, this._width, this._height ); - this._scissor = new Vector4( 0, 0, this._width, this._height ); - this._scissorTest = false; - this._attributes = null; this._geometries = null; this._nodes = null; @@ -103,6 +91,9 @@ class Renderer { this._quad = new QuadMesh( new NodeMaterial() ); + this._defaultCanvasRenderTarget = new CanvasRenderTarget( Object.assign( {}, parameters, { domElement: this.domElement } ) ); + + this._activeCanvas = null; this._currentRenderContext = null; this._opaqueSort = null; @@ -116,7 +107,7 @@ class Renderer { this._clearDepth = 1; this._clearStencil = 0; - this._renderTarget = null; + this._renderTarget = this._defaultCanvasRenderTarget; this._activeCubeFace = 0; this._activeMipmapLevel = 0; @@ -251,8 +242,8 @@ class Renderer { // - renderContext.depth = this.depth; - renderContext.stencil = this.stencil; + renderContext.depth = renderTarget.depth; + renderContext.stencil = renderTarget.stencil; if ( ! renderContext.clippingContext ) renderContext.clippingContext = new ClippingContext(); renderContext.clippingContext.updateGlobal( this, camera ); @@ -287,7 +278,12 @@ class Renderer { // - if ( renderTarget !== null ) { + if ( renderTarget.isCanvasRenderTarget ) { + + renderContext.textures = null; + renderContext.depthTexture = null; + + } else { this._textures.updateRenderTarget( renderTarget, activeMipmapLevel ); @@ -296,11 +292,6 @@ class Renderer { renderContext.textures = renderTargetData.textures; renderContext.depthTexture = renderTargetData.depthTexture; - } else { - - renderContext.textures = null; - renderContext.depthTexture = null; - } // @@ -455,13 +446,15 @@ class Renderer { const { currentColorSpace } = this; - const useToneMapping = this._renderTarget === null && ( this.toneMapping !== NoToneMapping ); + const renderTarget = this._renderTarget; + + const useToneMapping = renderTarget.isCanvasRenderTarget === true && ( this.toneMapping !== NoToneMapping ); const useColorSpace = currentColorSpace !== LinearSRGBColorSpace && currentColorSpace !== NoColorSpace; if ( useToneMapping === false && useColorSpace === false ) return null; - const { width, height } = this.getDrawingBufferSize( _drawingBufferSize ); - const { depth, stencil } = this; + const { width, height } = renderTarget.getDrawingBufferSize( _drawingBufferSize ); + const { depth, stencil } = renderTarget; let frameBufferTarget = this._frameBufferTarget; @@ -488,11 +481,11 @@ class Renderer { frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; frameBufferTarget.setSize( width, height ); - frameBufferTarget.viewport.copy( this._viewport ); - frameBufferTarget.scissor.copy( this._scissor ); - frameBufferTarget.viewport.multiplyScalar( this._pixelRatio ); - frameBufferTarget.scissor.multiplyScalar( this._pixelRatio ); - frameBufferTarget.scissorTest = this._scissorTest; + frameBufferTarget.viewport.copy( renderTarget.viewport ); + frameBufferTarget.scissor.copy( renderTarget.scissor ); + frameBufferTarget.scissorTest = renderTarget.scissorTest; + frameBufferTarget.viewport.multiplyScalar( renderTarget.pixelRatio ); + frameBufferTarget.scissor.multiplyScalar( renderTarget.pixelRatio ); return frameBufferTarget; @@ -515,7 +508,6 @@ class Renderer { const sceneRef = ( scene.isScene === true ) ? scene : _scene; const outputRenderTarget = this._renderTarget; - const activeCubeFace = this._activeCubeFace; const activeMipmapLevel = this._activeMipmapLevel; @@ -542,6 +534,7 @@ class Renderer { this._currentRenderContext = renderContext; this._currentRenderObjectFunction = this._renderObjectFunction || this.renderObject; + // this.info.calls ++; @@ -570,19 +563,11 @@ class Renderer { // - let viewport = this._viewport; - let scissor = this._scissor; - let pixelRatio = this._pixelRatio; - - if ( renderTarget !== null ) { - - viewport = renderTarget.viewport; - scissor = renderTarget.scissor; - pixelRatio = 1; + const viewport = renderTarget.viewport; + const scissor = renderTarget.scissor; + const pixelRatio = renderTarget.pixelRatio ? renderTarget.pixelRatio : 1; - } - - this.getDrawingBufferSize( _drawingBufferSize ); + this._defaultCanvasRenderTarget.getDrawingBufferSize( _drawingBufferSize ); _screen.set( 0, 0, _drawingBufferSize.width, _drawingBufferSize.height ); @@ -597,7 +582,7 @@ class Renderer { renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false; renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor(); - renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false; + renderContext.scissor = renderTarget._scissorTest && renderContext.scissorValue.equals( _screen ) === false; renderContext.scissorValue.width >>= activeMipmapLevel; renderContext.scissorValue.height >>= activeMipmapLevel; @@ -628,8 +613,18 @@ class Renderer { // - if ( renderTarget !== null ) { + if ( renderTarget.isCanvasRenderTarget ) { + + renderContext.textures = null; + renderContext.depthTexture = null; + renderContext.width = renderTarget.domElement.width; + renderContext.height = renderTarget.domElement.height; + + this._activeCanvas = renderTarget; + } else { + + this._activeCanvas = this._defaultCanvasRenderTarget; this._textures.updateRenderTarget( renderTarget, activeMipmapLevel ); const renderTargetData = this._textures.get( renderTarget ); @@ -638,21 +633,11 @@ class Renderer { renderContext.depthTexture = renderTargetData.depthTexture; renderContext.width = renderTargetData.width; renderContext.height = renderTargetData.height; - renderContext.renderTarget = renderTarget; - renderContext.depth = renderTarget.depthBuffer; - renderContext.stencil = renderTarget.stencilBuffer; - - } else { - - renderContext.textures = null; - renderContext.depthTexture = null; - renderContext.width = this.domElement.width; - renderContext.height = this.domElement.height; - renderContext.depth = this.depth; - renderContext.stencil = this.stencil; } + renderContext.colorSpace = this.currentColorSpace; + renderContext.renderTarget = renderTarget; renderContext.width >>= activeMipmapLevel; renderContext.height >>= activeMipmapLevel; renderContext.activeCubeFace = activeCubeFace; @@ -756,70 +741,49 @@ class Renderer { getContext() { - return this.backend.getContext(); + return this.backend.getContext( this._defaultCanvasRenderTarget ); } getPixelRatio() { - return this._pixelRatio; + return this._defaultCanvasRenderTarget.pixelRatio; } getDrawingBufferSize( target ) { - return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor(); + return this._defaultCanvasRenderTarget.getDrawingBufferSize( target ); } - getSize( target ) { + getActiveCanvasRenderTarget() { - return target.set( this._width, this._height ); + return this._activeCanvas; } - setPixelRatio( value = 1 ) { - - this._pixelRatio = value; + getSize( target ) { - this.setSize( this._width, this._height, false ); + return this._defaultCanvasRenderTarget.getSize( target ); } - setDrawingBufferSize( width, height, pixelRatio ) { - - this._width = width; - this._height = height; + setPixelRatio( value = 1 ) { - this._pixelRatio = pixelRatio; + this._defaultCanvasRenderTarget.setPixelRatio( value ); - this.domElement.width = Math.floor( width * pixelRatio ); - this.domElement.height = Math.floor( height * pixelRatio ); + } - this.setViewport( 0, 0, width, height ); + setDrawingBufferSize( width, height, pixelRatio ) { - if ( this._initialized ) this.backend.updateSize(); + this._defaultCanvasRenderTarget.setDrawingBufferSize( width, height, pixelRatio ); } setSize( width, height, updateStyle = true ) { - this._width = width; - this._height = height; - - this.domElement.width = Math.floor( width * this._pixelRatio ); - this.domElement.height = Math.floor( height * this._pixelRatio ); - - if ( updateStyle === true ) { - - this.domElement.style.width = width + 'px'; - this.domElement.style.height = height + 'px'; - - } - - this.setViewport( 0, 0, width, height ); - - if ( this._initialized ) this.backend.updateSize(); + this._defaultCanvasRenderTarget.setSize( width, height, updateStyle ); } @@ -837,42 +801,25 @@ class Renderer { getScissor( target ) { - const scissor = this._scissor; - - target.x = scissor.x; - target.y = scissor.y; - target.width = scissor.width; - target.height = scissor.height; - - return target; + return this._defaultCanvasRenderTarget.getScissor( target ); } setScissor( x, y, width, height ) { - const scissor = this._scissor; - - if ( x.isVector4 ) { - - scissor.copy( x ); - - } else { - - scissor.set( x, y, width, height ); - - } + this._defaultCanvasRenderTarget.setScissor( x, y, width, height ); } getScissorTest() { - return this._scissorTest; + return this._defaultCanvasRenderTarget.scissorTest; } setScissorTest( boolean ) { - this._scissorTest = boolean; + this._defaultCanvasRenderTarget.scissorTest = boolean; this.backend.setScissorTest( boolean ); @@ -880,26 +827,13 @@ class Renderer { getViewport( target ) { - return target.copy( this._viewport ); + return this._defaultCanvasRenderTarget.getViewport( target ); } setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) { - const viewport = this._viewport; - - if ( x.isVector4 ) { - - viewport.copy( x ); - - } else { - - viewport.set( x, y, width, height ); - - } - - viewport.minDepth = minDepth; - viewport.maxDepth = maxDepth; + return this._defaultCanvasRenderTarget.setViewport( x, y, width, height, minDepth, maxDepth ); } @@ -940,6 +874,29 @@ class Renderer { } + get depth() { + + return this._defaultCanvasRenderTarget.depth; + + } + + set depth( value ) { + + this._defaultCanvasRenderTarget.depth = value; + + } + get stencil() { + + return this._defaultCanvasRenderTarget.stencil; + + } + + set stencil( value ) { + + this._defaultCanvasRenderTarget.stencil = value; + + } + getClearStencil() { return this._clearStencil; @@ -970,11 +927,11 @@ class Renderer { } - const renderTarget = this._renderTarget || this._getFrameBufferTarget(); + const renderTarget = this._renderTarget.isCanvasRenderTarget === true ? this._getFrameBufferTarget() : this._renderTarget; let renderTargetData = null; - if ( renderTarget !== null ) { + if ( ! renderTarget.isCanvasRenderTarget ) { this._textures.updateRenderTarget( renderTarget ); @@ -982,9 +939,9 @@ class Renderer { } - this.backend.clear( color, depth, stencil, renderTargetData ); + this.backend.clear( color, depth, stencil, renderTargetData, renderTarget ); - if ( renderTarget !== null && this._renderTarget === null ) { + if ( renderTarget !== null && this._renderTarget.isCanvasRenderTarget === true ) { // If a color space transform or tone mapping is required, // the clear operation clears the intermediate renderTarget texture, but does not update the screen canvas. @@ -1048,11 +1005,27 @@ class Renderer { } + get outputColorSpace() { + + return this._defaultCanvasRenderTarget.outputColorSpace; + + } + + set outputColorSpace( colorSpace ) { + + this._defaultCanvasRenderTarget.outputColorSpace = colorSpace; + + } + get currentColorSpace() { const renderTarget = this._renderTarget; - if ( renderTarget !== null ) { + if ( renderTarget.isCanvasRenderTarget ) { + + return renderTarget.outputColorSpace; + + } else { const texture = renderTarget.texture; @@ -1060,8 +1033,6 @@ class Renderer { } - return this.outputColorSpace; - } dispose() { @@ -1084,7 +1055,7 @@ class Renderer { setRenderTarget( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - this._renderTarget = renderTarget; + this._renderTarget = renderTarget === null ? this._defaultCanvasRenderTarget : renderTarget; this._activeCubeFace = activeCubeFace; this._activeMipmapLevel = activeMipmapLevel; @@ -1092,7 +1063,7 @@ class Renderer { getRenderTarget() { - return this._renderTarget; + return this._renderTarget === this._defaultCanvasRenderTarget ? null : this._renderTarget; } @@ -1368,6 +1339,8 @@ class Renderer { // process renderable objects + const renderTarget = this._renderTarget; + for ( let i = 0, il = renderList.length; i < il; i ++ ) { const renderItem = renderList[ i ]; @@ -1392,7 +1365,7 @@ class Renderer { const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth; const viewportValue = this._currentRenderContext.viewportValue; - viewportValue.copy( vp ).multiplyScalar( this._pixelRatio ).floor(); + viewportValue.copy( vp ).multiplyScalar( renderTarget.pixelRatio ).floor(); viewportValue.minDepth = minDepth; viewportValue.maxDepth = maxDepth; diff --git a/src/renderers/webgl-fallback/WebGLBackend.js b/src/renderers/webgl-fallback/WebGLBackend.js index 50f8e4521fc080..375e151c4ff7a9 100644 --- a/src/renderers/webgl-fallback/WebGLBackend.js +++ b/src/renderers/webgl-fallback/WebGLBackend.js @@ -1196,12 +1196,6 @@ class WebGLBackend extends Backend { } - updateSize() { - - //console.warn( 'Abstract class.' ); - - } - hasFeature( name ) { const keysMatching = Object.keys( GLFeatureName ).filter( key => GLFeatureName[ key ] === name ); diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index c8daf4620a39b6..071ec69be68051 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -33,9 +33,6 @@ class WebGPUBackend extends Backend { this.trackTimestamp = ( parameters.trackTimestamp === true ); this.device = null; - this.context = null; - this.colorBuffer = null; - this.defaultRenderPassdescriptor = null; this.utils = new WebGPUUtils( this ); this.attributeUtils = new WebGPUAttributeUtils( this ); @@ -101,21 +98,26 @@ class WebGPUBackend extends Backend { } - const context = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgpu' ); - this.device = device; - this.context = context; - const alphaMode = parameters.alpha ? 'premultiplied' : 'opaque'; + } + + _configureContext( canvasRenderTarget ) { + + const context = ( canvasRenderTarget.context !== undefined ) ? canvasRenderTarget.context : canvasRenderTarget.domElement.getContext( 'webgpu' ); + const canvasRenderTargetData = this.get( canvasRenderTarget ); + + const alphaMode = canvasRenderTarget.alpha ? 'premultiplied' : 'opaque'; - this.context.configure( { + context.configure( { device: this.device, format: GPUTextureFormat.BGRA8Unorm, usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, alphaMode: alphaMode } ); - this.updateSize(); + canvasRenderTargetData.contextGPU = context; + canvasRenderTargetData.ready = true; } @@ -131,34 +133,40 @@ class WebGPUBackend extends Backend { } - getContext() { + getContext( canvasRenderTarget ) { - return this.context; + return this.get( canvasRenderTarget ).contextGPU; } - _getDefaultRenderPassDescriptor() { + _getCanvasRenderPassDescriptor( canvasRenderTarget ) { - let descriptor = this.defaultRenderPassdescriptor; + const antialias = canvasRenderTarget.antialias; - if ( descriptor === null ) { + const canvasRenderTargetData = this.get( canvasRenderTarget ); - const renderer = this.renderer; + if ( ! canvasRenderTargetData.ready ) this._configureContext( canvasRenderTarget ); + + const contextGPU = canvasRenderTargetData.contextGPU; + + let descriptor = canvasRenderTargetData.descriptor; + + if ( canvasRenderTarget.version !== canvasRenderTargetData.version ) { descriptor = { colorAttachments: [ { view: null } ], depthStencilAttachment: { - view: this.textureUtils.getDepthBuffer( renderer.depth, renderer.stencil ).createView() + view: this.textureUtils.getDepthBuffer( canvasRenderTarget ).createView() } }; const colorAttachment = descriptor.colorAttachments[ 0 ]; - if ( this.renderer.samples > 0 ) { + if ( antialias ) { - colorAttachment.view = this.colorBuffer.createView(); + colorAttachment.view = this.textureUtils.getColorBuffer( canvasRenderTarget ).createView(); } else { @@ -166,19 +174,20 @@ class WebGPUBackend extends Backend { } - this.defaultRenderPassdescriptor = descriptor; + canvasRenderTargetData.descriptor = descriptor; + canvasRenderTargetData.version = canvasRenderTarget.version; } const colorAttachment = descriptor.colorAttachments[ 0 ]; - if ( this.renderer.samples > 0 ) { + if ( antialias ) { - colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + colorAttachment.resolveTarget = contextGPU.getCurrentTexture().createView(); } else { - colorAttachment.view = this.context.getCurrentTexture().createView(); + colorAttachment.view = contextGPU.getCurrentTexture().createView(); } @@ -312,7 +321,7 @@ class WebGPUBackend extends Backend { if ( renderContext.textures === null ) { - descriptor = this._getDefaultRenderPassDescriptor(); + descriptor = this._getCanvasRenderPassDescriptor( renderContext.renderTarget ); } else { @@ -578,7 +587,7 @@ class WebGPUBackend extends Backend { } - clear( color, depth, stencil, renderTargetData = null ) { + clear( color, depth, stencil, renderTargetData = null, renderTarget ) { const device = this.device; const renderer = this.renderer; @@ -599,12 +608,15 @@ class WebGPUBackend extends Backend { } - if ( renderTargetData === null ) { + if ( renderTarget.isCanvasRenderTarget ) { + + supportsDepth = renderTarget.depth; + supportsStencil = renderTarget.stencil; - supportsDepth = renderer.depth; - supportsStencil = renderer.stencil; + depth = depth && supportsDepth; + stencil = stencil && supportsStencil; - const descriptor = this._getDefaultRenderPassDescriptor(); + const descriptor = this._getCanvasRenderPassDescriptor( renderTarget ); if ( color ) { @@ -985,10 +997,12 @@ class WebGPUBackend extends Backend { const utils = this.utils; - const sampleCount = utils.getSampleCountRenderContext( renderObject.context ); - const colorSpace = utils.getCurrentColorSpace( renderObject.context ); - const colorFormat = utils.getCurrentColorFormat( renderObject.context ); - const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context ); + const renderContext = renderObject.context; + + const sampleCount = utils.getSampleCount( renderContext.sampleCount ); + const colorSpace = renderContext.colorSpace; + const colorFormat = utils.getCurrentColorFormat( renderContext ); + const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderContext ); const primitiveTopology = utils.getPrimitiveTopology( object, material ); let needsUpdate = false; @@ -1050,8 +1064,8 @@ class WebGPUBackend extends Backend { material.stencilFail, material.stencilZFail, material.stencilZPass, material.stencilFuncMask, material.stencilWriteMask, material.side, - utils.getSampleCountRenderContext( renderContext ), - utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ), + utils.getSampleCount( renderContext.sampleCount ), + renderContext.colorSpace, utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ), utils.getPrimitiveTopology( object, material ), renderObject.clippingContextVersion ].join(); @@ -1303,15 +1317,6 @@ class WebGPUBackend extends Backend { } - // canvas - - updateSize() { - - this.colorBuffer = this.textureUtils.getColorBuffer(); - this.defaultRenderPassdescriptor = null; - - } - // utils public getMaxAnisotropy() { @@ -1383,11 +1388,13 @@ class WebGPUBackend extends Backend { const renderContextData = this.get( renderContext ); + const canvasRenderTarget = renderContext.renderTarget; + const { encoder, descriptor } = renderContextData; let sourceGPU = null; - if ( renderContext.renderTarget ) { + if ( ! renderContext.renderTarget.isCanvasRenderTarget ) { if ( texture.isDepthTexture ) { @@ -1403,11 +1410,11 @@ class WebGPUBackend extends Backend { if ( texture.isDepthTexture ) { - sourceGPU = this.textureUtils.getDepthBuffer( renderContext.depth, renderContext.stencil ); + sourceGPU = this.get( canvasRenderTarget ).depthTextureGPU; } else { - sourceGPU = this.context.getCurrentTexture(); + sourceGPU = this.get( canvasRenderTarget ).contextGPU.getCurrentTexture(); } diff --git a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js index a09a44d20c85a1..60772b9ca9322d 100644 --- a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -25,13 +25,13 @@ class WebGPUPipelineUtils { _getSampleCount( renderObjectContext ) { - return this.backend.utils.getSampleCountRenderContext( renderObjectContext ); + return this.backend.utils.getSampleCount( renderObjectContext.sampleCount ); } createRenderPipeline( renderObject, promises ) { - const { object, material, geometry, pipeline } = renderObject; + const { object, material, geometry, pipeline, context } = renderObject; const { vertexProgram, fragmentProgram } = pipeline; const backend = this.backend; @@ -85,9 +85,9 @@ class WebGPUPipelineUtils { const targets = []; - if ( renderObject.context.textures !== null ) { + if ( context.textures !== null ) { - const textures = renderObject.context.textures; + const textures = context.textures; for ( let i = 0; i < textures.length; i ++ ) { @@ -103,7 +103,7 @@ class WebGPUPipelineUtils { } else { - const colorFormat = utils.getCurrentColorFormat( renderObject.context ); + const colorFormat = utils.getCurrentColorFormat( context ); targets.push( { format: colorFormat, @@ -118,9 +118,9 @@ class WebGPUPipelineUtils { const primitiveState = this._getPrimitiveState( object, geometry, material ); const depthCompare = this._getDepthCompare( material ); - const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context ); + const depthStencilFormat = utils.getCurrentDepthStencilFormat( context ); - const sampleCount = this._getSampleCount( renderObject.context ); + const sampleCount = this._getSampleCount( context ); const pipelineDescriptor = { label: 'renderPipeline', @@ -178,7 +178,7 @@ class WebGPUPipelineUtils { const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderContext ); const colorFormat = utils.getCurrentColorFormat( renderContext ); - const sampleCount = this._getSampleCount( renderObject.context ); + const sampleCount = this._getSampleCount( renderContext ); const descriptor = { label: 'renderBundleEncoder', diff --git a/src/renderers/webgpu/utils/WebGPUTextureUtils.js b/src/renderers/webgpu/utils/WebGPUTextureUtils.js index e2630335eb3881..8355fd7468fe74 100644 --- a/src/renderers/webgpu/utils/WebGPUTextureUtils.js +++ b/src/renderers/webgpu/utils/WebGPUTextureUtils.js @@ -16,7 +16,7 @@ import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from '../../../constants.js'; import { CubeTexture } from '../../../textures/CubeTexture.js'; -import { DepthTexture } from '../../../textures/DepthTexture.js'; +import { Vector2 } from '../../../math/Vector2.js'; import { Texture } from '../../../textures/Texture.js'; const _compareToWebGPU = { @@ -31,6 +31,7 @@ const _compareToWebGPU = { }; const _flipMap = [ 0, 1, 3, 2, 4, 5 ]; +const _vector2 = new Vector2(); class WebGPUTextureUtils { @@ -43,11 +44,6 @@ class WebGPUTextureUtils { this.defaultTexture = {}; this.defaultCubeTexture = {}; - this.colorBuffer = null; - - this.depthTexture = new DepthTexture(); - this.depthTexture.name = 'depthBuffer'; - } createSampler( texture ) { @@ -238,72 +234,70 @@ class WebGPUTextureUtils { } - getColorBuffer() { - - if ( this.colorBuffer ) this.colorBuffer.destroy(); + getBuffer( canvasRenderTarget, format, label ) { const backend = this.backend; - const { width, height } = backend.getDrawingBufferSize(); + const { width, height } = canvasRenderTarget.getDrawingBufferSize( _vector2 ); - this.colorBuffer = backend.device.createTexture( { - label: 'colorBuffer', + return backend.device.createTexture( { + label, size: { - width: width, - height: height, + width, + height, depthOrArrayLayers: 1 }, - sampleCount: backend.utils.getSampleCount( backend.renderer.samples ), - format: GPUTextureFormat.BGRA8Unorm, + sampleCount: canvasRenderTarget.sampleCount, + format, usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC } ); - return this.colorBuffer; - } - getDepthBuffer( depth = true, stencil = false ) { + getColorBuffer( canvasRenderTarget ) { const backend = this.backend; - const { width, height } = backend.getDrawingBufferSize(); - const depthTexture = this.depthTexture; - const depthTextureGPU = backend.get( depthTexture ).texture; + const canvasRenderTargetData = backend.get( canvasRenderTarget ); - let format, type; + let colorBuffer = canvasRenderTargetData.colorBuffer; - if ( stencil ) { + if ( colorBuffer ) colorBuffer.destroy(); - format = DepthStencilFormat; - type = UnsignedInt248Type; + colorBuffer = this.getBuffer( canvasRenderTarget, GPUTextureFormat.BGRA8Unorm, 'colorBuffer' ); - } else if ( depth ) { + canvasRenderTargetData.colorBuffer = colorBuffer; - format = DepthFormat; - type = UnsignedIntType; + return colorBuffer; - } + } - if ( depthTextureGPU !== undefined ) { + getDepthBuffer( canvasRenderTarget ) { - if ( depthTexture.image.width === width && depthTexture.image.height === height && depthTexture.format === format && depthTexture.type === type ) { + const backend = this.backend; - return depthTextureGPU; + const canvasRenderTargetData = backend.get( canvasRenderTarget ); - } + let depthTextureGPU = canvasRenderTargetData.depthTextureGPU; + + if ( depthTextureGPU ) depthTextureGPU.destroy(); + + let format; + + if ( canvasRenderTarget.stencil ) { + + format = GPUTextureFormat.Depth24PlusStencil8; + + } else if ( canvasRenderTarget.depth ) { - this.destroyTexture( depthTexture ); + format = GPUTextureFormat.Depth24Plus; } - depthTexture.name = 'depthBuffer'; - depthTexture.format = format; - depthTexture.type = type; - depthTexture.image.width = width; - depthTexture.image.height = height; + depthTextureGPU = this.getBuffer( canvasRenderTarget, format, 'depthBuffer' ); - this.createTexture( depthTexture, { sampleCount: backend.utils.getSampleCount( backend.renderer.samples ), width, height } ); + canvasRenderTargetData.depthTextureGPU = depthTextureGPU; - return backend.get( depthTexture ).texture; + return depthTextureGPU; } diff --git a/src/renderers/webgpu/utils/WebGPUUtils.js b/src/renderers/webgpu/utils/WebGPUUtils.js index 3fe6a06e39658a..d1cb1bd4de7256 100644 --- a/src/renderers/webgpu/utils/WebGPUUtils.js +++ b/src/renderers/webgpu/utils/WebGPUUtils.js @@ -55,18 +55,6 @@ class WebGPUUtils { } - getCurrentColorSpace( renderContext ) { - - if ( renderContext.textures !== null ) { - - return renderContext.textures[ 0 ].colorSpace; - - } - - return this.backend.renderer.outputColorSpace; - - } - getPrimitiveTopology( object, material ) { if ( object.isPoints ) return GPUPrimitiveTopology.PointList; @@ -97,18 +85,6 @@ class WebGPUUtils { } - getSampleCountRenderContext( renderContext ) { - - if ( renderContext.textures !== null ) { - - return this.getSampleCount( renderContext.sampleCount ); - - } - - return this.getSampleCount( this.backend.renderer.samples ); - - } - } export default WebGPUUtils; diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index 90f534583296d9..b4931ca69d1dab 100644 --- a/test/e2e/puppeteer.js +++ b/test/e2e/puppeteer.js @@ -115,6 +115,7 @@ const exceptionList = [ 'webgpu_compute_audio', 'webgpu_compute_texture', 'webgpu_compute_texture_pingpong', + 'webgpu_multiple_canvases', 'webgpu_materials', 'webgpu_sandbox', 'webgpu_sprites',