Skip to content

Commit

Permalink
WebGPURenderer: Support MSAA with Postprocessing (mrdoob#28784)
Browse files Browse the repository at this point in the history
* WebGPURenderer: Support MSAA with Postprocessing

* cleanup

* cleanup and revert 3dlut

* refactor AA and samples logic

* add multisampled_texture support

* cleanup

* more cleanup

* temporary patch webgl backend

* fix

* feedbacks

* resolveTarget doesn't exist for depth textures
  • Loading branch information
RenaudRohlinger committed Jul 4, 2024
1 parent 2f900fc commit 913edeb
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 65 deletions.
20 changes: 16 additions & 4 deletions src/nodes/display/PassNode.js
Original file line number Diff line number Diff line change
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;

// Disable MSAA for WebGL backend for now
if ( renderer.backend.isWebGLBackend === true ) {

this.renderTarget.samples = 0;

}

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

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 @@ -43,15 +43,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 @@ -457,7 +460,7 @@ class Renderer {
generateMipmaps: false,
minFilter: LinearFilter,
magFilter: LinearFilter,
samples: this.backend.parameters.antialias ? 4 : 0
samples: this.samples
} );

frameBufferTarget.isPostProcessingRenderTarget = true;
Expand Down
24 changes: 5 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 @@ -269,7 +255,7 @@ class WebGPUBackend extends Backend {
const depthTextureData = this.get( renderContext.depthTexture );

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

descriptor = {
Expand Down Expand Up @@ -976,7 +962,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 +1027,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
6 changes: 6 additions & 0 deletions src/renderers/webgpu/utils/WebGPUBindingUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ class WebGPUBindingUtils {

const texture = {}; // GPUTextureBindingLayout

if ( binding.texture.isMultisampleRenderTargetTexture === true ) {

texture.multisampled = true;

}

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

0 comments on commit 913edeb

Please sign in to comment.