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

RFC: WebGPURenderer prototype single uniform buffer update / pass #27388

Open
wants to merge 10 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 @@ -405,6 +405,7 @@
"webgpu_skinning_points",
"webgpu_sky",
"webgpu_sprites",
"webgpu_sprites_common_buffer",
"webgpu_storage_buffer",
"webgpu_texturegrad",
"webgpu_textures_2d-array",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
213 changes: 213 additions & 0 deletions examples/webgpu_sprites_common_buffer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<html lang="en">
<head>
<title>three.js - WebGPU - Sprites - Common buffer</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">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> WebGPU - Sprites - Common buffer
<div id="buffer-stats" style="
position: absolute;
top: 60px;
left: 0;
padding: 10px;
background: rgba( 0, 0, 0, 0.5 );
color: #fff;
font-family: monospace;
font-size: 12px;
line-height: 1.5;
pointer-events: none;
text-align: left;
"></div>
</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 { texture, uv, userData, rangeFog, color, SpriteNodeMaterial, reference } from 'three/tsl';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused imports SpriteNodeMaterial, reference.

let camera, scene, renderer, material;

let map;

let group;

let imageWidth = 1, imageHeight = 1;

const amount = 200;
const radius = 500;

const bufferStats = document.getElementById( 'buffer-stats' );

init();

function addSprite( material ) {

const x = Math.random() - 0.5;
const y = Math.random() - 0.5;
const z = Math.random() - 0.5;

const sprite = new THREE.Sprite( material );

sprite.position.set( x, y, z );
sprite.position.normalize();
sprite.position.multiplyScalar( radius );

// individual rotation per sprite
sprite.userData.rotation = 0;

group.add( sprite );

}

function init() {

const width = window.innerWidth;
const height = window.innerHeight;

camera = new THREE.PerspectiveCamera( 60, width / height, 1, 2100 );
camera.position.z = 1500;

scene = new THREE.Scene();
scene.fogNode = rangeFog( color( 0x0000ff ), 1500, 2100 );

// create sprites

const textureLoader = new THREE.TextureLoader();

map = textureLoader.load( 'textures/sprite1.png', ( map ) => {

imageWidth = map.image.width;
imageHeight = map.image.height;

} );

group = new THREE.Group();

const textureNode = texture( map );

material = new THREE.SpriteNodeMaterial();
material.colorNode = textureNode.mul( uv() ).mul( 2 ).saturate();
material.opacityNode = textureNode.a;
material.rotationNode = userData( 'rotation', 'float' ); // get value of: sprite.userData.rotation
material.transparent = true;

for ( let a = 0; a < amount; a ++ ) {

addSprite( material );

}

scene.add( group );

//

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( render );
renderer.commonBufferSize = 1024;

document.body.appendChild( renderer.domElement );

window.addEventListener( 'resize', onWindowResize );

}

function onWindowResize() {

const width = window.innerWidth;
const height = window.innerHeight;

camera.aspect = width / height;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}

function render() {

const time = Date.now() / 1000;

for ( let i = 0, l = group.children.length; i < l; i ++ ) {

const sprite = group.children[ i ];

if ( sprite === undefined ) {

continue;


} else {

if ( Math.round( Math.random( ) * 1000 ) === 1 ) {

sprite.removeFromParent();

continue;

}

}

const scale = Math.sin( time + sprite.position.x * 0.01 ) * 0.3 + 1.0;

sprite.userData.rotation += 0.1 * ( i / l );
sprite.scale.set( scale * imageWidth, scale * imageHeight, 1.0 );

if ( Math.round( Math.random( ) * 1000 ) === 1 ) {

addSprite( material );

}

}

group.rotation.x = time * 0.05;
group.rotation.y = time * 0.075;
group.rotation.z = time * 0.1;

renderer.render( scene, camera );

const info = renderer.info;

if ( info.render.calls % 20 === 0 ) {

const stats = info.memory.common;
const lists = stats.freeLists;

let listText = '';

for ( let i = 1, l = lists.length; i < l; i ++ ) {

listText += `Blocks ${ i } - length: ${ lists[ i ]} `;

}

bufferStats.innerHTML =
`Unused bytes: ${ stats.unused }<br>` +
`Alloc: ${ stats.alloc } (reused ${ stats.reused }) Free: ${ stats.free } <br><br>` +
`Free block lists (size: ${ stats.blockSize } B)<br>` +
listText;

}

}

</script>
</body>
</html>
18 changes: 18 additions & 0 deletions src/renderers/common/Bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@ class Bindings extends DataMap {

}

delete( renderObject ) {

const bindings = renderObject.getBindings();

for ( const bindGroup of bindings ) {

for ( const binding of bindGroup.bindings ) {

if ( binding.isNodeUniformsGroup && binding._isCommon === true && ! binding.groupNode.shared ) binding.freeCommon();

}

}

super.delete( renderObject );

}

}

export default Bindings;
Loading
Loading