Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: multiple canvas support #27628

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Binary file added examples/screenshots/webgpu_multiple_canvases.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
258 changes: 258 additions & 0 deletions examples/webgpu_multiple_canvases.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgpu - Multiple Canvases</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
#container {
padding-top: 3em;
display: grid;
grid-template-columns: repeat( auto-fill, minmax( 200px, 1fr ) );
justify-items: center;
column-gap: 10px;
row-gap: 10px;
}

#container > div {
position: relative;
border: 1px solid white;
}

canvas {
width: 200px;
height: 200px;
}

.label {
position: absolute;
color: white;
bottom: 0;
}
</style>
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank">threejs</a> webgpu - Multiple Canvases
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.webgpu.js",
"three/tsl": "../build/three.webgpu.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';

import WebGPU from 'three/addons/capabilities/WebGPU.js';

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

import Stats from 'three/addons/libs/stats.module.js';

let camera, renderer;
let stats;

const geometries = [];
const materials = [];
const canvases = [];
const colorSpaces = [ THREE.NoColorSpace, THREE.SRGBColorSpace, THREE.LinearSRGBColorSpace ];

const container = document.getElementById( 'container' );

const offScreen = new Set();

const observer = new IntersectionObserver( ( entries ) => {

for ( let i = 0; i < entries.length; i ++ ) {

const entry = entries[ i ];

if ( entry.intersectionRatio === 0 ) {

offScreen.add( entry.target );

} else {

offScreen.delete( entry.target );

}

}

} );

const parameters = {
canvases: 6,
observed: false
};

initGUI();
init();

function initGUI() {

const gui = new GUI();
gui.add( parameters, 'canvases', 1, 500 ).step( 1 );
gui.add( parameters, 'observed' );

}

function makeCanvasRenderTarget( container ) {

const canvasRenderTarget = new THREE.CanvasRenderTarget( { antialias: Math.random() < 0.5 ? true : false } );

canvasRenderTarget.outputColorSpace = colorSpaces[ Math.floor( Math.random() * colorSpaces.length ) ];

const domElement = canvasRenderTarget.domElement;

const frame = document.createElement( 'div' );
const label = document.createElement( 'div' );

label.innerText = 'color space ' + ( canvasRenderTarget.outputColorSpace || 'none' ) + ( canvasRenderTarget.antialias ? ' antialias' : '' );
label.classList.add( 'label' );

frame.appendChild( label );
frame.appendChild( domElement );

container.appendChild( frame );

observer.observe( domElement );

canvasRenderTarget.setPixelRatio( window.devicePixelRatio );
canvasRenderTarget.setSize( domElement.clientWidth, domElement.clientHeight );

return canvasRenderTarget;

}

function makeScene() {

const scene = new THREE.Scene();
const dLight = new THREE.DirectionalLight();
const aLight = new THREE.AmbientLight();

const mesh = new THREE.Mesh(
geometries[ Math.floor( Math.random() * geometries.length ) ],
materials[ Math.floor( Math.random() * materials.length ) ]
);

scene.background = new THREE.Color( 0x222222 );

dLight.position.set( -1, 1, 1 );
scene.add( mesh );
scene.add( dLight );
scene.add( aLight );

const renderTarget = makeCanvasRenderTarget( container );

canvases.push( {
mesh,
scene,
renderTarget,
rotateX: ( Math.random() - 0.5 ) / 1000,
rotateY: ( Math.random() - 0.5 ) / 1000,
domElement: renderTarget.domElement
} );

}

function init() {

if ( ! WebGPU.isAvailable() ) {

document.body.appendChild( WebGPU.getErrorMessage() );

throw new Error( 'No WebGPU or WebGL2 support' );

}

renderer = new THREE.WebGPURenderer();
renderer.setAnimationLoop( render );

stats = new Stats();
document.body.appendChild( stats.dom );

// Scene setup

camera = new THREE.PerspectiveCamera( 70, 1, 0.1, 50 );
camera.position.z = 4;

geometries.push(
new THREE.TorusGeometry( 1, 0.3, 128, 32 ),
new THREE.TorusKnotGeometry( 1, 0.3, 128, 32 ),
new THREE.IcosahedronGeometry(),
new THREE.BoxGeometry(),
new THREE.TetrahedronGeometry()
);

materials.push(
new THREE.MeshPhongNodeMaterial( { color: 0x00ee00 } ),
new THREE.MeshPhongNodeMaterial( { color: 0xee0000 } ),
new THREE.MeshPhongNodeMaterial( { color: 0xeeee00 } ),
new THREE.MeshPhongNodeMaterial( { color: 0x00eeee } ),
new THREE.MeshPhongNodeMaterial( { color: 0xee00ee } ),
new THREE.MeshPhongNodeMaterial( { color: 0x0000ee } ),
);

}

function render( time ) {

stats.update();

const diff = canvases.length - parameters.canvases;

if ( diff > 0 ) {

for ( let i = 0; i < diff; i ++ ) {

const canvas = canvases.pop();
const domElement = canvas.renderTarget.domElement;

observer.unobserve( domElement );
offScreen.delete( domElement );

domElement.remove();
canvas.renderTarget.dispose();

}

} else if ( diff < 0 ) {

for ( let i = 0; i < - diff; i ++ ) {

makeScene();

}

}

for ( let i = 0; i < canvases.length; i ++ ) {

const canvas = canvases[ i ];

// skip rendering of offscreen
if ( parameters.observed && offScreen.has( canvas.domElement ) ) continue;

canvas.mesh.rotation.y = time * canvas.rotateX;
canvas.mesh.rotation.x = time * canvas.rotateY;

renderer.setRenderTarget( canvas.renderTarget );
renderer.render( canvas.scene, camera );

}

}

</script>
<div id="container"></div>
</body>
</html>
1 change: 1 addition & 0 deletions src/Three.WebGPU.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
5 changes: 3 additions & 2 deletions src/nodes/display/PassNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
4 changes: 2 additions & 2 deletions src/nodes/display/ViewportNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

}

Expand Down
2 changes: 1 addition & 1 deletion src/nodes/display/ViewportTextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ViewportTextureNode extends TextureNode {
updateBefore( frame ) {

const renderer = frame.renderer;
renderer.getDrawingBufferSize( _size );
renderer.getActiveCanvasRenderTarget().getDrawingBufferSize( _size );

//

Expand Down
4 changes: 2 additions & 2 deletions src/nodes/utils/ReflectorNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) );

Expand Down Expand Up @@ -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 );

Expand Down
24 changes: 1 addition & 23 deletions src/renderers/common/Backend.js
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -89,8 +85,6 @@ class Backend {

getContext() { }

updateSize() { }

// utils

resolveTimestampAsync( /*renderContext, type*/ ) { }
Expand All @@ -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() {

Expand Down
Loading
Loading