diff --git a/examples/files.json b/examples/files.json index 0b69f8e9829a20..4a1345e3939190 100644 --- a/examples/files.json +++ b/examples/files.json @@ -351,6 +351,7 @@ "webgpu_materials_toon", "webgpu_materials_video", "webgpu_materialx_noise", + "webgpu_multiple_canvases", "webgpu_multiple_rendertargets", "webgpu_multiple_rendertargets_readback", "webgpu_morphtargets", diff --git a/examples/jsm/nodes/display/PassNode.js b/examples/jsm/nodes/display/PassNode.js index acfdd502651ab0..92aee89d3f6a95 100644 --- a/examples/jsm/nodes/display/PassNode.js +++ b/examples/jsm/nodes/display/PassNode.js @@ -135,9 +135,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/examples/jsm/nodes/display/ViewportNode.js b/examples/jsm/nodes/display/ViewportNode.js index ebc9aa425889d5..4afa0efa4d9a54 100644 --- a/examples/jsm/nodes/display/ViewportNode.js +++ b/examples/jsm/nodes/display/ViewportNode.js @@ -47,11 +47,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/examples/jsm/nodes/display/ViewportTextureNode.js b/examples/jsm/nodes/display/ViewportTextureNode.js index 5a856789c6acf8..0394fd242cbcb0 100644 --- a/examples/jsm/nodes/display/ViewportTextureNode.js +++ b/examples/jsm/nodes/display/ViewportTextureNode.js @@ -31,7 +31,7 @@ class ViewportTextureNode extends TextureNode { updateBefore( frame ) { const renderer = frame.renderer; - renderer.getDrawingBufferSize( _size ); + renderer.getActiveCanvasRenderTarget().getDrawingBufferSize( _size ); // diff --git a/examples/jsm/nodes/utils/ReflectorNode.js b/examples/jsm/nodes/utils/ReflectorNode.js index cd6b1414d67937..a5cc423d14deb5 100644 --- a/examples/jsm/nodes/utils/ReflectorNode.js +++ b/examples/jsm/nodes/utils/ReflectorNode.js @@ -55,7 +55,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 ) ); @@ -126,7 +126,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/examples/jsm/renderers/common/Backend.js b/examples/jsm/renderers/common/Backend.js index 78d01f8b95abb5..b9eba356382d11 100644 --- a/examples/jsm/renderers/common/Backend.js +++ b/examples/jsm/renderers/common/Backend.js @@ -1,9 +1,7 @@ -let vector2 = null; -let vector4 = null; let color4 = null; import Color4 from './Color4.js'; -import { Vector2, Vector4, REVISION, createCanvasElement } from 'three'; +import { REVISION, createCanvasElement } from 'three'; class Backend { @@ -86,8 +84,6 @@ class Backend { getContext() { } - updateSize() { } - // utils resolveTimestampAsync( renderContext, type ) { } @@ -104,22 +100,6 @@ class Backend { } - getDrawingBufferSize() { - - vector2 = vector2 || new Vector2(); - - return this.renderer.getDrawingBufferSize( vector2 ); - - } - - getScissor() { - - vector4 = vector4 || new Vector4(); - - return this.renderer.getScissor( vector4 ); - - } - setScissorTest( boolean ) { } getClearColor() { diff --git a/examples/jsm/renderers/common/CanvasRenderTarget.js b/examples/jsm/renderers/common/CanvasRenderTarget.js new file mode 100644 index 00000000000000..6920b74038a4c8 --- /dev/null +++ b/examples/jsm/renderers/common/CanvasRenderTarget.js @@ -0,0 +1,225 @@ +import { EventDispatcher, Vector4, REVISION, createCanvasElement, SRGBColorSpace } from 'three'; + +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/examples/jsm/renderers/common/RenderContexts.js b/examples/jsm/renderers/common/RenderContexts.js index da21e6d9a0fed9..ee6d5e3c05e686 100644 --- a/examples/jsm/renderers/common/RenderContexts.js +++ b/examples/jsm/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/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index 93854cdb7e924b..687a26021ac30b 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -12,10 +12,11 @@ import Background from './Background.js'; import Nodes from './nodes/Nodes.js'; import Color4 from './Color4.js'; import ClippingContext from './ClippingContext.js'; -import { Scene, Frustum, Matrix4, Vector2, Vector3, Vector4, DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, RenderTarget, HalfFloatType, RGBAFormat } from 'three'; +import { Scene, Frustum, Matrix4, Vector2, Vector3, Vector4, DoubleSide, BackSide, FrontSide, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, RenderTarget, HalfFloatType, RGBAFormat } from 'three'; import { NodeMaterial } from '../../nodes/Nodes.js'; import QuadMesh from '../../objects/QuadMesh.js'; import RenderBundles from './RenderBundles.js'; +import CanvasRenderTarget from './CanvasRenderTarget.js'; const _scene = new Scene(); const _drawingBufferSize = new Vector2(); @@ -53,16 +54,11 @@ 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(); @@ -73,14 +69,6 @@ class Renderer { // 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; @@ -94,6 +82,9 @@ class Renderer { this._textures = null; this._background = null; + this._defaultCanvasRenderTarget = new CanvasRenderTarget( Object.assign( {}, parameters, { domElement: this.domElement } ) ); + + this._activeCanvas = null; this._currentRenderContext = null; this._opaqueSort = null; @@ -107,7 +98,7 @@ class Renderer { this._clearDepth = 1; this._clearStencil = 0; - this._renderTarget = null; + this._renderTarget = this._defaultCanvasRenderTarget; this._activeCubeFace = 0; this._activeMipmapLevel = 0; @@ -235,8 +226,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 ); @@ -271,7 +262,12 @@ class Renderer { // - if ( renderTarget !== null ) { + if ( renderTarget.isCanvasRenderTarget ) { + + renderContext.textures = null; + renderContext.depthTexture = null; + + } else { this._textures.updateRenderTarget( renderTarget, activeMipmapLevel ); @@ -280,11 +276,6 @@ class Renderer { renderContext.textures = renderTargetData.textures; renderContext.depthTexture = renderTargetData.depthTexture; - } else { - - renderContext.textures = null; - renderContext.depthTexture = null; - } // @@ -423,13 +414,15 @@ class Renderer { const { currentColorSpace } = this; - const useToneMapping = this._renderTarget === null && ( this.toneMapping !== NoToneMapping || this.toneMappingNode !== null ); + const renderTarget = this._renderTarget; + + const useToneMapping = renderTarget.isCanvasRenderTarget === true && ( this.toneMapping !== NoToneMapping || this.toneMappingNode !== null ); 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; @@ -456,11 +449,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; @@ -483,7 +476,6 @@ class Renderer { const sceneRef = ( scene.isScene === true ) ? scene : _scene; const outputRenderTarget = this._renderTarget; - const activeCubeFace = this._activeCubeFace; const activeMipmapLevel = this._activeMipmapLevel; @@ -510,6 +502,7 @@ class Renderer { this._currentRenderContext = renderContext; this._currentRenderObjectFunction = this._renderObjectFunction || this.renderObject; + // this.info.calls ++; @@ -537,19 +530,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 ); @@ -564,7 +549,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; @@ -595,8 +580,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 ); @@ -605,21 +600,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; @@ -716,70 +701,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 ); } @@ -797,42 +761,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 ); @@ -840,26 +787,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 ); } @@ -900,6 +834,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; @@ -930,11 +887,11 @@ class Renderer { } - const renderTarget = this._renderTarget || this._getFrameBufferTarget(); + const renderTarget = this._renderTarget.isCanvasRenderTarget !== true ? this._renderTarget : this._getFrameBufferTarget(); let renderTargetData = null; - if ( renderTarget !== null ) { + if ( ! renderTarget.isCanvasRenderTarget ) { this._textures.updateRenderTarget( renderTarget ); @@ -942,7 +899,7 @@ class Renderer { } - this.backend.clear( color, depth, stencil, renderTargetData ); + this.backend.clear( color, depth, stencil, renderTargetData, renderTarget ); } @@ -990,11 +947,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; @@ -1002,8 +975,6 @@ class Renderer { } - return this.outputColorSpace; - } dispose() { @@ -1026,7 +997,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; @@ -1034,7 +1005,7 @@ class Renderer { getRenderTarget() { - return this._renderTarget; + return this._renderTarget === this._defaultCanvasRenderTarget ? null : this._renderTarget; } @@ -1309,6 +1280,8 @@ class Renderer { // process renderable objects + const renderTarget = this._renderTarget; + for ( let i = 0, il = renderList.length; i < il; i ++ ) { const renderItem = renderList[ i ]; @@ -1333,7 +1306,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/examples/jsm/renderers/webgl/WebGLBackend.js b/examples/jsm/renderers/webgl/WebGLBackend.js index 0c11cbff8dcea2..ef2ea0d1820d00 100644 --- a/examples/jsm/renderers/webgl/WebGLBackend.js +++ b/examples/jsm/renderers/webgl/WebGLBackend.js @@ -1102,12 +1102,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/examples/jsm/renderers/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js index f28e1bc41e0426..746556f2d1460e 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js @@ -25,29 +25,11 @@ class WebGPUBackend extends Backend { this.isWebGPUBackend = true; - // some parameters require default values other than "undefined" - this.parameters.alpha = ( parameters.alpha === undefined ) ? true : parameters.alpha; - - this.parameters.antialias = ( parameters.antialias === true ); - - if ( this.parameters.antialias === true ) { - - this.parameters.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount; - - } else { - - this.parameters.sampleCount = 1; - - } - this.parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits; 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 ); @@ -113,23 +95,28 @@ 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 ); - this.context.configure( { + const alphaMode = canvasRenderTarget.alpha ? 'premultiplied' : 'opaque'; + + 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; - } + }; get coordinateSystem() { @@ -143,28 +130,30 @@ class WebGPUBackend extends Backend { } - getContext() { + getContext( canvasRenderTarget ) { - return this.context; + return this.get( canvasRenderTarget ).contextGPU; } - _getDefaultRenderPassDescriptor() { + _getCanvasRenderPassDescriptor( canvasRenderTarget ) { + + const antialias = canvasRenderTarget.antialias; - let descriptor = this.defaultRenderPassdescriptor; + const canvasRenderTargetData = this.get( canvasRenderTarget ); - const antialias = this.parameters.antialias; + if ( ! canvasRenderTargetData.ready ) this._configureContext( canvasRenderTarget ); - if ( descriptor === null ) { + let { contextGPU, descriptor } = canvasRenderTargetData; - const renderer = this.renderer; + 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() } }; @@ -172,7 +161,7 @@ class WebGPUBackend extends Backend { if ( antialias === true ) { - colorAttachment.view = this.colorBuffer.createView(); + colorAttachment.view = this.textureUtils.getColorBuffer( canvasRenderTarget ).createView(); } else { @@ -180,7 +169,8 @@ class WebGPUBackend extends Backend { } - this.defaultRenderPassdescriptor = descriptor; + canvasRenderTargetData.descriptor = descriptor; + canvasRenderTargetData.version = canvasRenderTarget.version; } @@ -188,11 +178,11 @@ class WebGPUBackend extends Backend { if ( antialias === true ) { - colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + colorAttachment.resolveTarget = contextGPU.getCurrentTexture().createView(); } else { - colorAttachment.view = this.context.getCurrentTexture().createView(); + colorAttachment.view = contextGPU.getCurrentTexture().createView(); } @@ -326,7 +316,7 @@ class WebGPUBackend extends Backend { if ( renderContext.textures === null ) { - descriptor = this._getDefaultRenderPassDescriptor(); + descriptor = this._getCanvasRenderPassDescriptor( renderContext.renderTarget ); } else { @@ -592,7 +582,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; @@ -613,12 +603,15 @@ class WebGPUBackend extends Backend { } - if ( renderTargetData === null ) { + if ( renderTarget.isCanvasRenderTarget ) { - supportsDepth = renderer.depth; - supportsStencil = renderer.stencil; + supportsDepth = renderTarget.depth; + supportsStencil = renderTarget.stencil; - const descriptor = this._getDefaultRenderPassDescriptor(); + depth = depth && supportsDepth; + stencil = stencil && supportsStencil; + + const descriptor = this._getCanvasRenderPassDescriptor( renderTarget ); if ( color ) { @@ -947,10 +940,12 @@ class WebGPUBackend extends Backend { const utils = this.utils; - const sampleCount = utils.getSampleCount( 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 = 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; @@ -1012,8 +1007,8 @@ class WebGPUBackend extends Backend { material.stencilFail, material.stencilZFail, material.stencilZPass, material.stencilFuncMask, material.stencilWriteMask, material.side, - utils.getSampleCount( renderContext ), - utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ), + renderContext.sampleCount, + renderContext.colorSpace, utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ), utils.getPrimitiveTopology( object, material ), renderObject.clippingContextVersion ].join(); @@ -1248,15 +1243,6 @@ class WebGPUBackend extends Backend { } - // canvas - - updateSize() { - - this.colorBuffer = this.textureUtils.getColorBuffer(); - this.defaultRenderPassdescriptor = null; - - } - // utils public getMaxAnisotropy() { @@ -1313,11 +1299,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 ) { @@ -1333,11 +1321,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/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js index d78112e3675f09..d590a23160282f 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -46,7 +46,7 @@ class WebGPUPipelineUtils { createRenderPipeline( renderObject, promises ) { - const { object, material, geometry, pipeline } = renderObject; + const { object, material, geometry, pipeline, context } = renderObject; const { vertexProgram, fragmentProgram } = pipeline; const backend = this.backend; @@ -89,9 +89,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 ++ ) { @@ -107,7 +107,7 @@ class WebGPUPipelineUtils { } else { - const colorFormat = utils.getCurrentColorFormat( renderObject.context ); + const colorFormat = utils.getCurrentColorFormat( context ); targets.push( { format: colorFormat, @@ -122,9 +122,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 = context.sampleCount; const pipelineDescriptor = { label: 'renderPipeline', @@ -182,7 +182,7 @@ class WebGPUPipelineUtils { const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderContext ); const colorFormat = utils.getCurrentColorFormat( renderContext ); - const sampleCount = this._getSampleCount( renderObject.context ); + const sampleCount = renderContext.sampleCount; const descriptor = { label: 'renderBundleEncoder', diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js index f91091f8dd7d8e..58e18c0aff9599 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js @@ -3,7 +3,7 @@ import { } from './WebGPUConstants.js'; import { - ByteType, ShortType, CubeTexture, Texture, + ByteType, ShortType, CubeTexture, Texture, Vector2, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, RepeatWrapping, MirroredRepeatWrapping, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, @@ -13,7 +13,7 @@ import { NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare, IntType, RedIntegerFormat, RGIntegerFormat, RGBAIntegerFormat } from 'three'; -import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, DepthTexture } from 'three'; +import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three'; import WebGPUTexturePassUtils from './WebGPUTexturePassUtils.js'; @@ -29,6 +29,7 @@ const _compareToWebGPU = { }; const _flipMap = [ 0, 1, 3, 2, 4, 5 ]; +const _vector2 = new Vector2(); class WebGPUTextureUtils { @@ -41,11 +42,6 @@ class WebGPUTextureUtils { this.defaultTexture = {}; this.defaultCubeTexture = {}; - this.colorBuffer = null; - - this.depthTexture = new DepthTexture(); - this.depthTexture.name = 'depthBuffer'; - } createSampler( texture ) { @@ -247,72 +243,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.parameters.sampleCount, - 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.parameters.sampleCount, width, height } ); + canvasRenderTargetData.depthTextureGPU = depthTextureGPU; - return backend.get( depthTexture ).texture; + return depthTextureGPU; } diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js index 52d3bea5ad4c03..e1b176bff0eb93 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js +++ b/examples/jsm/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; @@ -76,18 +64,6 @@ class WebGPUUtils { } - getSampleCount( renderContext ) { - - if ( renderContext.textures !== null ) { - - return renderContext.sampleCount; - - } - - return this.backend.parameters.sampleCount; - - } - } export default WebGPUUtils; 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..39eeed40de2431 --- /dev/null +++ b/examples/webgpu_multiple_canvases.html @@ -0,0 +1,262 @@ + + + + three.js webgpu - Multiple Canvases + + + + + + +
+ threejs webgpu - Multiple Canvases +
+ + + + +
+ + diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index 94c48c24f0ded0..b0d07f0b3be08c 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',