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: StorageTexture #26769

Merged
merged 2 commits into from
Sep 17, 2023
Merged
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 @@ -315,6 +315,7 @@
"webgpu_compute",
"webgpu_compute_particles",
"webgpu_compute_texture",
"webgpu_compute_texture_pingpong",
"webgpu_cubemap_adjustments",
"webgpu_cubemap_dynamic",
"webgpu_cubemap_mix",
Expand Down
4 changes: 1 addition & 3 deletions examples/jsm/renderers/common/Bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ class Bindings extends DataMap {

if ( binding.isSampledTexture ) {

const store = binding.store === true;

this.textures.updateTexture( binding.texture, { store } );
this.textures.updateTexture( binding.texture );

} else if ( binding.isStorageBuffer ) {

Expand Down
19 changes: 19 additions & 0 deletions examples/jsm/renderers/common/StorageTexture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Texture, LinearFilter } from 'three';

class StorageTexture extends Texture {

constructor( width = 1, height = 1 ) {

super();

this.image = { width, height };

this.magFilter = LinearFilter;
this.minFilter = LinearFilter;

this.isStorageTexture = true;

}
}

export default StorageTexture;
4 changes: 1 addition & 3 deletions examples/jsm/renderers/common/Textures.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ class Textures extends DataMap {

//

if ( isRenderTarget || options.store === true ) {

//if ( options.store === true ) options.levels = 1; /* no mipmaps? */
if ( isRenderTarget || texture.isStorageTexture === true ) {

backend.createSampler( texture );
backend.createTexture( texture, options );
Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class WebGPUTextureUtils {

let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC;

if ( options.store === true ) {
if ( texture.isStorageTexture === true ) {

usage |= GPUTextureUsage.STORAGE_BINDING;

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions examples/webgpu_compute_texture.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import WebGPU from 'three/addons/capabilities/WebGPU.js';
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
import StorageTexture from 'three/addons/renderers/common/StorageTexture.js';

let camera, scene, renderer;

Expand All @@ -57,10 +58,7 @@

const width = 512, height = 512;

const storageTexture = new THREE.Texture();
storageTexture.image = { width, height };
storageTexture.magFilter = THREE.LinearFilter;
storageTexture.minFilter = THREE.LinearFilter;
const storageTexture = new StorageTexture( width, height );

// create function

Expand Down
178 changes: 178 additions & 0 deletions examples/webgpu_compute_texture_pingpong.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<html lang="en">
<head>
<title>three.js - WebGPU - Compute Ping/Pong Texture</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 - Compute Ping/Pong Texture
<br>Texture generated using GPU Compute.
</div>

<script async src="https://unpkg.com/es-module-shims@1.8.0/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/",
"three/nodes": "./jsm/nodes/Nodes.js"
}
}
</script>

<script type="module">

import * as THREE from 'three';
import { texture, textureStore, wgslFn, code, instanceIndex } from 'three/nodes';

import WebGPU from 'three/addons/capabilities/WebGPU.js';
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
import StorageTexture from 'three/addons/renderers/common/StorageTexture.js';

let camera, scene, renderer;
let computeToPing, computeToPong;
let pingTexture, pongTexture;
let material;
let phase = true;

init();
render();

function init() {

if ( WebGPU.isAvailable() === false ) {

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

throw new Error( 'No WebGPU support' );

}

const aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 2 );
camera.position.z = 1;

scene = new THREE.Scene();

// texture

const width = 512, height = 512;

pingTexture = new StorageTexture( width, height );
pongTexture = new StorageTexture( width, height );

// compute init

const rand2 = code( `
fn rand2( n: vec2f ) -> f32 {

return fract( sin( dot( n, vec2f( 12.9898, 4.1414 ) ) ) * 43758.5453 );

}
` );

const computeInitWGSL = wgslFn( `
fn computeInitWGSL( writeTex: texture_storage_2d<rgba8unorm, write>, index: u32 ) -> void {

let posX = index % ${ width };
let posY = index / ${ width };
let indexUV = vec2u( posX, posY );
let uv = getUV( posX, posY );

textureStore( writeTex, indexUV, vec4f( vec3f( rand2( uv ) ), 1 ) );

}

fn getUV( posX: u32, posY: u32 ) -> vec2f {

let uv = vec2f( f32( posX ) / ${ width }.0, f32( posY ) / ${ height }.0 );

return uv;

}
`, [ rand2 ] );

const computeInitNode = computeInitWGSL( { writeTex: textureStore( pingTexture ), index: instanceIndex } ).compute( width * height );

// compute loop

const computePingPongWGSL = wgslFn( `
fn computePingPongWGSL( readTex: texture_2d<f32>, writeTex: texture_storage_2d<rgba8unorm, write>, index: u32 ) -> void {

let posX = index % ${ width };
let posY = index / ${ width };
let indexUV = vec2u( posX, posY );

let color = vec3f( rand2( textureLoad( readTex, indexUV, 0 ).xy ) );

textureStore( writeTex, indexUV, vec4f( color, 1 ) );

}
`, [ rand2 ] );

computeToPong = computePingPongWGSL( { readTex: texture( pingTexture ), writeTex: textureStore( pongTexture ), index: instanceIndex } ).compute( width * height );
computeToPing = computePingPongWGSL( { readTex: texture( pongTexture ), writeTex: textureStore( pingTexture ), index: instanceIndex } ).compute( width * height );

//

material = new THREE.MeshBasicMaterial( { color: 0xffffff, map: pongTexture } );

const plane = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), material );
scene.add( plane );

renderer = new WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( render );
document.body.appendChild( renderer.domElement );

window.addEventListener( 'resize', onWindowResize );

// compute init

renderer.compute( computeInitNode );

}

function onWindowResize() {

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

const aspect = window.innerWidth / window.innerHeight;

const frustumHeight = camera.top - camera.bottom;

camera.left = - frustumHeight * aspect / 2;
camera.right = frustumHeight * aspect / 2;

camera.updateProjectionMatrix();

render();

}

function render() {

// compute step

renderer.compute( phase ? computeToPong : computeToPing );

material.map = phase ? pongTexture : pingTexture;

phase = ! phase;

// render step

// update material texture node

renderer.render( scene, camera );

}

</script>
</body>
</html>
1 change: 1 addition & 0 deletions test/e2e/puppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const exceptionList = [
'webgpu_compute',
'webgpu_compute_particles',
'webgpu_compute_texture',
'webgpu_compute_texture_pingpong',
'webgpu_cubemap_dynamic',
'webgpu_depth_texture',
'webgpu_instance_mesh',
Expand Down