Browse Source

WebGPURenderer: Support MSAA with Postprocessing (#28784)

* 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
Renaud Rohlinger 1 year ago
parent
commit
913edeb6b5

+ 16 - 4
src/nodes/display/PassNode.js

@@ -43,13 +43,14 @@ class PassTextureNode extends TextureNode {
 
 
 class PassNode extends TempNode {
 class PassNode extends TempNode {
 
 
-	constructor( scope, scene, camera ) {
+	constructor( scope, scene, camera, options = {} ) {
 
 
 		super( 'vec4' );
 		super( 'vec4' );
 
 
 		this.scope = scope;
 		this.scope = scope;
 		this.scene = scene;
 		this.scene = scene;
 		this.camera = camera;
 		this.camera = camera;
+		this.options = options;
 
 
 		this._pixelRatio = 1;
 		this._pixelRatio = 1;
 		this._width = 1;
 		this._width = 1;
@@ -60,7 +61,7 @@ class PassNode extends TempNode {
 		//depthTexture.type = FloatType;
 		//depthTexture.type = FloatType;
 		depthTexture.name = 'PostProcessingDepth';
 		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.texture.name = 'PostProcessing';
 		renderTarget.depthTexture = depthTexture;
 		renderTarget.depthTexture = depthTexture;
 
 
@@ -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();
 		return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getLinearDepthNode();
 
 
@@ -194,7 +206,7 @@ PassNode.DEPTH = 'depth';
 
 
 export default PassNode;
 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 texturePass = ( pass, texture ) => nodeObject( new PassTextureNode( pass, texture ) );
 export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );
 export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );
 
 

+ 6 - 3
src/renderers/common/Renderer.js

@@ -43,15 +43,18 @@ class Renderer {
 
 
 		const {
 		const {
 			logarithmicDepthBuffer = false,
 			logarithmicDepthBuffer = false,
-			alpha = true
+			alpha = true,
+			antialias = false,
+			samples = 0
 		} = parameters;
 		} = parameters;
 
 
 		// public
 		// public
-
 		this.domElement = backend.getDomElement();
 		this.domElement = backend.getDomElement();
 
 
 		this.backend = backend;
 		this.backend = backend;
 
 
+		this.samples = samples || ( antialias === true ) ? 4 : 0;
+
 		this.autoClear = true;
 		this.autoClear = true;
 		this.autoClearColor = true;
 		this.autoClearColor = true;
 		this.autoClearDepth = true;
 		this.autoClearDepth = true;
@@ -457,7 +460,7 @@ class Renderer {
 				generateMipmaps: false,
 				generateMipmaps: false,
 				minFilter: LinearFilter,
 				minFilter: LinearFilter,
 				magFilter: LinearFilter,
 				magFilter: LinearFilter,
-				samples: this.backend.parameters.antialias ? 4 : 0
+				samples: this.samples
 			} );
 			} );
 
 
 			frameBufferTarget.isPostProcessingRenderTarget = true;
 			frameBufferTarget.isPostProcessingRenderTarget = true;

+ 5 - 19
src/renderers/webgpu/WebGPUBackend.js

@@ -28,18 +28,6 @@ class WebGPUBackend extends Backend {
 		// some parameters require default values other than "undefined"
 		// some parameters require default values other than "undefined"
 		this.parameters.alpha = ( parameters.alpha === undefined ) ? true : parameters.alpha;
 		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.parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits;
 
 
 		this.trackTimestamp = ( parameters.trackTimestamp === true );
 		this.trackTimestamp = ( parameters.trackTimestamp === true );
@@ -153,8 +141,6 @@ class WebGPUBackend extends Backend {
 
 
 		let descriptor = this.defaultRenderPassdescriptor;
 		let descriptor = this.defaultRenderPassdescriptor;
 
 
-		const antialias = this.parameters.antialias;
-
 		if ( descriptor === null ) {
 		if ( descriptor === null ) {
 
 
 			const renderer = this.renderer;
 			const renderer = this.renderer;
@@ -170,7 +156,7 @@ class WebGPUBackend extends Backend {
 
 
 			const colorAttachment = descriptor.colorAttachments[ 0 ];
 			const colorAttachment = descriptor.colorAttachments[ 0 ];
 
 
-			if ( antialias === true ) {
+			if ( this.renderer.samples > 0 ) {
 
 
 				colorAttachment.view = this.colorBuffer.createView();
 				colorAttachment.view = this.colorBuffer.createView();
 
 
@@ -186,7 +172,7 @@ class WebGPUBackend extends Backend {
 
 
 		const colorAttachment = descriptor.colorAttachments[ 0 ];
 		const colorAttachment = descriptor.colorAttachments[ 0 ];
 
 
-		if ( antialias === true ) {
+		if ( this.renderer.samples > 0 ) {
 
 
 			colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();
 			colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();
 
 
@@ -269,7 +255,7 @@ class WebGPUBackend extends Backend {
 			const depthTextureData = this.get( renderContext.depthTexture );
 			const depthTextureData = this.get( renderContext.depthTexture );
 
 
 			const depthStencilAttachment = {
 			const depthStencilAttachment = {
-				view: depthTextureData.texture.createView(),
+				view: depthTextureData.texture.createView()
 			};
 			};
 
 
 			descriptor = {
 			descriptor = {
@@ -976,7 +962,7 @@ class WebGPUBackend extends Backend {
 
 
 		const utils = this.utils;
 		const utils = this.utils;
 
 
-		const sampleCount = utils.getSampleCount( renderObject.context );
+		const sampleCount = utils.getSampleCountRenderContext( renderObject.context );
 		const colorSpace = utils.getCurrentColorSpace( renderObject.context );
 		const colorSpace = utils.getCurrentColorSpace( renderObject.context );
 		const colorFormat = utils.getCurrentColorFormat( renderObject.context );
 		const colorFormat = utils.getCurrentColorFormat( renderObject.context );
 		const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
 		const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
@@ -1041,7 +1027,7 @@ class WebGPUBackend extends Backend {
 			material.stencilFail, material.stencilZFail, material.stencilZPass,
 			material.stencilFail, material.stencilZFail, material.stencilZPass,
 			material.stencilFuncMask, material.stencilWriteMask,
 			material.stencilFuncMask, material.stencilWriteMask,
 			material.side,
 			material.side,
-			utils.getSampleCount( renderContext ),
+			utils.getSampleCountRenderContext( renderContext ),
 			utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
 			utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
 			utils.getPrimitiveTopology( object, material ),
 			utils.getPrimitiveTopology( object, material ),
 			renderObject.clippingContextVersion
 			renderObject.clippingContextVersion

+ 12 - 4
src/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -241,7 +241,7 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 
 		this._include( 'repeatWrapping' );
 		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 } ) )`;
 		return `textureLoad( ${ textureProperty }, threejs_repeatWrapping( ${ uvSnippet }, ${ dimension } ), i32( ${ levelSnippet } ) )`;
 
 
@@ -269,7 +269,7 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 
 	isUnfilterable( texture ) {
 	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;
 
 
 	}
 	}
 
 
@@ -864,6 +864,14 @@ ${ flowData.code }
 
 
 				let textureType;
 				let textureType;
 
 
+				let multisampled = '';
+
+				if ( texture.isMultisampleRenderTargetTexture === true ) {
+
+					multisampled = '_multisampled';
+
+				}
+
 				if ( texture.isCubeTexture === true ) {
 				if ( texture.isCubeTexture === true ) {
 
 
 					textureType = 'texture_cube<f32>';
 					textureType = 'texture_cube<f32>';
@@ -874,7 +882,7 @@ ${ flowData.code }
 
 
 				} else if ( texture.isDepthTexture === true ) {
 				} else if ( texture.isDepthTexture === true ) {
 
 
-					textureType = 'texture_depth_2d';
+					textureType = `texture_depth${multisampled}_2d`;
 
 
 				} else if ( texture.isVideoTexture === true ) {
 				} else if ( texture.isVideoTexture === true ) {
 
 
@@ -895,7 +903,7 @@ ${ flowData.code }
 
 
 					const componentPrefix = this.getComponentTypeFromTexture( texture ).charAt( 0 );
 					const componentPrefix = this.getComponentTypeFromTexture( texture ).charAt( 0 );
 
 
-					textureType = `texture_2d<${ componentPrefix }32>`;
+					textureType = `texture${multisampled}_2d<${ componentPrefix }32>`;
 
 
 				}
 				}
 
 

+ 6 - 0
src/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -71,6 +71,12 @@ class WebGPUBindingUtils {
 
 
 				const texture = {}; // GPUTextureBindingLayout
 				const texture = {}; // GPUTextureBindingLayout
 
 
+				if ( binding.texture.isMultisampleRenderTargetTexture === true ) {
+
+					texture.multisampled = true;
+
+				}
+
 				if ( binding.texture.isDepthTexture ) {
 				if ( binding.texture.isDepthTexture ) {
 
 
 					texture.sampleType = GPUTextureSampleType.Depth;
 					texture.sampleType = GPUTextureSampleType.Depth;

+ 1 - 16
src/renderers/webgpu/utils/WebGPUPipelineUtils.js

@@ -25,22 +25,7 @@ class WebGPUPipelineUtils {
 
 
 	_getSampleCount( renderObjectContext ) {
 	_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 );
 
 
 	}
 	}
 
 

+ 5 - 16
src/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -119,20 +119,9 @@ class WebGPUTextureUtils {
 
 
 		let sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
 		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;
 		let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC;
 
 
@@ -190,7 +179,7 @@ class WebGPUTextureUtils {
 
 
 		}
 		}
 
 
-		if ( texture.isRenderTargetTexture && sampleCount > 1 ) {
+		if ( texture.isRenderTargetTexture && sampleCount > 1 && ! texture.isMultisampleRenderTargetTexture ) {
 
 
 			const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU );
 			const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU );
 
 
@@ -263,7 +252,7 @@ class WebGPUTextureUtils {
 				height: height,
 				height: height,
 				depthOrArrayLayers: 1
 				depthOrArrayLayers: 1
 			},
 			},
-			sampleCount: backend.parameters.sampleCount,
+			sampleCount: backend.utils.getSampleCount( backend.renderer.samples ),
 			format: GPUTextureFormat.BGRA8Unorm,
 			format: GPUTextureFormat.BGRA8Unorm,
 			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
 			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
 		} );
 		} );
@@ -312,7 +301,7 @@ class WebGPUTextureUtils {
 		depthTexture.image.width = width;
 		depthTexture.image.width = width;
 		depthTexture.image.height = height;
 		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;
 		return backend.get( depthTexture ).texture;
 
 

+ 24 - 3
src/renderers/webgpu/utils/WebGPUUtils.js

@@ -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 ) {
 		if ( renderContext.textures !== null ) {
 
 
-			return renderContext.sampleCount;
+			return this.getSampleCount( renderContext.sampleCount );
 
 
 		}
 		}
 
 
-		return this.backend.parameters.sampleCount;
+		return this.getSampleCount( this.backend.renderer.samples );
 
 
 	}
 	}