From 2d1621f76a92a5f17527cc0c1d4a9be546ab6416 Mon Sep 17 00:00:00 2001 From: sunag Date: Sat, 22 Apr 2023 12:51:41 -0300 Subject: [PATCH] WebGPURenderer: Backdrop Node - Part 1/3 (#25903) * WebGPURenderer: Fix re-configure context * WebGPURenderer: Improve state in favor of access WebGPU encoders * NodeMaterial: Reduce duplicate initialization of properties * WebGPUTextures: Support RenderTargetTexture mipmaps if necessary. * WebGPUBackground: Move to renderState * WebGPUNodes: .updateBefore() fixes * WebGPURenderer: Added .copyFramebufferToRenderTarget() * NodeMaterial: Added backdropNode * Added webgpu_backdrop example * update title * cleanup * rotate just on idle * WebGPURenderList: Update include --- examples/files.json | 1 + examples/jsm/nodes/Nodes.js | 2 + examples/jsm/nodes/core/NodeBuilder.js | 8 + examples/jsm/nodes/display/ViewportNode.js | 2 +- .../display/ViewportSharedTextureNode.js | 30 ++++ .../jsm/nodes/display/ViewportTextureNode.js | 61 +++++++ .../jsm/nodes/lighting/LightingContextNode.js | 21 ++- .../nodes/materials/LineBasicNodeMaterial.js | 9 - .../nodes/materials/MeshBasicNodeMaterial.js | 9 - .../nodes/materials/MeshNormalNodeMaterial.js | 4 - .../nodes/materials/MeshPhongNodeMaterial.js | 9 - .../materials/MeshStandardNodeMaterial.js | 13 -- examples/jsm/nodes/materials/NodeMaterial.js | 24 ++- .../jsm/renderers/webgpu/WebGPUBackground.js | 14 +- .../jsm/renderers/webgpu/WebGPURenderLists.js | 23 +++ .../renderers/webgpu/WebGPURenderStates.js | 26 +-- .../jsm/renderers/webgpu/WebGPURenderer.js | 165 +++++++++-------- .../jsm/renderers/webgpu/WebGPUTextures.js | 25 ++- .../jsm/renderers/webgpu/nodes/WebGPUNodes.js | 17 +- examples/screenshots/webgpu_backdrop.jpg | Bin 0 -> 3413 bytes examples/webgpu_backdrop.html | 166 ++++++++++++++++++ 21 files changed, 464 insertions(+), 165 deletions(-) create mode 100644 examples/jsm/nodes/display/ViewportSharedTextureNode.js create mode 100644 examples/jsm/nodes/display/ViewportTextureNode.js create mode 100644 examples/screenshots/webgpu_backdrop.jpg create mode 100644 examples/webgpu_backdrop.html diff --git a/examples/files.json b/examples/files.json index f665a98f3ea476..efd9ffe0b5d952 100644 --- a/examples/files.json +++ b/examples/files.json @@ -327,6 +327,7 @@ ], "webgpu": [ "webgpu_audio_processing", + "webgpu_backdrop", "webgpu_compute", "webgpu_cubemap_adjustments", "webgpu_cubemap_mix", diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index 3c25c501337b94..2ba8263f8d114b 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -93,6 +93,8 @@ export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/No export { default as PosterizeNode, posterize } from './display/PosterizeNode.js'; export { default as ToneMappingNode, toneMapping } from './display/ToneMappingNode.js'; export { default as ViewportNode, viewportCoordinate, viewportResolution, viewportTopLeft, viewportBottomLeft, viewportTopRight, viewportBottomRight } from './display/ViewportNode.js'; +export { default as ViewportTextureNode, viewportTexture } from './display/ViewportTextureNode.js'; +export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js'; // code export { default as ExpressionNode, expression } from './code/ExpressionNode.js'; diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index b7c588af239ee6..25395b1023917e 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -39,6 +39,7 @@ class NodeBuilder { this.nodes = []; this.updateNodes = []; + this.updateBeforeNodes = []; this.hashNodes = {}; this.lightsNode = null; @@ -89,6 +90,7 @@ class NodeBuilder { if ( this.nodes.indexOf( node ) === - 1 ) { const updateType = node.getUpdateType(); + const updateBeforeType = node.getUpdateBeforeType(); if ( updateType !== NodeUpdateType.NONE ) { @@ -96,6 +98,12 @@ class NodeBuilder { } + if ( updateBeforeType !== NodeUpdateType.NONE ) { + + this.updateBeforeNodes.push( node ); + + } + this.nodes.push( node ); this.setHashNode( node, node.getHash( this ) ); diff --git a/examples/jsm/nodes/display/ViewportNode.js b/examples/jsm/nodes/display/ViewportNode.js index 572253afd958ef..faf013a5a5844b 100644 --- a/examples/jsm/nodes/display/ViewportNode.js +++ b/examples/jsm/nodes/display/ViewportNode.js @@ -43,7 +43,7 @@ class ViewportNode extends Node { update( { renderer } ) { - renderer.getSize( resolution ); + renderer.getDrawingBufferSize( resolution ); } diff --git a/examples/jsm/nodes/display/ViewportSharedTextureNode.js b/examples/jsm/nodes/display/ViewportSharedTextureNode.js new file mode 100644 index 00000000000000..395a3daeb2a871 --- /dev/null +++ b/examples/jsm/nodes/display/ViewportSharedTextureNode.js @@ -0,0 +1,30 @@ +import ViewportTextureNode from './ViewportTextureNode.js'; +import { addNodeClass } from '../core/Node.js'; +import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; +import { viewportTopLeft } from './ViewportNode.js'; + +let rtt = null; + +class ViewportSharedTextureNode extends ViewportTextureNode { + + constructor( uv = viewportTopLeft ) { + + super( uv ); + + } + + constructRTT( builder ) { + + return rtt || ( rtt = builder.getRenderTarget() ); + + } + +} + +export default ViewportSharedTextureNode; + +export const viewportSharedTexture = nodeProxy( ViewportSharedTextureNode ); + +addNodeElement( 'viewportSharedTexture', viewportSharedTexture ); + +addNodeClass( ViewportSharedTextureNode ); diff --git a/examples/jsm/nodes/display/ViewportTextureNode.js b/examples/jsm/nodes/display/ViewportTextureNode.js new file mode 100644 index 00000000000000..57a1d6308b287f --- /dev/null +++ b/examples/jsm/nodes/display/ViewportTextureNode.js @@ -0,0 +1,61 @@ +import TextureNode from '../accessors/TextureNode.js'; +import { NodeUpdateType } from '../core/constants.js'; +import { addNodeClass } from '../core/Node.js'; +import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; +import { viewportTopLeft } from './ViewportNode.js'; +import { Vector2 } from 'three'; + +let size = new Vector2(); + +class ViewportTextureNode extends TextureNode { + + constructor( uv = viewportTopLeft, level = null ) { + + super( null, uv, level ); + + this.rtt = null; + + this.isOutputTextureNode = true; + + this.updateBeforeType = NodeUpdateType.FRAME; + + } + + constructRTT( builder ) { + + return builder.getRenderTarget(); + + } + + construct( builder ) { + + if ( this.rtt === null ) this.rtt = this.constructRTT( builder ); + + this.value = this.rtt.texture; + + return super.construct( builder ); + + } + + updateBefore( frame ) { + + const rtt = this.rtt; + + const renderer = frame.renderer; + renderer.getDrawingBufferSize( size ); + + rtt.setSize( size.width, size.height ); + + renderer.copyFramebufferToRenderTarget( rtt ); + + } + +} + +export default ViewportTextureNode; + +export const viewportTexture = nodeProxy( ViewportTextureNode ); + +addNodeElement( 'viewportTexture', viewportTexture ); + +addNodeClass( ViewportTextureNode ); diff --git a/examples/jsm/nodes/lighting/LightingContextNode.js b/examples/jsm/nodes/lighting/LightingContextNode.js index ef18e9e150161b..c5a74e6c793442 100644 --- a/examples/jsm/nodes/lighting/LightingContextNode.js +++ b/examples/jsm/nodes/lighting/LightingContextNode.js @@ -1,16 +1,19 @@ import ContextNode from '../core/ContextNode.js'; import { temp } from '../core/VarNode.js'; import { add } from '../math/OperatorNode.js'; +import { mix } from '../math/MathNode.js'; import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeProxy, float, vec3 } from '../shadernode/ShaderNode.js'; class LightingContextNode extends ContextNode { - constructor( node, lightingModelNode = null ) { + constructor( node, lightingModelNode = null, backdropNode = null, backdropAlphaNode = null ) { super( node ); this.lightingModelNode = lightingModelNode; + this.backdropNode = backdropNode; + this.backdropAlphaNode = backdropAlphaNode; } @@ -22,7 +25,7 @@ class LightingContextNode extends ContextNode { construct( builder ) { - const { lightingModelNode } = this; + const { lightingModelNode, backdropNode, backdropAlphaNode } = this; const context = this.context = {}; // reset context const properties = builder.getNodeProperties( this ); @@ -30,8 +33,18 @@ class LightingContextNode extends ContextNode { const directDiffuse = temp( vec3() ), directSpecular = temp( vec3() ), indirectDiffuse = temp( vec3() ), - indirectSpecular = temp( vec3() ), - total = add( directDiffuse, directSpecular, indirectDiffuse, indirectSpecular ); + indirectSpecular = temp( vec3() ); + + let totalDiffuse = add( directDiffuse, indirectDiffuse ); + + if ( backdropNode !== null ) { + + totalDiffuse = vec3( backdropAlphaNode !== null ? mix( totalDiffuse, backdropNode, backdropAlphaNode ) : backdropNode ); + + } + + const totalSpecular = add( directSpecular, indirectSpecular ); + const total = add( totalDiffuse, totalSpecular ); const reflectedLight = { directDiffuse, diff --git a/examples/jsm/nodes/materials/LineBasicNodeMaterial.js b/examples/jsm/nodes/materials/LineBasicNodeMaterial.js index 5d67a89d1eb29d..a946c742799765 100644 --- a/examples/jsm/nodes/materials/LineBasicNodeMaterial.js +++ b/examples/jsm/nodes/materials/LineBasicNodeMaterial.js @@ -15,15 +15,6 @@ class LineBasicNodeMaterial extends NodeMaterial { this.lights = false; this.normals = false; - this.colorNode = null; - this.opacityNode = null; - - this.alphaTestNode = null; - - this.lightNode = null; - - this.positionNode = null; - this.setDefaultValues( defaultValues ); this.setValues( parameters ); diff --git a/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js b/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js index 4440049e9a2713..f4d6866b4c51a4 100644 --- a/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js @@ -14,15 +14,6 @@ class MeshBasicNodeMaterial extends NodeMaterial { this.lights = false; - this.colorNode = null; - this.opacityNode = null; - - this.alphaTestNode = null; - - this.lightNode = null; - - this.positionNode = null; - this.setDefaultValues( defaultValues ); this.setValues( parameters ); diff --git a/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js b/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js index 9710f3f47f841a..b2574ed3f66c4d 100644 --- a/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js @@ -17,10 +17,6 @@ class MeshNormalNodeMaterial extends NodeMaterial { this.isMeshNormalNodeMaterial = true; - this.opacityNode = null; - - this.positionNode = null; - this.setDefaultValues( defaultValues ); this.setValues( parameters ); diff --git a/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js b/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js index 6ae1fb6f5a0f69..f87436598fcdf1 100644 --- a/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js @@ -18,18 +18,9 @@ class MeshPhongNodeMaterial extends NodeMaterial { this.lights = true; - this.colorNode = null; - this.opacityNode = null; - this.shininessNode = null; this.specularNode = null; - this.alphaTestNode = null; - - this.lightNode = null; - - this.positionNode = null; - this.setDefaultValues( defaultValues ); this.setValues( parameters ); diff --git a/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js b/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js index 23993e114fff80..bd156909cff656 100644 --- a/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js @@ -18,24 +18,11 @@ class MeshStandardNodeMaterial extends NodeMaterial { this.isMeshStandardNodeMaterial = true; - this.colorNode = null; - this.opacityNode = null; - - this.alphaTestNode = null; - - this.normalNode = null; - this.emissiveNode = null; this.metalnessNode = null; this.roughnessNode = null; - this.envNode = null; - - this.lightsNode = null; - - this.positionNode = null; - this.setDefaultValues( defaultValues ); this.setValues( parameters ); diff --git a/examples/jsm/nodes/materials/NodeMaterial.js b/examples/jsm/nodes/materials/NodeMaterial.js index e785209d17e74e..5e1555e9908dc6 100644 --- a/examples/jsm/nodes/materials/NodeMaterial.js +++ b/examples/jsm/nodes/materials/NodeMaterial.js @@ -11,9 +11,10 @@ import { positionLocal } from '../accessors/PositionNode.js'; import { skinning } from '../accessors/SkinningNode.js'; import { texture } from '../accessors/TextureNode.js'; import { lightsWithoutWrap } from '../lighting/LightsNode.js'; +import { mix } from '../math/MathNode.js'; +import { float, vec3, vec4 } from '../shadernode/ShaderNode.js'; import AONode from '../lighting/AONode.js'; import EnvironmentNode from '../lighting/EnvironmentNode.js'; -import { float, vec3, vec4 } from '../shadernode/ShaderNode.js'; const NodeMaterials = new Map(); @@ -31,6 +32,16 @@ class NodeMaterial extends ShaderMaterial { this.normals = true; this.lightsNode = null; + this.envNode = null; + + this.colorNode = null; + this.normalNode = null; + this.opacityNode = null; + this.backdropNode = null; + this.backdropAlphaNode = null; + this.alphaTestNode = null; + + this.positionNode = null; } @@ -193,6 +204,7 @@ class NodeMaterial extends ShaderMaterial { constructLighting( builder ) { const { material } = builder; + const { backdropNode, backdropAlphaNode, emissiveNode } = this; // OUTGOING LIGHT @@ -205,15 +217,19 @@ class NodeMaterial extends ShaderMaterial { if ( lightsNode && lightsNode.hasLight !== false ) { - outgoingLightNode = lightsNode.lightingContext( lightingModelNode ); + outgoingLightNode = lightsNode.lightingContext( lightingModelNode, backdropNode, backdropAlphaNode ); + + } else if ( backdropNode !== null ) { + + outgoingLightNode = vec3( backdropAlphaNode !== null ? mix( outgoingLightNode, backdropNode, backdropAlphaNode ) : backdropNode ); } // EMISSIVE - if ( ( this.emissiveNode && this.emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) { + if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) { - outgoingLightNode = outgoingLightNode.add( this.emissiveNode ? vec3( this.emissiveNode ) : materialEmissive ); + outgoingLightNode = outgoingLightNode.add( emissiveNode ? vec3( emissiveNode ) : materialEmissive ); } diff --git a/examples/jsm/renderers/webgpu/WebGPUBackground.js b/examples/jsm/renderers/webgpu/WebGPUBackground.js index c211ddfa0cd1d9..cd86150933f982 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackground.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackground.js @@ -25,7 +25,7 @@ class WebGPUBackground { } - update( renderList, scene, renderPassDescriptor, renderAttachments ) { + update( scene, renderList, renderState ) { const renderer = this.renderer; const background = ( scene.isScene === true ) ? scene.backgroundNode || this.properties.get( scene ).backgroundNode || scene.background : null; @@ -103,8 +103,8 @@ class WebGPUBackground { // configure render pass descriptor - const colorAttachment = renderPassDescriptor.colorAttachments[ 0 ]; - const depthStencilAttachment = renderPassDescriptor.depthStencilAttachment; + const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ]; + const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment; if ( renderer.autoClear === true || forceClear === true ) { @@ -123,7 +123,7 @@ class WebGPUBackground { } - if ( renderAttachments.depth ) { + if ( renderState.depth ) { if ( renderer.autoClearDepth === true ) { @@ -140,7 +140,7 @@ class WebGPUBackground { } - if ( renderAttachments.stencil ) { + if ( renderState.stencil ) { if ( renderer.autoClearStencil === true ) { @@ -162,14 +162,14 @@ class WebGPUBackground { colorAttachment.loadOp = GPULoadOp.Load; colorAttachment.storeOp = GPUStoreOp.Store; - if ( renderAttachments.depth ) { + if ( renderState.depth ) { depthStencilAttachment.depthLoadOp = GPULoadOp.Load; depthStencilAttachment.depthStoreOp = GPUStoreOp.Store; } - if ( renderAttachments.stencil ) { + if ( renderState.stencil ) { depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderLists.js b/examples/jsm/renderers/webgpu/WebGPURenderLists.js index 98fd2adc09f6ea..79d693f0e8260f 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderLists.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderLists.js @@ -1,4 +1,5 @@ import WebGPUWeakMap from './WebGPUWeakMap.js'; +import { lights } from '../../nodes/Nodes.js'; function painterSortStable( a, b ) { @@ -58,6 +59,9 @@ class WebGPURenderList { this.opaque = []; this.transparent = []; + this.lightsNode = lights( [] ); + this.lightsArray = []; + } init() { @@ -66,6 +70,9 @@ class WebGPURenderList { this.opaque.length = 0; this.transparent.length = 0; + this.lightsArray.length = 0; + + return this; } @@ -123,6 +130,18 @@ class WebGPURenderList { } + pushLight( light ) { + + this.lightsArray.push( light ); + + } + + getLightsNode() { + + return this.lightsNode.fromLights( this.lightsArray ); + + } + sort( customOpaqueSort, customTransparentSort ) { if ( this.opaque.length > 1 ) this.opaque.sort( customOpaqueSort || painterSortStable ); @@ -132,6 +151,10 @@ class WebGPURenderList { finish() { + // update lights + + this.lightsNode.fromLights( this.lightsArray ); + // Clear references from inactive renderItems in the list for ( let i = this.renderItemsIndex, il = this.renderItems.length; i < il; i ++ ) { diff --git a/examples/jsm/renderers/webgpu/WebGPURenderStates.js b/examples/jsm/renderers/webgpu/WebGPURenderStates.js index 771abfa2723643..21c5d926a66b69 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderStates.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderStates.js @@ -1,31 +1,17 @@ import WebGPUWeakMap from './WebGPUWeakMap.js'; -import { lights } from '../../nodes/Nodes.js'; class WebGPURenderState { constructor() { - this.lightsNode = lights( [] ); + this.depth = true; + this.stencil = true; - this.lightsArray = []; + // defined by renderer(backend) - } - - init() { - - this.lightsArray.length = 0; - - } - - pushLight( light ) { - - this.lightsArray.push( light ); - - } - - getLightsNode() { - - return this.lightsNode.fromLights( this.lightsArray ); + this.descriptorGPU = null; + this.encoderGPU = null; + this.currentPassGPU = null; } diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 2f0df2632e10cd..031b3109b1817d 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -1,4 +1,4 @@ -import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName } from './constants.js'; +import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName, GPULoadOp } from './constants.js'; import WebGPUAnimation from './WebGPUAnimation.js'; import WebGPURenderObjects from './WebGPURenderObjects.js'; import WebGPUAttributes from './WebGPUAttributes.js'; @@ -14,7 +14,6 @@ import WebGPUTextures from './WebGPUTextures.js'; import WebGPUBackground from './WebGPUBackground.js'; import WebGPUNodes from './nodes/WebGPUNodes.js'; import WebGPUUtils from './WebGPUUtils.js'; - import { Frustum, Matrix4, Vector3, Color, SRGBColorSpace, NoToneMapping, DepthFormat } from 'three'; console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' ); @@ -140,7 +139,6 @@ class WebGPURenderer { this._currentRenderState = null; - this._currentRenderList = null; this._opaqueSort = null; this._transparentSort = null; @@ -224,16 +222,12 @@ class WebGPURenderer { const context = ( parameters.context !== undefined ) ? parameters.context : this.domElement.getContext( 'webgpu' ); - context.configure( { - device, - format: GPUTextureFormat.BGRA8Unorm, // this is the only valid context format right now (r121) - alphaMode: 'premultiplied' - } ); - this._adapter = adapter; this._device = device; this._context = context; + this._configureContext(); + this._info = new WebGPUInfo(); this._properties = new WebGPUProperties(); this._attributes = new WebGPUAttributes( device ); @@ -265,13 +259,24 @@ class WebGPURenderer { if ( this._initialized === false ) await this.init(); - // + // preserve render tree const nodeFrame = this._nodes.nodeFrame; const previousRenderId = nodeFrame.renderId; + const previousRenderState = this._currentRenderState; + + // + + const renderState = this._renderStates.get( scene, camera ); + const renderTarget = this._renderTarget; + + this._currentRenderState = renderState; + nodeFrame.renderId ++; + // + if ( this._animation.isAnimating === false ) nodeFrame.update(); if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); @@ -283,25 +288,22 @@ class WebGPURenderer { _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromProjectionMatrix( _projScreenMatrix ); - this._currentRenderList = this._renderLists.get( scene, camera ); - this._currentRenderList.init(); - - this._currentRenderState = this._renderStates.get( scene, camera ); - this._currentRenderState.init(); + const renderList = this._renderLists.get( scene, camera ); + renderList.init(); - this._projectObject( scene, camera, 0 ); + this._projectObject( scene, camera, 0, renderList ); - this._currentRenderList.finish(); + renderList.finish(); if ( this.sortObjects === true ) { - this._currentRenderList.sort( this._opaqueSort, this._transparentSort ); + renderList.sort( this._opaqueSort, this._transparentSort ); } // prepare render pass descriptor - const renderPassDescriptor = { + renderState.descriptorGPU = { colorAttachments: [ { view: null } ], @@ -310,15 +312,8 @@ class WebGPURenderer { } }; - const renderAttachments = { - depth: true, - stencil: true - }; - - const colorAttachment = renderPassDescriptor.colorAttachments[ 0 ]; - const depthStencilAttachment = renderPassDescriptor.depthStencilAttachment; - - const renderTarget = this._renderTarget; + const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ]; + const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment; if ( renderTarget !== null ) { @@ -331,7 +326,7 @@ class WebGPURenderer { colorAttachment.view = renderTargetProperties.colorTextureGPU.createView(); depthStencilAttachment.view = renderTargetProperties.depthTextureGPU.createView(); - renderAttachments.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true; + renderState.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true; } else { @@ -360,13 +355,14 @@ class WebGPURenderer { // - this._background.update( this._currentRenderList, scene, renderPassDescriptor, renderAttachments ); + this._background.update( scene, renderList, renderState ); // start render pass const device = this._device; - const cmdEncoder = device.createCommandEncoder( {} ); - const passEncoder = cmdEncoder.beginRenderPass( renderPassDescriptor ); + + renderState.encoderGPU = device.createCommandEncoder( {} ); + renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU ); // global rasterization settings for all renderable objects @@ -377,7 +373,7 @@ class WebGPURenderer { const width = Math.floor( vp.width * this._pixelRatio ); const height = Math.floor( vp.height * this._pixelRatio ); - passEncoder.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth ); + renderState.currentPassGPU.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth ); } @@ -388,28 +384,29 @@ class WebGPURenderer { const width = Math.floor( sc.width * this._pixelRatio ); const height = Math.floor( sc.height * this._pixelRatio ); - passEncoder.setScissorRect( sc.x, sc.y, width, height ); + renderState.currentPassGPU.setScissorRect( sc.x, sc.y, width, height ); } - // lights node - - const lightsNode = this._currentRenderState.getLightsNode(); - // process render lists - const opaqueObjects = this._currentRenderList.opaque; - const transparentObjects = this._currentRenderList.transparent; + const opaqueObjects = renderList.opaque; + const transparentObjects = renderList.transparent; + const lightsNode = renderList.lightsNode; - if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode, passEncoder ); - if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode, passEncoder ); + if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode, renderState ); + if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode, renderState ); // finish render pass - passEncoder.end(); - device.queue.submit( [ cmdEncoder.finish() ] ); + renderState.currentPassGPU.end(); + + device.queue.submit( [ renderState.encoderGPU.finish() ] ); + + // restore render tree nodeFrame.renderId = previousRenderId; + this._currentRenderState = previousRenderState; } @@ -544,6 +541,42 @@ class WebGPURenderer { } + copyFramebufferToRenderTarget( renderTarget ) { + + const renderState = this._currentRenderState; + const { encoderGPU, descriptorGPU } = renderState; + + const texture = renderTarget.texture; + texture.internalFormat = GPUTextureFormat.BGRA8Unorm; + + this._textures.initRenderTarget( renderTarget ); + + const sourceGPU = this._context.getCurrentTexture(); + const destinationGPU = this._textures.getTextureGPU( texture ); + + renderState.currentPassGPU.end(); + + encoderGPU.copyTextureToTexture( + { + texture: sourceGPU + }, + { + texture: destinationGPU + }, + [ + texture.image.width, + texture.image.height + ] + ); + + descriptorGPU.colorAttachments[ 0 ].loadOp = GPULoadOp.Load; + if ( renderState.depth ) descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Load; + if ( renderState.stencil ) descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; + + renderState.currentPassGPU = encoderGPU.beginRenderPass( descriptorGPU ); + + } + getViewport( target ) { const viewport = this._viewport; @@ -733,10 +766,7 @@ class WebGPURenderer { } - _projectObject( object, camera, groupOrder ) { - - const currentRenderList = this._currentRenderList; - const currentRenderState = this._currentRenderState; + _projectObject( object, camera, groupOrder, renderList ) { if ( object.visible === false ) return; @@ -754,13 +784,7 @@ class WebGPURenderer { } else if ( object.isLight ) { - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - //currentRenderState.pushShadow( object ); - - } + renderList.pushLight( object ); } else if ( object.isSprite ) { @@ -777,7 +801,7 @@ class WebGPURenderer { if ( material.visible ) { - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + renderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } @@ -811,7 +835,7 @@ class WebGPURenderer { if ( groupMaterial && groupMaterial.visible ) { - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); } @@ -819,7 +843,7 @@ class WebGPURenderer { } else if ( material.visible ) { - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + renderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } @@ -833,13 +857,13 @@ class WebGPURenderer { for ( let i = 0, l = children.length; i < l; i ++ ) { - this._projectObject( children[ i ], camera, groupOrder ); + this._projectObject( children[ i ], camera, groupOrder, renderList ); } } - _renderObjects( renderList, camera, scene, lightsNode, passEncoder ) { + _renderObjects( renderList, camera, scene, lightsNode ) { // process renderable objects @@ -866,9 +890,9 @@ class WebGPURenderer { const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth; const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth; - passEncoder.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth ); + this._currentRenderState.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth ); - this._renderObject( object, scene, camera2, geometry, material, group, lightsNode, passEncoder ); + this._renderObject( object, scene, camera2, geometry, material, group, lightsNode ); } @@ -876,7 +900,7 @@ class WebGPURenderer { } else { - this._renderObject( object, scene, camera, geometry, material, group, lightsNode, passEncoder ); + this._renderObject( object, scene, camera, geometry, material, group, lightsNode ); } @@ -884,9 +908,7 @@ class WebGPURenderer { } - _renderObject( object, scene, camera, geometry, material, group, lightsNode, passEncoder ) { - - const info = this._info; + _renderObject( object, scene, camera, geometry, material, group, lightsNode ) { material = scene.overrideMaterial !== null ? scene.overrideMaterial : material; @@ -904,6 +926,11 @@ class WebGPURenderer { // + const passEncoder = this._currentRenderState.currentPassGPU; + const info = this._info; + + // + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); @@ -1055,7 +1082,7 @@ class WebGPURenderer { }, sampleCount: this._parameters.sampleCount, format: GPUTextureFormat.BGRA8Unorm, - usage: GPUTextureUsage.RENDER_ATTACHMENT + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC } ); } @@ -1079,7 +1106,7 @@ class WebGPURenderer { }, sampleCount: this._parameters.sampleCount, format: GPUTextureFormat.Depth24PlusStencil8, - usage: GPUTextureUsage.RENDER_ATTACHMENT + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC } ); } @@ -1095,7 +1122,7 @@ class WebGPURenderer { this._context.configure( { device: device, format: GPUTextureFormat.BGRA8Unorm, - usage: GPUTextureUsage.RENDER_ATTACHMENT, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, alphaMode: 'premultiplied' } ); diff --git a/examples/jsm/renderers/webgpu/WebGPUTextures.js b/examples/jsm/renderers/webgpu/WebGPUTextures.js index f177a0328faa52..ba6e7196b1f408 100644 --- a/examples/jsm/renderers/webgpu/WebGPUTextures.js +++ b/examples/jsm/renderers/webgpu/WebGPUTextures.js @@ -238,8 +238,13 @@ class WebGPUTextures { const width = renderTarget.width; const height = renderTarget.height; - const colorTextureFormat = this._getFormat( renderTarget.texture ); - const label = renderTarget.texture.name ? '_' + renderTarget.texture.name : ''; + + const texture = renderTarget.texture; + + const colorTextureFormat = texture.internalFormat || this._getFormat( texture ); + const label = texture.name ? '_' + texture.name : ''; + const needsMipmaps = this._needsMipmaps( texture ); + const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps ); const colorTextureGPU = device.createTexture( { label: 'renderTarget' + label, @@ -248,8 +253,9 @@ class WebGPUTextures { height: height, depthOrArrayLayers: 1 }, + mipLevelCount: mipLevelCount, format: colorTextureFormat, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST } ); this.info.memory.textures ++; @@ -262,7 +268,7 @@ class WebGPUTextures { // Since it's not possible to see just from a texture object whether it belongs to a render // target or not, we need the initializedRTT flag. - const textureProperties = properties.get( renderTarget.texture ); + const textureProperties = properties.get( texture ); textureProperties.textureGPU = colorTextureGPU; textureProperties.initializedRTT = false; @@ -278,7 +284,7 @@ class WebGPUTextures { depthOrArrayLayers: 1 }, format: depthTextureFormat, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST } ); this.info.memory.textures ++; @@ -379,7 +385,7 @@ class WebGPUTextures { const needsMipmaps = this._needsMipmaps( texture ); const dimension = this._getDimension( texture ); const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps ); - const format = this._getFormat( texture ); + const format = texture.internalFormat || this._getFormat( texture ); let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; @@ -435,6 +441,10 @@ class WebGPUTextures { } + } else if ( texture.isRenderTargetTexture ) { + + if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor ); + } else if ( texture.isDepthTexture !== true && image !== null ) { this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps ); @@ -550,7 +560,8 @@ class WebGPUTextures { width: Math.ceil( width / blockData.width ) * blockData.width, height: Math.ceil( height / blockData.width ) * blockData.width, depthOrArrayLayers: 1 - } ); + } + ); } diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js b/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js index e563cec06bba29..c932b6fae86276 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js +++ b/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js @@ -82,7 +82,6 @@ class WebGPUNodes { } - getCacheKey( scene, lightsNode ) { const environmentNode = this.getEnvironmentNode( scene ); @@ -253,26 +252,25 @@ class WebGPUNodes { } - getUpdateNodes( renderObject ) { + getNodeFrame( renderObject ) { - const nodeBuilder = this.get( renderObject ); const nodeFrame = this.nodeFrame; - nodeFrame.scene = renderObject.scene; nodeFrame.object = renderObject.object; nodeFrame.camera = renderObject.camera; nodeFrame.renderer = renderObject.renderer; nodeFrame.material = renderObject.material; - return nodeBuilder.updateNodes; + return nodeFrame; } updateBefore( renderObject ) { - const nodeFrame = this.nodeFrame; + const nodeFrame = this.getNodeFrame( renderObject ); + const nodeBuilder = this.get( renderObject ); - for ( const node of this.getUpdateNodes( renderObject ) ) { + for ( const node of nodeBuilder.updateBeforeNodes ) { nodeFrame.updateBeforeNode( node ); @@ -282,9 +280,10 @@ class WebGPUNodes { update( renderObject ) { - const nodeFrame = this.nodeFrame; + const nodeFrame = this.getNodeFrame( renderObject ); + const nodeBuilder = this.get( renderObject ); - for ( const node of this.getUpdateNodes( renderObject ) ) { + for ( const node of nodeBuilder.updateNodes ) { nodeFrame.updateNode( node ); diff --git a/examples/screenshots/webgpu_backdrop.jpg b/examples/screenshots/webgpu_backdrop.jpg new file mode 100644 index 0000000000000000000000000000000000000000..001fe203e0b94e476d12e5c1c76aa979a0fbc77f GIT binary patch literal 3413 zcmex=iF;N$`UAd82aiwDGkXk%h!W@hDLXJZFTlSKSKz#z!M@QZN*Gov5_lOQ9r zAmjfdjEjJ7WCc47=uik?WMXDvWn%|Afm;CRY-VPlV_AVN1*)tC$}zAAvI;30I#U-U>$dGXcJ4ZK_{h;?$4{I*b?NeztJkjIxOwa0qsLF4K70P+<*SdMK7aZ8?fZ|P zzZe;qA>IL!82$lzoRJ9>=IkK_0Zp(e6?1osf!KmTtr@Gvt1BaB&)!Jgq?{R@T#ObuKN n8l%)`5R9gR(Tp%!7K|2$qcy^4Z8%yRj@E|5u{QkA@c$+N?bpn5 literal 0 HcmV?d00001 diff --git a/examples/webgpu_backdrop.html b/examples/webgpu_backdrop.html new file mode 100644 index 00000000000000..a9e146f6d1e174 --- /dev/null +++ b/examples/webgpu_backdrop.html @@ -0,0 +1,166 @@ + + + + three.js - WebGPU - Backdrop + + + + + + +
+ three.js WebGPU - Backdrop +
+ + + + + + + +