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: Support MSAA with Postprocessing #28784

Merged
merged 12 commits into from
Jul 4, 2024
22 changes: 17 additions & 5 deletions src/nodes/display/PassNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { nodeObject } from '../shadernode/ShaderNode.js';
import { uniform } from '../core/UniformNode.js';
import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';

import { HalfFloatType/*, FloatType*/ } from '../../constants.js';
import { FloatType, HalfFloatType/*, FloatType*/ } from '../../constants.js';
import { Vector2 } from '../../math/Vector2.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
import { RenderTarget } from '../../core/RenderTarget.js';
Expand Down Expand Up @@ -43,13 +43,14 @@ class PassTextureNode extends TextureNode {

class PassNode extends TempNode {

constructor( scope, scene, camera ) {
constructor( scope, scene, camera, options = {} ) {

super( 'vec4' );

this.scope = scope;
this.scene = scene;
this.camera = camera;
this.options = options;

this._pixelRatio = 1;
this._width = 1;
Expand All @@ -60,7 +61,7 @@ class PassNode extends TempNode {
//depthTexture.type = FloatType;
depthTexture.name = 'PostProcessingDepth';

const renderTarget = new RenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType } );
const renderTarget = new RenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType, ...options } );
renderTarget.texture.name = 'PostProcessing';
renderTarget.depthTexture = depthTexture;

Expand Down Expand Up @@ -130,7 +131,18 @@ class PassNode extends TempNode {

}

setup() {
setup( { renderer } ) {

this.renderTarget.samples = this.options.samples === undefined ? renderer.samples : this.options.samples;

this.renderTarget.depthTexture.isMultisampleRenderTargetTexture = this.renderTarget.samples > 1;

for ( let i = 0; i < this.renderTarget.textures.length; i ++ ) {

const type = this.renderTarget.textures[ i ].type;
this.renderTarget.textures[ i ].isMultisampleRenderTargetTexture = this.renderTarget.samples > 1 && type !== HalfFloatType && type !== FloatType;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the purpose of type !== HalfFloatType && type !== FloatType?

Does that mean multisampled (half) float textures are not allowed?


}

return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getLinearDepthNode();

Expand Down Expand Up @@ -194,7 +206,7 @@ PassNode.DEPTH = 'depth';

export default PassNode;

export const pass = ( scene, camera ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera ) );
export const pass = ( scene, camera, options ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera, options ) );
export const texturePass = ( pass, texture ) => nodeObject( new PassTextureNode( pass, texture ) );
export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );

Expand Down
9 changes: 6 additions & 3 deletions src/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,18 @@ class Renderer {

const {
logarithmicDepthBuffer = false,
alpha = true
alpha = true,
antialias = false,
samples = 0
} = parameters;

// public

this.domElement = backend.getDomElement();

this.backend = backend;

this.samples = samples || ( antialias === true ) ? 4 : 0;

this.autoClear = true;
this.autoClearColor = true;
this.autoClearDepth = true;
Expand Down Expand Up @@ -456,7 +459,7 @@ class Renderer {
generateMipmaps: false,
minFilter: LinearFilter,
magFilter: LinearFilter,
samples: this.backend.parameters.antialias ? 4 : 0
samples: this.samples
} );

frameBufferTarget.isPostProcessingRenderTarget = true;
Expand Down
41 changes: 22 additions & 19 deletions src/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,6 @@ class WebGPUBackend extends Backend {
// some parameters require default values other than "undefined"
this.parameters.alpha = ( parameters.alpha === undefined ) ? true : parameters.alpha;

this.parameters.antialias = ( parameters.antialias === true );

if ( this.parameters.antialias === true ) {

this.parameters.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount;

} else {

this.parameters.sampleCount = 1;

}

this.parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits;

this.trackTimestamp = ( parameters.trackTimestamp === true );
Expand Down Expand Up @@ -153,8 +141,6 @@ class WebGPUBackend extends Backend {

let descriptor = this.defaultRenderPassdescriptor;

const antialias = this.parameters.antialias;

if ( descriptor === null ) {

const renderer = this.renderer;
Expand All @@ -170,7 +156,7 @@ class WebGPUBackend extends Backend {

const colorAttachment = descriptor.colorAttachments[ 0 ];

if ( antialias === true ) {
if ( this.renderer.samples > 0 ) {

colorAttachment.view = this.colorBuffer.createView();

Expand All @@ -186,7 +172,7 @@ class WebGPUBackend extends Backend {

const colorAttachment = descriptor.colorAttachments[ 0 ];

if ( antialias === true ) {
if ( this.renderer.samples > 0 ) {

colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();

Expand Down Expand Up @@ -268,8 +254,25 @@ class WebGPUBackend extends Backend {

const depthTextureData = this.get( renderContext.depthTexture );

const depthTextureView = depthTextureData.texture.createView();

let viewDepth, resolveTargetDepth;

if ( depthTextureData.msaaTexture !== undefined ) {

viewDepth = depthTextureData.msaaTexture.createView();
resolveTargetDepth = depthTextureView;

} else {

viewDepth = depthTextureView;
resolveTargetDepth = undefined;

}

const depthStencilAttachment = {
view: depthTextureData.texture.createView(),
view: viewDepth,
resolveTarget: resolveTargetDepth,
};

descriptor = {
Expand Down Expand Up @@ -976,7 +979,7 @@ class WebGPUBackend extends Backend {

const utils = this.utils;

const sampleCount = utils.getSampleCount( renderObject.context );
const sampleCount = utils.getSampleCountRenderContext( renderObject.context );
const colorSpace = utils.getCurrentColorSpace( renderObject.context );
const colorFormat = utils.getCurrentColorFormat( renderObject.context );
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
Expand Down Expand Up @@ -1041,7 +1044,7 @@ class WebGPUBackend extends Backend {
material.stencilFail, material.stencilZFail, material.stencilZPass,
material.stencilFuncMask, material.stencilWriteMask,
material.side,
utils.getSampleCount( renderContext ),
utils.getSampleCountRenderContext( renderContext ),
utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
utils.getPrimitiveTopology( object, material ),
renderObject.clippingContextVersion
Expand Down
16 changes: 12 additions & 4 deletions src/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class WGSLNodeBuilder extends NodeBuilder {

this._include( 'repeatWrapping' );

const dimension = `textureDimensions( ${ textureProperty }, 0 )`;
const dimension = texture.isMultisampleRenderTargetTexture === true ? `textureDimensions( ${ textureProperty } )` : `textureDimensions( ${ textureProperty }, 0 )`;

return `textureLoad( ${ textureProperty }, threejs_repeatWrapping( ${ uvSnippet }, ${ dimension } ), i32( ${ levelSnippet } ) )`;

Expand Down Expand Up @@ -269,7 +269,7 @@ class WGSLNodeBuilder extends NodeBuilder {

isUnfilterable( texture ) {

return this.getComponentTypeFromTexture( texture ) !== 'float' || ( texture.isDataTexture === true && texture.type === FloatType );
return this.getComponentTypeFromTexture( texture ) !== 'float' || ( texture.isDataTexture === true && texture.type === FloatType ) || texture.isMultisampleRenderTargetTexture === true;

}

Expand Down Expand Up @@ -864,6 +864,14 @@ ${ flowData.code }

let textureType;

let multisampled = '';

if ( texture.isMultisampleRenderTargetTexture === true ) {

multisampled = '_multisampled';

}

if ( texture.isCubeTexture === true ) {

textureType = 'texture_cube<f32>';
Expand All @@ -874,7 +882,7 @@ ${ flowData.code }

} else if ( texture.isDepthTexture === true ) {

textureType = 'texture_depth_2d';
textureType = `texture_depth${multisampled}_2d`;

} else if ( texture.isVideoTexture === true ) {

Expand All @@ -895,7 +903,7 @@ ${ flowData.code }

const componentPrefix = this.getComponentTypeFromTexture( texture ).charAt( 0 );

textureType = `texture_2d<${ componentPrefix }32>`;
textureType = `texture${multisampled}_2d<${ componentPrefix }32>`;

}

Expand Down
12 changes: 12 additions & 0 deletions src/renderers/webgpu/utils/WebGPUBindingUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ class WebGPUBindingUtils {

const texture = {}; // GPUTextureBindingLayout

if ( binding.texture.isMultisampleRenderTargetTexture ) {

texture.multisampled = true;

if ( ! binding.texture.isDepthTexture ) {

texture.sampleType = GPUTextureSampleType.UnfilterableFloat;

}

}

if ( binding.texture.isDepthTexture ) {

texture.sampleType = GPUTextureSampleType.Depth;
Expand Down
17 changes: 1 addition & 16 deletions src/renderers/webgpu/utils/WebGPUPipelineUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,7 @@ class WebGPUPipelineUtils {

_getSampleCount( renderObjectContext ) {

let sampleCount = this.backend.utils.getSampleCount( renderObjectContext );

if ( sampleCount > 1 ) {

// WebGPU only supports power-of-two sample counts and 2 is not a valid value
sampleCount = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) );

if ( sampleCount === 2 ) {

sampleCount = 4;

}

}

return sampleCount;
return this.backend.utils.getSampleCountRenderContext( renderObjectContext );

}

Expand Down
21 changes: 5 additions & 16 deletions src/renderers/webgpu/utils/WebGPUTextureUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,9 @@ class WebGPUTextureUtils {

let sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;

if ( sampleCount > 1 ) {
sampleCount = backend.utils.getSampleCount( sampleCount );

// WebGPU only supports power-of-two sample counts and 2 is not a valid value
sampleCount = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) );

if ( sampleCount === 2 ) {

sampleCount = 4;

}

}

const primarySampleCount = texture.isRenderTargetTexture ? 1 : sampleCount;
const primarySampleCount = texture.isRenderTargetTexture && ! texture.isMultisampleRenderTargetTexture ? 1 : sampleCount;

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

Expand Down Expand Up @@ -190,7 +179,7 @@ class WebGPUTextureUtils {

}

if ( texture.isRenderTargetTexture && sampleCount > 1 ) {
if ( texture.isRenderTargetTexture && sampleCount > 1 && ! texture.isMultisampleRenderTargetTexture ) {

const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU );

Expand Down Expand Up @@ -263,7 +252,7 @@ class WebGPUTextureUtils {
height: height,
depthOrArrayLayers: 1
},
sampleCount: backend.parameters.sampleCount,
sampleCount: backend.utils.getSampleCount( backend.renderer.samples ),
format: GPUTextureFormat.BGRA8Unorm,
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
} );
Expand Down Expand Up @@ -312,7 +301,7 @@ class WebGPUTextureUtils {
depthTexture.image.width = width;
depthTexture.image.height = height;

this.createTexture( depthTexture, { sampleCount: backend.parameters.sampleCount, width, height } );
this.createTexture( depthTexture, { sampleCount: backend.utils.getSampleCount( backend.renderer.samples ), width, height } );

return backend.get( depthTexture ).texture;

Expand Down
27 changes: 24 additions & 3 deletions src/renderers/webgpu/utils/WebGPUUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,36 @@ class WebGPUUtils {

}

getSampleCount( renderContext ) {
getSampleCount( sampleCount ) {

let count = 1;

if ( sampleCount > 1 ) {

// WebGPU only supports power-of-two sample counts and 2 is not a valid value
count = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) );

if ( count === 2 ) {

count = 4;

}

}

return count;

}

getSampleCountRenderContext( renderContext ) {

if ( renderContext.textures !== null ) {

return renderContext.sampleCount;
return this.getSampleCount( renderContext.sampleCount );

}

return this.backend.parameters.sampleCount;
return this.getSampleCount( this.backend.renderer.samples );

}

Expand Down
Loading