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',