Skip to content

WebGPURenderer: Array-Based RenderTarget Refactor #30959

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

Merged
merged 18 commits into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from 11 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
8 changes: 4 additions & 4 deletions examples/jsm/helpers/TextureHelperGPU.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class TextureHelper extends Mesh {

colorNode = texture3D( texture ).sample( uvw );

} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
} else if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {

colorNode = textureNode( texture ).sample( uvw.xy ).depth( uvw.z );

Expand Down Expand Up @@ -100,7 +100,7 @@ function getImageCount( texture ) {

return 6;

} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
} else if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {

return texture.image.depth;

Expand All @@ -122,7 +122,7 @@ function getAlpha( texture ) {

return 1;

} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
} else if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {

return Math.max( 1 / texture.image.depth, 0.25 );

Expand Down Expand Up @@ -192,7 +192,7 @@ function createSliceGeometry( texture, width, height, depth ) {
const v = texture.flipY ? uv.getY( j ) : 1 - uv.getY( j );
const w = sliceCount === 1
? 1
: texture.isDataArrayTexture || texture.isCompressedArrayTexture
: texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture
? i
: i / ( sliceCount - 1 );

Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/tsl/shadows/TileShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
ShadowBaseNode,
Plane,
Line3,
DepthArrayTexture,
DepthTexture,
LessCompare,
Vector2,
RedFormat,
Expand Down Expand Up @@ -159,10 +159,10 @@ class TileShadowNode extends ShadowBaseNode {
// Clear existing lights/nodes if re-initializing
this.disposeLightsAndNodes();

const depthTexture = new DepthArrayTexture( shadowWidth, shadowHeight, tileCount );
const depthTexture = new DepthTexture( shadowWidth, shadowHeight, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, tileCount );
depthTexture.compareFunction = LessCompare;
depthTexture.name = 'ShadowDepthArrayTexture';
const shadowMap = builder.createRenderTargetArray( shadowWidth, shadowHeight, tileCount, { format: RedFormat } );
const shadowMap = builder.createRenderTarget( shadowWidth, shadowHeight, { format: RedFormat, depth: tileCount } );
shadowMap.depthTexture = depthTexture;
shadowMap.texture.name = 'ShadowTexture';
this.shadowMap = shadowMap;
Expand Down
3 changes: 2 additions & 1 deletion examples/webgpu_rendertarget_2d-array_3d.html
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@
materialQuad.depthTest = false;
materialQuad.outputNode = vec4( texture( mapArray ).depth( uZCoord ).rgb, 1 );

const fboArray = new THREE.RenderTargetArray( size.width, size.height, size.depth, {
const fboArray = new THREE.RenderTarget( size.width, size.height, {
depthBuffer: false,
depth: size.depth
} );
fboArray.texture.name = 'RenderTargetArray';

Expand Down
5 changes: 2 additions & 3 deletions examples/webgpu_shadowmap_array.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
async function init() {

// Renderer setup
renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: false } );
renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
Expand Down Expand Up @@ -88,8 +88,7 @@
// Set up the tile shadow mapping
const tsm = new TileShadowNode( dirLight, {
tilesX: 2,
tilesY: 2,
debug: true
tilesY: 2
} );


Expand Down
2 changes: 0 additions & 2 deletions src/Three.Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export { CompressedCubeTexture } from './textures/CompressedCubeTexture.js';
export { CubeTexture } from './textures/CubeTexture.js';
export { CanvasTexture } from './textures/CanvasTexture.js';
export { DepthTexture } from './textures/DepthTexture.js';
export { DepthArrayTexture } from './textures/DepthArrayTexture.js';
export { Texture } from './textures/Texture.js';
export * from './geometries/Geometries.js';
export * from './materials/Materials.js';
Expand Down Expand Up @@ -89,7 +88,6 @@ export { AnimationClip } from './animation/AnimationClip.js';
export { AnimationAction } from './animation/AnimationAction.js';
export { RenderTarget } from './core/RenderTarget.js';
export { RenderTarget3D } from './core/RenderTarget3D.js';
export { RenderTargetArray } from './core/RenderTargetArray.js';
export { Uniform } from './core/Uniform.js';
export { UniformsGroup } from './core/UniformsGroup.js';
export { InstancedBufferGeometry } from './core/InstancedBufferGeometry.js';
Expand Down
34 changes: 18 additions & 16 deletions src/core/RenderTarget.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class RenderTarget extends EventDispatcher {
* @property {?Texture} [depthTexture=null] - Reference to a depth texture.
* @property {number} [samples=0] - The MSAA samples count.
* @property {number} [count=1] - Defines the number of color attachments . Must be at least `1`.
* @property {number} [depth=1] - The texture depth.
* @property {boolean} [multiview=false] - Whether this target is used for multiview rendering.
*/

Expand All @@ -49,6 +50,21 @@ class RenderTarget extends EventDispatcher {

super();

options = Object.assign( {
generateMipmaps: false,
internalFormat: null,
minFilter: LinearFilter,
depthBuffer: true,
stencilBuffer: false,
resolveDepthBuffer: true,
resolveStencilBuffer: true,
depthTexture: null,
samples: 0,
count: 1,
depth: 1,
multiview: false
}, options );

/**
* This flag can be used for type testing.
*
Expand Down Expand Up @@ -80,7 +96,7 @@ class RenderTarget extends EventDispatcher {
* @type {number}
* @default 1
*/
this.depth = options.depth ? options.depth : 1;
this.depth = options.depth;

/**
* A rectangular area inside the render target's viewport. Fragments that are
Expand Down Expand Up @@ -108,21 +124,7 @@ class RenderTarget extends EventDispatcher {
*/
this.viewport = new Vector4( 0, 0, width, height );

const image = { width: width, height: height, depth: this.depth };

options = Object.assign( {
generateMipmaps: false,
internalFormat: null,
minFilter: LinearFilter,
depthBuffer: true,
stencilBuffer: false,
resolveDepthBuffer: true,
resolveStencilBuffer: true,
depthTexture: null,
samples: 0,
count: 1,
multiview: false
}, options );
const image = { width: width, height: height, depth: options.depth };

const texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );

Expand Down
40 changes: 0 additions & 40 deletions src/core/RenderTargetArray.js

This file was deleted.

17 changes: 0 additions & 17 deletions src/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import BindGroup from '../../renderers/common/BindGroup.js';

import { REVISION, IntType, UnsignedIntType, LinearFilter, LinearMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapLinearFilter } from '../../constants.js';
import { RenderTarget } from '../../core/RenderTarget.js';
import { RenderTargetArray } from '../../core/RenderTargetArray.js';
import { Color } from '../../math/Color.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
Expand Down Expand Up @@ -458,22 +457,6 @@ class NodeBuilder {

}

/**
* Factory method for creating an instance of {@link RenderTargetArray} with the given
* dimensions and options.
*
* @param {number} width - The width of the render target.
* @param {number} height - The height of the render target.
* @param {number} depth - The depth of the render target.
* @param {Object} options - The options of the render target.
* @return {RenderTargetArray} The render target.
*/
createRenderTargetArray( width, height, depth, options ) {

return new RenderTargetArray( width, height, depth, options );

}

/**
* Factory method for creating an instance of {@link CubeRenderTarget} with the given
* dimensions and options.
Expand Down
8 changes: 4 additions & 4 deletions src/nodes/lighting/ShadowFilterNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const BasicShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord

let basic = texture( depthTexture, shadowCoord.xy ).label( 't_basic' );

if ( depthTexture.isDepthArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

basic = basic.depth( depthLayer );

Expand All @@ -50,7 +50,7 @@ export const PCFShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord,

let depth = texture( depthTexture, uv );

if ( depthTexture.isDepthArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

depth = depth.depth( depthLayer );

Expand Down Expand Up @@ -111,7 +111,7 @@ export const PCFSoftShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoo

let depth = texture( depthTexture, uv );

if ( depthTexture.isDepthArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

depth = depth.depth( depthLayer );

Expand Down Expand Up @@ -189,7 +189,7 @@ export const VSMShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord,

let distribution = texture( depthTexture ).sample( shadowCoord.xy );

if ( depthTexture.isDepthArrayTexture || depthTexture.isDataArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

distribution = distribution.depth( depthLayer );

Expand Down
16 changes: 8 additions & 8 deletions src/nodes/lighting/ShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const VSMPassVertical = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPass,

let depth = shadowPass.sample( add( screenCoordinate.xy, vec2( 0, uvOffset ).mul( radius ) ).div( size ) );

if ( shadowPass.value.isDepthArrayTexture || shadowPass.value.isDataArrayTexture ) {
if ( shadowPass.value.isArrayTexture ) {

depth = depth.depth( depthLayer );

Expand Down Expand Up @@ -131,7 +131,7 @@ const VSMPassHorizontal = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPas

let distribution = shadowPass.sample( add( screenCoordinate.xy, vec2( uvOffset, 0 ).mul( radius ) ).div( size ) );

if ( shadowPass.value.isDepthArrayTexture || shadowPass.value.isDataArrayTexture ) {
if ( shadowPass.value.isArrayTexture ) {

distribution = distribution.depth( depthLayer );

Expand Down Expand Up @@ -395,11 +395,11 @@ class ShadowNode extends ShadowBaseNode {

depthTexture.compareFunction = null; // VSM does not use textureSampleCompare()/texture2DCompare()

if ( shadowMap.isRenderTargetArray ) {
if ( shadowMap.depth > 1 ) {

if ( ! shadowMap._vsmShadowMapVertical ) {

shadowMap._vsmShadowMapVertical = builder.createRenderTargetArray( shadow.mapSize.width, shadow.mapSize.height, shadowMap.depth, { format: RGFormat, type: HalfFloatType, depthBuffer: false } );
shadowMap._vsmShadowMapVertical = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height, { format: RGFormat, type: HalfFloatType, depth: shadowMap.depth, depthBuffer: false } );
shadowMap._vsmShadowMapVertical.texture.name = 'VSMVertical';

}
Expand All @@ -408,7 +408,7 @@ class ShadowNode extends ShadowBaseNode {

if ( ! shadowMap._vsmShadowMapHorizontal ) {

shadowMap._vsmShadowMapHorizontal = builder.createRenderTargetArray( shadow.mapSize.width, shadow.mapSize.height, shadowMap.depth, { format: RGFormat, type: HalfFloatType, depthBuffer: false } );
shadowMap._vsmShadowMapHorizontal = builder.createRenderTarget( shadow.mapSize.width, shadow.mapSize.height, { format: RGFormat, type: HalfFloatType, depth: shadowMap.depth, depthBuffer: false } );
shadowMap._vsmShadowMapHorizontal.texture.name = 'VSMHorizontal';

}
Expand All @@ -425,15 +425,15 @@ class ShadowNode extends ShadowBaseNode {

let shadowPassVertical = texture( depthTexture );

if ( depthTexture.isDepthArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

shadowPassVertical = shadowPassVertical.depth( this.depthLayer );

}

let shadowPassHorizontal = texture( this.vsmShadowMapVertical.texture );

if ( depthTexture.isDepthArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

shadowPassHorizontal = shadowPassHorizontal.depth( this.depthLayer );

Expand Down Expand Up @@ -477,7 +477,7 @@ class ShadowNode extends ShadowBaseNode {

let shadowColor = texture( shadowMap.texture, shadowCoord );

if ( depthTexture.isDepthArrayTexture ) {
if ( depthTexture.isArrayTexture ) {

shadowColor = shadowColor.depth( this.depthLayer );

Expand Down
14 changes: 2 additions & 12 deletions src/renderers/common/Textures.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import DataMap from './DataMap.js';

import { Vector3 } from '../../math/Vector3.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
import { DepthArrayTexture } from '../../textures/DepthArrayTexture.js';
import { DepthStencilFormat, DepthFormat, UnsignedIntType, UnsignedInt248Type, UnsignedByteType } from '../../constants.js';

const _size = /*@__PURE__*/ new Vector3();
Expand Down Expand Up @@ -77,15 +76,7 @@ class Textures extends DataMap {

if ( depthTexture === undefined && useDepthTexture ) {

if ( renderTarget.multiview === true && size.depth > 1 ) {

depthTexture = new DepthArrayTexture();

} else {

depthTexture = new DepthTexture();

}
depthTexture = new DepthTexture();

depthTexture.format = renderTarget.stencilBuffer ? DepthStencilFormat : DepthFormat;
depthTexture.type = renderTarget.stencilBuffer ? UnsignedInt248Type : UnsignedIntType; // FloatType
Expand All @@ -106,7 +97,7 @@ class Textures extends DataMap {
depthTexture.needsUpdate = true;
depthTexture.image.width = mipWidth;
depthTexture.image.height = mipHeight;
depthTexture.image.depth = depthTexture.isDepthArrayTexture ? depthTexture.image.depth : 1;
depthTexture.image.depth = depthTexture.isArrayTexture ? depthTexture.image.depth : 1;

}

Expand Down Expand Up @@ -147,7 +138,6 @@ class Textures extends DataMap {

const texture = textures[ i ];

texture.isTextureArray = renderTarget.multiview === true && size.depth > 1;
Copy link
Collaborator

@Mugen87 Mugen87 Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fear this removal breaks multiview. When rendering in XR, the framebuffer render target is resized with a depth value larger 1.

frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 );

But since the isTextureArray /isArrayTexture is only set in the ctor, the flag isn't set correctly. So there are two options: Restore this line or make sure Texture.isArrayTexture is also set in RenderTarget.setSize() for all render target textures.

Copy link
Collaborator Author

@RenaudRohlinger RenaudRohlinger Apr 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure Texture.isArrayTexture is also set in RenderTarget.setSize() for all render target textures.

I think this option makes a lot more sense I will try.

if ( textureNeedsUpdate ) texture.needsUpdate = true;

this.updateTexture( texture, options );
Expand Down
Loading