From a16e9fe3e0099533750f7c54051e73e364879b11 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 18 May 2023 04:33:02 -0300 Subject: [PATCH 01/26] Universal Renderer and WebGPUBackend ( WIP ) --- examples/jsm/nodes/Nodes.js | 4 +- examples/jsm/nodes/core/NodeBuilder.js | 19 + .../Animation.js} | 4 +- .../jsm/renderers/universal/Attributes.js | 75 + examples/jsm/renderers/universal/Backend.js | 160 +++ .../jsm/renderers/universal/Background.js | 133 ++ examples/jsm/renderers/universal/Binding.js | 11 + examples/jsm/renderers/universal/Bindings.js | 188 +++ examples/jsm/renderers/universal/Buffer.js | 38 + .../BufferUtils.js} | 4 +- .../ChainMap.js} | 16 +- .../renderers/universal/ComputePipeline.js | 17 + .../ComputePipelines.js} | 8 +- examples/jsm/renderers/universal/DataMap.js | 54 + .../Geometries.js} | 37 +- .../WebGPUInfo.js => universal/Info.js} | 4 +- examples/jsm/renderers/universal/Pipeline.js | 13 + examples/jsm/renderers/universal/Pipelines.js | 319 +++++ .../renderers/universal/ProgrammableStage.js | 18 + .../Properties.js} | 4 +- .../jsm/renderers/universal/RenderContext.js | 30 + .../jsm/renderers/universal/RenderContexts.js | 38 + .../RenderList.js} | 41 +- .../jsm/renderers/universal/RenderLists.js | 38 + .../RenderObject.js} | 33 +- .../jsm/renderers/universal/RenderObjects.js | 91 ++ .../jsm/renderers/universal/RenderPipeline.js | 16 + .../RenderPipelines.js} | 82 +- .../RenderTarget.js} | 4 +- examples/jsm/renderers/universal/Renderer.js | 740 ++++++++++ .../jsm/renderers/universal/SampledTexture.js | 72 + examples/jsm/renderers/universal/Sampler.js | 19 + .../jsm/renderers/universal/StorageBuffer.js | 18 + .../TextureRenderer.js} | 8 +- .../TextureUtils.js} | 6 +- examples/jsm/renderers/universal/Textures.js | 87 ++ .../WebGPUUniform.js => universal/Uniform.js} | 22 +- .../jsm/renderers/universal/UniformBuffer.js | 15 + .../UniformsGroup.js} | 30 +- examples/jsm/renderers/universal/constants.js | 14 + .../nodes/NodeRender.js} | 6 +- .../nodes/NodeSampledTexture.js} | 2 +- .../nodes/NodeSampler.js} | 4 +- .../nodes/NodeUniform.js} | 2 +- .../jsm/renderers/universal/nodes/Nodes.js | 318 +++++ .../jsm/renderers/webgpu/WebGPUAttributes.js | 199 --- .../jsm/renderers/webgpu/WebGPUBackground.js | 182 --- .../jsm/renderers/webgpu/WebGPUBinding.js | 22 - .../jsm/renderers/webgpu/WebGPUBindings.js | 270 ---- examples/jsm/renderers/webgpu/WebGPUBuffer.js | 43 - .../webgpu/WebGPUProgrammableStage.js | 22 - .../renderers/webgpu/WebGPURenderObjects.js | 50 - .../renderers/webgpu/WebGPURenderStates.js | 54 - .../jsm/renderers/webgpu/WebGPURenderer.js | 1230 +---------------- .../renderers/webgpu/WebGPUSampledTexture.js | 75 - .../jsm/renderers/webgpu/WebGPUSampler.js | 29 - .../renderers/webgpu/WebGPUStorageBuffer.js | 20 - .../renderers/webgpu/WebGPUUniformBuffer.js | 18 - .../webgpu/backends/webgpu/WebGPUBackend.js | 667 +++++++++ .../webgpu/builder/WGSLNodeBuilder.js} | 41 +- .../webgpu/builder}/WGSLNodeFunction.js | 4 +- .../webgpu/builder}/WGSLNodeParser.js | 2 +- .../webgpu/utils/WebGPUAttributeUtils.js | 188 +++ .../webgpu/utils/WebGPUBindingUtils.js | 134 ++ .../webgpu/utils/WebGPUConstants.js} | 7 - .../webgpu/utils/WebGPUPipelineUtils.js} | 420 +++--- .../webgpu/utils/WebGPUTextureMipmapUtils.js | 163 +++ .../webgpu/utils/WebGPUTextureUtils.js} | 830 ++++------- .../webgpu/utils}/WebGPUUtils.js | 57 +- 69 files changed, 4324 insertions(+), 3265 deletions(-) rename examples/jsm/renderers/{webgpu/WebGPUAnimation.js => universal/Animation.js} (93%) create mode 100644 examples/jsm/renderers/universal/Attributes.js create mode 100644 examples/jsm/renderers/universal/Backend.js create mode 100644 examples/jsm/renderers/universal/Background.js create mode 100644 examples/jsm/renderers/universal/Binding.js create mode 100644 examples/jsm/renderers/universal/Bindings.js create mode 100644 examples/jsm/renderers/universal/Buffer.js rename examples/jsm/renderers/{webgpu/WebGPUBufferUtils.js => universal/BufferUtils.js} (78%) rename examples/jsm/renderers/{webgpu/WebGPUWeakMap.js => universal/ChainMap.js} (83%) create mode 100644 examples/jsm/renderers/universal/ComputePipeline.js rename examples/jsm/renderers/{webgpu/WebGPUComputePipelines.js => universal/ComputePipelines.js} (83%) create mode 100644 examples/jsm/renderers/universal/DataMap.js rename examples/jsm/renderers/{webgpu/WebGPUGeometries.js => universal/Geometries.js} (81%) rename examples/jsm/renderers/{webgpu/WebGPUInfo.js => universal/Info.js} (95%) create mode 100644 examples/jsm/renderers/universal/Pipeline.js create mode 100644 examples/jsm/renderers/universal/Pipelines.js create mode 100644 examples/jsm/renderers/universal/ProgrammableStage.js rename examples/jsm/renderers/{webgpu/WebGPUProperties.js => universal/Properties.js} (87%) create mode 100644 examples/jsm/renderers/universal/RenderContext.js create mode 100644 examples/jsm/renderers/universal/RenderContexts.js rename examples/jsm/renderers/{webgpu/WebGPURenderLists.js => universal/RenderList.js} (86%) create mode 100644 examples/jsm/renderers/universal/RenderLists.js rename examples/jsm/renderers/{webgpu/WebGPURenderObject.js => universal/RenderObject.js} (64%) create mode 100644 examples/jsm/renderers/universal/RenderObjects.js create mode 100644 examples/jsm/renderers/universal/RenderPipeline.js rename examples/jsm/renderers/{webgpu/WebGPURenderPipelines.js => universal/RenderPipelines.js} (72%) rename examples/jsm/renderers/{webgpu/WebGPURenderTarget.js => universal/RenderTarget.js} (69%) create mode 100644 examples/jsm/renderers/universal/Renderer.js create mode 100644 examples/jsm/renderers/universal/SampledTexture.js create mode 100644 examples/jsm/renderers/universal/Sampler.js create mode 100644 examples/jsm/renderers/universal/StorageBuffer.js rename examples/jsm/renderers/{webgpu/WebGPUTextureRenderer.js => universal/TextureRenderer.js} (70%) rename examples/jsm/renderers/{webgpu/WebGPUTextureUtils.js => universal/TextureUtils.js} (95%) create mode 100644 examples/jsm/renderers/universal/Textures.js rename examples/jsm/renderers/{webgpu/WebGPUUniform.js => universal/Uniform.js} (77%) create mode 100644 examples/jsm/renderers/universal/UniformBuffer.js rename examples/jsm/renderers/{webgpu/WebGPUUniformsGroup.js => universal/UniformsGroup.js} (88%) create mode 100644 examples/jsm/renderers/universal/constants.js rename examples/jsm/renderers/{webgpu/nodes/WebGPUNodes.js => universal/nodes/NodeRender.js} (98%) rename examples/jsm/renderers/{webgpu/nodes/WebGPUNodeSampledTexture.js => universal/nodes/NodeSampledTexture.js} (95%) rename examples/jsm/renderers/{webgpu/nodes/WebGPUNodeSampler.js => universal/nodes/NodeSampler.js} (68%) rename examples/jsm/renderers/{webgpu/nodes/WebGPUNodeUniform.js => universal/nodes/NodeUniform.js} (98%) create mode 100644 examples/jsm/renderers/universal/nodes/Nodes.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUAttributes.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUBackground.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUBinding.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUBindings.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUBuffer.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPURenderObjects.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPURenderStates.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUSampledTexture.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUSampler.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js delete mode 100644 examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js create mode 100644 examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js rename examples/jsm/renderers/webgpu/{nodes/WebGPUNodeBuilder.js => backends/webgpu/builder/WGSLNodeBuilder.js} (93%) rename examples/jsm/{nodes/parsers => renderers/webgpu/backends/webgpu/builder}/WGSLNodeFunction.js (92%) rename examples/jsm/{nodes/parsers => renderers/webgpu/backends/webgpu/builder}/WGSLNodeParser.js (75%) create mode 100644 examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js create mode 100644 examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js rename examples/jsm/renderers/webgpu/{constants.js => backends/webgpu/utils/WebGPUConstants.js} (97%) rename examples/jsm/renderers/webgpu/{WebGPURenderPipeline.js => backends/webgpu/utils/WebGPUPipelineUtils.js} (63%) create mode 100644 examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureMipmapUtils.js rename examples/jsm/renderers/webgpu/{WebGPUTextures.js => backends/webgpu/utils/WebGPUTextureUtils.js} (57%) rename examples/jsm/renderers/webgpu/{ => backends/webgpu/utils}/WebGPUUtils.js (56%) diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index 45b3c690edd544..2eb8401f31a3ef 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -141,8 +141,8 @@ export { default as NodeObjectLoader } from './loaders/NodeObjectLoader.js'; export { default as NodeMaterialLoader } from './loaders/NodeMaterialLoader.js'; // parsers -export { default as WGSLNodeParser } from './parsers/WGSLNodeParser.js'; -export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js'; +//export { default as WGSLNodeParser } from './parsers/WGSLNodeParser.js'; +//export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js'; // materials export * from './materials/Materials.js'; diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 29e2cb6e732d62..fa387acd90dc54 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -68,6 +68,9 @@ class NodeBuilder { this.flowCode = { vertex: '', fragment: '', compute: [] }; this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 }; this.codes = { vertex: [], fragment: [], compute: [] }; + this.bindings = { vertex: [], fragment: [], compute: [] }; + this.bindingsOffset = { vertex: 0, fragment: 0, compute: 0 }; + this.bindingsArray = null; this.attributes = []; this.bufferAttributes = []; this.varyings = []; @@ -93,6 +96,22 @@ class NodeBuilder { } + getBindings() { + + let bindingsArray = this.bindingsArray; + + if ( bindingsArray === null ) { + + const bindings = this.bindings; + + this.bindingsArray = bindingsArray = ( this.material !== null ) ? [ ...bindings.vertex, ...bindings.fragment ] : bindings.compute; + + } + + return bindingsArray; + + } + setHashNode( node, hash ) { this.hashNodes[ hash ] = node; diff --git a/examples/jsm/renderers/webgpu/WebGPUAnimation.js b/examples/jsm/renderers/universal/Animation.js similarity index 93% rename from examples/jsm/renderers/webgpu/WebGPUAnimation.js rename to examples/jsm/renderers/universal/Animation.js index 8603d47304cd1c..50da8c0591db22 100644 --- a/examples/jsm/renderers/webgpu/WebGPUAnimation.js +++ b/examples/jsm/renderers/universal/Animation.js @@ -1,4 +1,4 @@ -class WebGPUAnimation { +class Animation { constructor() { @@ -55,4 +55,4 @@ class WebGPUAnimation { } -export default WebGPUAnimation; +export default Animation; diff --git a/examples/jsm/renderers/universal/Attributes.js b/examples/jsm/renderers/universal/Attributes.js new file mode 100644 index 00000000000000..48df53e54ad8d5 --- /dev/null +++ b/examples/jsm/renderers/universal/Attributes.js @@ -0,0 +1,75 @@ +import DataMap from './DataMap.js'; +import { AttributeType } from './Constants.js'; +import { DynamicDrawUsage } from "three"; + +class Attributes extends DataMap { + + constructor( backend ) { + + super(); + + this.backend = backend; + + } + + delete( attribute ) { + + const data = this.delete( attribute ); + + if ( data !== undefined ) { + + this.backend.destroyAttribute( attribute ); + + } + + } + + update( attribute, type ) { + + let data = this.get( attribute ); + + if ( data.version === undefined ) { + + if ( type === AttributeType.VERTEX ) { + + this.backend.createAttribute( attribute ); + + } else if ( type === AttributeType.INDEX ) { + + this.backend.createIndexAttribute( attribute ); + + } else if ( type === AttributeType.STORAGE ) { + + this.backend.createStorageAttribute( attribute ); + + } + + data.version = this._getBufferAttribute( attribute ).version; + + } else { + + const bufferAttribute = this._getBufferAttribute( attribute ); + + if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) { + + this.backend.updateAttribute( attribute ); + + data.version = bufferAttribute.version; + + } + + } + + } + + _getBufferAttribute( attribute ) { + + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + + return attribute; + + } + +} + +export default Attributes; diff --git a/examples/jsm/renderers/universal/Backend.js b/examples/jsm/renderers/universal/Backend.js new file mode 100644 index 00000000000000..4e90bf464a4268 --- /dev/null +++ b/examples/jsm/renderers/universal/Backend.js @@ -0,0 +1,160 @@ +let vector2 = null; +let vector4 = null; + +import { Vector2, Vector4 } from 'three'; + +class Backend { + + constructor( parameters = {} ) { + + this.parameters = Object.assign( {}, parameters ); + this.properties = new WeakMap(); + this.renderer = null; + this.domElement = null; + + } + + async init( renderer ) { + + this.renderer = renderer; + + } + + // render context + + begin( renderContext ) { } + + finish( renderContext ) { } + + // render object + + draw( renderObject, info ) { } + + // program + + createProgram( program ) { } + + destroyProgram( program ) { } + + // bindings + + createBindings( renderObject ) { } + + updateBindings( renderObject ) { } + + // pipeline + + createRenderPipeline( renderObject ) { } + + createComputePipeline( computeNode, pipeline ) { } + + destroyPipeline( pipeline ) { } + + // cache key + + needsUpdate( renderObject ) { } // return Boolean ( fast test ) + + getCacheKey( renderObject ) { } // return String + + // node builder + + createNodeBuilder( renderObject ) { } // return NodeBuilder (ADD IT) + + // textures + + createSampler( texture ) { } + + createDefaultTexture( texture ) { } + + createTexture( texture ) { } + + // attributes + + createAttribute( attribute) { } + + createIndexAttribute( attribute) { } + + updateAttribute( attribute ) { } + + destroyAttribute( attribute ) { } + + // canvas + + updateSize() { } + + // utils + + hasFeature( name ) { } // return Boolean + + getInstanceCount( renderObject ) { + + const { object, geometry } = renderObject; + + return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 ); + + } + + getDrawingBufferSize() { + + vector2 = vector2 || new Vector2(); + + return this.renderer.getDrawingBufferSize( vector2 ); + + } + + getScissor() { + + vector4 = vector4 || new Vector4(); + + return this.renderer.getScissor( vector4 ); + + } + + getDomElement() { + + let domElement = this.domElement; + + if ( domElement === null ) { + + this.domElement = domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : this.createCanvasElement(); + + } + + return domElement; + + } + + createCanvasElement() { + + const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + canvas.style.display = 'block'; + return canvas; + + } + + // resource properties + + get( object ) { + + let map = this.properties.get( object ); + + if ( map === undefined ) { + + map = {}; + this.properties.set( object, map ); + + } + + return map; + + } + + delete( object ) { + + this.properties.delete( object ); + + } + +} + +export default Backend; diff --git a/examples/jsm/renderers/universal/Background.js b/examples/jsm/renderers/universal/Background.js new file mode 100644 index 00000000000000..5e14ea59d295b1 --- /dev/null +++ b/examples/jsm/renderers/universal/Background.js @@ -0,0 +1,133 @@ +import DataMap from './DataMap.js'; +import { Color, Mesh, BoxGeometry, BackSide } from 'three'; +import { context, positionWorldDirection, MeshBasicNodeMaterial } from '../../nodes/Nodes.js'; + +let _clearAlpha; +const _clearColor = new Color(); + +class Background extends DataMap { + + constructor( renderer, nodes ) { + + super(); + + this.renderer = renderer; + this.nodes = nodes; + + this.boxMesh = null; + this.boxMeshNode = null; + + } + + update( scene, renderList, renderContext ) { + + const renderer = this.renderer; + const background = ( scene.isScene === true ) ? this.nodes.getBackgroundNode( scene ) || scene.background : null; + + let forceClear = false; + + if ( background === null ) { + + // no background settings, use clear color configuration from the renderer + + _clearColor.copy( renderer._clearColor ); + _clearAlpha = renderer._clearAlpha; + + } else if ( background.isColor === true ) { + + // background is an opaque color + + _clearColor.copy( background ); + _clearAlpha = 1; + forceClear = true; + + } else if ( background.isNode === true ) { + + const sceneData = this.get( scene ); + const backgroundNode = background; + + _clearColor.copy( renderer._clearColor ); + _clearAlpha = renderer._clearAlpha; + + let boxMesh = this.boxMesh; + + if ( boxMesh === null ) { + + this.boxMeshNode = context( backgroundNode, { + // @TODO: Add Texture2D support using node context + getUVNode: () => positionWorldDirection + } ); + + const nodeMaterial = new MeshBasicNodeMaterial(); + nodeMaterial.colorNode = this.boxMeshNode; + nodeMaterial.side = BackSide; + nodeMaterial.depthTest = false; + nodeMaterial.depthWrite = false; + nodeMaterial.fog = false; + + this.boxMesh = boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), nodeMaterial ); + boxMesh.frustumCulled = false; + + boxMesh.onBeforeRender = function ( renderer, scene, camera ) { + + const scale = camera.far; + + this.matrixWorld.makeScale( scale, scale, scale ).copyPosition( camera.matrixWorld ); + + }; + + } + + const backgroundCacheKey = backgroundNode.getCacheKey(); + + if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) { + + this.boxMeshNode.node = backgroundNode; + + boxMesh.material.needsUpdate = true; + + sceneData.backgroundCacheKey = backgroundCacheKey; + + } + + renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); + + } else { + + console.error( 'THREE.Renderer: Unsupported background configuration.', background ); + + } + + // + + if ( renderer.autoClear === true || forceClear === true ) { + + _clearColor.multiplyScalar( _clearAlpha ); + + const clearColorValue = renderContext.clearColorValue; + + clearColorValue.r = _clearColor.r; + clearColorValue.g = _clearColor.g; + clearColorValue.b = _clearColor.b; + clearColorValue.a = _clearAlpha; + + renderContext.depthClearValue = renderer._clearDepth; + renderContext.stencilClearValue = renderer._clearStencil; + + renderContext.clearColor = renderer.autoClearColor === true; + renderContext.clearDepth = renderer.autoClearDepth === true; + renderContext.clearStencil = renderer.autoClearStencil === true; + + } else { + + renderContext.clearColor = false; + renderContext.clearDepth = false; + renderContext.clearStencil = false; + + } + + } + +} + +export default Background; diff --git a/examples/jsm/renderers/universal/Binding.js b/examples/jsm/renderers/universal/Binding.js new file mode 100644 index 00000000000000..a6138d501b92a3 --- /dev/null +++ b/examples/jsm/renderers/universal/Binding.js @@ -0,0 +1,11 @@ +class Binding { + + constructor( name = '' ) { + + this.name = name; + + } + +} + +export default Binding; diff --git a/examples/jsm/renderers/universal/Bindings.js b/examples/jsm/renderers/universal/Bindings.js new file mode 100644 index 00000000000000..13728821541aa1 --- /dev/null +++ b/examples/jsm/renderers/universal/Bindings.js @@ -0,0 +1,188 @@ +import DataMap from './DataMap.js'; + +class Bindings extends DataMap { + + constructor( backend, nodes, textures, attributes, pipelines, info ) { + + super(); + + this.backend = backend; + this.textures = textures; + this.pipelines = pipelines; + this.attributes = attributes; + this.nodes = nodes; + this.info = info; + + this.pipelines.bindings = this; // assign bindings to pipelines + + this.updateMap = new WeakMap(); + + } + + getForRender( renderObject ) { + + const bindings = renderObject.getBindings(); + + let data = this.get( renderObject ); + + if ( data.bindings !== bindings ) { + + // each object defines an array of bindings (ubos, textures, samplers etc.) + + data.bindings = bindings; + + this._init( bindings ); + + const pipline = this.pipelines.getForRender( renderObject ); + + this.backend.createBindings( bindings, pipline ); + + } + + return data.bindings; + + } + + getForCompute( computeNode ) { + + let data = this.get( computeNode ); + + if ( data.bindings === undefined ) { + + const nodeBuilder = this.nodes.getForCompute( computeNode ); + + data.bindings = nodeBuilder.getBindings(); + + this._init( data ); + + const pipeline = this.pipelines.getForCompute( computeNode ); + + this.backend.createBindings( data, pipeline ); + + this.uniformBindings.set( computeNode, data ); + + } + + return data.bindings; + + } + + updateForCompute( computeNode ) { + + this._update( computeNode, this.getForCompute( computeNode ) ); + + } + + updateForRender( renderObject ) { + + this._update( renderObject, this.getForRender( renderObject ) ); + + } + + _init( bindings ) { + + for ( const binding of bindings ) { + + if ( binding.isSampler || binding.isSampledTexture ) { + + this.textures.initTexture( binding.texture ); + + } else if ( binding.isStorageBuffer ) { + + const attribute = binding.attribute; + + this.attributes.update( attribute, AttributeType.STORAGE ); + + } + + } + + } + + _update( object, bindings ) { + + const { backend } = this; + + const updateMap = this.updateMap; + const frame = this.info.render.frame; + + let needsBindingsUpdate = false; + + // iterate over all bindings and check if buffer updates or a new binding group is required + + for ( const binding of bindings ) { + + const isShared = binding.isShared; + const isUpdated = updateMap.get( binding ) === frame; + + if ( isShared && isUpdated ) continue; + + if ( binding.isUniformBuffer ) { + + const needsBufferWrite = binding.update(); + + if ( needsBufferWrite ) { + + backend.updateBinding( binding ); + + } + + } else if ( binding.isSampledTexture ) { + + const needsTextureUpdate = binding.update(); + + if ( needsTextureUpdate ) { + + this.textures.updateTexture( binding.texture ); + + needsBindingsUpdate = true; + + } + + } else if ( binding.isStorageBuffer ) { + + //const attribute = binding.attribute; + + } else if ( binding.isSampler ) { +/* + const texture = binding.getTexture(); + + textures.updateSampler( texture ); + + const samplerGPU = textures.getSampler( texture ); + + if ( binding.samplerGPU !== samplerGPU ) { + + binding.samplerGPU = samplerGPU; + needsBindGroupRefresh = true; + + } +*/ + } + + updateMap.set( binding, frame ); + + } + + if ( needsBindingsUpdate === true ) { + + //this.pipelines.getPipeline( object ) + const pipline = this.pipelines.getForRender( object ); + + this.backend.updateBindings( bindings, pipline ); + + } + + } + + dispose() { + + super.dispose(); + + this.updateMap = new WeakMap(); + + } + +} + +export default Bindings; diff --git a/examples/jsm/renderers/universal/Buffer.js b/examples/jsm/renderers/universal/Buffer.js new file mode 100644 index 00000000000000..4d97c3a1980cce --- /dev/null +++ b/examples/jsm/renderers/universal/Buffer.js @@ -0,0 +1,38 @@ +import Binding from './Binding.js'; +import { getFloatLength } from './BufferUtils.js'; + +class Buffer extends Binding { + + constructor( name, buffer = null ) { + + super( name ); + + this.isBuffer = true; + + this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT; + + this.buffer = buffer; + + } + + getByteLength() { + + return getFloatLength( this.buffer.byteLength ); + + } + + getBuffer() { + + return this.buffer; + + } + + update() { + + return true; + + } + +} + +export default Buffer; diff --git a/examples/jsm/renderers/webgpu/WebGPUBufferUtils.js b/examples/jsm/renderers/universal/BufferUtils.js similarity index 78% rename from examples/jsm/renderers/webgpu/WebGPUBufferUtils.js rename to examples/jsm/renderers/universal/BufferUtils.js index b1b1bfc2e09fbc..b3e82b3072819b 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBufferUtils.js +++ b/examples/jsm/renderers/universal/BufferUtils.js @@ -1,10 +1,10 @@ -import { GPUChunkSize } from './constants.js'; +import { GPUChunkBytes } from './Constants.js'; function getFloatLength( floatLength ) { // ensure chunk size alignment (STD140 layout) - return floatLength + ( ( GPUChunkSize - ( floatLength % GPUChunkSize ) ) % GPUChunkSize ); + return floatLength + ( ( GPUChunkBytes - ( floatLength % GPUChunkBytes ) ) % GPUChunkBytes ); } diff --git a/examples/jsm/renderers/webgpu/WebGPUWeakMap.js b/examples/jsm/renderers/universal/ChainMap.js similarity index 83% rename from examples/jsm/renderers/webgpu/WebGPUWeakMap.js rename to examples/jsm/renderers/universal/ChainMap.js index 62a77748eddd2a..6b5c18e5cecc4a 100644 --- a/examples/jsm/renderers/webgpu/WebGPUWeakMap.js +++ b/examples/jsm/renderers/universal/ChainMap.js @@ -1,8 +1,8 @@ -export default class WebGPUWeakMap extends WeakMap { +export default class ChainMap { constructor() { - super(); + this.weakMap = new WeakMap(); } @@ -10,7 +10,7 @@ export default class WebGPUWeakMap extends WeakMap { if ( Array.isArray( keys ) ) { - let map = this; + let map = this.weakMap; for ( let i = 0; i < keys.length - 1; i ++ ) { @@ -34,7 +34,7 @@ export default class WebGPUWeakMap extends WeakMap { if ( Array.isArray( keys ) ) { - let map = this; + let map = this.weakMap; for ( let i = 0; i < keys.length - 1; i ++ ) { @@ -60,7 +60,7 @@ export default class WebGPUWeakMap extends WeakMap { if ( Array.isArray( keys ) ) { - let map = this; + let map = this.weakMap; for ( let i = 0; i < keys.length - 1; i ++ ) { @@ -80,4 +80,10 @@ export default class WebGPUWeakMap extends WeakMap { } + dispose() { + + this.weakMap.clear(); + + } + } diff --git a/examples/jsm/renderers/universal/ComputePipeline.js b/examples/jsm/renderers/universal/ComputePipeline.js new file mode 100644 index 00000000000000..8f29dc4756dbbe --- /dev/null +++ b/examples/jsm/renderers/universal/ComputePipeline.js @@ -0,0 +1,17 @@ +import Pipeline from './Pipeline.js'; + +class ComputePipeline extends Pipeline { + + constructor( cacheKey, computeProgram ) { + + super( cacheKey ); + + this.computeProgram = computeProgram; + + this.isComputePipeline = true; + + } + +} + +export default ComputePipeline; diff --git a/examples/jsm/renderers/webgpu/WebGPUComputePipelines.js b/examples/jsm/renderers/universal/ComputePipelines.js similarity index 83% rename from examples/jsm/renderers/webgpu/WebGPUComputePipelines.js rename to examples/jsm/renderers/universal/ComputePipelines.js index cad813b0a34d15..d2a06e85e1657a 100644 --- a/examples/jsm/renderers/webgpu/WebGPUComputePipelines.js +++ b/examples/jsm/renderers/universal/ComputePipelines.js @@ -1,6 +1,6 @@ -import WebGPUProgrammableStage from './WebGPUProgrammableStage.js'; +import ProgrammableStage from './ProgrammableStage.js'; -class WebGPUComputePipelines { +class ComputePipelines { constructor( device, nodes ) { @@ -45,7 +45,7 @@ class WebGPUComputePipelines { if ( stageCompute === undefined ) { - stageCompute = new WebGPUProgrammableStage( device, computeShader, 'compute' ); + stageCompute = new ProgrammableStage( device, computeShader, 'compute' ); this.stages.compute.set( shader, stageCompute ); @@ -75,4 +75,4 @@ class WebGPUComputePipelines { } -export default WebGPUComputePipelines; +export default ComputePipelines; diff --git a/examples/jsm/renderers/universal/DataMap.js b/examples/jsm/renderers/universal/DataMap.js new file mode 100644 index 00000000000000..e08900296cccf6 --- /dev/null +++ b/examples/jsm/renderers/universal/DataMap.js @@ -0,0 +1,54 @@ +class DataMap { + + constructor() { + + this.datas = new WeakMap(); + + } + + get( object ) { + + let map = this.datas.get( object ); + + if ( map === undefined ) { + + map = {}; + this.datas.set( object, map ); + + } + + return map; + + } + + delete( object ) { + + let map; + + if ( this.datas.has( object ) ) { + + map = this.datas.get( object ); + + this.datas.delete( object ); + + } + + return map; + + } + + has( object ) { + + return this.datas.has( object ); + + } + + dispose() { + + this.datas.clear(); + + } + +} + +export default DataMap; diff --git a/examples/jsm/renderers/webgpu/WebGPUGeometries.js b/examples/jsm/renderers/universal/Geometries.js similarity index 81% rename from examples/jsm/renderers/webgpu/WebGPUGeometries.js rename to examples/jsm/renderers/universal/Geometries.js index 92193aad90c83d..b1229787e449d8 100644 --- a/examples/jsm/renderers/webgpu/WebGPUGeometries.js +++ b/examples/jsm/renderers/universal/Geometries.js @@ -1,3 +1,5 @@ +import DataMap from './DataMap.js'; +import { AttributeType } from './Constants.js'; import { Uint32BufferAttribute, Uint16BufferAttribute } from 'three'; function arrayNeedsUint32( array ) { @@ -64,12 +66,13 @@ function getWireframeIndex( geometry ) { } -class WebGPUGeometries { +class Geometries extends DataMap { - constructor( attributes, properties, info ) { + constructor( attributes, info ) { + super(); + this.attributes = attributes; - this.properties = properties; this.info = info; this.wireframes = new WeakMap(); @@ -81,7 +84,7 @@ class WebGPUGeometries { const geometry = renderObject.geometry; - return this.properties.has( geometry ) && this.properties.get( geometry ).initialized === true; + return super.has( geometry ) && this.get( geometry ).initialized === true; } @@ -96,11 +99,13 @@ class WebGPUGeometries { initGeometry( renderObject ) { const geometry = renderObject.geometry; - const geometryProperties = this.properties.get( geometry ); + const geometryProperties = this.get( geometry ); geometryProperties.initialized = true; - const dispose = () => { + this.info.memory.geometries ++; + + const onDispose = () => { this.info.memory.geometries --; @@ -109,13 +114,13 @@ class WebGPUGeometries { if ( index !== null ) { - this.attributes.remove( index ); + this.attributes.delete( index ); } for ( const geometryAttribute of geometryAttributes ) { - this.attributes.remove( geometryAttribute ); + this.attributes.delete( geometryAttribute ); } @@ -123,17 +128,15 @@ class WebGPUGeometries { if ( wireframeAttribute !== undefined ) { - this.attributes.remove( wireframeAttribute ); + this.attributes.delete( wireframeAttribute ); } - geometry.removeEventListener( 'dispose', dispose ); + geometry.removeEventListener( 'dispose', onDispose ); }; - this.info.memory.geometries ++; - - geometry.addEventListener( 'dispose', dispose ); + geometry.addEventListener( 'dispose', onDispose ); } @@ -158,7 +161,7 @@ class WebGPUGeometries { for ( const attribute of attributes ) { - this.attributes.update( attribute ); + this.attributes.update( attribute, AttributeType.VERTEX ); } @@ -166,7 +169,7 @@ class WebGPUGeometries { if ( index !== null ) { - this.attributes.update( index, true ); + this.attributes.update( index, AttributeType.INDEX ); } @@ -192,7 +195,7 @@ class WebGPUGeometries { } else if ( wireframeAttribute.version !== getWireframeVersion( geometry ) ) { - this.attributes.remove( wireframeAttribute ); + this.attributes.delete( wireframeAttribute ); wireframeAttribute = getWireframeIndex( geometry ); @@ -210,4 +213,4 @@ class WebGPUGeometries { } -export default WebGPUGeometries; +export default Geometries; diff --git a/examples/jsm/renderers/webgpu/WebGPUInfo.js b/examples/jsm/renderers/universal/Info.js similarity index 95% rename from examples/jsm/renderers/webgpu/WebGPUInfo.js rename to examples/jsm/renderers/universal/Info.js index a62e6d626009ef..77d773e7a03ab4 100644 --- a/examples/jsm/renderers/webgpu/WebGPUInfo.js +++ b/examples/jsm/renderers/universal/Info.js @@ -1,4 +1,4 @@ -class WebGPUInfo { +class Info { constructor() { @@ -70,4 +70,4 @@ class WebGPUInfo { } -export default WebGPUInfo; +export default Info; diff --git a/examples/jsm/renderers/universal/Pipeline.js b/examples/jsm/renderers/universal/Pipeline.js new file mode 100644 index 00000000000000..8f0f30418f5893 --- /dev/null +++ b/examples/jsm/renderers/universal/Pipeline.js @@ -0,0 +1,13 @@ +class Pipeline { + + constructor( cacheKey ) { + + this.cacheKey = cacheKey; + + this.usedTimes = 0; + + } + +} + +export default Pipeline; diff --git a/examples/jsm/renderers/universal/Pipelines.js b/examples/jsm/renderers/universal/Pipelines.js new file mode 100644 index 00000000000000..c3b36c5820659f --- /dev/null +++ b/examples/jsm/renderers/universal/Pipelines.js @@ -0,0 +1,319 @@ +import DataMap from './DataMap.js'; +import RenderPipeline from './RenderPipeline.js'; +import ComputePipeline from './ComputePipeline.js'; +import ProgrammableStage from './ProgrammableStage.js'; + +class Pipelines extends DataMap { + + constructor( backend, nodes ) { + + super(); + + this.backend = backend; + this.nodes = nodes; + + this.bindings = null; // set by the bindings + + this.pipelines = new Map(); + this.programs = { + vertex: new Map(), + fragment: new Map(), + compute: new Map() + }; + + } + + has( object ) { + + return this.pipelines.get( object ) !== undefined; + + } + + getForCompute( computeNode ) { + + const { backend } = this; + + let data = this.get( computeNode ); + + if ( data.pipeline === undefined ) { + + // release previous cache + + this._releasePipeline( computeNode ); + + // get shader + + const nodeBuilder = this.nodes.getForCompute( computeNode ); + + // programmable stage + + let stageCompute = this.programs.compute.get( nodeBuilder.computeShader ); + + if ( stageCompute === undefined ) { + + stageCompute = new ProgrammableStage( nodeBuilder.computeShader, 'compute' ); + this.programs.compute.set( nodeBuilder.computeShader, stageCompute ); + + backend.createProgram( stageCompute ); + + } + + // determine compute pipeline + + const pipeline = this._getComputePipeline( computeNode, stageCompute ); + + // keep track of all used times + + pipeline.usedTimes ++; + stageCompute.usedTimes ++; + + // + + data.pipeline = pipeline; + + } + + return data.pipeline; + + } + + getForRender( renderObject ) { + + const { backend } = this; + + let data = this.get( renderObject ); + + if ( this._needsUpdate( renderObject ) ) { + + // release previous cache + + this._releasePipeline( renderObject ); + + // get shader + + const nodeBuilder = this.nodes.getForRender( renderObject ); + + // programmable stages + + let stageVertex = this.programs.vertex.get( nodeBuilder.vertexShader ); + + if ( stageVertex === undefined ) { + + stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' ); + this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex ); + + backend.createProgram( stageVertex ); + + } + + let stageFragment = this.programs.fragment.get( nodeBuilder.fragmentShader ); + + if ( stageFragment === undefined ) { + + stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' ); + this.programs.fragment.set( nodeBuilder.fragmentShader, stageFragment ); + + backend.createProgram( stageFragment ); + + } + + // determine render pipeline + + const pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment ); + + // keep track of all used times + + pipeline.usedTimes ++; + stageVertex.usedTimes ++; + stageFragment.usedTimes ++; + + // + + data.pipeline = pipeline; + + } + + return data.pipeline; + + } + + remove( object ) { + + this._releasePipeline( object ); + + } + + dispose() { + + super.dispose(); + + this.pipelines = new Map(); + this.shaderModules = { + vertex: new Map(), + fragment: new Map(), + compute: new Map() + }; + + } + + _getComputePipeline( computeNode, stageCompute ) { + + // check for existing pipeline + + const cacheKey = 'compute:' + stageCompute.id; + + let pipeline = this.pipelines.get( cacheKey ); + + if ( pipeline === undefined ) { + + pipeline = new ComputePipeline( cacheKey, stageCompute ); + this.pipelines.set( cacheKey, computeNode ); + this.pipelines.set( computeNode, pipeline ); + + this.backend.createComputePipeline( pipeline ); + + } + + return pipeline; + + } + + _getRenderPipeline( renderObject, stageVertex, stageFragment ) { + + // check for existing pipeline + + const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment ); + + let pipeline = this.pipelines.get( cacheKey ); + + if ( pipeline === undefined ) { + + pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment ); + this.pipelines.set( cacheKey, pipeline ); + this.pipelines.set( pipeline, cacheKey ); + + renderObject.pipeline = pipeline; + + this.backend.createRenderPipeline( renderObject ); + + } else { + + // assign a shared pipeline to renderObject + + renderObject.pipeline = pipeline; + + } + + return pipeline; + + } + + _getRenderCacheKey( renderObject, stageVertex, stageFragment ) { + + const { material } = renderObject; + + const parameters = [ + stageVertex.id, stageFragment.id, + material.transparent, material.blending, material.premultipliedAlpha, + material.blendSrc, material.blendDst, material.blendEquation, + material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha, + material.colorWrite, + material.depthWrite, material.depthTest, material.depthFunc, + material.stencilWrite, material.stencilFunc, + material.stencilFail, material.stencilZFail, material.stencilZPass, + material.stencilFuncMask, material.stencilWriteMask, + material.side, + this.backend.getCacheKey( renderObject ) + ]; + + return parameters.join(); + + } + + _releasePipeline( object ) { + + const data = this.get( object ); + + const pipeline = data.pipeline; + delete data.pipeline; + + this.bindings.delete( object ); + + if ( pipeline && -- pipeline.usedTimes === 0 ) { + + const cacheKey = this.pipelines.get( pipeline ); + + this.pipelines.delete( cacheKey ); + this.pipelines.delete( pipeline ); + + if ( pipeline.isComputePipeline ) { + + this._releaseProgram( pipeline.computeProgram ); + + } else { + + this._releaseProgram( pipeline.vertexProgram ); + this._releaseProgram( pipeline.fragmentProgram ); + + } + + } + + } + + _releaseProgram( program ) { + + if ( -- program.usedTimes === 0 ) { + + const code = program.code; + const stage = program.stage; + + this.programs[ stage ].delete( code ); + + } + + } + + _needsUpdate( renderObject ) { + + const data = this.get( renderObject ); + const material = renderObject.material; + + let needsUpdate = this.backend.needsUpdate( renderObject ); + + // check material state + + if ( data.material !== material || data.materialVersion !== material.version || + data.transparent !== material.transparent || data.blending !== material.blending || data.premultipliedAlpha !== material.premultipliedAlpha || + data.blendSrc !== material.blendSrc || data.blendDst !== material.blendDst || data.blendEquation !== material.blendEquation || + data.blendSrcAlpha !== material.blendSrcAlpha || data.blendDstAlpha !== material.blendDstAlpha || data.blendEquationAlpha !== material.blendEquationAlpha || + data.colorWrite !== material.colorWrite || + data.depthWrite !== material.depthWrite || data.depthTest !== material.depthTest || data.depthFunc !== material.depthFunc || + data.stencilWrite !== material.stencilWrite || data.stencilFunc !== material.stencilFunc || + data.stencilFail !== material.stencilFail || data.stencilZFail !== material.stencilZFail || data.stencilZPass !== material.stencilZPass || + data.stencilFuncMask !== material.stencilFuncMask || data.stencilWriteMask !== material.stencilWriteMask || + data.side !== material.side + ) { + + data.material = material; data.materialVersion = material.version; + data.transparent = material.transparent; data.blending = material.blending; data.premultipliedAlpha = material.premultipliedAlpha; + data.blendSrc = material.blendSrc; data.blendDst = material.blendDst; data.blendEquation = material.blendEquation; + data.blendSrcAlpha = material.blendSrcAlpha; data.blendDstAlpha = material.blendDstAlpha; data.blendEquationAlpha = material.blendEquationAlpha; + data.colorWrite = material.colorWrite; + data.depthWrite = material.depthWrite; data.depthTest = material.depthTest; data.depthFunc = material.depthFunc; + data.stencilWrite = material.stencilWrite; data.stencilFunc = material.stencilFunc; + data.stencilFail = material.stencilFail; data.stencilZFail = material.stencilZFail; data.stencilZPass = material.stencilZPass; + data.stencilFuncMask = material.stencilFuncMask; data.stencilWriteMask = material.stencilWriteMask; + data.side = material.side; + + needsUpdate = true; + + } + + return needsUpdate || data.pipeline !== undefined; + + } + +} + +export default Pipelines; diff --git a/examples/jsm/renderers/universal/ProgrammableStage.js b/examples/jsm/renderers/universal/ProgrammableStage.js new file mode 100644 index 00000000000000..d42bc1b6071353 --- /dev/null +++ b/examples/jsm/renderers/universal/ProgrammableStage.js @@ -0,0 +1,18 @@ +let _id = 0; + +class ProgrammableStage { + + constructor( code, type ) { + + this.id = _id ++; + + this.code = code; + this.stage = type; + + this.usedTimes = 0; + + } + +} + +export default ProgrammableStage; diff --git a/examples/jsm/renderers/webgpu/WebGPUProperties.js b/examples/jsm/renderers/universal/Properties.js similarity index 87% rename from examples/jsm/renderers/webgpu/WebGPUProperties.js rename to examples/jsm/renderers/universal/Properties.js index f70a2e87ab54d8..ee3d48db3dfd55 100644 --- a/examples/jsm/renderers/webgpu/WebGPUProperties.js +++ b/examples/jsm/renderers/universal/Properties.js @@ -1,4 +1,4 @@ -class WebGPUProperties { +class Properties { constructor() { @@ -41,4 +41,4 @@ class WebGPUProperties { } -export default WebGPUProperties; +export default Properties; diff --git a/examples/jsm/renderers/universal/RenderContext.js b/examples/jsm/renderers/universal/RenderContext.js new file mode 100644 index 00000000000000..86b2a6bd130b8f --- /dev/null +++ b/examples/jsm/renderers/universal/RenderContext.js @@ -0,0 +1,30 @@ +import { Vector4 } from "three"; + +class RenderContext { + + constructor() { + + this.color = true; + this.clearColor = true; + this.clearColorValue = { r: 0, g: 0, b: 0, a: 1 }; + + this.depth = true; + this.clearDepth = true; + this.clearDepthValue = 1; + + this.stencil = true; + this.clearStencil = true; + this.clearStencilValue = 1; + + this.viewport = false; + this.viewportValue = new Vector4(); + + this.scissor = false; + this.scissorValue = new Vector4(); + this.scissorTest = false; + + } + +} + +export default RenderContext; diff --git a/examples/jsm/renderers/universal/RenderContexts.js b/examples/jsm/renderers/universal/RenderContexts.js new file mode 100644 index 00000000000000..cee11f0d406baa --- /dev/null +++ b/examples/jsm/renderers/universal/RenderContexts.js @@ -0,0 +1,38 @@ +import ChainMap from './ChainMap.js'; +import RenderContext from './RenderContext.js'; + +class RenderContexts { + + constructor() { + + this.renderStates = new ChainMap(); + + } + + get( scene, camera ) { + + const chainKey = [ scene, camera ]; + + let renderState = this.renderStates.get( chainKey ); + + if ( renderState === undefined ) { + + renderState = new RenderContext(); + + this.renderStates.set( chainKey, renderState ); + + } + + return renderState; + + } + + dispose() { + + this.renderStates = new ChainMap(); + + } + +} + +export default RenderContexts; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderLists.js b/examples/jsm/renderers/universal/RenderList.js similarity index 86% rename from examples/jsm/renderers/webgpu/WebGPURenderLists.js rename to examples/jsm/renderers/universal/RenderList.js index 79d693f0e8260f..f92af2f4627100 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderLists.js +++ b/examples/jsm/renderers/universal/RenderList.js @@ -1,4 +1,3 @@ -import WebGPUWeakMap from './WebGPUWeakMap.js'; import { lights } from '../../nodes/Nodes.js'; function painterSortStable( a, b ) { @@ -49,7 +48,7 @@ function reversePainterSortStable( a, b ) { } -class WebGPURenderList { +class RenderList { constructor() { @@ -176,40 +175,4 @@ class WebGPURenderList { } -class WebGPURenderLists { - - constructor() { - - this.lists = new WeakMap(); - this.lists = new WebGPUWeakMap(); - - } - - get( scene, camera ) { - - const lists = this.lists; - const keys = [ scene, camera ]; - - let list = lists.get( keys ); - - if ( list === undefined ) { - - list = new WebGPURenderList(); - lists.set( keys, list ); - - - } - - return list; - - } - - dispose() { - - this.lists = new WeakMap(); - - } - -} - -export default WebGPURenderLists; +export default RenderList; diff --git a/examples/jsm/renderers/universal/RenderLists.js b/examples/jsm/renderers/universal/RenderLists.js new file mode 100644 index 00000000000000..9090e6e001a403 --- /dev/null +++ b/examples/jsm/renderers/universal/RenderLists.js @@ -0,0 +1,38 @@ +import ChainMap from './ChainMap.js'; +import RenderList from './RenderList.js'; + +class RenderLists { + + constructor() { + + this.lists = new ChainMap(); + + } + + get( scene, camera ) { + + const lists = this.lists; + const keys = [ scene, camera ]; + + let list = lists.get( keys ); + + if ( list === undefined ) { + + list = new RenderList(); + lists.set( keys, list ); + + } + + return list; + + } + + dispose() { + + this.lists = new ChainMap(); + + } + +} + +export default RenderLists; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderObject.js b/examples/jsm/renderers/universal/RenderObject.js similarity index 64% rename from examples/jsm/renderers/webgpu/WebGPURenderObject.js rename to examples/jsm/renderers/universal/RenderObject.js index f9ccc86c6763a4..ca24b40ead0cb5 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderObject.js +++ b/examples/jsm/renderers/universal/RenderObject.js @@ -1,10 +1,11 @@ -export default class WebGPURenderObject { +export default class RenderObject { - constructor( renderer, nodes, object, material, scene, camera, lightsNode ) { + constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode ) { - this.renderer = renderer; - this.nodes = nodes; + this._nodes = nodes; + this._geometries = geometries; + this.renderer = renderer; this.object = object; this.material = material; this.scene = scene; @@ -14,17 +15,37 @@ export default class WebGPURenderObject { this.geometry = object.geometry; this.attributes = null; + this.context = null; + this.pipeline = null; this._materialVersion = - 1; this._materialCacheKey = ''; } + getNodeBuilder() { + + return this._nodes.getForRender( this ); + + } + + getBindings() { + + return this.getNodeBuilder().getBindings(); + + } + + getIndex() { + + return this._geometries.getIndex( this ); + + } + getAttributes() { if ( this.attributes !== null ) return this.attributes; - const nodeAttributes = this.nodes.get( this ).getAttributesArray(); + const nodeAttributes = this.getNodeBuilder().getAttributesArray(); const geometry = this.geometry; const attributes = []; @@ -55,7 +76,7 @@ export default class WebGPURenderObject { const cacheKey = []; cacheKey.push( 'material:' + this._materialCacheKey ); - cacheKey.push( 'nodes:' + this.nodes.getCacheKey( scene, lightsNode ) ); + cacheKey.push( 'nodes:' + this._nodes.getCacheKey( scene, lightsNode ) ); return '{' + cacheKey.join( ',' ) + '}'; diff --git a/examples/jsm/renderers/universal/RenderObjects.js b/examples/jsm/renderers/universal/RenderObjects.js new file mode 100644 index 00000000000000..1501bfbda17504 --- /dev/null +++ b/examples/jsm/renderers/universal/RenderObjects.js @@ -0,0 +1,91 @@ +import DataMap from './DataMap.js'; +import ChainMap from './ChainMap.js'; +import RenderObject from './RenderObject.js'; + +class RenderObjects extends ChainMap { + + constructor( renderer, nodes, geometries, pipelines, info ) { + + super(); + + this.renderer = renderer; + this.nodes = nodes; + this.geometries = geometries; + this.pipelines = pipelines; + this.info = info; + + this.dataMap = new DataMap(); + + } + + get( object, material, scene, camera, lightsNode ) { + + const chainKey = [ object, material, scene, camera, lightsNode ]; + + let renderObject = super.get( chainKey ); + + if ( renderObject === undefined ) { + + renderObject = new RenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode ); + + this._initRenderObject( renderObject ); + + this.set( chainKey, renderObject ); + + } else { + + const data = this.dataMap.get( renderObject ); + const cacheKey = renderObject.getCacheKey(); + + if ( data.cacheKey !== cacheKey ) { + + data.cacheKey = cacheKey; + + this.pipelines.delete( renderObject ); + this.nodes.delete( renderObject ); + + } + + } + + return renderObject; + + } + + dispose() { + + super.dispose(); + + this.dataMap.clear(); + + } + + _initRenderObject( renderObject ) { + + const data = this.dataMap.get( renderObject ); + + if ( data.initialized !== true ) { + + data.initialized = true; + + const onDispose = () => { + + renderObject.material.removeEventListener( 'dispose', onDispose ); + + this.pipelines.delete( renderObject ); + this.nodes.delete( renderObject ); + + this.delete( chainKey ); + + }; + + renderObject.material.addEventListener( 'dispose', onDispose ); + + } + + } + + +} + +export default RenderObjects; diff --git a/examples/jsm/renderers/universal/RenderPipeline.js b/examples/jsm/renderers/universal/RenderPipeline.js new file mode 100644 index 00000000000000..d9aeadda798322 --- /dev/null +++ b/examples/jsm/renderers/universal/RenderPipeline.js @@ -0,0 +1,16 @@ +import Pipeline from './Pipeline.js'; + +class RenderPipeline extends Pipeline { + + constructor( cacheKey, vertexProgram, fragmentProgram ) { + + super( cacheKey ); + + this.vertexProgram = vertexProgram; + this.fragmentProgram = fragmentProgram; + + } + +} + +export default RenderPipeline; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderPipelines.js b/examples/jsm/renderers/universal/RenderPipelines.js similarity index 72% rename from examples/jsm/renderers/webgpu/WebGPURenderPipelines.js rename to examples/jsm/renderers/universal/RenderPipelines.js index f9bee8df6b380f..f51af414c04eb4 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderPipelines.js +++ b/examples/jsm/renderers/universal/RenderPipelines.js @@ -1,10 +1,11 @@ -import WebGPURenderPipeline from './WebGPURenderPipeline.js'; -import WebGPUProgrammableStage from './WebGPUProgrammableStage.js'; +import RenderPipeline from './RenderPipeline.js'; +import ProgrammableStage from './ProgrammableStage.js'; -class WebGPURenderPipelines { +class RenderPipelines { - constructor( device, nodes, utils ) { + constructor( backend, device, nodes, utils ) { + this.backend = backend; this.device = device; this.nodes = nodes; this.utils = utils; @@ -14,7 +15,7 @@ class WebGPURenderPipelines { this.pipelines = []; this.cache = new WeakMap(); - this.stages = { + this.programs = { vertex: new Map(), fragment: new Map() }; @@ -23,7 +24,7 @@ class WebGPURenderPipelines { get( renderObject ) { - const device = this.device; + const { backend, device } = this; const cache = this._getCache( renderObject ); let currentPipeline = cache.currentPipeline; @@ -40,21 +41,25 @@ class WebGPURenderPipelines { // programmable stages - let stageVertex = this.stages.vertex.get( nodeBuilder.vertexShader ); + let stageVertex = this.programs.vertex.get( nodeBuilder.vertexShader ); if ( stageVertex === undefined ) { - stageVertex = new WebGPUProgrammableStage( device, nodeBuilder.vertexShader, 'vertex' ); - this.stages.vertex.set( nodeBuilder.vertexShader, stageVertex ); + stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' ); + this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex ); + + backend.createProgram( stageVertex ); } - let stageFragment = this.stages.fragment.get( nodeBuilder.fragmentShader ); + let stageFragment = this.programs.fragment.get( nodeBuilder.fragmentShader ); if ( stageFragment === undefined ) { - stageFragment = new WebGPUProgrammableStage( device, nodeBuilder.fragmentShader, 'fragment' ); - this.stages.fragment.set( nodeBuilder.fragmentShader, stageFragment ); + stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' ); + this.programs.fragment.set( nodeBuilder.fragmentShader, stageFragment ); + + backend.createProgram( stageFragment ); } @@ -116,11 +121,13 @@ class WebGPURenderPipelines { if ( pipeline === undefined ) { - pipeline = new WebGPURenderPipeline( this.device, this.utils ); - pipeline.init( renderObject, cacheKey, stageVertex, stageFragment ); - + pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment ); pipelines.push( pipeline ); + renderObject.pipeline = pipeline; + + this.backend.createPipeline( renderObject ); + } return pipeline; @@ -129,8 +136,7 @@ class WebGPURenderPipelines { _computeCacheKey( renderObject, stageVertex, stageFragment ) { - const { object, material } = renderObject; - const utils = this.utils; + const { material } = renderObject; const parameters = [ stageVertex.id, stageFragment.id, @@ -143,9 +149,7 @@ class WebGPURenderPipelines { material.stencilFail, material.stencilZFail, material.stencilZPass, material.stencilFuncMask, material.stencilWriteMask, material.side, - utils.getSampleCount(), - utils.getCurrentColorSpace(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(), - utils.getPrimitiveTopology( object, material ) + this.backend.getCacheKey( renderObject ) ]; return parameters.join(); @@ -184,21 +188,21 @@ class WebGPURenderPipelines { pipelines[ i ] = pipelines[ pipelines.length - 1 ]; pipelines.pop(); - this._releaseStage( pipeline.stageVertex ); - this._releaseStage( pipeline.stageFragment ); + this._releaseProgram( pipeline.stageVertex ); + this._releaseProgram( pipeline.stageFragment ); } } - _releaseStage( stage ) { + _releaseProgram( stage ) { if ( -- stage.usedTimes === 0 ) { const code = stage.code; - const type = stage.type; + const stage = stage.stage; - this.stages[ type ].delete( code ); + this.programs[ stage ].delete( code ); } @@ -209,7 +213,7 @@ class WebGPURenderPipelines { const cache = this._getCache( renderObject ); const material = renderObject.material; - let needsUpdate = false; + let needsUpdate = this.backend.needsUpdate( renderObject ); // check pipeline state @@ -217,7 +221,8 @@ class WebGPURenderPipelines { // check material state - if ( cache.material !== material || cache.materialVersion !== material.version || + if ( needsUpdate === true || + cache.material !== material || cache.materialVersion !== material.version || cache.transparent !== material.transparent || cache.blending !== material.blending || cache.premultipliedAlpha !== material.premultipliedAlpha || cache.blendSrc !== material.blendSrc || cache.blendDst !== material.blendDst || cache.blendEquation !== material.blendEquation || cache.blendSrcAlpha !== material.blendSrcAlpha || cache.blendDstAlpha !== material.blendDstAlpha || cache.blendEquationAlpha !== material.blendEquationAlpha || @@ -244,31 +249,10 @@ class WebGPURenderPipelines { } - // check renderer state - - const utils = this.utils; - - const sampleCount = utils.getSampleCount(); - const colorSpace = utils.getCurrentColorSpace(); - const colorFormat = utils.getCurrentColorFormat(); - const depthStencilFormat = utils.getCurrentDepthStencilFormat(); - - if ( cache.sampleCount !== sampleCount || cache.colorSpace !== colorSpace || - cache.colorFormat !== colorFormat || cache.depthStencilFormat !== depthStencilFormat ) { - - cache.sampleCount = sampleCount; - cache.colorSpace = colorSpace; - cache.colorFormat = colorFormat; - cache.depthStencilFormat = depthStencilFormat; - - needsUpdate = true; - - } - return needsUpdate; } } -export default WebGPURenderPipelines; +export default RenderPipelines; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderTarget.js b/examples/jsm/renderers/universal/RenderTarget.js similarity index 69% rename from examples/jsm/renderers/webgpu/WebGPURenderTarget.js rename to examples/jsm/renderers/universal/RenderTarget.js index 4726614088457c..43d5b9cad56d0d 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderTarget.js +++ b/examples/jsm/renderers/universal/RenderTarget.js @@ -2,7 +2,7 @@ import { WebGLRenderTarget } from 'three'; // @TODO: Consider rename WebGLRenderTarget to just RenderTarget -class WebGPURenderTarget extends WebGLRenderTarget { +class RenderTarget extends WebGLRenderTarget { constructor( width, height, options = {} ) { @@ -12,4 +12,4 @@ class WebGPURenderTarget extends WebGLRenderTarget { } -export default WebGPURenderTarget; +export default RenderTarget; diff --git a/examples/jsm/renderers/universal/Renderer.js b/examples/jsm/renderers/universal/Renderer.js new file mode 100644 index 00000000000000..cd15f8f1526262 --- /dev/null +++ b/examples/jsm/renderers/universal/Renderer.js @@ -0,0 +1,740 @@ +import Animation from './Animation.js'; +import RenderObjects from './RenderObjects.js'; +import Attributes from './Attributes.js'; +import Geometries from './Geometries.js'; +import Info from './Info.js'; +import Pipelines from './Pipelines.js'; +import Bindings from './Bindings.js'; +import RenderLists from './RenderLists.js'; +import RenderContexts from './RenderContexts.js'; +import Textures from './Textures.js'; +import Background from './Background.js'; +import Nodes from './nodes/Nodes.js'; +import { Frustum, Matrix4, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping, DepthFormat } from 'three'; + +const _drawingBufferSize = new Vector4(); +const _frustum = new Frustum(); +const _projScreenMatrix = new Matrix4(); +const _vector3 = new Vector3(); + +class Renderer { + + constructor( backend ) { + + this.isRenderer = true; + + // public + + this.domElement = backend.getDomElement(); + + this.backend = backend; + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + this.outputColorSpace = SRGBColorSpace; + + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; + + this.sortObjects = true; + + // internals + + this._pixelRatio = 1; + this._width = this.domElement.width; + this._height = this.domElement.height; + + this._viewport = new Vector4( 0, 0, this._width, this._height ); + this._scissor = new Vector4( 0, 0, this._width, this._height ); + this._scissorTest = false; + + this._info = null; + this._properties = null; + this._attributes = null; + this._geometries = null; + this._nodes = null; + this._bindings = null; + this._objects = null; + this._pipelines = null; + this._renderLists = null; + this._renderContexts = null; + this._textures = null; + this._background = null; + + this._animation = new Animation(); + + this._currentRenderContext = null; + this._lastRenderContext = null; + + this._opaqueSort = null; + this._transparentSort = null; + + this._clearAlpha = 1; + this._clearColor = new Color( 0x000000 ); + this._clearDepth = 1; + this._clearStencil = 0; + + this._renderTarget = null; + + this._initialized = false; + this._initPromise = null; + + // backwards compatibility + + this.shadow = { + shadowMap: {} + }; + + } + + async init() { + + if ( this._initialized ) { + + throw new Error( 'WebGPURenderer: Device has already been initialized.' ); + + } + + if ( this._initPromise !== null ) { + + return this._initPromise; + + } + + this._initPromise = new Promise( async ( resolve, reject ) => { + + const backend = this.backend; + + try { + + await backend.init( this ); + + } catch( error ) { + + reject( error ); + return; + + } + + this._info = new Info(); + this._nodes = new Nodes( this, backend ); + this._attributes = new Attributes( backend ); + this._background = new Background( this, this._nodes ); + this._geometries = new Geometries( this._attributes, this._info ); + this._textures = new Textures( backend, this._info ); + this._pipelines = new Pipelines( backend, this._nodes ); + this._bindings = new Bindings( backend, this._nodes, this._textures, this._attributes, this._pipelines, this._info ); + this._objects = new RenderObjects( this, this._nodes, this._geometries, this._pipelines, this._info ); + this._renderLists = new RenderLists(); + this._renderContexts = new RenderContexts(); + + // + + this._animation.setNodes( this._nodes ); + this._animation.start(); + + this._initialized = true; + + resolve(); + + } ); + + return this._initPromise; + + } + + async render( scene, camera ) { + + if ( this._initialized === false ) await this.init(); + + // preserve render tree + + const nodeFrame = this._nodes.nodeFrame; + + const previousRenderId = nodeFrame.renderId; + const previousRenderState = this._currentRenderContext; + + // + + const renderContext = this._renderContexts.get( scene, camera ); + const renderTarget = this._renderTarget; + + this._currentRenderContext = renderContext; + + nodeFrame.renderId ++; + + // + + if ( this._animation.isAnimating === false ) nodeFrame.update(); + + if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); + + if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); + + if ( this._info.autoReset === true ) this._info.reset(); + + this._info.render.frame ++; + + // + + this.getDrawingBufferSize( _drawingBufferSize ); + + renderContext.viewportValue.copy( this._viewport ).multiplyScalar( this._pixelRatio ).floor(); + renderContext.viewport = renderContext.viewportValue.equals( _drawingBufferSize ) === false; + + renderContext.scissorValue.copy( this._scissor ).multiplyScalar( this._pixelRatio ).floor(); + renderContext.scissor = renderContext.scissorValue.equals( _drawingBufferSize ) === false; + + // + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromProjectionMatrix( _projScreenMatrix ); + + const renderList = this._renderLists.get( scene, camera ); + renderList.init(); + + this._projectObject( scene, camera, 0, renderList ); + + renderList.finish(); + + if ( this.sortObjects === true ) { + + renderList.sort( this._opaqueSort, this._transparentSort ); + + } + + // + + renderContext.renderTarget = renderTarget; + + // + + this._nodes.updateScene( scene ); + + // + + this._background.update( scene, renderList, renderContext ); + + // + + this.backend.beginRender( renderContext ); + + // process render lists + + const opaqueObjects = renderList.opaque; + const transparentObjects = renderList.transparent; + const lightsNode = renderList.lightsNode; + + if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode ); + if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode ); + + // finish render pass + + this.backend.finishRender( renderContext ); + + // restore render tree + + nodeFrame.renderId = previousRenderId; + this._currentRenderContext = previousRenderState; + + this._lastRenderContext = renderContext; + + } + + setAnimationLoop( callback ) { + + if ( this._initialized === false ) this.init(); + + const animation = this._animation; + + animation.setAnimationLoop( callback ); + + ( callback === null ) ? animation.stop() : animation.start(); + + } + + async getArrayBuffer( attribute ) { + + return await this._attributes.getArrayBuffer( attribute ); + + } + + getContext() { + + return this._context; + + } + + getPixelRatio() { + + return this._pixelRatio; + + } + + getDrawingBufferSize( target ) { + + return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor(); + + } + + getSize( target ) { + + return target.set( this._width, this._height ); + + } + + setPixelRatio( value = 1 ) { + + this._pixelRatio = value; + + this.setSize( this._width, this._height, false ); + + } + + setDrawingBufferSize( width, height, pixelRatio ) { + + this._width = width; + this._height = height; + + this._pixelRatio = pixelRatio; + + this.domElement.width = Math.floor( width * pixelRatio ); + this.domElement.height = Math.floor( height * pixelRatio ); + + this.setViewport( 0, 0, width, height ); + + if ( this._initialized ) this.backend.updateSize(); + + } + + setSize( width, height, updateStyle = true ) { + + this._width = width; + this._height = height; + + this.domElement.width = Math.floor( width * this._pixelRatio ); + this.domElement.height = Math.floor( height * this._pixelRatio ); + + if ( updateStyle === true ) { + + this.domElement.style.width = width + 'px'; + this.domElement.style.height = height + 'px'; + + } + + this.setViewport( 0, 0, width, height ); + + if ( this._initialized ) this.backend.updateSize(); + + } + + setOpaqueSort( method ) { + + this._opaqueSort = method; + + } + + setTransparentSort( method ) { + + this._transparentSort = method; + + } + + getScissor( target ) { + + const scissor = this._scissor; + + target.x = scissor.x; + target.y = scissor.y; + target.width = scissor.width; + target.height = scissor.height; + + return target; + + } + + setScissor( x, y, width, height ) { + + const scissor = this._scissor; + + if ( x.isVector4 ) { + + scissor.set( x.x, x.y, x.z, x.w ); + + } else { + + scissor.set( x, y, width, height ); + + } + + } + + getScissorTest() { + + return this._scissorTest; + + } + + setScissorTest( boolean ) { + + this._scissorTest = boolean; + + } + + copyFramebufferToRenderTarget( renderTarget ) { + + this.backend.copyFramebufferToRenderTarget( renderTarget ); + + } + + getViewport( target ) { + + return target.copy( this._viewport ); + + } + + setViewport( x, y, width, height /*minDepth = 0, maxDepth = 1*/ ) { + + const viewport = this._viewport; + + if ( x.isVector4 ) { + + viewport.copy( x ); + + } else { + + viewport.set( x, y, width, height ); + + } + + } + + getClearColor( target ) { + + return target.copy( this._clearColor ); + + } + + setClearColor( color, alpha = 1 ) { + + this._clearColor.set( color ); + this._clearAlpha = alpha; + + } + + getClearAlpha() { + + return this._clearAlpha; + + } + + setClearAlpha( alpha ) { + + this._clearAlpha = alpha; + + } + + getClearDepth() { + + return this._clearDepth; + + } + + setClearDepth( depth ) { + + this._clearDepth = depth; + + } + + getClearStencil() { + + return this._clearStencil; + + } + + setClearStencil( stencil ) { + + this._clearStencil = stencil; + + } + + clear( color = true, depth = true, stencil = true ) { + + this.backend.clear( color, depth, stencil ); + + } + + clearColor() { + + this.backend.clear( true, false, false ); + + } + + clearDepth() { + + this.backend.clear( false, true, false ); + + } + + clearStencil() { + + this.backend.clear( false, false, true ); + + } + + dispose() { + + this._objects.dispose(); + this._properties.dispose(); + this._pipelines.dispose(); + this._nodes.dispose(); + this._bindings.dispose(); + this._info.dispose(); + this._renderLists.dispose(); + this._renderContexts.dispose(); + this._textures.dispose(); + + this.setRenderTarget( null ); + this.setAnimationLoop( null ); + + } + + setRenderTarget( renderTarget ) { + + this._renderTarget = renderTarget; + + } + + async compute( computeNodes ) { + + if ( this._initialized === false ) await this.init(); + + const backend = this.backend; + const piplines = this._pipelines; + const computeGroup = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ]; + + backend.beginCompute( computeGroup ); + + for ( const computeNode of computeGroup ) { + + // onInit + + if ( piplines.has( computeNode ) === false ) { + + computeNode.onInit( { renderer: this } ); + + } + + this._nodes.updateForCompute( computeNode ); + this._bindings.updateForCompute( computeNode ); + + const computePipeline = piplines.getForCompute( computeNode ); + const computeBindings = this._bindings.getForCompute( computeNode ); + + backend.compute( computeGroup, computeNode, computeBindings, computePipeline ); + + } + + backend.finishCompute( computeGroup ); + + } + + getRenderTarget() { + + return this._renderTarget; + + } + + hasFeature( name ) { + + return this.backend.hasFeature( name ); + + } + + _projectObject( object, camera, groupOrder, renderList ) { + + if ( object.visible === false ) return; + + const visible = object.layers.test( camera.layers ); + + if ( visible ) { + + if ( object.isGroup ) { + + groupOrder = object.renderOrder; + + } else if ( object.isLOD ) { + + if ( object.autoUpdate === true ) object.update( camera ); + + } else if ( object.isLight ) { + + renderList.pushLight( object ); + + } else if ( object.isSprite ) { + + if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { + + if ( this.sortObjects === true ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix ); + + } + + const geometry = object.geometry; + const material = object.material; + + if ( material.visible ) { + + renderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + + } + + } + + } else if ( object.isLineLoop ) { + + console.error( 'THREE.WebGPURenderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' ); + + } else if ( object.isMesh || object.isLine || object.isPoints ) { + + if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + + const geometry = object.geometry; + const material = object.material; + + if ( this.sortObjects === true ) { + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + _vector3 + .copy( geometry.boundingSphere.center ) + .applyMatrix4( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); + + } + + if ( Array.isArray( material ) ) { + + const groups = geometry.groups; + + for ( let i = 0, l = groups.length; i < l; i ++ ) { + + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; + + if ( groupMaterial && groupMaterial.visible ) { + + renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + + } + + } + + } else if ( material.visible ) { + + renderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + + } + + } + + } + + } + + const children = object.children; + + for ( let i = 0, l = children.length; i < l; i ++ ) { + + this._projectObject( children[ i ], camera, groupOrder, renderList ); + + } + + } + + _renderObjects( renderList, camera, scene, lightsNode ) { + + // process renderable objects + + for ( let i = 0, il = renderList.length; i < il; i ++ ) { + + const renderItem = renderList[ i ]; + + // @TODO: Add support for multiple materials per object. This will require to extract + // the material from the renderItem object and pass it with its group data to _renderObject(). + + const { object, geometry, material, group } = renderItem; + + if ( camera.isArrayCamera ) { + + const cameras = camera.cameras; + + for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { + + const camera2 = cameras[ j ]; + + if ( object.layers.test( camera2.layers ) ) { + + const vp = camera2.viewport; + const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth; + const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth; + + this._currentRenderContext.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth ); + + this._renderObject( object, scene, camera2, geometry, material, group, lightsNode ); + + } + + } + + } else { + + this._renderObject( object, scene, camera, geometry, material, group, lightsNode ); + + } + + } + + } + + _renderObject( object, scene, camera, geometry, material, group, lightsNode ) { + + material = scene.overrideMaterial !== null ? scene.overrideMaterial : material; + + // + + object.onBeforeRender( this, scene, camera, geometry, material, group ); + + // + + const renderObject = this._objects.get( object, material, scene, camera, lightsNode ); + renderObject.context = this._currentRenderContext; + + // + + this._nodes.updateBefore( renderObject ); + + // + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + + // + + this._nodes.updateForRender( renderObject ); + this._geometries.update( renderObject ); + this._bindings.updateForRender( renderObject ); + + // + + this.backend.draw( renderObject, this._info ); + + } + +} + +export default Renderer; diff --git a/examples/jsm/renderers/universal/SampledTexture.js b/examples/jsm/renderers/universal/SampledTexture.js new file mode 100644 index 00000000000000..be4b4c25118c24 --- /dev/null +++ b/examples/jsm/renderers/universal/SampledTexture.js @@ -0,0 +1,72 @@ +import Binding from './Binding.js'; + +let id = 0; + +class SampledTexture extends Binding { + + constructor( name, texture ) { + + super( name ); + + this.id = id ++; + + this.texture = texture; + this.version = 0; + + this.isSampledTexture = true; + + } + + update() { + + if ( this.version !== this.texture.version ) { + + this.version = this.texture.version; + + return true; + + } + + return false; + + } + +} + +class WebGPUSampledArrayTexture extends SampledTexture { + + constructor( name, texture ) { + + super( name, texture ); + + this.isSampledArrayTexture = true; + + } + +} + +class WebGPUSampled3DTexture extends SampledTexture { + + constructor( name, texture ) { + + super( name, texture ); + + this.isSampled3DTexture = true; + + } + +} + +class WebGPUSampledCubeTexture extends SampledTexture { + + constructor( name, texture ) { + + super( name, texture ); + + this.isSampledCubeTexture = true; + + } + +} + +export { SampledTexture as WebGPUSampledTexture, WebGPUSampledArrayTexture, WebGPUSampled3DTexture, WebGPUSampledCubeTexture }; diff --git a/examples/jsm/renderers/universal/Sampler.js b/examples/jsm/renderers/universal/Sampler.js new file mode 100644 index 00000000000000..d489d503ad1de3 --- /dev/null +++ b/examples/jsm/renderers/universal/Sampler.js @@ -0,0 +1,19 @@ +import Binding from './Binding.js'; +//import { GPUBindingType } from './constants.js'; + +class Sampler extends Binding { + + constructor( name, texture ) { + + super( name ); + + this.texture = texture; + this.version = 0; + + this.isSampler = true; + + } + +} + +export default Sampler; diff --git a/examples/jsm/renderers/universal/StorageBuffer.js b/examples/jsm/renderers/universal/StorageBuffer.js new file mode 100644 index 00000000000000..5a9df742d90e38 --- /dev/null +++ b/examples/jsm/renderers/universal/StorageBuffer.js @@ -0,0 +1,18 @@ +import Buffer from './Buffer.js'; +//import { GPUBindingType } from './constants.js'; + +class StorageBuffer extends Buffer { + + constructor( name, attribute ) { + + super( name, attribute.array ); + + this.attribute = attribute; + + this.isStorageBuffer = true; + + } + +} + +export default StorageBuffer; diff --git a/examples/jsm/renderers/webgpu/WebGPUTextureRenderer.js b/examples/jsm/renderers/universal/TextureRenderer.js similarity index 70% rename from examples/jsm/renderers/webgpu/WebGPUTextureRenderer.js rename to examples/jsm/renderers/universal/TextureRenderer.js index da82960b6784a1..61c42ab83da43d 100644 --- a/examples/jsm/renderers/webgpu/WebGPUTextureRenderer.js +++ b/examples/jsm/renderers/universal/TextureRenderer.js @@ -1,12 +1,12 @@ -import WebGPURenderTarget from './WebGPURenderTarget.js'; +import RenderTarget from './RenderTarget.js'; -class WebGPUTextureRenderer { +class TextureRenderer { constructor( renderer, options = {} ) { this.renderer = renderer; - this.renderTarget = new WebGPURenderTarget( 1, 1, options ); + this.renderTarget = new RenderTarget( 1, 1, options ); } @@ -35,4 +35,4 @@ class WebGPUTextureRenderer { } -export default WebGPUTextureRenderer; +export default TextureRenderer; diff --git a/examples/jsm/renderers/webgpu/WebGPUTextureUtils.js b/examples/jsm/renderers/universal/TextureUtils.js similarity index 95% rename from examples/jsm/renderers/webgpu/WebGPUTextureUtils.js rename to examples/jsm/renderers/universal/TextureUtils.js index 535df382201890..0b78e4e2636532 100644 --- a/examples/jsm/renderers/webgpu/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/universal/TextureUtils.js @@ -18,11 +18,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './constants.js'; +//import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './constants.js'; // ported from https://github.com/toji/web-texture-tool/blob/master/src/webgpu-mipmap-generator.js -class WebGPUTextureUtils { +class TextureUtils { constructor( device ) { @@ -182,4 +182,4 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { } -export default WebGPUTextureUtils; +export default TextureUtils; diff --git a/examples/jsm/renderers/universal/Textures.js b/examples/jsm/renderers/universal/Textures.js new file mode 100644 index 00000000000000..e27ac17417ea39 --- /dev/null +++ b/examples/jsm/renderers/universal/Textures.js @@ -0,0 +1,87 @@ +import DataMap from './DataMap.js'; + +class Textures extends DataMap { + + constructor( backend, info ) { + + super(); + + this.backend = backend; + this.info = info; + + } + + initTexture( texture ) { + + const data = this.get( texture ); + if ( data.initialized ) return; + + data.initialized = true; + + this.backend.createSampler( texture ); + this.backend.createDefaultTexture( texture ); + + this.info.memory.textures ++; + + // dispose + + const onDispose = () => { + + texture.removeEventListener( 'dispose', onDispose ); + + this.backend.destroyTexture( texture ); + + this.delete( texture ); + + this.info.memory.textures --; + + } + + texture.addEventListener( 'dispose', onDispose ); + + } + + updateTexture( texture ) { + + let needsUpdate = false; + + const data = this.get( texture ); + + if ( texture.version > 0 && data.version !== texture.version ) { + + const image = texture.image; + + if ( image === undefined ) { + + console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' ); + + } else if ( image.complete === false ) { + + console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' ); + + } else { + + // + + this.initTexture( texture ); + + // + + this.backend.createTexture( texture ); + this.backend.updateTexture( texture ); + + data.version = texture.version; + + needsUpdate = true; + + } + + } + + return needsUpdate; + + } + +} + +export default Textures; diff --git a/examples/jsm/renderers/webgpu/WebGPUUniform.js b/examples/jsm/renderers/universal/Uniform.js similarity index 77% rename from examples/jsm/renderers/webgpu/WebGPUUniform.js rename to examples/jsm/renderers/universal/Uniform.js index 6ca6afb71cbeeb..ba87bdcfeac6d6 100644 --- a/examples/jsm/renderers/webgpu/WebGPUUniform.js +++ b/examples/jsm/renderers/universal/Uniform.js @@ -1,6 +1,6 @@ import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three'; -class WebGPUUniform { +class Uniform { constructor( name, value = null ) { @@ -28,7 +28,7 @@ class WebGPUUniform { } -class FloatUniform extends WebGPUUniform { +class FloatUniform extends Uniform { constructor( name, value = 0 ) { @@ -43,7 +43,7 @@ class FloatUniform extends WebGPUUniform { } -class Vector2Uniform extends WebGPUUniform { +class Vector2Uniform extends Uniform { constructor( name, value = new Vector2() ) { @@ -58,7 +58,7 @@ class Vector2Uniform extends WebGPUUniform { } -class Vector3Uniform extends WebGPUUniform { +class Vector3Uniform extends Uniform { constructor( name, value = new Vector3() ) { @@ -73,7 +73,7 @@ class Vector3Uniform extends WebGPUUniform { } -class Vector4Uniform extends WebGPUUniform { +class Vector4Uniform extends Uniform { constructor( name, value = new Vector4() ) { @@ -88,7 +88,7 @@ class Vector4Uniform extends WebGPUUniform { } -class ColorUniform extends WebGPUUniform { +class ColorUniform extends Uniform { constructor( name, value = new Color() ) { @@ -103,7 +103,7 @@ class ColorUniform extends WebGPUUniform { } -class Matrix3Uniform extends WebGPUUniform { +class Matrix3Uniform extends Uniform { constructor( name, value = new Matrix3() ) { @@ -118,7 +118,7 @@ class Matrix3Uniform extends WebGPUUniform { } -class Matrix4Uniform extends WebGPUUniform { +class Matrix4Uniform extends Uniform { constructor( name, value = new Matrix4() ) { @@ -133,4 +133,8 @@ class Matrix4Uniform extends WebGPUUniform { } -export { FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, Matrix3Uniform, Matrix4Uniform }; +export { + FloatUniform, + Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, + Matrix3Uniform, Matrix4Uniform +}; diff --git a/examples/jsm/renderers/universal/UniformBuffer.js b/examples/jsm/renderers/universal/UniformBuffer.js new file mode 100644 index 00000000000000..4edd9f8f8e6be4 --- /dev/null +++ b/examples/jsm/renderers/universal/UniformBuffer.js @@ -0,0 +1,15 @@ +import Buffer from './Buffer.js'; + +class UniformBuffer extends Buffer { + + constructor( name, buffer = null ) { + + super( name, buffer ); + + this.isUniformBuffer = true; + + } + +} + +export default UniformBuffer; diff --git a/examples/jsm/renderers/webgpu/WebGPUUniformsGroup.js b/examples/jsm/renderers/universal/UniformsGroup.js similarity index 88% rename from examples/jsm/renderers/webgpu/WebGPUUniformsGroup.js rename to examples/jsm/renderers/universal/UniformsGroup.js index af376d87d1f0da..48e9c592935233 100644 --- a/examples/jsm/renderers/webgpu/WebGPUUniformsGroup.js +++ b/examples/jsm/renderers/universal/UniformsGroup.js @@ -1,7 +1,7 @@ -import WebGPUUniformBuffer from './WebGPUUniformBuffer.js'; -import { GPUChunkSize } from './constants.js'; +import UniformBuffer from './UniformBuffer.js'; +import { GPUChunkBytes } from './constants.js'; -class WebGPUUniformsGroup extends WebGPUUniformBuffer { +class UniformsGroup extends UniformBuffer { constructor( name ) { @@ -65,8 +65,8 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { // offset within a single chunk in bytes - const chunkOffset = offset % GPUChunkSize; - const remainingSizeInChunk = GPUChunkSize - chunkOffset; + const chunkOffset = offset % GPUChunkBytes; + const remainingSizeInChunk = GPUChunkBytes - chunkOffset; // conformance tests @@ -74,7 +74,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { // check for chunk overflow - offset += ( GPUChunkSize - chunkOffset ); + offset += ( GPUChunkBytes - chunkOffset ); } else if ( chunkOffset % uniform.boundary !== 0 ) { @@ -90,7 +90,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { } - return Math.ceil( offset / GPUChunkSize ) * GPUChunkSize; + return Math.ceil( offset / GPUChunkBytes ) * GPUChunkBytes; } @@ -130,7 +130,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const v = uniform.getValue(); const offset = uniform.offset; @@ -149,7 +149,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const v = uniform.getValue(); const offset = uniform.offset; @@ -170,7 +170,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const v = uniform.getValue(); const offset = uniform.offset; @@ -192,7 +192,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const v = uniform.getValue(); const offset = uniform.offset; @@ -215,7 +215,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const c = uniform.getValue(); const offset = uniform.offset; @@ -237,7 +237,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const e = uniform.getValue().elements; const offset = uniform.offset; @@ -267,7 +267,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer { let updated = false; - const a = this.buffer; + const a = this.getBuffer(); const e = uniform.getValue().elements; const offset = uniform.offset; @@ -296,4 +296,4 @@ function arraysEqual( a, b, offset ) { } -export default WebGPUUniformsGroup; +export default UniformsGroup; diff --git a/examples/jsm/renderers/universal/constants.js b/examples/jsm/renderers/universal/constants.js new file mode 100644 index 00000000000000..71d984237cae12 --- /dev/null +++ b/examples/jsm/renderers/universal/constants.js @@ -0,0 +1,14 @@ +export const AttributeType = { + VERTEX: 1, + INDEX: 2, + STORAGE: 4 +}; + +// size of a chunk in bytes (STD140 layout) + +export const GPUChunkBytes = 16; + +// @TODO: Move to src/constants.js + +export const BlendColorFactor = 211; +export const OneMinusBlendColorFactor = 212; diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js b/examples/jsm/renderers/universal/nodes/NodeRender.js similarity index 98% rename from examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js rename to examples/jsm/renderers/universal/nodes/NodeRender.js index c932b6fae86276..cda2af2d8da79a 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js +++ b/examples/jsm/renderers/universal/nodes/NodeRender.js @@ -1,8 +1,8 @@ -import WebGPUNodeBuilder from './WebGPUNodeBuilder.js'; +import WebGPUNodeBuilder from '../../webgpu/backends/webgpu/builder/WGSLNodeBuilder.js'; import { NoToneMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three'; import { NodeFrame, cubeTexture, texture, rangeFog, densityFog, reference, toneMapping, positionWorld, modelWorldMatrix, transformDirection, equirectUV, viewportBottomLeft } from '../../../nodes/Nodes.js'; -class WebGPUNodes { +class NodeRender { constructor( renderer, properties ) { @@ -299,4 +299,4 @@ class WebGPUNodes { } -export default WebGPUNodes; +export default NodeRender; diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampledTexture.js b/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js similarity index 95% rename from examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampledTexture.js rename to examples/jsm/renderers/universal/nodes/NodeSampledTexture.js index 12ce201de3852b..3f0ffcf8288ee1 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampledTexture.js +++ b/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js @@ -1,4 +1,4 @@ -import { WebGPUSampledTexture, WebGPUSampledCubeTexture } from '../WebGPUSampledTexture.js'; +import { WebGPUSampledTexture, WebGPUSampledCubeTexture } from '../SampledTexture.js'; class WebGPUNodeSampledTexture extends WebGPUSampledTexture { diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampler.js b/examples/jsm/renderers/universal/nodes/NodeSampler.js similarity index 68% rename from examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampler.js rename to examples/jsm/renderers/universal/nodes/NodeSampler.js index c0a645b6ae4963..0b727582f55fcd 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampler.js +++ b/examples/jsm/renderers/universal/nodes/NodeSampler.js @@ -1,6 +1,6 @@ -import WebGPUSampler from '../WebGPUSampler.js'; +import Sampler from '../Sampler.js'; -class WebGPUNodeSampler extends WebGPUSampler { +class WebGPUNodeSampler extends Sampler { constructor( name, textureNode ) { diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniform.js b/examples/jsm/renderers/universal/nodes/NodeUniform.js similarity index 98% rename from examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniform.js rename to examples/jsm/renderers/universal/nodes/NodeUniform.js index 06239366a0db36..ff89218d8dad06 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniform.js +++ b/examples/jsm/renderers/universal/nodes/NodeUniform.js @@ -1,7 +1,7 @@ import { FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, Matrix3Uniform, Matrix4Uniform -} from '../WebGPUUniform.js'; +} from '../Uniform.js'; class FloatNodeUniform extends FloatUniform { diff --git a/examples/jsm/renderers/universal/nodes/Nodes.js b/examples/jsm/renderers/universal/nodes/Nodes.js new file mode 100644 index 00000000000000..81d3d7ac25426f --- /dev/null +++ b/examples/jsm/renderers/universal/nodes/Nodes.js @@ -0,0 +1,318 @@ +import DataMap from '../DataMap.js'; +import { NoToneMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three'; +import { NodeFrame, cubeTexture, texture, rangeFog, densityFog, reference, toneMapping, positionWorld, modelWorldMatrix, transformDirection, equirectUV, viewportBottomLeft } from '../../../nodes/Nodes.js'; + +class Nodes extends DataMap { + + constructor( renderer, backend ) { + + super(); + + this.renderer = renderer; + this.backend = backend; + this.nodeFrame = new NodeFrame(); + + } + + getForRender( renderObject ) { + + const renderObjectProperties = this.get( renderObject ); + + let nodeBuilder = renderObjectProperties.nodeBuilder; + + if ( nodeBuilder === undefined ) { + + nodeBuilder = this.backend.createNodeBuilder( renderObject.object, this.renderer ); + nodeBuilder.material = renderObject.material; + nodeBuilder.lightsNode = renderObject.lightsNode; + nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene ); + nodeBuilder.fogNode = this.getFogNode( renderObject.scene ); + nodeBuilder.toneMappingNode = this.getToneMappingNode(); + nodeBuilder.build(); + + renderObjectProperties.nodeBuilder = nodeBuilder; + + } + + return nodeBuilder; + + } + + getForCompute( computeNode ) { + + const computeProperties = this.get( computeNode ); + + let nodeBuilder = computeProperties.nodeBuilder; + + if ( nodeBuilder === undefined ) { + + nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer ); + nodeBuilder.build(); + + computeProperties.nodeBuilder = nodeBuilder; + + } + + return nodeBuilder; + + } + + getEnvironmentNode( scene ) { + + return scene.environmentNode || this.get( scene ).environmentNode || null; + + } + + getBackgroundNode( scene ) { + + return scene.backgroundNode || this.get( scene ).backgroundNode || null; + + } + + getFogNode( scene ) { + + return scene.fogNode || this.get( scene ).fogNode || null; + + } + + getToneMappingNode() { + + return this.renderer.toneMappingNode || this.get( this.renderer ).toneMappingNode || null; + + } + + getCacheKey( scene, lightsNode ) { + + const environmentNode = this.getEnvironmentNode( scene ); + const fogNode = this.getFogNode( scene ); + const toneMappingNode = this.getToneMappingNode(); + + const cacheKey = []; + + if ( lightsNode ) cacheKey.push( 'lightsNode:' + lightsNode.getCacheKey() ); + if ( environmentNode ) cacheKey.push( 'environmentNode:' + environmentNode.getCacheKey() ); + if ( fogNode ) cacheKey.push( 'fogNode:' + fogNode.getCacheKey() ); + if ( toneMappingNode ) cacheKey.push( 'toneMappingNode:' + toneMappingNode.getCacheKey() ); + + return '{' + cacheKey.join( ',' ) + '}'; + + } + + updateScene( scene ) { + + this.updateEnvironment( scene ); + this.updateFog( scene ); + this.updateBackground( scene ); + this.updateToneMapping(); + + } + + updateToneMapping() { + + const renderer = this.renderer; + const rendererProperties = this.get( renderer ); + const rendererToneMapping = renderer.toneMapping; + + if ( rendererToneMapping !== NoToneMapping ) { + + if ( rendererProperties.toneMapping !== rendererToneMapping ) { + + rendererProperties.toneMappingNode = toneMapping( rendererToneMapping, reference( 'toneMappingExposure', 'float', renderer ) ); + rendererProperties.toneMapping = rendererToneMapping; + + } + + } else { + + delete rendererProperties.toneMappingNode; + delete rendererProperties.toneMapping; + + } + + } + + updateBackground( scene ) { + + const sceneProperties = this.get( scene ); + const background = scene.background; + + if ( background ) { + + if ( sceneProperties.background !== background ) { + + let backgroundNode = null; + + if ( background.isCubeTexture === true ) { + + backgroundNode = cubeTexture( background, transformDirection( positionWorld, modelWorldMatrix ) ); + + } else if ( background.isTexture === true ) { + + let nodeUV = null; + + if ( background.mapping === EquirectangularReflectionMapping || background.mapping === EquirectangularRefractionMapping ) { + + nodeUV = equirectUV(); + + } else { + + nodeUV = viewportBottomLeft; + + } + + backgroundNode = texture( background, nodeUV ); + + } else if ( background.isColor !== true ) { + + console.error( 'WebGPUNodes: Unsupported background configuration.', background ); + + } + + sceneProperties.backgroundNode = backgroundNode; + sceneProperties.background = background; + + } + + } else if ( sceneProperties.backgroundNode ) { + + delete sceneProperties.backgroundNode; + delete sceneProperties.background; + + } + + } + + updateFog( scene ) { + + const sceneProperties = this.get( scene ); + const fog = scene.fog; + + if ( fog ) { + + if ( sceneProperties.fog !== fog ) { + + let fogNode = null; + + if ( fog.isFogExp2 ) { + + fogNode = densityFog( reference( 'color', 'color', fog ), reference( 'density', 'float', fog ) ); + + } else if ( fog.isFog ) { + + fogNode = rangeFog( reference( 'color', 'color', fog ), reference( 'near', 'float', fog ), reference( 'far', 'float', fog ) ); + + } else { + + console.error( 'WebGPUNodes: Unsupported fog configuration.', fog ); + + } + + sceneProperties.fogNode = fogNode; + sceneProperties.fog = fog; + + } + + } else { + + delete sceneProperties.fogNode; + delete sceneProperties.fog; + + } + + } + + updateEnvironment( scene ) { + + const sceneProperties = this.get( scene ); + const environment = scene.environment; + + if ( environment ) { + + if ( sceneProperties.environment !== environment ) { + + let environmentNode = null; + + if ( environment.isCubeTexture === true ) { + + environmentNode = cubeTexture( environment ); + + } else if ( environment.isTexture === true ) { + + environmentNode = texture( environment ); + + } else { + + console.error( 'WebGPUNodes: Unsupported environment configuration.', environment ); + + } + + sceneProperties.environmentNode = environmentNode; + sceneProperties.environment = environment; + + } + + } else if ( sceneProperties.environmentNode ) { + + delete sceneProperties.environmentNode; + delete sceneProperties.environment; + + } + + } + + getNodeFrame( 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 nodeFrame; + + } + + updateBefore( renderObject ) { + + const nodeFrame = this.getNodeFrame( renderObject ); + const nodeBuilder = this.getForRender( renderObject ); + + for ( const node of nodeBuilder.updateBeforeNodes ) { + + nodeFrame.updateBeforeNode( node ); + + } + + } + + updateForCompute( computeNode ) { + + + + } + + updateForRender( renderObject ) { + + const nodeFrame = this.getNodeFrame( renderObject ); + const nodeBuilder = this.getForRender( renderObject ); + + for ( const node of nodeBuilder.updateNodes ) { + + nodeFrame.updateNode( node ); + + } + + } + + dispose() { + + super.dispose(); + + this.nodeFrame = new NodeFrame(); + + } + +} + +export default Nodes; diff --git a/examples/jsm/renderers/webgpu/WebGPUAttributes.js b/examples/jsm/renderers/webgpu/WebGPUAttributes.js deleted file mode 100644 index 881d1f809f528b..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUAttributes.js +++ /dev/null @@ -1,199 +0,0 @@ -import { DynamicDrawUsage } from "three"; - -class WebGPUAttributes { - - constructor( device ) { - - this.buffers = new WeakMap(); - this.device = device; - - } - - get( attribute ) { - - const bufferAttribute = this._getBufferAttribute( attribute ); - - return this.buffers.get( bufferAttribute ); - - } - - remove( attribute ) { - - const bufferAttribute = this._getBufferAttribute( attribute ); - - const data = this.buffers.get( bufferAttribute ); - - if ( data ) { - - this._destroyBuffers( data ); - - this.buffers.delete( bufferAttribute ); - - } - - } - - update( attribute, isIndex = false, gpuUsage = null ) { - - const bufferAttribute = this._getBufferAttribute( attribute ); - - let data = this.buffers.get( bufferAttribute ); - - if ( data === undefined ) { - - if ( gpuUsage === null ) { - - gpuUsage = ( isIndex === true ) ? GPUBufferUsage.INDEX : GPUBufferUsage.VERTEX; - - } - - data = this._createBuffer( bufferAttribute, gpuUsage ); - - this.buffers.set( bufferAttribute, data ); - - } else if ( gpuUsage && gpuUsage !== data.usage ) { - - this._destroyBuffers( data ); - - data = this._createBuffer( bufferAttribute, gpuUsage ); - - this.buffers.set( bufferAttribute, data ); - - } else if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) { - - this._writeBuffer( data.buffer, bufferAttribute ); - - data.version = bufferAttribute.version; - - } - - } - - async getArrayBuffer( attribute ) { - - const data = this.get( attribute ); - const device = this.device; - - const gpuBuffer = data.buffer; - const size = gpuBuffer.size; - - let gpuReadBuffer = data.readBuffer; - let needsUnmap = true; - - if ( gpuReadBuffer === null ) { - - gpuReadBuffer = device.createBuffer( { - label: attribute.name, - size, - usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ - } ); - - needsUnmap = false; - - data.readBuffer = gpuReadBuffer; - - } - - const cmdEncoder = device.createCommandEncoder( {} ); - - cmdEncoder.copyBufferToBuffer( - gpuBuffer, - 0, - gpuReadBuffer, - 0, - size - ); - - if ( needsUnmap ) gpuReadBuffer.unmap(); - - const gpuCommands = cmdEncoder.finish(); - device.queue.submit( [ gpuCommands ] ); - - await gpuReadBuffer.mapAsync( GPUMapMode.READ ); - - const arrayBuffer = gpuReadBuffer.getMappedRange(); - - return arrayBuffer; - - } - - _getBufferAttribute( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - return attribute; - - } - - _createBuffer( bufferAttribute, usage ) { - - const array = bufferAttribute.array; - const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441 - - const buffer = this.device.createBuffer( { - label: bufferAttribute.name, - size, - usage: usage | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, - mappedAtCreation: true - } ); - - new array.constructor( buffer.getMappedRange() ).set( array ); - - buffer.unmap(); - - bufferAttribute.onUploadCallback(); - - return { - version: bufferAttribute.version, - buffer, - readBuffer: null, - usage - }; - - } - - _writeBuffer( buffer, attribute ) { - - const device = this.device; - - const array = attribute.array; - const updateRange = attribute.updateRange; - - if ( updateRange.count === - 1 ) { - - // Not using update ranges - - device.queue.writeBuffer( - buffer, - 0, - array, - 0 - ); - - } else { - - device.queue.writeBuffer( - buffer, - 0, - array, - updateRange.offset * array.BYTES_PER_ELEMENT, - updateRange.count * array.BYTES_PER_ELEMENT - ); - - updateRange.count = - 1; // reset range - - } - - } - - _destroyBuffers( { buffer, readBuffer } ) { - - buffer.destroy(); - - if ( readBuffer !== null ) readBuffer.destroy(); - - } - -} - -export default WebGPUAttributes; diff --git a/examples/jsm/renderers/webgpu/WebGPUBackground.js b/examples/jsm/renderers/webgpu/WebGPUBackground.js deleted file mode 100644 index d1e2fa66c9d7f8..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUBackground.js +++ /dev/null @@ -1,182 +0,0 @@ -import { GPULoadOp, GPUStoreOp } from './constants.js'; -import { Color, Mesh, BoxGeometry, BackSide } from 'three'; -import { context, positionWorldDirection, MeshBasicNodeMaterial } from '../../nodes/Nodes.js'; - -let _clearAlpha; -const _clearColor = new Color(); - -class WebGPUBackground { - - constructor( renderer, properties ) { - - this.renderer = renderer; - this.properties = properties; - - this.boxMesh = null; - this.boxMeshNode = null; - - } - - update( scene, renderList, renderState ) { - - const renderer = this.renderer; - const background = ( scene.isScene === true ) ? scene.backgroundNode || this.properties.get( scene ).backgroundNode || scene.background : null; - - let forceClear = false; - - if ( background === null ) { - - // no background settings, use clear color configuration from the renderer - - _clearColor.copy( renderer._clearColor ); - _clearAlpha = renderer._clearAlpha; - - } else if ( background.isColor === true ) { - - // background is an opaque color - - _clearColor.copy( background ); - _clearAlpha = 1; - forceClear = true; - - } else if ( background.isNode === true ) { - - const sceneProperties = this.properties.get( scene ); - const backgroundNode = background; - - _clearColor.copy( renderer._clearColor ); - _clearAlpha = renderer._clearAlpha; - - let boxMesh = this.boxMesh; - - if ( boxMesh === null ) { - - this.boxMeshNode = context( backgroundNode, { - // @TODO: Add Texture2D support using node context - getUVNode: () => positionWorldDirection - } ); - - const nodeMaterial = new MeshBasicNodeMaterial(); - nodeMaterial.colorNode = this.boxMeshNode; - nodeMaterial.side = BackSide; - nodeMaterial.depthTest = false; - nodeMaterial.depthWrite = false; - nodeMaterial.fog = false; - - this.boxMesh = boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), nodeMaterial ); - boxMesh.frustumCulled = false; - - boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - - const scale = camera.far; - - this.matrixWorld.makeScale( scale, scale, scale ).copyPosition( camera.matrixWorld ); - - }; - - } - - const backgroundCacheKey = backgroundNode.getCacheKey(); - - if ( sceneProperties.backgroundCacheKey !== backgroundCacheKey ) { - - this.boxMeshNode.node = backgroundNode; - - boxMesh.material.needsUpdate = true; - - sceneProperties.backgroundCacheKey = backgroundCacheKey; - - } - - renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); - - } else { - - console.error( 'THREE.WebGPURenderer: Unsupported background configuration.', background ); - - } - - // configure render pass descriptor - - const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ]; - const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment; - - if ( renderer.autoClear === true || forceClear === true ) { - - if ( renderer.autoClearColor === true ) { - - _clearColor.multiplyScalar( _clearAlpha ); - - colorAttachment.clearValue = { r: _clearColor.r, g: _clearColor.g, b: _clearColor.b, a: _clearAlpha }; - colorAttachment.loadOp = GPULoadOp.Clear; - colorAttachment.storeOp = GPUStoreOp.Store; - - } else { - - colorAttachment.loadOp = GPULoadOp.Load; - colorAttachment.storeOp = GPUStoreOp.Store; - - } - - if ( renderState.depth ) { - - if ( renderer.autoClearDepth === true ) { - - depthStencilAttachment.depthClearValue = renderer._clearDepth; - depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; - depthStencilAttachment.depthStoreOp = GPUStoreOp.Store; - - } else { - - depthStencilAttachment.depthLoadOp = GPULoadOp.Load; - depthStencilAttachment.depthStoreOp = GPUStoreOp.Store; - - } - - } - - if ( renderState.stencil ) { - - if ( renderer.autoClearStencil === true ) { - - depthStencilAttachment.stencilClearValue = renderer._clearStencil; - depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; - depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; - - } else { - - depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; - depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; - - } - - } - - } else { - - colorAttachment.loadOp = GPULoadOp.Load; - colorAttachment.storeOp = GPUStoreOp.Store; - - if ( renderState.depth ) { - - depthStencilAttachment.depthLoadOp = GPULoadOp.Load; - depthStencilAttachment.depthStoreOp = GPUStoreOp.Store; - - } - - if ( renderState.stencil ) { - - depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; - depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; - - } - - } - - this.forceClear = false; - - } - -} - -export default WebGPUBackground; diff --git a/examples/jsm/renderers/webgpu/WebGPUBinding.js b/examples/jsm/renderers/webgpu/WebGPUBinding.js deleted file mode 100644 index 9dfb302b38159a..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUBinding.js +++ /dev/null @@ -1,22 +0,0 @@ -class WebGPUBinding { - - constructor( name = '' ) { - - this.name = name; - this.visibility = null; - - this.type = null; // read-only - - this.isShared = false; - - } - - setVisibility( visibility ) { - - this.visibility = visibility; - - } - -} - -export default WebGPUBinding; diff --git a/examples/jsm/renderers/webgpu/WebGPUBindings.js b/examples/jsm/renderers/webgpu/WebGPUBindings.js deleted file mode 100644 index 07d75b54ef4d15..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUBindings.js +++ /dev/null @@ -1,270 +0,0 @@ -class WebGPUBindings { - - constructor( device, info, properties, textures, renderPipelines, computePipelines, attributes, nodes ) { - - this.device = device; - this.info = info; - this.properties = properties; - this.textures = textures; - this.renderPipelines = renderPipelines; - this.computePipelines = computePipelines; - this.attributes = attributes; - this.nodes = nodes; - - this.uniformsData = new WeakMap(); - - this.updateMap = new WeakMap(); - - } - - get( renderObject ) { - - let data = this.uniformsData.get( renderObject ); - - if ( data === undefined ) { - - // each object defines an array of bindings (ubos, textures, samplers etc.) - - const nodeBuilder = this.nodes.get( renderObject ); - const bindings = nodeBuilder.getBindings(); - - // setup (static) binding layout and (dynamic) binding group - - const pipeline = this.renderPipelines.get( renderObject ).pipeline; - - const bindLayout = pipeline.getBindGroupLayout( 0 ); - const bindGroup = this._createBindGroup( bindings, bindLayout ); - - data = { - layout: bindLayout, - group: bindGroup, - bindings: bindings - }; - - this.uniformsData.set( renderObject, data ); - - } - - return data; - - } - - getForCompute( computeNode ) { - - let data = this.uniformsData.get( computeNode ); - - if ( data === undefined ) { - - // each object defines an array of bindings (ubos, textures, samplers etc.) - - const nodeBuilder = this.nodes.getForCompute( computeNode ); - const bindings = nodeBuilder.getBindings(); - - // setup (static) binding layout and (dynamic) binding group - - const pipeline = this.computePipelines.get( computeNode ); - - const bindLayout = pipeline.getBindGroupLayout( 0 ); - const bindGroup = this._createBindGroup( bindings, bindLayout ); - - data = { - layout: bindLayout, - group: bindGroup, - bindings: bindings - }; - - this.uniformsData.set( computeNode, data ); - - } - - return data; - - } - - remove( object ) { - - this.uniformsData.delete( object ); - - } - - update( object ) { - - const textures = this.textures; - - const data = this.get( object ); - const bindings = data.bindings; - - const updateMap = this.updateMap; - const frame = this.info.render.frame; - - let needsBindGroupRefresh = false; - - // iterate over all bindings and check if buffer updates or a new binding group is required - - for ( const binding of bindings ) { - - const isShared = binding.isShared; - const isUpdated = updateMap.get( binding ) === frame; - - if ( isShared && isUpdated ) continue; - - if ( binding.isUniformBuffer ) { - - const buffer = binding.getBuffer(); - const needsBufferWrite = binding.update(); - - if ( needsBufferWrite === true ) { - - const bufferGPU = binding.bufferGPU; - - this.device.queue.writeBuffer( bufferGPU, 0, buffer, 0 ); - - } - - } else if ( binding.isStorageBuffer ) { - - const attribute = binding.attribute; - - this.attributes.update( attribute, false, binding.usage ); - - } else if ( binding.isSampler ) { - - const texture = binding.getTexture(); - - textures.updateSampler( texture ); - - const samplerGPU = textures.getSampler( texture ); - - if ( binding.samplerGPU !== samplerGPU ) { - - binding.samplerGPU = samplerGPU; - needsBindGroupRefresh = true; - - } - - } else if ( binding.isSampledTexture ) { - - const texture = binding.getTexture(); - - const needsTextureRefresh = textures.updateTexture( texture ); - - const textureGPU = textures.getTextureGPU( texture ); - - if ( textureGPU !== undefined && binding.textureGPU !== textureGPU || needsTextureRefresh === true ) { - - binding.textureGPU = textureGPU; - needsBindGroupRefresh = true; - - } - - } - - updateMap.set( binding, frame ); - - } - - if ( needsBindGroupRefresh === true ) { - - data.group = this._createBindGroup( bindings, data.layout ); - - } - - } - - dispose() { - - this.uniformsData = new WeakMap(); - this.updateMap = new WeakMap(); - - } - - _createBindGroup( bindings, layout ) { - - let bindingPoint = 0; - const entries = []; - - for ( const binding of bindings ) { - - if ( binding.isUniformBuffer ) { - - if ( binding.bufferGPU === null ) { - - const byteLength = binding.getByteLength(); - - binding.bufferGPU = this.device.createBuffer( { - label: 'bindingBuffer', - size: byteLength, - usage: binding.usage - } ); - - } - - entries.push( { binding: bindingPoint, resource: { buffer: binding.bufferGPU } } ); - - } else if ( binding.isStorageBuffer ) { - - if ( binding.bufferGPU === null ) { - - const attribute = binding.attribute; - - this.attributes.update( attribute, false, binding.usage ); - binding.bufferGPU = this.attributes.get( attribute ).buffer; - - } - - entries.push( { binding: bindingPoint, resource: { buffer: binding.bufferGPU } } ); - - } else if ( binding.isSampler ) { - - if ( binding.samplerGPU === null ) { - - binding.samplerGPU = this.textures.getDefaultSampler(); - - } - - entries.push( { binding: bindingPoint, resource: binding.samplerGPU } ); - - } else if ( binding.isSampledTexture ) { - - if ( binding.textureGPU === null ) { - - if ( binding.isSampledCubeTexture ) { - - binding.textureGPU = this.textures.getDefaultCubeTexture(); - - } else if ( binding.texture.isVideoTexture ) { - - binding.textureGPU = this.textures.getDefaultVideoTexture(); - - } else if ( binding.texture.isDepthTexture ) { - - binding.textureGPU = this.textures.getDefaultDepthTexture(); - - } else { - - binding.textureGPU = this.textures.getDefaultTexture(); - - } - - } - - const resource = binding.textureGPU instanceof GPUTexture ? binding.textureGPU.createView( { aspect: binding.aspect, dimension: binding.dimension } ) : binding.textureGPU; - - entries.push( { binding: bindingPoint, resource } ); - - } - - bindingPoint ++; - - } - - return this.device.createBindGroup( { - layout, - entries - } ); - - } - -} - -export default WebGPUBindings; diff --git a/examples/jsm/renderers/webgpu/WebGPUBuffer.js b/examples/jsm/renderers/webgpu/WebGPUBuffer.js deleted file mode 100644 index f3eb9bc0df6877..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUBuffer.js +++ /dev/null @@ -1,43 +0,0 @@ -import WebGPUBinding from './WebGPUBinding.js'; -import { getFloatLength } from './WebGPUBufferUtils.js'; - -class WebGPUBuffer extends WebGPUBinding { - - constructor( name, type, buffer = null ) { - - super( name ); - - this.isBuffer = true; - - this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT; - this.type = type; - this.visibility = GPUShaderStage.VERTEX; - - this.usage = GPUBufferUsage.COPY_DST; - - this.buffer = buffer; - this.bufferGPU = null; // set by the renderer - - } - - getByteLength() { - - return getFloatLength( this.buffer.byteLength ); - - } - - getBuffer() { - - return this.buffer; - - } - - update() { - - return true; - - } - -} - -export default WebGPUBuffer; diff --git a/examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js b/examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js deleted file mode 100644 index 8f281d54aa0203..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js +++ /dev/null @@ -1,22 +0,0 @@ -let _id = 0; - -class WebGPUProgrammableStage { - - constructor( device, code, type ) { - - this.id = _id ++; - - this.code = code; - this.type = type; - this.usedTimes = 0; - - this.stage = { - module: device.createShaderModule( { code, label: type } ), - entryPoint: 'main' - }; - - } - -} - -export default WebGPUProgrammableStage; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderObjects.js b/examples/jsm/renderers/webgpu/WebGPURenderObjects.js deleted file mode 100644 index c7a59e91f034d7..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPURenderObjects.js +++ /dev/null @@ -1,50 +0,0 @@ -import WebGPUWeakMap from './WebGPUWeakMap.js'; -import WebGPURenderObject from './WebGPURenderObject.js'; - -class WebGPURenderObjects { - - constructor( renderer, nodes, geometries, info ) { - - this.renderer = renderer; - this.nodes = nodes; - this.geometries = geometries; - this.info = info; - - this.cache = new WebGPUWeakMap(); - - } - - get( object, material, scene, camera, lightsNode ) { - - const chainKey = [ object, material, scene, camera, lightsNode ]; - - let renderObject = this.cache.get( chainKey ); - - if ( renderObject === undefined ) { - - renderObject = new WebGPURenderObject( this.renderer, this.nodes, object, material, scene, camera, lightsNode ); - - this.cache.set( chainKey, renderObject ); - - } - - return renderObject; - - } - - remove( object, material, scene, camera, lightsNode ) { - - this.cache.delete( [ object, material, scene, camera, lightsNode ] ); - - } - - dispose() { - - this.cache = new WebGPUWeakMap(); - this.updateMap = new WeakMap(); - - } - -} - -export default WebGPURenderObjects; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderStates.js b/examples/jsm/renderers/webgpu/WebGPURenderStates.js deleted file mode 100644 index 21c5d926a66b69..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPURenderStates.js +++ /dev/null @@ -1,54 +0,0 @@ -import WebGPUWeakMap from './WebGPUWeakMap.js'; - -class WebGPURenderState { - - constructor() { - - this.depth = true; - this.stencil = true; - - // defined by renderer(backend) - - this.descriptorGPU = null; - this.encoderGPU = null; - this.currentPassGPU = null; - - } - -} - -class WebGPURenderStates { - - constructor() { - - this.renderStates = new WebGPUWeakMap(); - - } - - get( scene, camera ) { - - const chainKey = [ scene, camera ]; - - let renderState = this.renderStates.get( chainKey ); - - if ( renderState === undefined ) { - - renderState = new WebGPURenderState(); - - this.renderStates.set( chainKey, renderState ); - - } - - return renderState; - - } - - dispose() { - - this.renderStates = new WebGPUWeakMap(); - - } - -} - -export default WebGPURenderStates; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 2ec9369a5d5d7e..84fd988b45bf8c 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -1,1233 +1,15 @@ -import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName, GPULoadOp } from './constants.js'; -import WebGPUAnimation from './WebGPUAnimation.js'; -import WebGPURenderObjects from './WebGPURenderObjects.js'; -import WebGPUAttributes from './WebGPUAttributes.js'; -import WebGPUGeometries from './WebGPUGeometries.js'; -import WebGPUInfo from './WebGPUInfo.js'; -import WebGPUProperties from './WebGPUProperties.js'; -import WebGPURenderPipelines from './WebGPURenderPipelines.js'; -import WebGPUComputePipelines from './WebGPUComputePipelines.js'; -import WebGPUBindings from './WebGPUBindings.js'; -import WebGPURenderLists from './WebGPURenderLists.js'; -import WebGPURenderStates from './WebGPURenderStates.js'; -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'; +import Renderer from '../universal/Renderer.js'; +import WebGPUBackend from './backends/webgpu/WebGPUBackend.js'; -let staticAdapter = null; - -if ( navigator.gpu !== undefined ) { - - staticAdapter = await navigator.gpu.requestAdapter(); - -} - -console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' ); - -Matrix4.prototype.makePerspective = function ( left, right, top, bottom, near, far ) { - - const te = this.elements; - const x = 2 * near / ( right - left ); - const y = 2 * near / ( top - bottom ); - - const a = ( right + left ) / ( right - left ); - const b = ( top + bottom ) / ( top - bottom ); - const c = - far / ( far - near ); - const d = - far * near / ( far - near ); - - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - - return this; - -}; - -Matrix4.prototype.makeOrthographic = function ( left, right, top, bottom, near, far ) { - - const te = this.elements; - const w = 1.0 / ( right - left ); - const h = 1.0 / ( top - bottom ); - const p = 1.0 / ( far - near ); - - const x = ( right + left ) * w; - const y = ( top + bottom ) * h; - const z = near * p; - - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 1 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - - return this; - -}; - -Frustum.prototype.setFromProjectionMatrix = function ( m ) { - - const planes = this.planes; - const me = m.elements; - const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize(); - - return this; - -}; - -const _frustum = new Frustum(); -const _projScreenMatrix = new Matrix4(); -const _vector3 = new Vector3(); - -class WebGPURenderer { +class WebGPURenderer extends Renderer { constructor( parameters = {} ) { - this.isWebGPURenderer = true; - - // public - - this.domElement = ( parameters.canvas !== undefined ) ? parameters.canvas : this._createCanvasElement(); - - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; - - this.outputColorSpace = SRGBColorSpace; - - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; - - this.sortObjects = true; - - // internals - - this._parameters = Object.assign( {}, parameters ); - - this._pixelRatio = 1; - this._width = this.domElement.width; - this._height = this.domElement.height; - - this._viewport = null; - this._scissor = null; - - this._adapter = null; - this._device = null; - this._context = null; - this._colorBuffer = null; - this._depthBuffer = null; - - this._info = null; - this._properties = null; - this._attributes = null; - this._geometries = null; - this._nodes = null; - this._bindings = null; - this._objects = null; - this._renderPipelines = null; - this._computePipelines = null; - this._renderLists = null; - this._renderStates = null; - this._textures = null; - this._background = null; - - this._animation = new WebGPUAnimation(); - - this._currentRenderState = null; - this._lastRenderState = null; - - this._opaqueSort = null; - this._transparentSort = null; - - this._clearAlpha = 1; - this._clearColor = new Color( 0x000000 ); - this._clearDepth = 1; - this._clearStencil = 0; - - this._renderTarget = null; - - this._initialized = false; - this._initPromise = null; - - // some parameters require default values other than "undefined" - - 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; - - // backwards compatibility - - this.shadow = { - shadowMap: {} - }; - - } - - async init() { - - if ( this._initialized === true ) { - - throw new Error( 'WebGPURenderer: Device has already been initialized.' ); - - } - - if ( this._initPromise !== null ) { - - return this._initPromise; - - } - - this._initPromise = new Promise( async ( resolve, reject ) => { - - const parameters = this._parameters; - - const adapterOptions = { - powerPreference: parameters.powerPreference - }; - - const adapter = await navigator.gpu.requestAdapter( adapterOptions ); - - if ( adapter === null ) { - - reject( new Error( 'WebGPURenderer: Unable to create WebGPU adapter.' ) ); - return; - - } - - // feature support - - const features = Object.values( GPUFeatureName ); - - const supportedFeatures = []; - - for ( const name of features ) { - - if ( adapter.features.has( name ) ) { - - supportedFeatures.push( name ); - - } - - } - - const deviceDescriptor = { - requiredFeatures: supportedFeatures, - requiredLimits: parameters.requiredLimits - }; - - const device = await adapter.requestDevice( deviceDescriptor ); - - const context = ( parameters.context !== undefined ) ? parameters.context : this.domElement.getContext( 'webgpu' ); - - this._adapter = adapter; - this._device = device; - this._context = context; - - this._configureContext(); - - this._info = new WebGPUInfo(); - this._properties = new WebGPUProperties(); - this._attributes = new WebGPUAttributes( device ); - this._geometries = new WebGPUGeometries( this._attributes, this._properties, this._info ); - this._textures = new WebGPUTextures( device, this._properties, this._info ); - this._utils = new WebGPUUtils( this ); - this._nodes = new WebGPUNodes( this, this._properties ); - this._objects = new WebGPURenderObjects( this, this._nodes, this._geometries, this._info ); - this._computePipelines = new WebGPUComputePipelines( device, this._nodes ); - this._renderPipelines = new WebGPURenderPipelines( device, this._nodes, this._utils ); - this._bindings = this._renderPipelines.bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes ); - this._renderLists = new WebGPURenderLists(); - this._renderStates = new WebGPURenderStates(); - this._background = new WebGPUBackground( this, this._properties ); - - // - - this._setupColorBuffer(); - this._setupDepthBuffer(); - - this._animation.setNodes( this._nodes ); - this._animation.start(); - - this._initialized = true; - - resolve(); - - } ); - - return this._initPromise; - - } - - async render( scene, camera ) { - - 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(); - - if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); - - if ( this._info.autoReset === true ) this._info.reset(); - - this._info.render.frame ++; - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); - - const renderList = this._renderLists.get( scene, camera ); - renderList.init(); - - this._projectObject( scene, camera, 0, renderList ); - - renderList.finish(); - - if ( this.sortObjects === true ) { - - renderList.sort( this._opaqueSort, this._transparentSort ); - - } - - // prepare render pass descriptor - - renderState.descriptorGPU = { - colorAttachments: [ { - view: null - } ], - depthStencilAttachment: { - view: null - } - }; - - const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ]; - const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment; - - if ( renderTarget !== null ) { - - this._textures.initRenderTarget( renderTarget ); - - // @TODO: Support RenderTarget with antialiasing. - - const renderTargetProperties = this._properties.get( renderTarget ); - - colorAttachment.view = renderTargetProperties.colorTextureGPU.createView(); - depthStencilAttachment.view = renderTargetProperties.depthTextureGPU.createView(); - - renderState.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true; - - } else { - - if ( this._parameters.antialias === true ) { - - colorAttachment.view = this._colorBuffer.createView(); - colorAttachment.resolveTarget = this._context.getCurrentTexture().createView(); - - } else { - - colorAttachment.view = this._context.getCurrentTexture().createView(); - colorAttachment.resolveTarget = undefined; - - } - - depthStencilAttachment.view = this._depthBuffer.createView(); - - } - - // - - this._nodes.updateEnvironment( scene ); - this._nodes.updateFog( scene ); - this._nodes.updateBackground( scene ); - this._nodes.updateToneMapping(); - - // - - this._background.update( scene, renderList, renderState ); - - // start render pass - - const device = this._device; - - renderState.encoderGPU = device.createCommandEncoder( {} ); - renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU ); - - // global rasterization settings for all renderable objects - - const vp = this._viewport; - - if ( vp !== null ) { - - const width = Math.floor( vp.width * this._pixelRatio ); - const height = Math.floor( vp.height * this._pixelRatio ); - - renderState.currentPassGPU.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth ); - - } - - const sc = this._scissor; - - if ( sc !== null ) { - - const width = Math.floor( sc.width * this._pixelRatio ); - const height = Math.floor( sc.height * this._pixelRatio ); - - renderState.currentPassGPU.setScissorRect( sc.x, sc.y, width, height ); - - } - - // process render lists - - const opaqueObjects = renderList.opaque; - const transparentObjects = renderList.transparent; - const lightsNode = renderList.lightsNode; - - 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 - - renderState.currentPassGPU.end(); - - device.queue.submit( [ renderState.encoderGPU.finish() ] ); - - // restore render tree - - nodeFrame.renderId = previousRenderId; - this._currentRenderState = previousRenderState; - - this._lastRenderState = renderState; - - } - - setAnimationLoop( callback ) { - - if ( this._initialized === false ) this.init(); - - const animation = this._animation; - - animation.setAnimationLoop( callback ); - - ( callback === null ) ? animation.stop() : animation.start(); - - } - - async getArrayBuffer( attribute ) { - - return await this._attributes.getArrayBuffer( attribute ); - - } - - getContext() { - - return this._context; - - } - - getPixelRatio() { - - return this._pixelRatio; - - } - - getDrawingBufferSize( target ) { - - return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor(); - - } - - getSize( target ) { - - return target.set( this._width, this._height ); - - } - - setPixelRatio( value = 1 ) { - - this._pixelRatio = value; - - this.setSize( this._width, this._height, false ); - - } - - setDrawingBufferSize( width, height, pixelRatio ) { - - this._width = width; - this._height = height; - - this._pixelRatio = pixelRatio; - - this.domElement.width = Math.floor( width * pixelRatio ); - this.domElement.height = Math.floor( height * pixelRatio ); - - this._configureContext(); - this._setupColorBuffer(); - this._setupDepthBuffer(); - - } - - setSize( width, height, updateStyle = true ) { - - this._width = width; - this._height = height; - - this.domElement.width = Math.floor( width * this._pixelRatio ); - this.domElement.height = Math.floor( height * this._pixelRatio ); - - if ( updateStyle === true ) { - - this.domElement.style.width = width + 'px'; - this.domElement.style.height = height + 'px'; - - } - - this._configureContext(); - this._setupColorBuffer(); - this._setupDepthBuffer(); - - } - - setOpaqueSort( method ) { - - this._opaqueSort = method; - - } - - setTransparentSort( method ) { - - this._transparentSort = method; - - } - - getScissor( target ) { - - const scissor = this._scissor; - - target.x = scissor.x; - target.y = scissor.y; - target.width = scissor.width; - target.height = scissor.height; - - return target; - - } - - setScissor( x, y, width, height ) { - - if ( x === null ) { - - this._scissor = null; - - } else { - - this._scissor = { - x: x, - y: y, - width: width, - height: height - }; - - } - - } - - 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; - - target.x = viewport.x; - target.y = viewport.y; - target.width = viewport.width; - target.height = viewport.height; - target.minDepth = viewport.minDepth; - target.maxDepth = viewport.maxDepth; - - return target; - - } - - setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) { - - if ( x === null ) { - - this._viewport = null; - - } else { - - this._viewport = { - x: x, - y: y, - width: width, - height: height, - minDepth: minDepth, - maxDepth: maxDepth - }; - - } - - } - - getClearColor( target ) { + const backend = new WebGPUBackend( parameters ); - return target.copy( this._clearColor ); + super( backend ); - } - - setClearColor( color, alpha = 1 ) { - - this._clearColor.set( color ); - this._clearAlpha = alpha; - - } - - getClearAlpha() { - - return this._clearAlpha; - - } - - setClearAlpha( alpha ) { - - this._clearAlpha = alpha; - - } - - getClearDepth() { - - return this._clearDepth; - - } - - setClearDepth( depth ) { - - this._clearDepth = depth; - - } - - getClearStencil() { - - return this._clearStencil; - - } - - setClearStencil( stencil ) { - - this._clearStencil = stencil; - - } - - clear( color = true, depth = true, stencil = true ) { - - const renderState = this._currentRenderState || this._lastRenderState; - if ( renderState === null ) return; - - depth = depth && renderState.depth; - stencil = stencil && renderState.stencil; - - const descriptorGPU = renderState.descriptorGPU; - const colorAttachment = descriptorGPU.colorAttachments[ 0 ]; - - // @TODO: Include render target in clear operation. - if ( this._parameters.antialias === true ) { - - colorAttachment.view = this._colorBuffer.createView(); - colorAttachment.resolveTarget = this._context.getCurrentTexture().createView(); - - } else { - - colorAttachment.view = this._context.getCurrentTexture().createView(); - colorAttachment.resolveTarget = undefined; - - } - - descriptorGPU.depthStencilAttachment.view = this._depthBuffer.createView(); - - if ( color ) { - - colorAttachment.loadOp = GPULoadOp.Clear; - colorAttachment.clearValue = { r: this._clearColor.r, g: this._clearColor.g, b: this._clearColor.b, a: this._clearAlpha }; - - } - - if ( depth ) { - - descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; - descriptorGPU.depthStencilAttachment.depthClearValue = this._clearDepth; - - } - - if ( stencil ) { - - descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; - descriptorGPU.depthStencilAttachment.stencilClearValue = this._clearStencil; - - } - - renderState.encoderGPU = this._device.createCommandEncoder( {} ); - renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU ); - - renderState.currentPassGPU.end(); - - this._device.queue.submit( [ renderState.encoderGPU.finish() ] ); - - } - - clearColor() { - - this.clear( true, false, false ); - - } - - clearDepth() { - - this.clear( false, true, false ); - - } - - clearStencil() { - - this.clear( false, false, true ); - - } - - dispose() { - - this._objects.dispose(); - this._properties.dispose(); - this._renderPipelines.dispose(); - this._computePipelines.dispose(); - this._nodes.dispose(); - this._bindings.dispose(); - this._info.dispose(); - this._renderLists.dispose(); - this._renderStates.dispose(); - this._textures.dispose(); - - this.setRenderTarget( null ); - this.setAnimationLoop( null ); - - } - - setRenderTarget( renderTarget ) { - - this._renderTarget = renderTarget; - - } - - async compute( ...computeNodes ) { - - if ( this._initialized === false ) await this.init(); - - const device = this._device; - const computePipelines = this._computePipelines; - - const cmdEncoder = device.createCommandEncoder( {} ); - const passEncoder = cmdEncoder.beginComputePass(); - - for ( const computeNode of computeNodes ) { - - // onInit - - if ( computePipelines.has( computeNode ) === false ) { - - computeNode.onInit( { renderer: this } ); - - } - - // pipeline - - const pipeline = computePipelines.get( computeNode ); - passEncoder.setPipeline( pipeline ); - - // node - - //this._nodes.update( computeNode ); - - // bind group - - const bindGroup = this._bindings.getForCompute( computeNode ).group; - this._bindings.update( computeNode ); - passEncoder.setBindGroup( 0, bindGroup ); - - passEncoder.dispatchWorkgroups( computeNode.dispatchCount ); - - } - - passEncoder.end(); - device.queue.submit( [ cmdEncoder.finish() ] ); - - } - - getRenderTarget() { - - return this._renderTarget; - - } - - hasFeature( name ) { - - const adapter = this._adapter || staticAdapter; - - // - - const features = Object.values( GPUFeatureName ); - - if ( features.includes( name ) === false ) { - - throw new Error( 'THREE.WebGPURenderer: Unknown WebGPU GPU feature: ' + name ); - - } - - // - - return adapter.features.has( name ); - - } - - _projectObject( object, camera, groupOrder, renderList ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible ) { - - if ( object.isGroup ) { - - groupOrder = object.renderOrder; - - } else if ( object.isLOD ) { - - if ( object.autoUpdate === true ) object.update( camera ); - - } else if ( object.isLight ) { - - renderList.pushLight( object ); - - } else if ( object.isSprite ) { - - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - - if ( this.sortObjects === true ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix ); - - } - - const geometry = object.geometry; - const material = object.material; - - if ( material.visible ) { - - renderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } else if ( object.isLineLoop ) { - - console.error( 'THREE.WebGPURenderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - - const geometry = object.geometry; - const material = object.material; - - if ( this.sortObjects === true ) { - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _vector3 - .copy( geometry.boundingSphere.center ) - .applyMatrix4( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - - } - - } - - } else if ( material.visible ) { - - renderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - this._projectObject( children[ i ], camera, groupOrder, renderList ); - - } - - } - - _renderObjects( renderList, camera, scene, lightsNode ) { - - // process renderable objects - - for ( let i = 0, il = renderList.length; i < il; i ++ ) { - - const renderItem = renderList[ i ]; - - // @TODO: Add support for multiple materials per object. This will require to extract - // the material from the renderItem object and pass it with its group data to _renderObject(). - - const { object, geometry, material, group } = renderItem; - - if ( camera.isArrayCamera ) { - - const cameras = camera.cameras; - - for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { - - const camera2 = cameras[ j ]; - - if ( object.layers.test( camera2.layers ) ) { - - const vp = camera2.viewport; - const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth; - const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth; - - this._currentRenderState.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth ); - - this._renderObject( object, scene, camera2, geometry, material, group, lightsNode ); - - } - - } - - } else { - - this._renderObject( object, scene, camera, geometry, material, group, lightsNode ); - - } - - } - - } - - _renderObject( object, scene, camera, geometry, material, group, lightsNode ) { - - material = scene.overrideMaterial !== null ? scene.overrideMaterial : material; - - // - - object.onBeforeRender( this, scene, camera, geometry, material, group ); - - // - - const renderObject = this._getRenderObject( object, material, scene, camera, lightsNode ); - - // - - this._nodes.updateBefore( renderObject ); - - // - - const passEncoder = this._currentRenderState.currentPassGPU; - const info = this._info; - - // - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - // updates - - this._nodes.update( renderObject ); - this._geometries.update( renderObject ); - this._bindings.update( renderObject ); - - // pipeline - - const renderPipeline = this._renderPipelines.get( renderObject ); - passEncoder.setPipeline( renderPipeline.pipeline ); - - // bind group - - const bindGroup = this._bindings.get( renderObject ).group; - passEncoder.setBindGroup( 0, bindGroup ); - - // index - - const index = this._geometries.getIndex( renderObject ); - - const hasIndex = ( index !== null ); - - if ( hasIndex === true ) { - - this._setupIndexBuffer( renderObject ); - - } - - // vertex buffers - - this._setupVertexBuffers( renderObject ); - - // draw - - const drawRange = geometry.drawRange; - const firstVertex = drawRange.start; - - const instanceCount = geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 ); - - if ( hasIndex === true ) { - - const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count; - - passEncoder.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 ); - - info.update( object, indexCount, instanceCount ); - - } else { - - const positionAttribute = geometry.attributes.position; - const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count; - - passEncoder.draw( vertexCount, instanceCount, firstVertex, 0 ); - - info.update( object, vertexCount, instanceCount ); - - } - - } - - _getRenderObject( object, material, scene, camera, lightsNode ) { - - const renderObject = this._objects.get( object, material, scene, camera, lightsNode ); - const renderObjectProperties = this._properties.get( renderObject ); - - if ( renderObjectProperties.initialized !== true ) { - - renderObjectProperties.initialized = true; - - const dispose = () => { - - this._renderPipelines.remove( renderObject ); - this._nodes.remove( renderObject ); - this._properties.remove( renderObject ); - - this._objects.remove( object, material, scene, camera, lightsNode ); - - renderObject.material.removeEventListener( 'dispose', dispose ); - - }; - - renderObject.material.addEventListener( 'dispose', dispose ); - - } - - const cacheKey = renderObject.getCacheKey(); - - if ( renderObjectProperties.cacheKey !== cacheKey ) { - - renderObjectProperties.cacheKey = cacheKey; - - this._renderPipelines.remove( renderObject ); - this._nodes.remove( renderObject ); - - } - - return renderObject; - - } - - _setupIndexBuffer( renderObject ) { - - const index = this._geometries.getIndex( renderObject ); - const passEncoder = this._currentRenderState.currentPassGPU; - - const buffer = this._attributes.get( index ).buffer; - const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32; - - passEncoder.setIndexBuffer( buffer, indexFormat ); - - } - - _setupVertexBuffers( renderObject ) { - - const passEncoder = this._currentRenderState.currentPassGPU; - const attributes = renderObject.getAttributes(); - - for ( let i = 0, l = attributes.length; i < l; i ++ ) { - - const buffer = this._attributes.get( attributes[ i ] ).buffer; - passEncoder.setVertexBuffer( i, buffer ); - - } - - } - - _setupColorBuffer() { - - const device = this._device; - - if ( device ) { - - if ( this._colorBuffer ) this._colorBuffer.destroy(); - - this._colorBuffer = this._device.createTexture( { - label: 'colorBuffer', - size: { - width: Math.floor( this._width * this._pixelRatio ), - height: Math.floor( this._height * this._pixelRatio ), - depthOrArrayLayers: 1 - }, - sampleCount: this._parameters.sampleCount, - format: GPUTextureFormat.BGRA8Unorm, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC - } ); - - } - - } - - _setupDepthBuffer() { - - const device = this._device; - - if ( device ) { - - if ( this._depthBuffer ) this._depthBuffer.destroy(); - - this._depthBuffer = this._device.createTexture( { - label: 'depthBuffer', - size: { - width: Math.floor( this._width * this._pixelRatio ), - height: Math.floor( this._height * this._pixelRatio ), - depthOrArrayLayers: 1 - }, - sampleCount: this._parameters.sampleCount, - format: GPUTextureFormat.Depth24PlusStencil8, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC - } ); - - } - - } - - _configureContext() { - - const device = this._device; - - if ( device ) { - - this._context.configure( { - device: device, - format: GPUTextureFormat.BGRA8Unorm, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, - alphaMode: 'premultiplied' - } ); - - } - - } - - _createCanvasElement() { - - const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.style.display = 'block'; - return canvas; + this.isWebGPURenderer = true; } diff --git a/examples/jsm/renderers/webgpu/WebGPUSampledTexture.js b/examples/jsm/renderers/webgpu/WebGPUSampledTexture.js deleted file mode 100644 index 8355e0e5f34fe5..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUSampledTexture.js +++ /dev/null @@ -1,75 +0,0 @@ -import WebGPUBinding from './WebGPUBinding.js'; -import { GPUBindingType, GPUTextureViewDimension, GPUTextureAspect } from './constants.js'; - -class WebGPUSampledTexture extends WebGPUBinding { - - constructor( name, texture ) { - - super( name ); - - this.isSampledTexture = true; - - this.texture = texture; - - this.dimension = GPUTextureViewDimension.TwoD; - - this.type = GPUBindingType.SampledTexture; - this.visibility = GPUShaderStage.FRAGMENT; - - this.aspect = texture.isDepthTexture ? GPUTextureAspect.DepthOnly : GPUTextureAspect.All; - - this.textureGPU = null; // set by the renderer - - } - - getTexture() { - - return this.texture; - - } - -} - -class WebGPUSampledArrayTexture extends WebGPUSampledTexture { - - constructor( name, texture ) { - - super( name, texture ); - - this.isSampledArrayTexture = true; - - this.dimension = GPUTextureViewDimension.TwoDArray; - - } - -} - -class WebGPUSampled3DTexture extends WebGPUSampledTexture { - - constructor( name, texture ) { - - super( name, texture ); - - this.isSampled3DTexture = true; - - this.dimension = GPUTextureViewDimension.ThreeD; - - } - -} - -class WebGPUSampledCubeTexture extends WebGPUSampledTexture { - - constructor( name, texture ) { - - super( name, texture ); - - this.isSampledCubeTexture = true; - - this.dimension = GPUTextureViewDimension.Cube; - - } - -} - -export { WebGPUSampledTexture, WebGPUSampledArrayTexture, WebGPUSampled3DTexture, WebGPUSampledCubeTexture }; diff --git a/examples/jsm/renderers/webgpu/WebGPUSampler.js b/examples/jsm/renderers/webgpu/WebGPUSampler.js deleted file mode 100644 index eaf1065cc5a8ee..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUSampler.js +++ /dev/null @@ -1,29 +0,0 @@ -import WebGPUBinding from './WebGPUBinding.js'; -import { GPUBindingType } from './constants.js'; - -class WebGPUSampler extends WebGPUBinding { - - constructor( name, texture ) { - - super( name ); - - this.isSampler = true; - - this.texture = texture; - - this.type = GPUBindingType.Sampler; - this.visibility = GPUShaderStage.FRAGMENT; - - this.samplerGPU = null; // set by the renderer - - } - - getTexture() { - - return this.texture; - - } - -} - -export default WebGPUSampler; diff --git a/examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js b/examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js deleted file mode 100644 index 54d6fd8c423e6d..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js +++ /dev/null @@ -1,20 +0,0 @@ -import WebGPUBuffer from './WebGPUBuffer.js'; -import { GPUBindingType } from './constants.js'; - -class WebGPUStorageBuffer extends WebGPUBuffer { - - constructor( name, attribute ) { - - super( name, GPUBindingType.StorageBuffer, attribute.array ); - - this.isStorageBuffer = true; - - this.usage |= GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE; - - this.attribute = attribute; - - } - -} - -export default WebGPUStorageBuffer; diff --git a/examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js b/examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js deleted file mode 100644 index 8ce93aa6938426..00000000000000 --- a/examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js +++ /dev/null @@ -1,18 +0,0 @@ -import WebGPUBuffer from './WebGPUBuffer.js'; -import { GPUBindingType } from './constants.js'; - -class WebGPUUniformBuffer extends WebGPUBuffer { - - constructor( name, buffer = null ) { - - super( name, GPUBindingType.UniformBuffer, buffer ); - - this.isUniformBuffer = true; - - this.usage |= GPUBufferUsage.UNIFORM; - - } - -} - -export default WebGPUUniformBuffer; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js new file mode 100644 index 00000000000000..054b27bc57168e --- /dev/null +++ b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js @@ -0,0 +1,667 @@ +import { + GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat +} from './utils/WebGPUConstants.js'; + +import WebGPUNodeBuilder from './builder/WGSLNodeBuilder.js'; +import Backend from '../../../universal/Backend.js'; + +import { Matrix4, Frustum } from 'three'; + +import WebGPUUtils from './utils/WebGPUUtils.js'; +import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js'; +import WebGPUBindingUtils from './utils/WebGPUBindingUtils.js'; +import WebGPUPipelineUtils from './utils/WebGPUPipelineUtils.js'; +import WebGPUTextureUtils from './utils/WebGPUTextureUtils.js'; + +// statics + +let staticAdapter = null; + +if ( navigator.gpu !== undefined ) { + + staticAdapter = await navigator.gpu.requestAdapter(); + +} + +// hacks + +let initializedHack = false; + +function initWebGPUHack() { + + if ( initializedHack ) return; + + console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' ); + + Matrix4.prototype.makePerspective = function ( left, right, top, bottom, near, far ) { + + const te = this.elements; + const x = 2 * near / ( right - left ); + const y = 2 * near / ( top - bottom ); + + const a = ( right + left ) / ( right - left ); + const b = ( top + bottom ) / ( top - bottom ); + const c = - far / ( far - near ); + const d = - far * near / ( far - near ); + + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + + return this; + + }; + + Matrix4.prototype.makeOrthographic = function ( left, right, top, bottom, near, far ) { + + const te = this.elements; + const w = 1.0 / ( right - left ); + const h = 1.0 / ( top - bottom ); + const p = 1.0 / ( far - near ); + + const x = ( right + left ) * w; + const y = ( top + bottom ) * h; + const z = near * p; + + te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 1 * p; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; + + return this; + + }; + + Frustum.prototype.setFromProjectionMatrix = function ( m ) { + + const planes = this.planes; + const me = m.elements; + const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; + + planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); + planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); + planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); + planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); + planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); + planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize(); + + return this; + + }; + + initializedHack = true; + +} + + +class WebGPUBackend extends Backend { + + constructor( parameters = {} ) { + + super( parameters ); + + // some parameters require default values other than "undefined" + + 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.adapter = null; + this.device = null; + this.context = null; + this.colorBuffer = null; + this.depthBuffer = null; + + this.utils = new WebGPUUtils( this ); + this.attributeUtils = new WebGPUAttributeUtils( this ); + this.bindingUtils = new WebGPUBindingUtils( this ); + this.pipelineUtils = new WebGPUPipelineUtils( this ); + this.textureUtils = new WebGPUTextureUtils( this ); + + } + + async init( renderer ) { + + await super.init( renderer ); + + // + + const parameters = this.parameters; + + const adapterOptions = { + powerPreference: parameters.powerPreference + }; + + const adapter = await navigator.gpu.requestAdapter( adapterOptions ); + + if ( adapter === null ) { + + throw new Error( 'WebGPUBackend: Unable to create WebGPU adapter.' ); + + } + + // feature support + + const features = Object.values( GPUFeatureName ); + + const supportedFeatures = []; + + for ( const name of features ) { + + if ( adapter.features.has( name ) ) { + + supportedFeatures.push( name ); + + } + + } + + const deviceDescriptor = { + requiredFeatures: supportedFeatures, + requiredLimits: parameters.requiredLimits + }; + + const device = await adapter.requestDevice( deviceDescriptor ); + + const context = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgpu' ); + + this.adapter = adapter; + this.device = device; + this.context = context; + + initWebGPUHack(); + + this.updateSize(); + + } + + beginRender( renderContext ) { + + const renderContextData = this.get( renderContext ); + + const device = this.device; + + const descriptor = { + colorAttachments: [ { + view: null + } ], + depthStencilAttachment: { + view: null + } + }; + + const colorAttachment = descriptor.colorAttachments[ 0 ]; + const depthStencilAttachment = descriptor.depthStencilAttachment; + + if ( this.parameters.antialias === true ) { + + colorAttachment.view = this.colorBuffer.createView(); + colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + + } else { + + colorAttachment.view = this.context.getCurrentTexture().createView(); + colorAttachment.resolveTarget = undefined; + + } + + depthStencilAttachment.view = this.depthBuffer.createView(); + + if ( renderContext.clearColor ) { + + colorAttachment.clearValue = renderContext.clearColorValue; + colorAttachment.loadOp = GPULoadOp.Clear; + colorAttachment.storeOp = GPUStoreOp.Store; + + } else { + + colorAttachment.loadOp = GPULoadOp.Load; + colorAttachment.storeOp = GPUStoreOp.Store; + + } + + if ( renderContext.depth ) { + + if ( renderContext.clearDepth ) { + + depthStencilAttachment.depthClearValue = renderContext.clearDepthValue; + depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; + depthStencilAttachment.depthStoreOp = GPUStoreOp.Store; + + } else { + + depthStencilAttachment.depthLoadOp = GPULoadOp.Load; + depthStencilAttachment.depthStoreOp = GPUStoreOp.Store; + + } + + } + + if ( renderContext.stencil ) { + + if ( renderContext.clearStencil ) { + + depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue; + depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; + depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; + + } else { + + depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; + depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; + + } + + } + + const encoder = device.createCommandEncoder( {} ); + const currentPass = encoder.beginRenderPass( descriptor ); + + renderContextData.descriptor = descriptor; + renderContextData.encoder = encoder; + renderContextData.currentPass = currentPass; + + } + + finishRender( renderContext ) { + + const renderContextData = this.get( renderContext ); + + const device = this.device; + + renderContextData.currentPass.end(); + + device.queue.submit( [ renderContextData.encoder.finish() ] ); + + } + + // compute + + beginCompute( computeGroup ) { + + const groupGPU = this.get( computeGroup ); + + groupGPU.cmdEncoderGPU = this.device.createCommandEncoder( {} ); + groupGPU.passEncoderGPU = groupGPU.cmdEncoderGPU.beginComputePass(); + + } + + compute( computeGroup, computeNode, bindings, pipeline ) { + + const { passEncoderGPU } = this.get( computeGroup ); + + // pipeline + + const pipelineGPU = this.get( pipeline ).pipeline; + passEncoderGPU.setPipeline( pipelineGPU ); + + // bind group + + const bindGroupGPU = this.get( bindings ).group; + passEncoderGPU.setBindGroup( 0, bindGroupGPU ); + + passEncoderGPU.dispatchWorkgroups( computeNode.dispatchCount ); + + } + + finishCompute( computeGroup ) { + + const groupData = this.get( computeGroup ); + + groupData.passEncoderGPU.end(); + this.device.queue.submit( [ groupData.cmdEncoderGPU.finish() ] ); + + } + + // render object + + draw( renderObject, info ) { + + const { object, geometry, context, pipeline } = renderObject; + + const bindingsData = this.get( renderObject.getBindings() ); + const contextData = this.get( context ); + const pipelineGPU = this.get( pipeline ).pipeline; + + // pipeline + + const passEncoderGPU = contextData.currentPass; + passEncoderGPU.setPipeline( pipelineGPU ); + + // bind group + + const bindGroupGPU = bindingsData.group; + passEncoderGPU.setBindGroup( 0, bindGroupGPU ); + + // index + + const index = renderObject.getIndex(); + + const hasIndex = ( index !== null ); + + if ( hasIndex === true ) { + + const buffer = this.get( index ).buffer; + const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32; + + passEncoderGPU.setIndexBuffer( buffer, indexFormat ); + + } + + // vertex buffers + + const attributes = renderObject.getAttributes(); + + for ( let i = 0, l = attributes.length; i < l; i ++ ) { + + const buffer = this.get( attributes[ i ] ).buffer; + passEncoderGPU.setVertexBuffer( i, buffer ); + + } + + // draw + + const drawRange = geometry.drawRange; + const firstVertex = drawRange.start; + + const instanceCount = this.getInstanceCount( renderObject ); + + if ( hasIndex === true ) { + + const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count; + + passEncoderGPU.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 ); + + info.update( object, indexCount, instanceCount ); + + } else { + + const positionAttribute = geometry.attributes.position; + const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count; + + passEncoderGPU.draw( vertexCount, instanceCount, firstVertex, 0 ); + + info.update( object, vertexCount, instanceCount ); + + } + + } + + // cache key + + needsUpdate( renderObject ) { + + const renderObjectGPU = this.get( renderObject ); + + const { object, material } = renderObject; + + const utils = this.utils; + + const sampleCount = utils.getSampleCount(); + const colorSpace = utils.getCurrentColorSpace(); + const colorFormat = utils.getCurrentColorFormat(); + const depthStencilFormat = utils.getCurrentDepthStencilFormat(); + const primitiveTopology = utils.getPrimitiveTopology( object, material ) + + let needsUpdate = false; + + if ( renderObjectGPU.sampleCount !== sampleCount || renderObjectGPU.colorSpace !== colorSpace || + renderObjectGPU.colorFormat !== colorFormat || renderObjectGPU.depthStencilFormat !== depthStencilFormat || + renderObjectGPU.primitiveTopology !== primitiveTopology ) { + + renderObjectGPU.sampleCount = sampleCount; + renderObjectGPU.colorSpace = colorSpace; + renderObjectGPU.colorFormat = colorFormat; + renderObjectGPU.depthStencilFormat = depthStencilFormat; + renderObjectGPU.primitiveTopology = primitiveTopology; + + needsUpdate = true; + + } + + return needsUpdate; + + } + + getCacheKey( renderObject ) { + + const { object, material } = renderObject; + + const utils = this.utils; + + return [ + utils.getSampleCount(), + utils.getCurrentColorSpace(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(), + utils.getPrimitiveTopology( object, material ) + ].join(); + + } + + // textures + + createSampler( texture ) { + + this.textureUtils.createSampler( texture ); + + } + + createDefaultTexture( texture ) { + + this.textureUtils.createDefaultTexture( texture ); + + } + + createTexture( texture ) { + + this.textureUtils.createTexture( texture ); + this.textureUtils.updateTexture( texture ); + + } + + updateTexture( texture ) { + + this.textureUtils.updateTexture( texture ); + + } + + destroyTexture( /*texture*/ ) { + + console.warn( '.destroyTexture() not implemented yet.' ); + + } + + // node builder + + createNodeBuilder( object, renderer ) { + + return new WebGPUNodeBuilder( object, renderer ); + + } + + // program + + createProgram( program ) { + + const programGPU = this.get( program ); + + programGPU.module = { + module: this.device.createShaderModule( { code: program.code, label: program.stage } ), + entryPoint: 'main' + }; + + } + + destroyProgram( program ) { + + this.delete( program ); + + } + + // pipelines + + createRenderPipeline( renderObject ) { + + this.pipelineUtils.createRenderPipeline( renderObject ); + + } + + createComputePipeline( computePipeline ) { + + this.pipelineUtils.createComputePipeline( computePipeline ); + + } + + // bindings + + createBindings( bindings, pipeline ) { + + this.bindingUtils.createBindings( bindings, pipeline ); + + } + + updateBindings( bindings, pipeline ) { + + this.bindingUtils.createBindings( bindings, pipeline ); + + } + + updateBinding( binding ) { + + this.bindingUtils.updateBinding( binding ); + + } + + // attributes + + createIndexAttribute( attribute ) { + + this.attributeUtils.createAttribute( attribute, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); + + } + + createAttribute( attribute ) { + + this.attributeUtils.createAttribute( attribute, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); + + } + + createStorageAttribute( attribute ) { + + this.attributeUtils.createAttribute( attribute, GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); + + } + + updateAttribute( attribute ) { + + this.attributeUtils.updateAttribute( attribute ); + + } + + destroyAttribute( /*attribute*/ ) { + + console.warn( '.destroyAttribute() not implemented yet.' ); + + } + + // canvas + + updateSize() { + + this._configureContext(); + this._setupColorBuffer(); + this._setupDepthBuffer(); + + } + + // utils public + + hasFeature( name ) { + + const adapter = this.adapter || staticAdapter; + + // + + const features = Object.values( GPUFeatureName ); + + if ( features.includes( name ) === false ) { + + throw new Error( 'THREE.WebGPURenderer: Unknown WebGPU GPU feature: ' + name ); + + } + + // + + return adapter.features.has( name ); + + } + + // utils + + _configureContext() { + + this.context.configure( { + device: this.device, + format: GPUTextureFormat.BGRA8Unorm, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + alphaMode: 'premultiplied' + } ); + + } + + _setupColorBuffer() { + + if ( this.colorBuffer ) this.colorBuffer.destroy(); + + const { width, height } = this.getDrawingBufferSize(); + + this.colorBuffer = this.device.createTexture( { + label: 'colorBuffer', + size: { + width: width, + height: height, + depthOrArrayLayers: 1 + }, + sampleCount: this.parameters.sampleCount, + format: GPUTextureFormat.BGRA8Unorm, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC + } ); + + } + + _setupDepthBuffer() { + + if ( this.depthBuffer ) this.depthBuffer.destroy(); + + const { width, height } = this.getDrawingBufferSize(); + + this.depthBuffer = this.device.createTexture( { + label: 'depthBuffer', + size: { + width: width, + height: height, + depthOrArrayLayers: 1 + }, + sampleCount: this.parameters.sampleCount, + format: GPUTextureFormat.Depth24PlusStencil8, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC + } ); + + } + +} + +export default WebGPUBackend; diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js similarity index 93% rename from examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js rename to examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js index 33293c613bddcd..73bfac3f38fe69 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js @@ -1,18 +1,20 @@ -import WebGPUUniformsGroup from '../WebGPUUniformsGroup.js'; +import UniformsGroup from '../../../../universal/UniformsGroup.js'; import { FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform -} from './WebGPUNodeUniform.js'; -import WebGPUNodeSampler from './WebGPUNodeSampler.js'; -import { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture } from './WebGPUNodeSampledTexture.js'; +} from '../../../../universal/nodes/NodeUniform.js'; +import WebGPUNodeSampler from '../../../../universal/nodes/NodeSampler.js'; +import { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture } from '../../../../universal/nodes/NodeSampledTexture.js'; -import WebGPUUniformBuffer from '../WebGPUUniformBuffer.js'; -import WebGPUStorageBuffer from '../WebGPUStorageBuffer.js'; -import { getVectorLength, getStrideLength } from '../WebGPUBufferUtils.js'; +import UniformBuffer from '../../../../universal/UniformBuffer.js'; +import StorageBuffer from '../../../../universal/StorageBuffer.js'; +import { getVectorLength, getStrideLength } from '../../../../universal/BufferUtils.js'; -import WebGPURenderTarget from '../WebGPURenderTarget.js'; +import RenderTarget from '../../../../universal/RenderTarget.js'; -import { NodeBuilder, WGSLNodeParser, CodeNode, NodeMaterial } from '../../../nodes/Nodes.js'; +import { NodeBuilder, CodeNode, NodeMaterial } from '../../../../../nodes/Nodes.js'; + +import WGSLNodeParser from './WGSLNodeParser.js'; const gpuShaderStageLib = { 'vertex': GPUShaderStage.VERTEX, @@ -97,9 +99,6 @@ class WebGPUNodeBuilder extends NodeBuilder { super( object, renderer, new WGSLNodeParser() ); - this.bindings = { vertex: [], fragment: [], compute: [] }; - this.bindingsOffset = { vertex: 0, fragment: 0, compute: 0 }; - this.uniformsGroup = {}; this.builtins = { @@ -250,14 +249,6 @@ class WebGPUNodeBuilder extends NodeBuilder { } - getBindings() { - - const bindings = this.bindings; - - return this.material !== null ? [ ...bindings.vertex, ...bindings.fragment ] : bindings.compute; - - } - getUniformFromNode( node, type, shaderStage ) { const uniformNode = super.getUniformFromNode( node, type, shaderStage ); @@ -305,9 +296,9 @@ class WebGPUNodeBuilder extends NodeBuilder { } else if ( type === 'buffer' || type === 'storageBuffer' ) { - const bufferClass = type === 'storageBuffer' ? WebGPUStorageBuffer : WebGPUUniformBuffer; + const bufferClass = type === 'storageBuffer' ? StorageBuffer : UniformBuffer; const buffer = new bufferClass( 'NodeBuffer_' + node.id, node.value ); - buffer.setVisibility( gpuShaderStageLib[ shaderStage ] ); + //buffer.setVisibility( gpuShaderStageLib[ shaderStage ] ); // add first textures in sequence and group for last const lastBinding = bindings[ bindings.length - 1 ]; @@ -323,8 +314,8 @@ class WebGPUNodeBuilder extends NodeBuilder { if ( uniformsGroup === undefined ) { - uniformsGroup = new WebGPUUniformsGroup( 'nodeUniforms' ); - uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); + uniformsGroup = new UniformsGroup( 'nodeUniforms' ); + //uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); this.uniformsGroup[ shaderStage ] = uniformsGroup; @@ -700,7 +691,7 @@ class WebGPUNodeBuilder extends NodeBuilder { getRenderTarget( width, height, options ) { - return new WebGPURenderTarget( width, height, options ); + return new RenderTarget( width, height, options ); } diff --git a/examples/jsm/nodes/parsers/WGSLNodeFunction.js b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeFunction.js similarity index 92% rename from examples/jsm/nodes/parsers/WGSLNodeFunction.js rename to examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeFunction.js index 0119477e37ef70..4e1740c8562656 100644 --- a/examples/jsm/nodes/parsers/WGSLNodeFunction.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeFunction.js @@ -1,5 +1,5 @@ -import NodeFunction from '../core/NodeFunction.js'; -import NodeFunctionInput from '../core/NodeFunctionInput.js'; +import NodeFunction from '../../../../../nodes/core/NodeFunction.js'; +import NodeFunctionInput from '../../../../../nodes/core/NodeFunctionInput.js'; const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i; const propertiesRegexp = /[a-z_0-9]+/ig; diff --git a/examples/jsm/nodes/parsers/WGSLNodeParser.js b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeParser.js similarity index 75% rename from examples/jsm/nodes/parsers/WGSLNodeParser.js rename to examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeParser.js index 4f62a3bc0bcc31..a3f7b2bdab2652 100644 --- a/examples/jsm/nodes/parsers/WGSLNodeParser.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeParser.js @@ -1,4 +1,4 @@ -import NodeParser from '../core/NodeParser.js'; +import NodeParser from '../../../../../nodes/core/NodeParser.js'; import WGSLNodeFunction from './WGSLNodeFunction.js'; class WGSLNodeParser extends NodeParser { diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js new file mode 100644 index 00000000000000..95f1e0c5c6fd78 --- /dev/null +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js @@ -0,0 +1,188 @@ +import { Float16BufferAttribute } from 'three'; + +const typedArraysToVertexFormatPrefix = new Map( [ + [ Int8Array, [ 'sint8', 'snorm8' ]], + [ Uint8Array, [ 'uint8', 'unorm8' ]], + [ Int16Array, [ 'sint16', 'snorm16' ]], + [ Uint16Array, [ 'uint16', 'unorm16' ]], + [ Int32Array, [ 'sint32', 'snorm32' ]], + [ Uint32Array, [ 'uint32', 'unorm32' ]], + [ Float32Array, [ 'float32', ]], +] ); + +const typedAttributeToVertexFormatPrefix = new Map( [ + [ Float16BufferAttribute, [ 'float16', ]], +] ); + +const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ + [ Int32Array, 'sint32' ], + [ Uint32Array, 'uint32' ], + [ Float32Array, 'float32' ] +] ); + +class WebGPUAttributeUtils { + + constructor( backend ) { + + this.backend = backend; + + } + + createAttribute( attribute, usage ) { + + const bufferAttribute = this.getBufferAttribute( attribute ); + + const backend = this.backend; + const device = backend.device; + + const array = bufferAttribute.array; + const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441 + + const buffer = device.createBuffer( { + label: bufferAttribute.name, + size : size, + usage: usage, + mappedAtCreation: true + } ); + + new array.constructor( buffer.getMappedRange() ).set( array ); + + buffer.unmap(); + + backend.get( attribute ).buffer = buffer; + + } + + getBufferAttribute( attribute ) { + + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + + return attribute; + + } + + updateAttribute( attribute ) { + + const bufferAttribute = this.getBufferAttribute( attribute ); + + const backend = this.backend; + const device = backend.device; + + const buffer = backend.get( bufferAttribute ).buffer; + + const array = bufferAttribute.array; + const updateRange = bufferAttribute.updateRange; + + if ( updateRange.count === - 1 ) { + + // Not using update ranges + + device.queue.writeBuffer( + buffer, + 0, + array, + 0 + ); + + } else { + + device.queue.writeBuffer( + buffer, + 0, + array, + updateRange.offset * array.BYTES_PER_ELEMENT, + updateRange.count * array.BYTES_PER_ELEMENT + ); + + updateRange.count = - 1; // reset range + + } + + } + + createShaderAttributes( renderObject ) { + + const attributes = renderObject.getAttributes(); + const shaderAttributes = []; + + for ( let slot = 0; slot < attributes.length; slot ++ ) { + + const geometryAttribute = attributes[ slot ]; + const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT; + + const format = this.getVertexFormat( geometryAttribute ); + + let arrayStride = geometryAttribute.itemSize * bytesPerElement; + let offset = 0; + + if ( geometryAttribute.isInterleavedBufferAttribute === true ) { + + // @TODO: It can be optimized for "vertexBuffers" on RenderPipeline + + arrayStride = geometryAttribute.data.stride * bytesPerElement; + offset = geometryAttribute.offset * bytesPerElement; + + } + + shaderAttributes.push( { + geometryAttribute, + arrayStride, + offset, + format, + slot + } ); + + } + + return shaderAttributes; + + } + + getVertexFormat( geometryAttribute ) { + + const { itemSize, normalized } = geometryAttribute; + const ArrayType = geometryAttribute.array.constructor; + const AttributeType = geometryAttribute.constructor; + + let format; + + if ( itemSize == 1 ) { + + format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType ); + + } else { + + const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType ); + const prefix = prefixOptions[ normalized ? 1 : 0 ]; + + if ( prefix ) { + + const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize; + const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4; + const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT; + + if ( paddedItemSize % 1 ) { + + throw new Error( 'THREE.WebGPURenderer: Bad vertex format item size.' ); + + } + + format = `${prefix}x${paddedItemSize}`; + + } + + } + + if ( ! format ) { + + console.error( 'THREE.WebGPURenderer: Vertex format not supported yet.' ); + + } + + return format; + + } + +} + +export default WebGPUAttributeUtils; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js new file mode 100644 index 00000000000000..9a1a358850558e --- /dev/null +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js @@ -0,0 +1,134 @@ +import { + GPUTextureAspect, GPUTextureViewDimension +} from './WebGPUConstants.js'; + +class WebGPUBindingUtils { + + constructor( backend ) { + + this.backend = backend; + + } + + createBindings( bindings, pipeline ) { + + const backend = this.backend; + const bindingsData = backend.get( bindings ); + + // setup (static) binding layout and (dynamic) binding group + + const pipelineGPU = backend.get( pipeline ).pipeline; + + const bindLayoutGPU = pipelineGPU.getBindGroupLayout( 0 ); + const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU ); + + bindingsData.layout = bindLayoutGPU; + bindingsData.group = bindGroupGPU; + bindingsData.bindings = bindings; + + } + + updateBinding( binding ) { + + const backend = this.backend; + const device = backend.device; + + const buffer = binding.getBuffer(); + const bufferGPU = backend.get( binding ).buffer; + + device.queue.writeBuffer( bufferGPU, 0, buffer, 0 ); + + } + + createBindGroup( bindings, layoutGPU ) { + + const backend = this.backend; + const device = backend.device; + + let bindingPoint = 0; + const entriesGPU = []; + + for ( const binding of bindings ) { + + if ( binding.isUniformBuffer ) { + + const bindingData = backend.get( binding ); + + if ( bindingData.buffer === undefined ) { + + const byteLength = binding.getByteLength(); + + const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST; + + const bufferGPU = device.createBuffer( { + label: 'bindingBuffer', + size: byteLength, + usage: usage + } ); + + bindingData.buffer = bufferGPU; + + } + + entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } ); + + } else if ( binding.isStorageBuffer ) { + + const bindingData = backend.get( binding ); + + if ( bindingData.buffer === undefined ) { + + const attribute = binding.attribute; + //const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST; + + //backend.attributeUtils.createAttribute( attribute, usage ); // @TODO: Move it to universal renderer + + bindingData.buffer = backend.get( attribute ).buffer; + + } + + entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } ); + + } else if ( binding.isSampler ) { + + const textureGPU = backend.get( binding.texture ); + + entriesGPU.push( { binding: bindingPoint, resource: textureGPU.sampler } ); + + } else if ( binding.isSampledTexture ) { + + const textureData = backend.get( binding.texture ); + + let dimensionGPU; + + if ( binding.isSampledCubeTexture ) { + + dimensionGPU = GPUTextureViewDimension.Cube; + + } else { + + dimensionGPU = GPUTextureViewDimension.TwoD; + + } + + const aspectGPU = GPUTextureAspect.All; + const resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionGPU } ); + + entriesGPU.push( { binding: bindingPoint, resource: resourceGPU } ); + + } + + bindingPoint ++; + + } + + return device.createBindGroup( { + layout: layoutGPU, + entries: entriesGPU + } ); + + } + +} + +export default WebGPUBindingUtils; diff --git a/examples/jsm/renderers/webgpu/constants.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js similarity index 97% rename from examples/jsm/renderers/webgpu/constants.js rename to examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js index 96b929a3a2137b..e9296480ea948f 100644 --- a/examples/jsm/renderers/webgpu/constants.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js @@ -314,10 +314,3 @@ export const GPUFeatureName = { BGRA8UNormStorage: 'bgra8unorm-storage', Float32Filterable: 'float32-filterable' }; - -export const GPUChunkSize = 16; // size of a chunk in bytes (STD140 layout) - -// @TODO: Move to src/constants.js - -export const BlendColorFactor = 211; -export const OneMinusBlendColorFactor = 212; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js similarity index 63% rename from examples/jsm/renderers/webgpu/WebGPURenderPipeline.js rename to examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js index bd796252a5edc8..860e8075d315a6 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js @@ -1,57 +1,35 @@ -import { GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js'; +import { + GPUInputStepMode, GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat +} from './WebGPUConstants.js'; + import { - Float16BufferAttribute, FrontSide, BackSide, DoubleSide, - NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, - NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc, - KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp, - NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, - AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, - ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor + NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, + NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, } from 'three'; -const typedArraysToVertexFormatPrefix = new Map( [ - [ Int8Array, [ 'sint8', 'snorm8' ]], - [ Uint8Array, [ 'uint8', 'unorm8' ]], - [ Int16Array, [ 'sint16', 'snorm16' ]], - [ Uint16Array, [ 'uint16', 'unorm16' ]], - [ Int32Array, [ 'sint32', 'snorm32' ]], - [ Uint32Array, [ 'uint32', 'unorm32' ]], - [ Float32Array, [ 'float32', ]], -] ); - -const typedAttributeToVertexFormatPrefix = new Map( [ - [ Float16BufferAttribute, [ 'float16', ]], -] ); +class WebGPUPipelineUtils { -const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ - [ Int32Array, 'sint32' ], - [ Uint32Array, 'uint32' ], - [ Float32Array, 'float32' ] -] ); + constructor( backend ) { -class WebGPURenderPipeline { + this.backend = backend; - constructor( device, utils ) { + } - this.cacheKey = null; - this.shaderAttributes = null; - this.stageVertex = null; - this.stageFragment = null; - this.usedTimes = 0; + createRenderPipeline( renderObject ) { - this._device = device; - this._utils = utils; - - } + const { object, material, geometry, pipeline } = renderObject; + const { vertexProgram, fragmentProgram } = pipeline; - init( renderObject, cacheKey, stageVertex, stageFragment ) { + const backend = this.backend; + const device = backend.device; + const utils = backend.utils; - const { object, material, geometry } = renderObject; + const pipelineGPU = backend.get( pipeline ); // determine shader attributes - const shaderAttributes = this._getShaderAttributes( renderObject ); + const shaderAttributes = backend.attributeUtils.createShaderAttributes( renderObject ); // vertex buffers @@ -70,11 +48,6 @@ class WebGPURenderPipeline { } - this.cacheKey = cacheKey; - this.shaderAttributes = shaderAttributes; - this.stageVertex = stageVertex; - this.stageFragment = stageFragment; - // blending let alphaBlend = {}; @@ -82,8 +55,8 @@ class WebGPURenderPipeline { if ( material.transparent === true && material.blending !== NoBlending ) { - alphaBlend = this._getAlphaBlend( material ); - colorBlend = this._getColorBlend( material ); + alphaBlend = this.getAlphaBlend( material ); + colorBlend = this.getColorBlend( material ); } @@ -94,26 +67,29 @@ class WebGPURenderPipeline { if ( material.stencilWrite === true ) { stencilFront = { - compare: this._getStencilCompare( material ), - failOp: this._getStencilOperation( material.stencilFail ), - depthFailOp: this._getStencilOperation( material.stencilZFail ), - passOp: this._getStencilOperation( material.stencilZPass ) + compare: this.getStencilCompare( material ), + failOp: this.getStencilOperation( material.stencilFail ), + depthFailOp: this.getStencilOperation( material.stencilZFail ), + passOp: this.getStencilOperation( material.stencilZPass ) }; } // - const primitiveState = this._getPrimitiveState( object, geometry, material ); - const colorWriteMask = this._getColorWriteMask( material ); - const depthCompare = this._getDepthCompare( material ); - const colorFormat = this._utils.getCurrentColorFormat(); - const depthStencilFormat = this._utils.getCurrentDepthStencilFormat(); - const sampleCount = this._utils.getSampleCount(); + const vertexModule = backend.get( vertexProgram ).module; + const fragmentModule = backend.get( fragmentProgram ).module; + + const primitiveState = this.getPrimitiveState( object, geometry, material ); + const colorWriteMask = this.getColorWriteMask( material ); + const depthCompare = this.getDepthCompare( material ); + const colorFormat = utils.getCurrentColorFormat(); + const depthStencilFormat = utils.getCurrentDepthStencilFormat(); + const sampleCount = utils.getSampleCount(); - this.pipeline = this._device.createRenderPipeline( { - vertex: Object.assign( {}, stageVertex.stage, { buffers: vertexBuffers } ), - fragment: Object.assign( {}, stageFragment.stage, { targets: [ { + pipelineGPU.pipeline = device.createRenderPipeline( { + vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ), + fragment: Object.assign( {}, fragmentModule, { targets: [ { format: colorFormat, blend: { alpha: alphaBlend, @@ -137,9 +113,25 @@ class WebGPURenderPipeline { layout: 'auto' } ); - } + } + + createComputePipeline( pipeline ) { - _getAlphaBlend( material ) { + const backend = this.backend; + const device = backend.device; + + const computeProgram = backend.get( pipeline.computeProgram ).module; + + const pipelineGPU = backend.get( pipeline ); + + pipelineGPU.pipeline = device.createComputePipeline( { + compute: computeProgram, + layout: 'auto' + } ); + + } + + getAlphaBlend( material ) { const blending = material.blending; const premultipliedAlpha = material.premultipliedAlpha; @@ -209,9 +201,9 @@ class WebGPURenderPipeline { if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) { alphaBlend = { - srcFactor: this._getBlendFactor( blendSrcAlpha ), - dstFactor: this._getBlendFactor( blendDstAlpha ), - operation: this._getBlendOperation( blendEquationAlpha ) + srcFactor: this.getBlendFactor( blendSrcAlpha ), + dstFactor: this.getBlendFactor( blendDstAlpha ), + operation: this.getBlendOperation( blendEquationAlpha ) }; } @@ -227,7 +219,7 @@ class WebGPURenderPipeline { } - _getBlendFactor( blend ) { + getBlendFactor( blend ) { let blendFactor; @@ -295,42 +287,7 @@ class WebGPURenderPipeline { } - _getBlendOperation( blendEquation ) { - - let blendOperation; - - switch ( blendEquation ) { - - case AddEquation: - blendOperation = GPUBlendOperation.Add; - break; - - case SubtractEquation: - blendOperation = GPUBlendOperation.Subtract; - break; - - case ReverseSubtractEquation: - blendOperation = GPUBlendOperation.ReverseSubtract; - break; - - case MinEquation: - blendOperation = GPUBlendOperation.Min; - break; - - case MaxEquation: - blendOperation = GPUBlendOperation.Max; - break; - - default: - console.error( 'THREE.WebGPURenderer: Blend equation not supported.', blendEquation ); - - } - - return blendOperation; - - } - - _getColorBlend( material ) { + getColorBlend( material ) { const blending = material.blending; const premultipliedAlpha = material.premultipliedAlpha; @@ -368,9 +325,9 @@ class WebGPURenderPipeline { break; case CustomBlending: - colorBlend.srcFactor = this._getBlendFactor( material.blendSrc ); - colorBlend.dstFactor = this._getBlendFactor( material.blendDst ); - colorBlend.operation = this._getBlendOperation( material.blendEquation ); + colorBlend.srcFactor = this.getBlendFactor( material.blendSrc ); + colorBlend.dstFactor = this.getBlendFactor( material.blendDst ); + colorBlend.operation = this.getBlendOperation( material.blendEquation ); break; default: @@ -382,110 +339,7 @@ class WebGPURenderPipeline { } - _getColorWriteMask( material ) { - - return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None; - - } - - _getDepthCompare( material ) { - - let depthCompare; - - if ( material.depthTest === false ) { - - depthCompare = GPUCompareFunction.Always; - - } else { - - const depthFunc = material.depthFunc; - - switch ( depthFunc ) { - - case NeverDepth: - depthCompare = GPUCompareFunction.Never; - break; - - case AlwaysDepth: - depthCompare = GPUCompareFunction.Always; - break; - - case LessDepth: - depthCompare = GPUCompareFunction.Less; - break; - - case LessEqualDepth: - depthCompare = GPUCompareFunction.LessEqual; - break; - - case EqualDepth: - depthCompare = GPUCompareFunction.Equal; - break; - - case GreaterEqualDepth: - depthCompare = GPUCompareFunction.GreaterEqual; - break; - - case GreaterDepth: - depthCompare = GPUCompareFunction.Greater; - break; - - case NotEqualDepth: - depthCompare = GPUCompareFunction.NotEqual; - break; - - default: - console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc ); - - } - - } - - return depthCompare; - - } - - _getPrimitiveState( object, geometry, material ) { - - const descriptor = {}; - - descriptor.topology = this._utils.getPrimitiveTopology( object, material ); - - if ( object.isLine === true && object.isLineSegments !== true ) { - - const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count; - descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value - - } - - switch ( material.side ) { - - case FrontSide: - descriptor.frontFace = GPUFrontFace.CW; - descriptor.cullMode = GPUCullMode.Front; - break; - - case BackSide: - descriptor.frontFace = GPUFrontFace.CW; - descriptor.cullMode = GPUCullMode.Back; - break; - - case DoubleSide: - descriptor.frontFace = GPUFrontFace.CW; - descriptor.cullMode = GPUCullMode.None; - break; - - default: - console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side ); - break; - - } - - return descriptor; - - } - - _getStencilCompare( material ) { + getStencilCompare( material ) { let stencilCompare; @@ -534,7 +388,7 @@ class WebGPURenderPipeline { } - _getStencilOperation( op ) { + getStencilOperation( op ) { let stencilOperation; @@ -581,89 +435,145 @@ class WebGPURenderPipeline { } - _getVertexFormat( geometryAttribute ) { + getBlendOperation( blendEquation ) { - const { itemSize, normalized } = geometryAttribute; - const ArrayType = geometryAttribute.array.constructor; - const AttributeType = geometryAttribute.constructor; + let blendOperation; - let format; + switch ( blendEquation ) { - if ( itemSize == 1 ) { + case AddEquation: + blendOperation = GPUBlendOperation.Add; + break; - format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType ); + case SubtractEquation: + blendOperation = GPUBlendOperation.Subtract; + break; - } else { + case ReverseSubtractEquation: + blendOperation = GPUBlendOperation.ReverseSubtract; + break; - const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType ); - const prefix = prefixOptions[ normalized ? 1 : 0 ]; + case MinEquation: + blendOperation = GPUBlendOperation.Min; + break; - if ( prefix ) { + case MaxEquation: + blendOperation = GPUBlendOperation.Max; + break; - const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize; - const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4; - const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT; + default: + console.error( 'THREE.WebGPURenderer: Blend equation not supported.', blendEquation ); - if ( paddedItemSize % 1 ) { + } - throw new Error( 'THREE.WebGPURenderer: Bad vertex format item size.' ); + return blendOperation; - } + } - format = `${prefix}x${paddedItemSize}`; + getPrimitiveState( object, geometry, material ) { - } + const descriptor = {}; + const utils = this.backend.utils; + + descriptor.topology = utils.getPrimitiveTopology( object, material ); + + if ( object.isLine === true && object.isLineSegments !== true ) { + + const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count; + descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value } - if ( ! format ) { + switch ( material.side ) { + + case FrontSide: + descriptor.frontFace = GPUFrontFace.CW; + descriptor.cullMode = GPUCullMode.Front; + break; + + case BackSide: + descriptor.frontFace = GPUFrontFace.CW; + descriptor.cullMode = GPUCullMode.Back; + break; + + case DoubleSide: + descriptor.frontFace = GPUFrontFace.CW; + descriptor.cullMode = GPUCullMode.None; + break; - console.error( 'THREE.WebGPURenderer: Vertex format not supported yet.' ); + default: + console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side ); + break; } - return format; + return descriptor; } - _getShaderAttributes( renderObject ) { + getColorWriteMask( material ) { - const attributes = renderObject.getAttributes(); - const shaderAttributes = []; + return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None; - for ( let slot = 0; slot < attributes.length; slot ++ ) { + } - const geometryAttribute = attributes[ slot ]; - const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT; + getDepthCompare( material ) { - const format = this._getVertexFormat( geometryAttribute ); + let depthCompare; - let arrayStride = geometryAttribute.itemSize * bytesPerElement; - let offset = 0; + if ( material.depthTest === false ) { - if ( geometryAttribute.isInterleavedBufferAttribute === true ) { + depthCompare = GPUCompareFunction.Always; - // @TODO: It can be optimized for "vertexBuffers" on RenderPipeline + } else { - arrayStride = geometryAttribute.data.stride * bytesPerElement; - offset = geometryAttribute.offset * bytesPerElement; + const depthFunc = material.depthFunc; - } + switch ( depthFunc ) { - shaderAttributes.push( { - geometryAttribute, - arrayStride, - offset, - format, - slot - } ); + case NeverDepth: + depthCompare = GPUCompareFunction.Never; + break; + + case AlwaysDepth: + depthCompare = GPUCompareFunction.Always; + break; + + case LessDepth: + depthCompare = GPUCompareFunction.Less; + break; + + case LessEqualDepth: + depthCompare = GPUCompareFunction.LessEqual; + break; + + case EqualDepth: + depthCompare = GPUCompareFunction.Equal; + break; + + case GreaterEqualDepth: + depthCompare = GPUCompareFunction.GreaterEqual; + break; + + case GreaterDepth: + depthCompare = GPUCompareFunction.Greater; + break; + + case NotEqualDepth: + depthCompare = GPUCompareFunction.NotEqual; + break; + + default: + console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc ); + + } } - return shaderAttributes; + return depthCompare; } } -export default WebGPURenderPipeline; +export default WebGPUPipelineUtils; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureMipmapUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureMipmapUtils.js new file mode 100644 index 00000000000000..c638581db20d3f --- /dev/null +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureMipmapUtils.js @@ -0,0 +1,163 @@ +import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './WebGPUConstants.js'; + +class WebGPUTextureMipmapUtils { + + constructor( device ) { + + this.device = device; + + const mipmapVertexSource = ` +struct VarysStruct { + @builtin( position ) Position: vec4, + @location( 0 ) vTex : vec2 +}; + +@vertex +fn main( @builtin( vertex_index ) vertexIndex : u32 ) -> VarysStruct { + + var Varys : VarysStruct; + + var pos = array< vec2, 4 >( + vec2( -1.0, 1.0 ), + vec2( 1.0, 1.0 ), + vec2( -1.0, -1.0 ), + vec2( 1.0, -1.0 ) + ); + + var tex = array< vec2, 4 >( + vec2( 0.0, 0.0 ), + vec2( 1.0, 0.0 ), + vec2( 0.0, 1.0 ), + vec2( 1.0, 1.0 ) + ); + + Varys.vTex = tex[ vertexIndex ]; + Varys.Position = vec4( pos[ vertexIndex ], 0.0, 1.0 ); + + return Varys; + +} +`; + + const mipmapFragmentSource = ` +@group( 0 ) @binding( 0 ) +var imgSampler : sampler; + +@group( 0 ) @binding( 1 ) +var img : texture_2d; + +@fragment +fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { + + return textureSample( img, imgSampler, vTex ); + +} +`; + + this.sampler = device.createSampler( { minFilter: GPUFilterMode.Linear } ); + + // We'll need a new pipeline for every texture format used. + this.pipelines = {}; + + this.mipmapVertexShaderModule = device.createShaderModule( { + label: 'mipmapVertex', + code: mipmapVertexSource + } ); + + this.mipmapFragmentShaderModule = device.createShaderModule( { + label: 'mipmapFragment', + code: mipmapFragmentSource + } ); + + } + + getMipmapPipeline( format ) { + + let pipeline = this.pipelines[ format ]; + + if ( pipeline === undefined ) { + + pipeline = this.device.createRenderPipeline( { + vertex: { + module: this.mipmapVertexShaderModule, + entryPoint: 'main' + }, + fragment: { + module: this.mipmapFragmentShaderModule, + entryPoint: 'main', + targets: [ { format } ] + }, + primitive: { + topology: GPUPrimitiveTopology.TriangleStrip, + stripIndexFormat: GPUIndexFormat.Uint32 + }, + layout: 'auto' + } ); + + this.pipelines[ format ] = pipeline; + + } + + return pipeline; + + } + + generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { + + const pipeline = this.getMipmapPipeline( textureGPUDescriptor.format ); + + const commandEncoder = this.device.createCommandEncoder( {} ); + const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. + + let srcView = textureGPU.createView( { + baseMipLevel: 0, + mipLevelCount: 1, + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer + } ); + + for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) { + + const dstView = textureGPU.createView( { + baseMipLevel: i, + mipLevelCount: 1, + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer + } ); + + const passEncoder = commandEncoder.beginRenderPass( { + colorAttachments: [ { + view: dstView, + loadOp: GPULoadOp.Clear, + storeOp: GPUStoreOp.Store, + clearValue: [ 0, 0, 0, 0 ] + } ] + } ); + + const bindGroup = this.device.createBindGroup( { + layout: bindGroupLayout, + entries: [ { + binding: 0, + resource: this.sampler + }, { + binding: 1, + resource: srcView + } ] + } ); + + passEncoder.setPipeline( pipeline ); + passEncoder.setBindGroup( 0, bindGroup ); + passEncoder.draw( 4, 1, 0, 0 ); + passEncoder.end(); + + srcView = dstView; + + } + + this.device.queue.submit( [ commandEncoder.finish() ] ); + + } + +} + +export default WebGPUTextureMipmapUtils; diff --git a/examples/jsm/renderers/webgpu/WebGPUTextures.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js similarity index 57% rename from examples/jsm/renderers/webgpu/WebGPUTextures.js rename to examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js index ba6e7196b1f408..69b316dc544704 100644 --- a/examples/jsm/renderers/webgpu/WebGPUTextures.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js @@ -1,476 +1,302 @@ -import { GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName } from './constants.js'; -import { VideoTexture, CubeTexture, Texture, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, RepeatWrapping, MirroredRepeatWrapping, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, +import { + GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName +} from './WebGPUConstants.js'; + +import { + VideoTexture, CubeTexture, Texture, + NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, + RepeatWrapping, MirroredRepeatWrapping, + RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat, DepthTexture, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type } from 'three'; -import WebGPUTextureUtils from './WebGPUTextureUtils.js'; -class WebGPUTextures { +import WebGPUTextureMipmapUtils from './WebGPUTextureMipmapUtils.js'; - constructor( device, properties, info ) { +class WebGPUTextureUtils { - this.device = device; - this.properties = properties; - this.info = info; + constructor( backend ) { - this.defaultTexture = null; - this.depthDefaultTexture = null; - this.defaultVideoTexture = null; - this.defaultCubeTexture = null; - this.defaultSampler = null; + this.backend = backend; - this.samplerCache = new Map(); - this.utils = null; + this.mipmapUtils = null; - } - - getDefaultSampler() { - - if ( this.defaultSampler === null ) { - - this.defaultSampler = this.device.createSampler( {} ); + this.defaultTexture = null; + this.defaultCubeTexture = null; - } - - return this.defaultSampler; - - } + } - getDefaultDepthTexture() { + createSampler( texture ) { - if ( this.depthDefaultTexture === null ) { + const backend = this.backend; + const device = backend.device; - const depthTexture = new DepthTexture(); - depthTexture.image.width = 1; - depthTexture.image.height = 1; + const textureGPU = backend.get( texture ); - this._uploadTexture( depthTexture ); - - this.depthDefaultTexture = this.getTextureGPU( depthTexture ); - - } - - return this.depthDefaultTexture; + textureGPU.sampler = device.createSampler( { + addressModeU: this._convertAddressMode( texture.wrapS ), + addressModeV: this._convertAddressMode( texture.wrapT ), + addressModeW: this._convertAddressMode( texture.wrapR ), + magFilter: this._convertFilterMode( texture.magFilter ), + minFilter: this._convertFilterMode( texture.minFilter ), + mipmapFilter: this._convertFilterMode( texture.minFilter ), + maxAnisotropy: texture.anisotropy + } ); } - getDefaultTexture() { - - if ( this.defaultTexture === null ) { - - const texture = new Texture(); - texture.minFilter = NearestFilter; - texture.magFilter = NearestFilter; + createDefaultTexture( texture ) { - this._uploadTexture( texture ); + let textureGPU; - this.defaultTexture = this.getTextureGPU( texture ); - - } + if ( texture.isCubeTexture ) { - return this.defaultTexture; + textureGPU = this._getDefaultCubeTextureGPU(); - } + } else { - getDefaultVideoTexture() { + textureGPU = this._getDefaultTextureGPU(); - if ( this.defaultVideoTexture === null ) { + } - const video = document.getElementById( 'video' ); + this.backend.get( texture ).texture = textureGPU; - const texture = new VideoTexture( video ); - texture.minFilter = NearestFilter; - texture.magFilter = NearestFilter; + } - this._uploadVideoTexture( texture ); + createTexture( texture ) { - this.defaultVideoTexture = this.getTextureGPU( texture ); + const backend = this.backend; - } + const textureData = backend.get( texture ); + if ( textureData.initialized ) return; - return this.defaultVideoTexture; - - } - - getDefaultCubeTexture() { - - if ( this.defaultCubeTexture === null ) { - - const texture = new CubeTexture(); - texture.minFilter = NearestFilter; - texture.magFilter = NearestFilter; - - this._uploadTexture( texture ); - - this.defaultCubeTexture = this.getTextureGPU( texture ); - - } + const { width, height, depth } = this._getSize( texture ); - return this.defaultCubeTexture; + const needsMipmaps = this._needsMipmaps( texture ); + const dimension = this._getDimension( texture ); + const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps ); + const format = texture.internalFormat || this._getFormat( texture ); - } + let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; - getTextureGPU( texture ) { + if ( texture.isCompressedTexture !== true ) { - const textureProperties = this.properties.get( texture ); + usage |= GPUTextureUsage.RENDER_ATTACHMENT; - return textureProperties.textureGPU; + } - } + const textureDescriptorGPU = { + label: texture.name, + size: { + width: width, + height: height, + depthOrArrayLayers: depth, + }, + mipLevelCount: mipLevelCount, + sampleCount: 1, + dimension: dimension, + format: format, + usage: usage + }; - getSampler( texture ) { + // texture creation - const textureProperties = this.properties.get( texture ); + textureData.texture = backend.device.createTexture( textureDescriptorGPU ); + textureData.initialized = true; - return textureProperties.samplerGPU; + textureData.needsMipmaps = needsMipmaps; + textureData.textureDescriptorGPU = textureDescriptorGPU; } - updateTexture( texture ) { - - let needsUpdate = false; - - const textureProperties = this.properties.get( texture ); - - if ( texture.version > 0 && textureProperties.version !== texture.version ) { - - const image = texture.image; + updateTexture( texture ) { - if ( image === undefined ) { + const textureData = this.backend.get( texture ); - console.warn( 'THREE.WebGPURenderer: Texture marked for update but image is undefined.' ); + const { needsMipmaps, textureDescriptorGPU } = textureData; - } else if ( image.complete === false ) { + // transfer texture data - console.warn( 'THREE.WebGPURenderer: Texture marked for update but image is incomplete.' ); - - } else { - - // texture init - - if ( textureProperties.initialized === undefined ) { - - textureProperties.initialized = true; - - const disposeCallback = onTextureDispose.bind( this ); - textureProperties.disposeCallback = disposeCallback; - - texture.addEventListener( 'dispose', disposeCallback ); - - this.info.memory.textures ++; - - } + if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) { - // + this._copyBufferToTexture( texture.image, textureData.texture, textureDescriptorGPU, needsMipmaps ); - if ( texture.isVideoTexture ) { + } else if ( texture.isCompressedTexture ) { - needsUpdate = this._uploadVideoTexture( texture ); + this._copyCompressedBufferToTexture( texture.mipmaps, textureData.texture, textureDescriptorGPU ); - } else { + } else if ( texture.isCubeTexture ) { - needsUpdate = this._uploadTexture( texture ); + if ( texture.image.length === 6 ) { - } + this._copyCubeMapToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps ); } - } - - // if the texture is used for RTT, it's necessary to init it once so the binding - // group's resource definition points to the respective GPUTexture + } else if ( texture.isRenderTargetTexture ) { - if ( textureProperties.initializedRTT === false ) { + if ( needsMipmaps === true ) this._generateMipmaps( textureData.texture, textureDescriptorGPU ); - textureProperties.initializedRTT = true; - needsUpdate = true; + } else if ( texture.isDepthTexture !== true && texture.image !== null ) { + this._copyImageToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps ); + } - return needsUpdate; - - } - - updateSampler( texture ) { - - const array = []; - - array.push( texture.wrapS ); - array.push( texture.wrapT ); - array.push( texture.wrapR ); - array.push( texture.magFilter ); - array.push( texture.minFilter ); - array.push( texture.anisotropy ); - - const key = array.join(); - let samplerGPU = this.samplerCache.get( key ); - - if ( samplerGPU === undefined ) { - - samplerGPU = this.device.createSampler( { - addressModeU: this._convertAddressMode( texture.wrapS ), - addressModeV: this._convertAddressMode( texture.wrapT ), - addressModeW: this._convertAddressMode( texture.wrapR ), - magFilter: this._convertFilterMode( texture.magFilter ), - minFilter: this._convertFilterMode( texture.minFilter ), - mipmapFilter: this._convertFilterMode( texture.minFilter ), - maxAnisotropy: texture.anisotropy - } ); - - this.samplerCache.set( key, samplerGPU ); - - } + // - const textureProperties = this.properties.get( texture ); - textureProperties.samplerGPU = samplerGPU; + textureData.version = texture.version; - } + } - initRenderTarget( renderTarget ) { + _getDefaultTextureGPU() { - const properties = this.properties; - const renderTargetProperties = properties.get( renderTarget ); + let defaultTexture = this.defaultTexture; - if ( renderTargetProperties.initialized === undefined ) { + if ( defaultTexture === null ) { - const device = this.device; + const texture = new Texture(); + texture.minFilter = NearestFilter; + texture.magFilter = NearestFilter; - const width = renderTarget.width; - const height = renderTarget.height; + this.createTexture( texture ); - const texture = renderTarget.texture; + this.defaultTexture = defaultTexture = 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, - size: { - width: width, - height: height, - depthOrArrayLayers: 1 - }, - mipLevelCount: mipLevelCount, - format: colorTextureFormat, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST - } ); + return this.backend.get( defaultTexture ).texture; - this.info.memory.textures ++; + } - renderTargetProperties.colorTextureGPU = colorTextureGPU; - renderTargetProperties.colorTextureFormat = colorTextureFormat; + _getDefaultCubeTextureGPU() { - // When the ".texture" or ".depthTexture" property of a render target is used as a map, - // the renderer has to find the respective GPUTexture objects to setup the bind groups. - // 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. + let defaultCubeTexture = this.defaultTexture; - const textureProperties = properties.get( texture ); - textureProperties.textureGPU = colorTextureGPU; - textureProperties.initializedRTT = false; + if ( defaultCubeTexture === null ) { - if ( renderTarget.depthBuffer === true ) { + const texture = new CubeTexture(); + texture.minFilter = NearestFilter; + texture.magFilter = NearestFilter; - const depthTextureFormat = renderTarget.depthTexture !== null ? this._getFormat( renderTarget.depthTexture ) : GPUTextureFormat.Depth24PlusStencil8; + this.createTexture( texture ); - const depthTextureGPU = device.createTexture( { - label: 'renderTarget' + label + '_depthBuffer', - size: { - width: width, - height: height, - depthOrArrayLayers: 1 - }, - format: depthTextureFormat, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST - } ); + this.defaultCubeTexture = defaultCubeTexture = texture; - this.info.memory.textures ++; + } - renderTargetProperties.depthTextureGPU = depthTextureGPU; - renderTargetProperties.depthTextureFormat = depthTextureFormat; + return this.backend.get( defaultCubeTexture ).texture; - if ( renderTarget.depthTexture !== null ) { + } - const depthTextureProperties = properties.get( renderTarget.depthTexture ); - depthTextureProperties.textureGPU = depthTextureGPU; - depthTextureProperties.initializedRTT = false; + _copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ) { - } + if ( this._isHTMLImage( image ) ) { - } + this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => { - // + this._copyExternalImageToTexture( imageBitmap, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ); - const disposeCallback = onRenderTargetDispose.bind( this ); - renderTargetProperties.disposeCallback = disposeCallback; + } ); - renderTarget.addEventListener( 'dispose', disposeCallback ); + } else { - // + // assume ImageBitmap - renderTargetProperties.initialized = true; + this._copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ); } } - dispose() { + _isHTMLImage( image ) { - this.samplerCache.clear(); + return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ); } - _convertAddressMode( value ) { - - let addressMode = GPUAddressMode.ClampToEdge; - - if ( value === RepeatWrapping ) { - - addressMode = GPUAddressMode.Repeat; - - } else if ( value === MirroredRepeatWrapping ) { + _copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU, needsMipmaps ) { - addressMode = GPUAddressMode.MirrorRepeat; - - } + for ( let i = 0; i < 6; i ++ ) { - return addressMode; + const image = images[ i ]; - } + if ( image.isDataTexture ) { - _convertFilterMode( value ) { + this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, needsMipmaps, i ); - let filterMode = GPUFilterMode.Linear; + } else { - if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) { + this._copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, i ); - filterMode = GPUFilterMode.Nearest; + } } - return filterMode; - } - _uploadVideoTexture( texture ) { + _copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) { - const device = this.device; - - const textureProperties = this.properties.get( texture ); - - const textureGPU = device.importExternalTexture( { - source: texture.source.data - } ); + const device = this.backend.device; - textureProperties.textureGPU = textureGPU; - //textureProperties.version = texture.version; // @TODO: Force update for now, study a better solution soon using native VideoTexture.update() to fix warns + device.queue.copyExternalImageToTexture( + { + source: image + }, { + texture: textureGPU, + mipLevel: 0, + origin: { x: 0, y: 0, z: originDepth } + }, { + width: image.width, + height: image.height, + depthOrArrayLayers: 1 + } + ); - return true; + if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth ); } - _uploadTexture( texture ) { + _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ) { - let needsUpdate = false; + if ( this.mipmapUtils === null ) { - const device = this.device; - const image = texture.image; - - const textureProperties = this.properties.get( texture ); - - const { width, height, depth } = this._getSize( texture ); - const needsMipmaps = this._needsMipmaps( texture ); - const dimension = this._getDimension( texture ); - const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps ); - const format = texture.internalFormat || this._getFormat( texture ); - - let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; - - if ( needsMipmaps ) { - - // current mipmap generation requires RENDER_ATTACHMENT - - usage |= GPUTextureUsage.RENDER_ATTACHMENT; + this.mipmapUtils = new WebGPUTextureMipmapUtils( this.backend.device ); } - const textureGPUDescriptor = { - label: texture.name, - size: { - width: width, - height: height, - depthOrArrayLayers: depth, - }, - mipLevelCount: mipLevelCount, - sampleCount: 1, - dimension: dimension, - format: format, - usage: usage - }; + this.mipmapUtils.generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ); - // texture creation - - let textureGPU = textureProperties.textureGPU; - - if ( textureGPU === undefined ) { - - textureGPU = device.createTexture( textureGPUDescriptor ); - - needsUpdate = true; - - } - - // transfer texture data - - if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) { - - this._copyBufferToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps ); - - } else if ( texture.isCompressedTexture ) { - - this._copyCompressedBufferToTexture( texture.mipmaps, textureGPU, textureGPUDescriptor ); - - } else if ( texture.isCubeTexture ) { - - if ( image.length === 6 ) { - - this._copyCubeMapToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps ); - - } - - } 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 ); + _getImageBitmapFromHTML( image, texture ) { - } + const width = image.width; + const height = image.height; - // + const options = {}; - textureProperties.textureGPU = textureGPU; - textureProperties.version = texture.version; + options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none'; + options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default'; - return needsUpdate; + return createImageBitmap( image, 0, 0, width, height, options ); } - _copyBufferToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth = 0 ) { + _copyBufferToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) { // @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture() // @TODO: Consider to support valid buffer layouts with other formats like RGB + const device = this.backend.device; + const data = image.data; - const bytesPerTexel = this._getBytesPerTexel( textureGPUDescriptor.format ); + const bytesPerTexel = this._getBytesPerTexel( textureDescriptorGPU.format ); const bytesPerRow = image.width * bytesPerTexel; - this.device.queue.writeTexture( + device.queue.writeTexture( { texture: textureGPU, mipLevel: 0, @@ -487,55 +313,17 @@ class WebGPUTextures { depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1 } ); - if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor, originDepth ); - - } - - _copyCubeMapToTexture( images, texture, textureGPU, textureGPUDescriptor, needsMipmaps ) { - - for ( let i = 0; i < 6; i ++ ) { - - const image = images[ i ]; - - if ( image.isDataTexture ) { - - this._copyBufferToTexture( image.image, textureGPU, textureGPUDescriptor, needsMipmaps, i ); - - } else { - - this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps, i ); - - } - - } + if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth ); } - _copyExternalImageToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth = 0 ) { - - this.device.queue.copyExternalImageToTexture( - { - source: image - }, { - texture: textureGPU, - mipLevel: 0, - origin: { x: 0, y: 0, z: originDepth } - }, { - width: image.width, - height: image.height, - depthOrArrayLayers: 1 - } - ); - - if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureGPUDescriptor, originDepth ); - - } - - _copyCompressedBufferToTexture( mipmaps, textureGPU, textureGPUDescriptor ) { + _copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) { // @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture() - const blockData = this._getBlockData( textureGPUDescriptor.format ); + const device = this.backend.device; + + const blockData = this._getBlockData( textureDescriptorGPU.format ); for ( let i = 0; i < mipmaps.length; i ++ ) { @@ -546,7 +334,7 @@ class WebGPUTextures { const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength; - this.device.queue.writeTexture( + device.queue.writeTexture( { texture: textureGPU, mipLevel: i @@ -567,19 +355,7 @@ class WebGPUTextures { } - _generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer ) { - - if ( this.utils === null ) { - - this.utils = new WebGPUTextureUtils( this.device ); // only create this helper if necessary - - } - - this.utils.generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer ); - - } - - _getBlockData( format ) { + _getBlockData( format ) { // this method is only relevant for compressed texture formats @@ -616,7 +392,75 @@ class WebGPUTextures { } - _getBytesPerTexel( format ) { + _convertAddressMode( value ) { + + let addressMode = GPUAddressMode.ClampToEdge; + + if ( value === RepeatWrapping ) { + + addressMode = GPUAddressMode.Repeat; + + } else if ( value === MirroredRepeatWrapping ) { + + addressMode = GPUAddressMode.MirrorRepeat; + + } + + return addressMode; + + } + + _convertFilterMode( value ) { + + let filterMode = GPUFilterMode.Linear; + + if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) { + + filterMode = GPUFilterMode.Nearest; + + } + + return filterMode; + + } + + _getSize( texture ) { + + const image = texture.image; + + let width, height, depth; + + if ( texture.isCubeTexture ) { + + const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null; + + width = faceImage ? faceImage.width : 1; + height = faceImage ? faceImage.height : 1; + depth = 6; // one image for each side of the cube map + + } else if ( image !== null ) { + + width = image.width; + height = image.height; + depth = ( image.depth !== undefined ) ? image.depth : 1; + + } else { + + width = height = depth = 1; + + } + + return { width, height, depth }; + + } + + _needsMipmaps( texture ) { + + return ( texture.isCompressedTexture !== true ) && ( texture.generateMipmaps === true ) && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter ); + + } + + _getBytesPerTexel( format ) { if ( format === GPUTextureFormat.R8Unorm ) return 1; if ( format === GPUTextureFormat.R16Float ) return 2; @@ -630,7 +474,7 @@ class WebGPUTextures { } - _getDimension( texture ) { + _getDimension( texture ) { let dimension; @@ -648,7 +492,29 @@ class WebGPUTextures { } - _getFormat( texture ) { + _getMipLevelCount( texture, width, height, needsMipmaps ) { + + let mipLevelCount; + + if ( texture.isCompressedTexture ) { + + mipLevelCount = texture.mipmaps.length; + + } else if ( needsMipmaps ) { + + mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1; + + } else { + + mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0) + + } + + return mipLevelCount; + + } + + _getFormat( texture ) { const format = texture.format; const type = texture.type; @@ -875,176 +741,6 @@ class WebGPUTextures { } - _isHTMLImage( image ) { - - return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ); - - } - - _copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth ) { - - if ( this._isHTMLImage( image ) ) { - - this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => { - - this._copyExternalImageToTexture( imageBitmap, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth ); - - } ); - - } else { - - // assume ImageBitmap - - this._copyExternalImageToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth ); - - } - - } - - _getImageBitmapFromHTML( image, texture ) { - - const width = image.width; - const height = image.height; - - const options = {}; - - options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none'; - options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default'; - - return createImageBitmap( image, 0, 0, width, height, options ); - - } - - _getImageBitmap( image, texture ) { - - const width = image.width; - const height = image.height; - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ) { - - const options = {}; - - options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none'; - options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default'; - - return createImageBitmap( image, 0, 0, width, height, options ); - - } else { - - // assume ImageBitmap - - return Promise.resolve( image ); - - } - - } - - _getMipLevelCount( texture, width, height, needsMipmaps ) { - - let mipLevelCount; - - if ( texture.isCompressedTexture ) { - - mipLevelCount = texture.mipmaps.length; - - } else if ( needsMipmaps ) { - - mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1; - - } else { - - mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0) - - } - - return mipLevelCount; - - } - - _getSize( texture ) { - - const image = texture.image; - - let width, height, depth; - - if ( texture.isCubeTexture ) { - - const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null; - - width = faceImage ? faceImage.width : 1; - height = faceImage ? faceImage.height : 1; - depth = 6; // one image for each side of the cube map - - } else if ( image !== null ) { - - width = image.width; - height = image.height; - depth = ( image.depth !== undefined ) ? image.depth : 1; - - } else { - - width = height = depth = 1; - - } - - return { width, height, depth }; - - } - - _needsMipmaps( texture ) { - - return ( texture.isCompressedTexture !== true ) && ( texture.generateMipmaps === true ) && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter ); - - } - -} - -function onRenderTargetDispose( event ) { - - const renderTarget = event.target; - const properties = this.properties; - - const renderTargetProperties = properties.get( renderTarget ); - - renderTarget.removeEventListener( 'dispose', renderTargetProperties.disposeCallback ); - - renderTargetProperties.colorTextureGPU.destroy(); - properties.remove( renderTarget.texture ); - - this.info.memory.textures --; - - if ( renderTarget.depthBuffer === true ) { - - renderTargetProperties.depthTextureGPU.destroy(); - - this.info.memory.textures --; - - if ( renderTarget.depthTexture !== null ) { - - properties.remove( renderTarget.depthTexture ); - - } - - } - - properties.remove( renderTarget ); - -} - -function onTextureDispose( event ) { - - const texture = event.target; - - const textureProperties = this.properties.get( texture ); - textureProperties.textureGPU.destroy(); - - texture.removeEventListener( 'dispose', textureProperties.disposeCallback ); - - this.properties.remove( texture ); - - this.info.memory.textures --; - } -export default WebGPUTextures; +export default WebGPUTextureUtils; diff --git a/examples/jsm/renderers/webgpu/WebGPUUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js similarity index 56% rename from examples/jsm/renderers/webgpu/WebGPUUtils.js rename to examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js index 800bd3eae25707..022fe3b955f36d 100644 --- a/examples/jsm/renderers/webgpu/WebGPUUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js @@ -1,40 +1,44 @@ -import { GPUPrimitiveTopology, GPUTextureFormat } from './constants.js'; +import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js'; class WebGPUUtils { - constructor( renderer ) { + constructor( backend ) { - this.renderer = renderer; + this.backend = backend; - } - - getCurrentColorSpace() { + } - const renderer = this.renderer; + getCurrentDepthStencilFormat() { + const renderer = this.backend.renderer; const renderTarget = renderer.getRenderTarget(); + let format; + if ( renderTarget !== null ) { - return renderTarget.texture.colorSpace; + format = this.get( renderTarget ).depthTextureFormat; - } + } else { - return renderer.outputColorSpace; + format = GPUTextureFormat.Depth24PlusStencil8; - } + } - getCurrentColorFormat() { + return format; - let format; + } - const renderer = this.renderer; + getCurrentColorFormat() { + + const renderer = this.backend.renderer; const renderTarget = renderer.getRenderTarget(); + let format; + if ( renderTarget !== null ) { - const renderTargetProperties = renderer._properties.get( renderTarget ); - format = renderTargetProperties.colorTextureFormat; + format = this.get( renderTarget ).colorTextureFormat; } else { @@ -46,29 +50,22 @@ class WebGPUUtils { } - getCurrentDepthStencilFormat() { - - let format; + getCurrentColorSpace() { - const renderer = this.renderer; + const renderer = this.backend.renderer; const renderTarget = renderer.getRenderTarget(); if ( renderTarget !== null ) { - const renderTargetProperties = renderer._properties.get( renderTarget ); - format = renderTargetProperties.depthTextureFormat; - - } else { - - format = GPUTextureFormat.Depth24PlusStencil8; + return renderTarget.texture.colorSpace; } - return format; + return renderer.outputColorSpace; } - getPrimitiveTopology( object, material ) { + getPrimitiveTopology( object, material ) { if ( object.isPoints ) return GPUPrimitiveTopology.PointList; else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList; @@ -77,9 +74,9 @@ class WebGPUUtils { } - getSampleCount() { + getSampleCount() { - return this.renderer._parameters.sampleCount; + return this.backend.parameters.sampleCount; } From 5b58ba9c49253146ee5734b784bac973d17b5c28 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 18 May 2023 04:40:04 -0300 Subject: [PATCH 02/26] cleanup --- .../jsm/renderers/universal/Attributes.js | 2 +- examples/jsm/renderers/universal/Backend.js | 96 ++--- examples/jsm/renderers/universal/Bindings.js | 10 +- .../jsm/renderers/universal/Geometries.js | 2 +- .../jsm/renderers/universal/RenderObjects.js | 2 +- .../renderers/universal/RenderPipelines.js | 2 +- examples/jsm/renderers/universal/Renderer.js | 10 +- examples/jsm/renderers/universal/Sampler.js | 1 - .../jsm/renderers/universal/StorageBuffer.js | 1 - .../jsm/renderers/universal/TextureUtils.js | 185 ---------- examples/jsm/renderers/universal/Textures.js | 2 +- examples/jsm/renderers/universal/Uniform.js | 8 +- .../jsm/renderers/universal/UniformsGroup.js | 2 +- examples/jsm/renderers/universal/constants.js | 2 +- .../jsm/renderers/universal/nodes/Nodes.js | 2 +- .../webgpu/backends/webgpu/WebGPUBackend.js | 342 +++++++++--------- 16 files changed, 241 insertions(+), 428 deletions(-) delete mode 100644 examples/jsm/renderers/universal/TextureUtils.js diff --git a/examples/jsm/renderers/universal/Attributes.js b/examples/jsm/renderers/universal/Attributes.js index 48df53e54ad8d5..4b66a06591e8fe 100644 --- a/examples/jsm/renderers/universal/Attributes.js +++ b/examples/jsm/renderers/universal/Attributes.js @@ -47,7 +47,7 @@ class Attributes extends DataMap { data.version = this._getBufferAttribute( attribute ).version; } else { - + const bufferAttribute = this._getBufferAttribute( attribute ); if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) { diff --git a/examples/jsm/renderers/universal/Backend.js b/examples/jsm/renderers/universal/Backend.js index 4e90bf464a4268..1244e6d9c2ea05 100644 --- a/examples/jsm/renderers/universal/Backend.js +++ b/examples/jsm/renderers/universal/Backend.js @@ -7,10 +7,10 @@ class Backend { constructor( parameters = {} ) { - this.parameters = Object.assign( {}, parameters ); + this.parameters = Object.assign( {}, parameters ); this.properties = new WeakMap(); this.renderer = null; - this.domElement = null; + this.domElement = null; } @@ -20,59 +20,59 @@ class Backend { } - // render context + // render context begin( renderContext ) { } - finish( renderContext ) { } + finish( renderContext ) { } - // render object + // render object draw( renderObject, info ) { } - // program + // program - createProgram( program ) { } + createProgram( program ) { } - destroyProgram( program ) { } + destroyProgram( program ) { } - // bindings + // bindings - createBindings( renderObject ) { } + createBindings( renderObject ) { } - updateBindings( renderObject ) { } + updateBindings( renderObject ) { } - // pipeline + // pipeline - createRenderPipeline( renderObject ) { } + createRenderPipeline( renderObject ) { } - createComputePipeline( computeNode, pipeline ) { } + createComputePipeline( computeNode, pipeline ) { } - destroyPipeline( pipeline ) { } + destroyPipeline( pipeline ) { } - // cache key + // cache key - needsUpdate( renderObject ) { } // return Boolean ( fast test ) + needsUpdate( renderObject ) { } // return Boolean ( fast test ) - getCacheKey( renderObject ) { } // return String + getCacheKey( renderObject ) { } // return String - // node builder + // node builder - createNodeBuilder( renderObject ) { } // return NodeBuilder (ADD IT) + createNodeBuilder( renderObject ) { } // return NodeBuilder (ADD IT) - // textures + // textures - createSampler( texture ) { } + createSampler( texture ) { } - createDefaultTexture( texture ) { } + createDefaultTexture( texture ) { } - createTexture( texture ) { } + createTexture( texture ) { } // attributes - createAttribute( attribute) { } + createAttribute( attribute ) { } - createIndexAttribute( attribute) { } + createIndexAttribute( attribute ) { } updateAttribute( attribute ) { } @@ -84,15 +84,15 @@ class Backend { // utils - hasFeature( name ) { } // return Boolean + hasFeature( name ) { } // return Boolean - getInstanceCount( renderObject ) { + getInstanceCount( renderObject ) { - const { object, geometry } = renderObject; + const { object, geometry } = renderObject; - return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 ); + return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 ); - } + } getDrawingBufferSize() { @@ -102,37 +102,37 @@ class Backend { } - getScissor() { + getScissor() { - vector4 = vector4 || new Vector4(); + vector4 = vector4 || new Vector4(); - return this.renderer.getScissor( vector4 ); + return this.renderer.getScissor( vector4 ); - } + } - getDomElement() { + getDomElement() { - let domElement = this.domElement; + let domElement = this.domElement; - if ( domElement === null ) { + if ( domElement === null ) { - this.domElement = domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : this.createCanvasElement(); + this.domElement = domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : this.createCanvasElement(); - } + } - return domElement; + return domElement; } - createCanvasElement() { + createCanvasElement() { - const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); canvas.style.display = 'block'; return canvas; - } + } - // resource properties + // resource properties get( object ) { @@ -149,11 +149,11 @@ class Backend { } - delete( object ) { + delete( object ) { - this.properties.delete( object ); + this.properties.delete( object ); - } + } } diff --git a/examples/jsm/renderers/universal/Bindings.js b/examples/jsm/renderers/universal/Bindings.js index 13728821541aa1..53f096cd1e6f40 100644 --- a/examples/jsm/renderers/universal/Bindings.js +++ b/examples/jsm/renderers/universal/Bindings.js @@ -6,7 +6,7 @@ class Bindings extends DataMap { super(); - this.backend = backend; + this.backend = backend; this.textures = textures; this.pipelines = pipelines; this.attributes = attributes; @@ -93,7 +93,7 @@ class Bindings extends DataMap { this.attributes.update( attribute, AttributeType.STORAGE ); - } + } } @@ -144,7 +144,7 @@ class Bindings extends DataMap { //const attribute = binding.attribute; } else if ( binding.isSampler ) { -/* + /* const texture = binding.getTexture(); textures.updateSampler( texture ); @@ -158,7 +158,7 @@ class Bindings extends DataMap { } */ - } + } updateMap.set( binding, frame ); @@ -168,7 +168,7 @@ class Bindings extends DataMap { //this.pipelines.getPipeline( object ) const pipline = this.pipelines.getForRender( object ); - + this.backend.updateBindings( bindings, pipline ); } diff --git a/examples/jsm/renderers/universal/Geometries.js b/examples/jsm/renderers/universal/Geometries.js index b1229787e449d8..e6e63ed830fa2b 100644 --- a/examples/jsm/renderers/universal/Geometries.js +++ b/examples/jsm/renderers/universal/Geometries.js @@ -71,7 +71,7 @@ class Geometries extends DataMap { constructor( attributes, info ) { super(); - + this.attributes = attributes; this.info = info; diff --git a/examples/jsm/renderers/universal/RenderObjects.js b/examples/jsm/renderers/universal/RenderObjects.js index 1501bfbda17504..76a8532944f1b6 100644 --- a/examples/jsm/renderers/universal/RenderObjects.js +++ b/examples/jsm/renderers/universal/RenderObjects.js @@ -76,7 +76,7 @@ class RenderObjects extends ChainMap { this.nodes.delete( renderObject ); this.delete( chainKey ); - + }; renderObject.material.addEventListener( 'dispose', onDispose ); diff --git a/examples/jsm/renderers/universal/RenderPipelines.js b/examples/jsm/renderers/universal/RenderPipelines.js index f51af414c04eb4..80a924e395e2d4 100644 --- a/examples/jsm/renderers/universal/RenderPipelines.js +++ b/examples/jsm/renderers/universal/RenderPipelines.js @@ -221,7 +221,7 @@ class RenderPipelines { // check material state - if ( needsUpdate === true || + if ( needsUpdate === true || cache.material !== material || cache.materialVersion !== material.version || cache.transparent !== material.transparent || cache.blending !== material.blending || cache.premultipliedAlpha !== material.premultipliedAlpha || cache.blendSrc !== material.blendSrc || cache.blendDst !== material.blendDst || cache.blendEquation !== material.blendEquation || diff --git a/examples/jsm/renderers/universal/Renderer.js b/examples/jsm/renderers/universal/Renderer.js index cd15f8f1526262..fdfd29a144fb98 100644 --- a/examples/jsm/renderers/universal/Renderer.js +++ b/examples/jsm/renderers/universal/Renderer.js @@ -10,7 +10,7 @@ import RenderContexts from './RenderContexts.js'; import Textures from './Textures.js'; import Background from './Background.js'; import Nodes from './nodes/Nodes.js'; -import { Frustum, Matrix4, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping, DepthFormat } from 'three'; +import { Frustum, Matrix4, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping } from 'three'; const _drawingBufferSize = new Vector4(); const _frustum = new Frustum(); @@ -27,7 +27,7 @@ class Renderer { this.domElement = backend.getDomElement(); - this.backend = backend; + this.backend = backend; this.autoClear = true; this.autoClearColor = true; @@ -112,7 +112,7 @@ class Renderer { await backend.init( this ); - } catch( error ) { + } catch ( error ) { reject( error ); return; @@ -212,7 +212,7 @@ class Renderer { // - this._nodes.updateScene( scene ); + this._nodes.updateScene( scene ); // @@ -730,7 +730,7 @@ class Renderer { this._bindings.updateForRender( renderObject ); // - + this.backend.draw( renderObject, this._info ); } diff --git a/examples/jsm/renderers/universal/Sampler.js b/examples/jsm/renderers/universal/Sampler.js index d489d503ad1de3..86fc056c0a7f90 100644 --- a/examples/jsm/renderers/universal/Sampler.js +++ b/examples/jsm/renderers/universal/Sampler.js @@ -1,5 +1,4 @@ import Binding from './Binding.js'; -//import { GPUBindingType } from './constants.js'; class Sampler extends Binding { diff --git a/examples/jsm/renderers/universal/StorageBuffer.js b/examples/jsm/renderers/universal/StorageBuffer.js index 5a9df742d90e38..a3cc685cf4e8b8 100644 --- a/examples/jsm/renderers/universal/StorageBuffer.js +++ b/examples/jsm/renderers/universal/StorageBuffer.js @@ -1,5 +1,4 @@ import Buffer from './Buffer.js'; -//import { GPUBindingType } from './constants.js'; class StorageBuffer extends Buffer { diff --git a/examples/jsm/renderers/universal/TextureUtils.js b/examples/jsm/renderers/universal/TextureUtils.js deleted file mode 100644 index 0b78e4e2636532..00000000000000 --- a/examples/jsm/renderers/universal/TextureUtils.js +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2020 Brandon Jones -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -//import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './constants.js'; - -// ported from https://github.com/toji/web-texture-tool/blob/master/src/webgpu-mipmap-generator.js - -class TextureUtils { - - constructor( device ) { - - this.device = device; - - const mipmapVertexSource = ` -struct VarysStruct { - @builtin( position ) Position: vec4, - @location( 0 ) vTex : vec2 -}; - -@vertex -fn main( @builtin( vertex_index ) vertexIndex : u32 ) -> VarysStruct { - - var Varys : VarysStruct; - - var pos = array< vec2, 4 >( - vec2( -1.0, 1.0 ), - vec2( 1.0, 1.0 ), - vec2( -1.0, -1.0 ), - vec2( 1.0, -1.0 ) - ); - - var tex = array< vec2, 4 >( - vec2( 0.0, 0.0 ), - vec2( 1.0, 0.0 ), - vec2( 0.0, 1.0 ), - vec2( 1.0, 1.0 ) - ); - - Varys.vTex = tex[ vertexIndex ]; - Varys.Position = vec4( pos[ vertexIndex ], 0.0, 1.0 ); - - return Varys; - -} -`; - - const mipmapFragmentSource = ` -@group( 0 ) @binding( 0 ) -var imgSampler : sampler; - -@group( 0 ) @binding( 1 ) -var img : texture_2d; - -@fragment -fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { - - return textureSample( img, imgSampler, vTex ); - -} -`; - - this.sampler = device.createSampler( { minFilter: GPUFilterMode.Linear } ); - - // We'll need a new pipeline for every texture format used. - this.pipelines = {}; - - this.mipmapVertexShaderModule = device.createShaderModule( { - label: 'mipmapVertex', - code: mipmapVertexSource - } ); - - this.mipmapFragmentShaderModule = device.createShaderModule( { - label: 'mipmapFragment', - code: mipmapFragmentSource - } ); - - } - - getMipmapPipeline( format ) { - - let pipeline = this.pipelines[ format ]; - - if ( pipeline === undefined ) { - - pipeline = this.device.createRenderPipeline( { - vertex: { - module: this.mipmapVertexShaderModule, - entryPoint: 'main' - }, - fragment: { - module: this.mipmapFragmentShaderModule, - entryPoint: 'main', - targets: [ { format } ] - }, - primitive: { - topology: GPUPrimitiveTopology.TriangleStrip, - stripIndexFormat: GPUIndexFormat.Uint32 - }, - layout: 'auto' - } ); - - this.pipelines[ format ] = pipeline; - - } - - return pipeline; - - } - - generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { - - const pipeline = this.getMipmapPipeline( textureGPUDescriptor.format ); - - const commandEncoder = this.device.createCommandEncoder( {} ); - const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. - - let srcView = textureGPU.createView( { - baseMipLevel: 0, - mipLevelCount: 1, - dimension: GPUTextureViewDimension.TwoD, - baseArrayLayer - } ); - - for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) { - - const dstView = textureGPU.createView( { - baseMipLevel: i, - mipLevelCount: 1, - dimension: GPUTextureViewDimension.TwoD, - baseArrayLayer - } ); - - const passEncoder = commandEncoder.beginRenderPass( { - colorAttachments: [ { - view: dstView, - loadOp: GPULoadOp.Clear, - storeOp: GPUStoreOp.Store, - clearValue: [ 0, 0, 0, 0 ] - } ] - } ); - - const bindGroup = this.device.createBindGroup( { - layout: bindGroupLayout, - entries: [ { - binding: 0, - resource: this.sampler - }, { - binding: 1, - resource: srcView - } ] - } ); - - passEncoder.setPipeline( pipeline ); - passEncoder.setBindGroup( 0, bindGroup ); - passEncoder.draw( 4, 1, 0, 0 ); - passEncoder.end(); - - srcView = dstView; - - } - - this.device.queue.submit( [ commandEncoder.finish() ] ); - - } - -} - -export default TextureUtils; diff --git a/examples/jsm/renderers/universal/Textures.js b/examples/jsm/renderers/universal/Textures.js index e27ac17417ea39..07752ff9901248 100644 --- a/examples/jsm/renderers/universal/Textures.js +++ b/examples/jsm/renderers/universal/Textures.js @@ -35,7 +35,7 @@ class Textures extends DataMap { this.info.memory.textures --; - } + }; texture.addEventListener( 'dispose', onDispose ); diff --git a/examples/jsm/renderers/universal/Uniform.js b/examples/jsm/renderers/universal/Uniform.js index ba87bdcfeac6d6..4386d3e755e025 100644 --- a/examples/jsm/renderers/universal/Uniform.js +++ b/examples/jsm/renderers/universal/Uniform.js @@ -133,8 +133,8 @@ class Matrix4Uniform extends Uniform { } -export { - FloatUniform, - Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, - Matrix3Uniform, Matrix4Uniform +export { + FloatUniform, + Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, + Matrix3Uniform, Matrix4Uniform }; diff --git a/examples/jsm/renderers/universal/UniformsGroup.js b/examples/jsm/renderers/universal/UniformsGroup.js index 48e9c592935233..a312159411e990 100644 --- a/examples/jsm/renderers/universal/UniformsGroup.js +++ b/examples/jsm/renderers/universal/UniformsGroup.js @@ -1,5 +1,5 @@ import UniformBuffer from './UniformBuffer.js'; -import { GPUChunkBytes } from './constants.js'; +import { GPUChunkBytes } from './Constants.js'; class UniformsGroup extends UniformBuffer { diff --git a/examples/jsm/renderers/universal/constants.js b/examples/jsm/renderers/universal/constants.js index 71d984237cae12..4e15efefbf7220 100644 --- a/examples/jsm/renderers/universal/constants.js +++ b/examples/jsm/renderers/universal/constants.js @@ -1,5 +1,5 @@ export const AttributeType = { - VERTEX: 1, + VERTEX: 1, INDEX: 2, STORAGE: 4 }; diff --git a/examples/jsm/renderers/universal/nodes/Nodes.js b/examples/jsm/renderers/universal/nodes/Nodes.js index 81d3d7ac25426f..21fad9ad63cabc 100644 --- a/examples/jsm/renderers/universal/nodes/Nodes.js +++ b/examples/jsm/renderers/universal/nodes/Nodes.js @@ -291,7 +291,7 @@ class Nodes extends DataMap { } - + updateForRender( renderObject ) { const nodeFrame = this.getNodeFrame( renderObject ); diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js index 054b27bc57168e..cfc3e66474c2c8 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js @@ -1,5 +1,5 @@ -import { - GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat +import { + GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat } from './utils/WebGPUConstants.js'; import WebGPUNodeBuilder from './builder/WGSLNodeBuilder.js'; @@ -100,11 +100,11 @@ function initWebGPUHack() { class WebGPUBackend extends Backend { - constructor( parameters = {} ) { + constructor( parameters = {} ) { - super( parameters ); + super( parameters ); - // some parameters require default values other than "undefined" + // some parameters require default values other than "undefined" this.parameters.antialias = ( parameters.antialias === true ); @@ -120,82 +120,82 @@ class WebGPUBackend extends Backend { this.parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits; - this.adapter = null; - this.device = null; - this.context = null; - this.colorBuffer = null; - this.depthBuffer = null; + this.adapter = null; + this.device = null; + this.context = null; + this.colorBuffer = null; + this.depthBuffer = null; this.utils = new WebGPUUtils( this ); this.attributeUtils = new WebGPUAttributeUtils( this ); - this.bindingUtils = new WebGPUBindingUtils( this ); + this.bindingUtils = new WebGPUBindingUtils( this ); this.pipelineUtils = new WebGPUPipelineUtils( this ); this.textureUtils = new WebGPUTextureUtils( this ); - } + } - async init( renderer ) { + async init( renderer ) { - await super.init( renderer ); + await super.init( renderer ); - // + // - const parameters = this.parameters; + const parameters = this.parameters; - const adapterOptions = { - powerPreference: parameters.powerPreference - }; + const adapterOptions = { + powerPreference: parameters.powerPreference + }; - const adapter = await navigator.gpu.requestAdapter( adapterOptions ); + const adapter = await navigator.gpu.requestAdapter( adapterOptions ); - if ( adapter === null ) { + if ( adapter === null ) { - throw new Error( 'WebGPUBackend: Unable to create WebGPU adapter.' ); + throw new Error( 'WebGPUBackend: Unable to create WebGPU adapter.' ); - } + } - // feature support + // feature support - const features = Object.values( GPUFeatureName ); + const features = Object.values( GPUFeatureName ); - const supportedFeatures = []; + const supportedFeatures = []; - for ( const name of features ) { + for ( const name of features ) { - if ( adapter.features.has( name ) ) { + if ( adapter.features.has( name ) ) { - supportedFeatures.push( name ); + supportedFeatures.push( name ); - } + } - } + } - const deviceDescriptor = { - requiredFeatures: supportedFeatures, - requiredLimits: parameters.requiredLimits - }; + const deviceDescriptor = { + requiredFeatures: supportedFeatures, + requiredLimits: parameters.requiredLimits + }; - const device = await adapter.requestDevice( deviceDescriptor ); + const device = await adapter.requestDevice( deviceDescriptor ); - const context = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgpu' ); + const context = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgpu' ); - this.adapter = adapter; - this.device = device; - this.context = context; + this.adapter = adapter; + this.device = device; + this.context = context; initWebGPUHack(); - this.updateSize(); + this.updateSize(); - } + } - beginRender( renderContext ) { + beginRender( renderContext ) { - const renderContextData = this.get( renderContext ); + const renderContextData = this.get( renderContext ); - const device = this.device; + const device = this.device; - const descriptor = { + const descriptor = { colorAttachments: [ { view: null } ], @@ -204,22 +204,22 @@ class WebGPUBackend extends Backend { } }; - const colorAttachment = descriptor.colorAttachments[ 0 ]; + const colorAttachment = descriptor.colorAttachments[ 0 ]; const depthStencilAttachment = descriptor.depthStencilAttachment; - if ( this.parameters.antialias === true ) { + if ( this.parameters.antialias === true ) { - colorAttachment.view = this.colorBuffer.createView(); - colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + colorAttachment.view = this.colorBuffer.createView(); + colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); - } else { + } else { - colorAttachment.view = this.context.getCurrentTexture().createView(); - colorAttachment.resolveTarget = undefined; + colorAttachment.view = this.context.getCurrentTexture().createView(); + colorAttachment.resolveTarget = undefined; - } + } - depthStencilAttachment.view = this.depthBuffer.createView(); + depthStencilAttachment.view = this.depthBuffer.createView(); if ( renderContext.clearColor ) { @@ -254,7 +254,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { if ( renderContext.clearStencil ) { - + depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue; depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store; @@ -268,26 +268,26 @@ class WebGPUBackend extends Backend { } - const encoder = device.createCommandEncoder( {} ); - const currentPass = encoder.beginRenderPass( descriptor ); + const encoder = device.createCommandEncoder( {} ); + const currentPass = encoder.beginRenderPass( descriptor ); - renderContextData.descriptor = descriptor; - renderContextData.encoder = encoder; + renderContextData.descriptor = descriptor; + renderContextData.encoder = encoder; renderContextData.currentPass = currentPass; - } - - finishRender( renderContext ) { + } - const renderContextData = this.get( renderContext ); + finishRender( renderContext ) { - const device = this.device; + const renderContextData = this.get( renderContext ); - renderContextData.currentPass.end(); + const device = this.device; + + renderContextData.currentPass.end(); device.queue.submit( [ renderContextData.encoder.finish() ] ); - } + } // compute @@ -311,7 +311,7 @@ class WebGPUBackend extends Backend { // bind group - const bindGroupGPU = this.get( bindings ).group; + const bindGroupGPU = this.get( bindings ).group; passEncoderGPU.setBindGroup( 0, bindGroupGPU ); passEncoderGPU.dispatchWorkgroups( computeNode.dispatchCount ); @@ -327,24 +327,24 @@ class WebGPUBackend extends Backend { } - // render object + // render object - draw( renderObject, info ) { + draw( renderObject, info ) { - const { object, geometry, context, pipeline } = renderObject; + const { object, geometry, context, pipeline } = renderObject; - const bindingsData = this.get( renderObject.getBindings() ); - const contextData = this.get( context ); - const pipelineGPU = this.get( pipeline ).pipeline; + const bindingsData = this.get( renderObject.getBindings() ); + const contextData = this.get( context ); + const pipelineGPU = this.get( pipeline ).pipeline; - // pipeline + // pipeline - const passEncoderGPU = contextData.currentPass; - passEncoderGPU.setPipeline( pipelineGPU ); + const passEncoderGPU = contextData.currentPass; + passEncoderGPU.setPipeline( pipelineGPU ); - // bind group + // bind group - const bindGroupGPU = bindingsData.group; + const bindGroupGPU = bindingsData.group; passEncoderGPU.setBindGroup( 0, bindGroupGPU ); // index @@ -355,10 +355,10 @@ class WebGPUBackend extends Backend { if ( hasIndex === true ) { - const buffer = this.get( index ).buffer; - const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32; + const buffer = this.get( index ).buffer; + const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32; - passEncoderGPU.setIndexBuffer( buffer, indexFormat ); + passEncoderGPU.setIndexBuffer( buffer, indexFormat ); } @@ -367,8 +367,8 @@ class WebGPUBackend extends Backend { const attributes = renderObject.getAttributes(); for ( let i = 0, l = attributes.length; i < l; i ++ ) { - - const buffer = this.get( attributes[ i ] ).buffer; + + const buffer = this.get( attributes[ i ] ).buffer; passEncoderGPU.setVertexBuffer( i, buffer ); } @@ -399,15 +399,15 @@ class WebGPUBackend extends Backend { } - } + } - // cache key + // cache key - needsUpdate( renderObject ) { + needsUpdate( renderObject ) { - const renderObjectGPU = this.get( renderObject ); + const renderObjectGPU = this.get( renderObject ); - const { object, material } = renderObject; + const { object, material } = renderObject; const utils = this.utils; @@ -415,19 +415,19 @@ class WebGPUBackend extends Backend { const colorSpace = utils.getCurrentColorSpace(); const colorFormat = utils.getCurrentColorFormat(); const depthStencilFormat = utils.getCurrentDepthStencilFormat(); - const primitiveTopology = utils.getPrimitiveTopology( object, material ) + const primitiveTopology = utils.getPrimitiveTopology( object, material ); - let needsUpdate = false; + let needsUpdate = false; if ( renderObjectGPU.sampleCount !== sampleCount || renderObjectGPU.colorSpace !== colorSpace || renderObjectGPU.colorFormat !== colorFormat || renderObjectGPU.depthStencilFormat !== depthStencilFormat || renderObjectGPU.primitiveTopology !== primitiveTopology ) { - renderObjectGPU.sampleCount = sampleCount; + renderObjectGPU.sampleCount = sampleCount; renderObjectGPU.colorSpace = colorSpace; renderObjectGPU.colorFormat = colorFormat; renderObjectGPU.depthStencilFormat = depthStencilFormat; - renderObjectGPU.primitiveTopology = primitiveTopology; + renderObjectGPU.primitiveTopology = primitiveTopology; needsUpdate = true; @@ -435,32 +435,32 @@ class WebGPUBackend extends Backend { return needsUpdate; - } + } - getCacheKey( renderObject ) { + getCacheKey( renderObject ) { - const { object, material } = renderObject; + const { object, material } = renderObject; const utils = this.utils; - return [ - utils.getSampleCount(), + return [ + utils.getSampleCount(), utils.getCurrentColorSpace(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(), utils.getPrimitiveTopology( object, material ) - ].join(); + ].join(); - } + } // textures createSampler( texture ) { - + this.textureUtils.createSampler( texture ); } createDefaultTexture( texture ) { - + this.textureUtils.createDefaultTexture( texture ); } @@ -478,7 +478,7 @@ class WebGPUBackend extends Backend { } - destroyTexture( /*texture*/ ) { + destroyTexture( /*texture*/ ) { console.warn( '.destroyTexture() not implemented yet.' ); @@ -492,32 +492,32 @@ class WebGPUBackend extends Backend { } - // program + // program - createProgram( program ) { + createProgram( program ) { - const programGPU = this.get( program ); + const programGPU = this.get( program ); - programGPU.module = { + programGPU.module = { module: this.device.createShaderModule( { code: program.code, label: program.stage } ), entryPoint: 'main' }; - } + } - destroyProgram( program ) { + destroyProgram( program ) { - this.delete( program ); + this.delete( program ); - } + } - // pipelines + // pipelines - createRenderPipeline( renderObject ) { + createRenderPipeline( renderObject ) { - this.pipelineUtils.createRenderPipeline( renderObject ); + this.pipelineUtils.createRenderPipeline( renderObject ); - } + } createComputePipeline( computePipeline ) { @@ -527,11 +527,11 @@ class WebGPUBackend extends Backend { // bindings - createBindings( bindings, pipeline ) { + createBindings( bindings, pipeline ) { this.bindingUtils.createBindings( bindings, pipeline ); - } + } updateBindings( bindings, pipeline ) { @@ -539,25 +539,25 @@ class WebGPUBackend extends Backend { } - updateBinding( binding ) { + updateBinding( binding ) { this.bindingUtils.updateBinding( binding ); } - // attributes + // attributes - createIndexAttribute( attribute ) { + createIndexAttribute( attribute ) { - this.attributeUtils.createAttribute( attribute, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); + this.attributeUtils.createAttribute( attribute, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); - } + } - createAttribute( attribute ) { + createAttribute( attribute ) { - this.attributeUtils.createAttribute( attribute, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); + this.attributeUtils.createAttribute( attribute, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST ); - } + } createStorageAttribute( attribute ) { @@ -565,11 +565,11 @@ class WebGPUBackend extends Backend { } - updateAttribute( attribute ) { + updateAttribute( attribute ) { this.attributeUtils.updateAttribute( attribute ); - } + } destroyAttribute( /*attribute*/ ) { @@ -577,15 +577,15 @@ class WebGPUBackend extends Backend { } - // canvas + // canvas - updateSize() { + updateSize() { - this._configureContext(); - this._setupColorBuffer(); - this._setupDepthBuffer(); + this._configureContext(); + this._setupColorBuffer(); + this._setupDepthBuffer(); - } + } // utils public @@ -609,56 +609,56 @@ class WebGPUBackend extends Backend { } - // utils + // utils - _configureContext() { + _configureContext() { - this.context.configure( { - device: this.device, - format: GPUTextureFormat.BGRA8Unorm, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, - alphaMode: 'premultiplied' - } ); + this.context.configure( { + device: this.device, + format: GPUTextureFormat.BGRA8Unorm, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + alphaMode: 'premultiplied' + } ); - } + } - _setupColorBuffer() { + _setupColorBuffer() { - if ( this.colorBuffer ) this.colorBuffer.destroy(); + if ( this.colorBuffer ) this.colorBuffer.destroy(); - const { width, height } = this.getDrawingBufferSize(); + const { width, height } = this.getDrawingBufferSize(); - this.colorBuffer = this.device.createTexture( { - label: 'colorBuffer', - size: { - width: width, - height: height, - depthOrArrayLayers: 1 - }, - sampleCount: this.parameters.sampleCount, - format: GPUTextureFormat.BGRA8Unorm, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC - } ); + this.colorBuffer = this.device.createTexture( { + label: 'colorBuffer', + size: { + width: width, + height: height, + depthOrArrayLayers: 1 + }, + sampleCount: this.parameters.sampleCount, + format: GPUTextureFormat.BGRA8Unorm, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC + } ); - } + } - _setupDepthBuffer() { + _setupDepthBuffer() { - if ( this.depthBuffer ) this.depthBuffer.destroy(); + if ( this.depthBuffer ) this.depthBuffer.destroy(); - const { width, height } = this.getDrawingBufferSize(); + const { width, height } = this.getDrawingBufferSize(); - this.depthBuffer = this.device.createTexture( { - label: 'depthBuffer', - size: { - width: width, - height: height, - depthOrArrayLayers: 1 - }, - sampleCount: this.parameters.sampleCount, - format: GPUTextureFormat.Depth24PlusStencil8, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC - } ); + this.depthBuffer = this.device.createTexture( { + label: 'depthBuffer', + size: { + width: width, + height: height, + depthOrArrayLayers: 1 + }, + sampleCount: this.parameters.sampleCount, + format: GPUTextureFormat.Depth24PlusStencil8, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC + } ); } From fa15e4d0b0c7fec99060e6c3c88ba6d218b9d4f6 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 18 May 2023 04:59:02 -0300 Subject: [PATCH 03/26] Rename constants.js to Constants.js --- examples/jsm/renderers/universal/{constants.js => Constants.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/jsm/renderers/universal/{constants.js => Constants.js} (100%) diff --git a/examples/jsm/renderers/universal/constants.js b/examples/jsm/renderers/universal/Constants.js similarity index 100% rename from examples/jsm/renderers/universal/constants.js rename to examples/jsm/renderers/universal/Constants.js From 29ba8ac32cb6e030e4e45be7bc6e7235b509a55e Mon Sep 17 00:00:00 2001 From: sunag Date: Mon, 22 May 2023 23:58:38 -0300 Subject: [PATCH 04/26] revisions --- .../display/ViewportSharedTextureNode.js | 6 +- .../jsm/nodes/display/ViewportTextureNode.js | 34 ++- .../jsm/nodes/lighting/AnalyticLightNode.js | 6 +- .../jsm/nodes/lighting/HemisphereLightNode.js | 4 +- examples/jsm/nodes/utils/EquirectUVNode.js | 5 +- .../jsm/renderers/universal/Attributes.js | 8 +- examples/jsm/renderers/universal/Backend.js | 8 +- examples/jsm/renderers/universal/Bindings.js | 57 ++-- examples/jsm/renderers/universal/Buffer.js | 10 +- .../jsm/renderers/universal/Geometries.js | 39 ++- examples/jsm/renderers/universal/Pipelines.js | 46 ++-- .../jsm/renderers/universal/Properties.js | 44 ---- .../jsm/renderers/universal/RenderContext.js | 10 +- .../jsm/renderers/universal/RenderObject.js | 10 + .../jsm/renderers/universal/RenderObjects.js | 9 +- examples/jsm/renderers/universal/Renderer.js | 103 ++++++-- .../jsm/renderers/universal/SampledTexture.js | 16 +- .../renderers/universal/TextureRenderer.js | 2 +- examples/jsm/renderers/universal/Textures.js | 162 +++++++++--- .../jsm/renderers/universal/UniformsGroup.js | 24 +- .../universal/nodes/NodeSampledTexture.js | 8 +- .../renderers/universal/nodes/NodeSampler.js | 4 +- .../jsm/renderers/webgpu/WebGPURenderer.js | 14 + .../webgpu/backends/webgpu/WebGPUBackend.js | 214 ++++++++++++--- .../webgpu/builder/WGSLNodeBuilder.js | 12 +- .../webgpu/utils/WebGPUAttributeUtils.js | 113 ++++++-- .../webgpu/utils/WebGPUBindingUtils.js | 75 +++--- .../backends/webgpu/utils/WebGPUConstants.js | 6 + .../webgpu/utils/WebGPUPipelineUtils.js | 118 +++++---- .../webgpu/utils/WebGPUTextureUtils.js | 247 ++++++++++++------ .../backends/webgpu/utils/WebGPUUtils.js | 49 ++-- examples/webgpu_backdrop.html | 4 +- examples/webgpu_depth_texture.html | 5 +- examples/webgpu_rtt.html | 6 +- 34 files changed, 960 insertions(+), 518 deletions(-) delete mode 100644 examples/jsm/renderers/universal/Properties.js diff --git a/examples/jsm/nodes/display/ViewportSharedTextureNode.js b/examples/jsm/nodes/display/ViewportSharedTextureNode.js index 395a3daeb2a871..108631ceb77921 100644 --- a/examples/jsm/nodes/display/ViewportSharedTextureNode.js +++ b/examples/jsm/nodes/display/ViewportSharedTextureNode.js @@ -3,7 +3,7 @@ import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; import { viewportTopLeft } from './ViewportNode.js'; -let rtt = null; +let sharedFramebuffer = null; class ViewportSharedTextureNode extends ViewportTextureNode { @@ -13,9 +13,9 @@ class ViewportSharedTextureNode extends ViewportTextureNode { } - constructRTT( builder ) { + constructFramebuffer( builder ) { - return rtt || ( rtt = builder.getRenderTarget() ); + return sharedFramebuffer || ( sharedFramebuffer = super.constructFramebuffer( builder ) ); } diff --git a/examples/jsm/nodes/display/ViewportTextureNode.js b/examples/jsm/nodes/display/ViewportTextureNode.js index 57a1d6308b287f..e1af41eb04ea1d 100644 --- a/examples/jsm/nodes/display/ViewportTextureNode.js +++ b/examples/jsm/nodes/display/ViewportTextureNode.js @@ -3,9 +3,9 @@ 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'; +import { Vector2, FramebufferTexture } from 'three'; -let size = new Vector2(); +const _size = new Vector2(); class ViewportTextureNode extends TextureNode { @@ -13,25 +13,21 @@ class ViewportTextureNode extends TextureNode { super( null, uv, level ); - this.rtt = null; - this.isOutputTextureNode = true; this.updateBeforeType = NodeUpdateType.FRAME; } - constructRTT( builder ) { + constructFramebuffer( /*builder*/ ) { - return builder.getRenderTarget(); + return new FramebufferTexture(); } construct( builder ) { - if ( this.rtt === null ) this.rtt = this.constructRTT( builder ); - - this.value = this.rtt.texture; + if ( this.value === null ) this.value = this.constructFramebuffer( builder ); return super.construct( builder ); @@ -39,14 +35,24 @@ class ViewportTextureNode extends TextureNode { updateBefore( frame ) { - const rtt = this.rtt; - const renderer = frame.renderer; - renderer.getDrawingBufferSize( size ); + renderer.getDrawingBufferSize( _size ); + + // + + const framebufferTexture = this.value; + + if ( framebufferTexture.image.width !== _size.width || framebufferTexture.image.height !== _size.height ) { + + framebufferTexture.image.width = _size.width; + framebufferTexture.image.height = _size.height; + framebufferTexture.needsUpdate = true; + + } - rtt.setSize( size.width, size.height ); + // - renderer.copyFramebufferToRenderTarget( rtt ); + renderer.copyFramebufferToTexture( framebufferTexture ); } diff --git a/examples/jsm/nodes/lighting/AnalyticLightNode.js b/examples/jsm/nodes/lighting/AnalyticLightNode.js index 04d3b287f6e01c..687066b79291e9 100644 --- a/examples/jsm/nodes/lighting/AnalyticLightNode.js +++ b/examples/jsm/nodes/lighting/AnalyticLightNode.js @@ -6,6 +6,7 @@ import { vec3 } from '../shadernode/ShaderNode.js'; import { reference } from '../accessors/ReferenceNode.js'; import { texture } from '../accessors/TextureNode.js'; import { positionWorld } from '../accessors/PositionNode.js'; +//import { step } from '../math/MathNode.js'; import { cond } from '../math/CondNode.js'; import MeshBasicNodeMaterial from '../materials/MeshBasicNodeMaterial.js'; @@ -51,6 +52,9 @@ class AnalyticLightNode extends LightingNode { const depthTexture = new DepthTexture(); depthTexture.minFilter = NearestFilter; depthTexture.magFilter = NearestFilter; + depthTexture.image.width = shadow.mapSize.width; + depthTexture.image.height = shadow.mapSize.height; + //depthTexture.compareFunction = THREE.LessCompare; rtt.depthTexture = depthTexture; @@ -128,7 +132,7 @@ class AnalyticLightNode extends LightingNode { } - update( frame ) { + update( /*frame*/ ) { const { light } = this; diff --git a/examples/jsm/nodes/lighting/HemisphereLightNode.js b/examples/jsm/nodes/lighting/HemisphereLightNode.js index ade9b69d69dde9..0c0ec936fdbd99 100644 --- a/examples/jsm/nodes/lighting/HemisphereLightNode.js +++ b/examples/jsm/nodes/lighting/HemisphereLightNode.js @@ -14,8 +14,8 @@ class HemisphereLightNode extends AnalyticLightNode { super( light ); - this.lightPositionNode = objectPosition; - this.lightDirectionNode = objectPosition.normalize(); + this.lightPositionNode = objectPosition( light ); + this.lightDirectionNode = this.lightPositionNode.normalize(); this.groundColorNode = uniform( new Color() ); diff --git a/examples/jsm/nodes/utils/EquirectUVNode.js b/examples/jsm/nodes/utils/EquirectUVNode.js index d53f2f16226e1a..9b3859f971ae41 100644 --- a/examples/jsm/nodes/utils/EquirectUVNode.js +++ b/examples/jsm/nodes/utils/EquirectUVNode.js @@ -1,5 +1,4 @@ import TempNode from '../core/TempNode.js'; -import { negate } from '../math/MathNode.js'; import { positionWorldDirection } from '../accessors/PositionNode.js'; import { nodeProxy, vec2 } from '../shadernode/ShaderNode.js'; import { addNodeClass } from '../core/Node.js'; @@ -16,10 +15,10 @@ class EquirectUVNode extends TempNode { construct() { - const dir = negate( this.dirNode ); + const dir = this.dirNode; const u = dir.z.atan2( dir.x ).mul( 1 / ( Math.PI * 2 ) ).add( 0.5 ); - const v = dir.y.clamp( - 1.0, 1.0 ).asin().mul( 1 / Math.PI ).add( 0.5 ); + const v = dir.y.negate().clamp( - 1.0, 1.0 ).asin().mul( 1 / Math.PI ).add( 0.5 ); // @TODO: The use of negate() here could be an NDC issue. return vec2( u, v ); diff --git a/examples/jsm/renderers/universal/Attributes.js b/examples/jsm/renderers/universal/Attributes.js index 4b66a06591e8fe..afb3467cf66ed5 100644 --- a/examples/jsm/renderers/universal/Attributes.js +++ b/examples/jsm/renderers/universal/Attributes.js @@ -1,6 +1,6 @@ import DataMap from './DataMap.js'; import { AttributeType } from './Constants.js'; -import { DynamicDrawUsage } from "three"; +import { DynamicDrawUsage } from 'three'; class Attributes extends DataMap { @@ -14,9 +14,9 @@ class Attributes extends DataMap { delete( attribute ) { - const data = this.delete( attribute ); + const attributeData = super.delete( attribute ); - if ( data !== undefined ) { + if ( attributeData !== undefined ) { this.backend.destroyAttribute( attribute ); @@ -26,7 +26,7 @@ class Attributes extends DataMap { update( attribute, type ) { - let data = this.get( attribute ); + const data = this.get( attribute ); if ( data.version === undefined ) { diff --git a/examples/jsm/renderers/universal/Backend.js b/examples/jsm/renderers/universal/Backend.js index 1244e6d9c2ea05..128ea9a870af01 100644 --- a/examples/jsm/renderers/universal/Backend.js +++ b/examples/jsm/renderers/universal/Backend.js @@ -8,7 +8,7 @@ class Backend { constructor( parameters = {} ) { this.parameters = Object.assign( {}, parameters ); - this.properties = new WeakMap(); + this.datas = new WeakMap(); this.renderer = null; this.domElement = null; @@ -136,12 +136,12 @@ class Backend { get( object ) { - let map = this.properties.get( object ); + let map = this.datas.get( object ); if ( map === undefined ) { map = {}; - this.properties.set( object, map ); + this.datas.set( object, map ); } @@ -151,7 +151,7 @@ class Backend { delete( object ) { - this.properties.delete( object ); + this.datas.delete( object ); } diff --git a/examples/jsm/renderers/universal/Bindings.js b/examples/jsm/renderers/universal/Bindings.js index 53f096cd1e6f40..c3197241540c9e 100644 --- a/examples/jsm/renderers/universal/Bindings.js +++ b/examples/jsm/renderers/universal/Bindings.js @@ -1,4 +1,5 @@ import DataMap from './DataMap.js'; +import { AttributeType } from './Constants.js'; class Bindings extends DataMap { @@ -23,7 +24,7 @@ class Bindings extends DataMap { const bindings = renderObject.getBindings(); - let data = this.get( renderObject ); + const data = this.get( renderObject ); if ( data.bindings !== bindings ) { @@ -33,9 +34,9 @@ class Bindings extends DataMap { this._init( bindings ); - const pipline = this.pipelines.getForRender( renderObject ); + const pipeline = this.pipelines.getForRender( renderObject ); - this.backend.createBindings( bindings, pipline ); + this.backend.createBindings( bindings, pipeline ); } @@ -45,21 +46,21 @@ class Bindings extends DataMap { getForCompute( computeNode ) { - let data = this.get( computeNode ); + const data = this.get( computeNode ); if ( data.bindings === undefined ) { const nodeBuilder = this.nodes.getForCompute( computeNode ); - data.bindings = nodeBuilder.getBindings(); + const bindings = nodeBuilder.getBindings(); - this._init( data ); + data.bindings = bindings; - const pipeline = this.pipelines.getForCompute( computeNode ); + this._init( bindings ); - this.backend.createBindings( data, pipeline ); + const pipeline = this.pipelines.getForCompute( computeNode ); - this.uniformBindings.set( computeNode, data ); + this.backend.createBindings( bindings, pipeline ); } @@ -85,7 +86,7 @@ class Bindings extends DataMap { if ( binding.isSampler || binding.isSampledTexture ) { - this.textures.initTexture( binding.texture ); + this.textures.updateTexture( binding.texture ); } else if ( binding.isStorageBuffer ) { @@ -119,9 +120,9 @@ class Bindings extends DataMap { if ( binding.isUniformBuffer ) { - const needsBufferWrite = binding.update(); + const needsUpdate = binding.update(); - if ( needsBufferWrite ) { + if ( needsUpdate ) { backend.updateBinding( binding ); @@ -129,35 +130,16 @@ class Bindings extends DataMap { } else if ( binding.isSampledTexture ) { - const needsTextureUpdate = binding.update(); + if ( binding.needsBindingsUpdate ) needsBindingsUpdate = true; - if ( needsTextureUpdate ) { + const needsUpdate = binding.update(); - this.textures.updateTexture( binding.texture ); + if ( needsUpdate ) { - needsBindingsUpdate = true; + this.textures.updateTexture( binding.texture ); } - } else if ( binding.isStorageBuffer ) { - - //const attribute = binding.attribute; - - } else if ( binding.isSampler ) { - /* - const texture = binding.getTexture(); - - textures.updateSampler( texture ); - - const samplerGPU = textures.getSampler( texture ); - - if ( binding.samplerGPU !== samplerGPU ) { - - binding.samplerGPU = samplerGPU; - needsBindGroupRefresh = true; - - } -*/ } updateMap.set( binding, frame ); @@ -166,10 +148,9 @@ class Bindings extends DataMap { if ( needsBindingsUpdate === true ) { - //this.pipelines.getPipeline( object ) - const pipline = this.pipelines.getForRender( object ); + const pipeline = this.pipelines.getForRender( object ); - this.backend.updateBindings( bindings, pipline ); + this.backend.updateBindings( bindings, pipeline ); } diff --git a/examples/jsm/renderers/universal/Buffer.js b/examples/jsm/renderers/universal/Buffer.js index 4d97c3a1980cce..a9a664396f0db9 100644 --- a/examples/jsm/renderers/universal/Buffer.js +++ b/examples/jsm/renderers/universal/Buffer.js @@ -11,19 +11,19 @@ class Buffer extends Binding { this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT; - this.buffer = buffer; + this._buffer = buffer; } - getByteLength() { + get byteLength() { - return getFloatLength( this.buffer.byteLength ); + return getFloatLength( this._buffer.byteLength ); } - getBuffer() { + get buffer() { - return this.buffer; + return this._buffer; } diff --git a/examples/jsm/renderers/universal/Geometries.js b/examples/jsm/renderers/universal/Geometries.js index e6e63ed830fa2b..bb9b5de31fb11f 100644 --- a/examples/jsm/renderers/universal/Geometries.js +++ b/examples/jsm/renderers/universal/Geometries.js @@ -76,7 +76,7 @@ class Geometries extends DataMap { this.info = info; this.wireframes = new WeakMap(); - this.geometryFrame = new WeakMap(); + this.attributeFrame = new WeakMap(); } @@ -92,16 +92,16 @@ class Geometries extends DataMap { if ( this.has( renderObject ) === false ) this.initGeometry( renderObject ); - this.updateFrameAttributes( renderObject ); + this.updateAttributes( renderObject ); } initGeometry( renderObject ) { const geometry = renderObject.geometry; - const geometryProperties = this.get( geometry ); + const geometryData = this.get( geometry ); - geometryProperties.initialized = true; + geometryData.initialized = true; this.info.memory.geometries ++; @@ -140,36 +140,35 @@ class Geometries extends DataMap { } - updateFrameAttributes( renderObject ) { - - const frame = this.info.render.frame; - const geometry = renderObject.geometry; + updateAttributes( renderObject ) { - if ( this.geometryFrame.get( geometry ) !== frame ) { + const attributes = renderObject.getAttributes(); - this.updateAttributes( renderObject ); + for ( const attribute of attributes ) { - this.geometryFrame.set( geometry, frame ); + this.updateAttribute( attribute, AttributeType.VERTEX ); } - } + const index = this.getIndex( renderObject ); - updateAttributes( renderObject ) { + if ( index !== null ) { - const attributes = renderObject.getAttributes(); + this.updateAttribute( index, AttributeType.INDEX ); - for ( const attribute of attributes ) { + } - this.attributes.update( attribute, AttributeType.VERTEX ); + } - } + updateAttribute( attribute, type ) { - const index = this.getIndex( renderObject ); + const frame = this.info.render.frame; - if ( index !== null ) { + if ( this.attributeFrame.get( attribute ) !== frame ) { + + this.attributes.update( attribute, type ); - this.attributes.update( index, AttributeType.INDEX ); + this.attributeFrame.set( attribute, frame ); } diff --git a/examples/jsm/renderers/universal/Pipelines.js b/examples/jsm/renderers/universal/Pipelines.js index c3b36c5820659f..5d36723c9da582 100644 --- a/examples/jsm/renderers/universal/Pipelines.js +++ b/examples/jsm/renderers/universal/Pipelines.js @@ -14,7 +14,7 @@ class Pipelines extends DataMap { this.bindings = null; // set by the bindings - this.pipelines = new Map(); + this.caches = new Map(); this.programs = { vertex: new Map(), fragment: new Map(), @@ -23,17 +23,11 @@ class Pipelines extends DataMap { } - has( object ) { - - return this.pipelines.get( object ) !== undefined; - - } - getForCompute( computeNode ) { const { backend } = this; - let data = this.get( computeNode ); + const data = this.get( computeNode ); if ( data.pipeline === undefined ) { @@ -60,7 +54,7 @@ class Pipelines extends DataMap { // determine compute pipeline - const pipeline = this._getComputePipeline( computeNode, stageCompute ); + const pipeline = this._getComputePipeline( stageCompute ); // keep track of all used times @@ -81,7 +75,7 @@ class Pipelines extends DataMap { const { backend } = this; - let data = this.get( renderObject ); + const data = this.get( renderObject ); if ( this._needsUpdate( renderObject ) ) { @@ -137,17 +131,19 @@ class Pipelines extends DataMap { } - remove( object ) { + delete( object ) { this._releasePipeline( object ); + super.delete( object ); + } dispose() { super.dispose(); - this.pipelines = new Map(); + this.caches.clear(); this.shaderModules = { vertex: new Map(), fragment: new Map(), @@ -156,19 +152,19 @@ class Pipelines extends DataMap { } - _getComputePipeline( computeNode, stageCompute ) { + _getComputePipeline( stageCompute ) { // check for existing pipeline const cacheKey = 'compute:' + stageCompute.id; - let pipeline = this.pipelines.get( cacheKey ); + let pipeline = this.caches.get( cacheKey ); if ( pipeline === undefined ) { pipeline = new ComputePipeline( cacheKey, stageCompute ); - this.pipelines.set( cacheKey, computeNode ); - this.pipelines.set( computeNode, pipeline ); + + this.caches.set( cacheKey, pipeline ); this.backend.createComputePipeline( pipeline ); @@ -184,13 +180,13 @@ class Pipelines extends DataMap { const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment ); - let pipeline = this.pipelines.get( cacheKey ); + let pipeline = this.caches.get( cacheKey ); if ( pipeline === undefined ) { pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment ); - this.pipelines.set( cacheKey, pipeline ); - this.pipelines.set( pipeline, cacheKey ); + + this.caches.set( cacheKey, pipeline ); renderObject.pipeline = pipeline; @@ -232,19 +228,13 @@ class Pipelines extends DataMap { _releasePipeline( object ) { - const data = this.get( object ); + const pipeline = this.get( object ).pipeline; - const pipeline = data.pipeline; - delete data.pipeline; - - this.bindings.delete( object ); + //this.bindings.delete( object ); if ( pipeline && -- pipeline.usedTimes === 0 ) { - const cacheKey = this.pipelines.get( pipeline ); - - this.pipelines.delete( cacheKey ); - this.pipelines.delete( pipeline ); + this.caches.delete( pipeline.cacheKey ); if ( pipeline.isComputePipeline ) { diff --git a/examples/jsm/renderers/universal/Properties.js b/examples/jsm/renderers/universal/Properties.js deleted file mode 100644 index ee3d48db3dfd55..00000000000000 --- a/examples/jsm/renderers/universal/Properties.js +++ /dev/null @@ -1,44 +0,0 @@ -class Properties { - - constructor() { - - this.properties = new WeakMap(); - - } - - get( object ) { - - let map = this.properties.get( object ); - - if ( map === undefined ) { - - map = {}; - this.properties.set( object, map ); - - } - - return map; - - } - - remove( object ) { - - this.properties.delete( object ); - - } - - has( object ) { - - return this.properties.has( object ); - - } - - dispose() { - - this.properties = new WeakMap(); - - } - -} - -export default Properties; diff --git a/examples/jsm/renderers/universal/RenderContext.js b/examples/jsm/renderers/universal/RenderContext.js index 86b2a6bd130b8f..5f5c8a91e83dcc 100644 --- a/examples/jsm/renderers/universal/RenderContext.js +++ b/examples/jsm/renderers/universal/RenderContext.js @@ -1,9 +1,13 @@ -import { Vector4 } from "three"; +import { Vector4 } from 'three'; + +let id = 0; class RenderContext { constructor() { + this.id = id ++; + this.color = true; this.clearColor = true; this.clearColorValue = { r: 0, g: 0, b: 0, a: 1 }; @@ -21,7 +25,9 @@ class RenderContext { this.scissor = false; this.scissorValue = new Vector4(); - this.scissorTest = false; + + this.texture = null; + this.depthTexture = null; } diff --git a/examples/jsm/renderers/universal/RenderObject.js b/examples/jsm/renderers/universal/RenderObject.js index ca24b40ead0cb5..1e655193b5b738 100644 --- a/examples/jsm/renderers/universal/RenderObject.js +++ b/examples/jsm/renderers/universal/RenderObject.js @@ -1,3 +1,5 @@ +let id = 0; + export default class RenderObject { constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode ) { @@ -5,6 +7,8 @@ export default class RenderObject { this._nodes = nodes; this._geometries = geometries; + this.id = id ++; + this.renderer = renderer; this.object = object; this.material = material; @@ -41,6 +45,12 @@ export default class RenderObject { } + getChainArray() { + + return [ this.object, this.material, this.scene, this.camera, this.lightsNode ]; + + } + getAttributes() { if ( this.attributes !== null ) return this.attributes; diff --git a/examples/jsm/renderers/universal/RenderObjects.js b/examples/jsm/renderers/universal/RenderObjects.js index 76a8532944f1b6..56c7313a62d2a2 100644 --- a/examples/jsm/renderers/universal/RenderObjects.js +++ b/examples/jsm/renderers/universal/RenderObjects.js @@ -20,9 +20,9 @@ class RenderObjects extends ChainMap { get( object, material, scene, camera, lightsNode ) { - const chainKey = [ object, material, scene, camera, lightsNode ]; + const chainArray = [ object, material, scene, camera, lightsNode ]; - let renderObject = super.get( chainKey ); + let renderObject = super.get( chainArray ); if ( renderObject === undefined ) { @@ -30,7 +30,7 @@ class RenderObjects extends ChainMap { this._initRenderObject( renderObject ); - this.set( chainKey, renderObject ); + this.set( chainArray, renderObject ); } else { @@ -67,6 +67,7 @@ class RenderObjects extends ChainMap { if ( data.initialized !== true ) { data.initialized = true; + data.cacheKey = renderObject.getCacheKey(); const onDispose = () => { @@ -75,7 +76,7 @@ class RenderObjects extends ChainMap { this.pipelines.delete( renderObject ); this.nodes.delete( renderObject ); - this.delete( chainKey ); + this.delete( renderObject.getChainArray() ); }; diff --git a/examples/jsm/renderers/universal/Renderer.js b/examples/jsm/renderers/universal/Renderer.js index fdfd29a144fb98..0e89c0611e88e9 100644 --- a/examples/jsm/renderers/universal/Renderer.js +++ b/examples/jsm/renderers/universal/Renderer.js @@ -10,9 +10,10 @@ import RenderContexts from './RenderContexts.js'; import Textures from './Textures.js'; import Background from './Background.js'; import Nodes from './nodes/Nodes.js'; -import { Frustum, Matrix4, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping } from 'three'; +import { Frustum, Matrix4, Vector2, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping } from 'three'; -const _drawingBufferSize = new Vector4(); +const _drawingBufferSize = new Vector2(); +const _screen = new Vector4(); const _frustum = new Frustum(); const _projScreenMatrix = new Matrix4(); const _vector3 = new Vector3(); @@ -84,8 +85,9 @@ class Renderer { // backwards compatibility - this.shadow = { - shadowMap: {} + this.shadowMap = { + enabled: false, + type: null }; } @@ -146,6 +148,12 @@ class Renderer { } + async compile( scene, camera ) { + + console.warn( 'THREE.Renderer: .compile() is not implemented yet.' ); + + } + async render( scene, camera ) { if ( this._initialized === false ) await this.init(); @@ -180,13 +188,30 @@ class Renderer { // + let viewport = this._viewport; + let scissor = this._scissor; + + if ( renderTarget !== null ) { + + viewport = renderTarget.viewport; + scissor = renderTarget.scissor; + + } + this.getDrawingBufferSize( _drawingBufferSize ); - renderContext.viewportValue.copy( this._viewport ).multiplyScalar( this._pixelRatio ).floor(); - renderContext.viewport = renderContext.viewportValue.equals( _drawingBufferSize ) === false; + _screen.set( 0, 0, _drawingBufferSize.width, _drawingBufferSize.height ); + + const minDepth = ( viewport.minDepth === undefined ) ? 0 : viewport.minDepth; + const maxDepth = ( viewport.maxDepth === undefined ) ? 1 : viewport.maxDepth; - renderContext.scissorValue.copy( this._scissor ).multiplyScalar( this._pixelRatio ).floor(); - renderContext.scissor = renderContext.scissorValue.equals( _drawingBufferSize ) === false; + renderContext.viewportValue.copy( viewport ).multiplyScalar( this._pixelRatio ).floor(); + renderContext.viewportValue.minDepth = minDepth; + renderContext.viewportValue.maxDepth = maxDepth; + renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false; + + renderContext.scissorValue.copy( scissor ).multiplyScalar( this._pixelRatio ).floor(); + renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false; // @@ -208,7 +233,21 @@ class Renderer { // - renderContext.renderTarget = renderTarget; + if ( renderTarget !== null ) { + + this._textures.updateRenderTarget( renderTarget ); + + const renderTargetData = this._textures.get( renderTarget ); + + renderContext.texture = renderTargetData.texture; + renderContext.depthTexture = renderTargetData.depthTexture; + + } else { + + renderContext.texture = null; + renderContext.depthTexture = null; + + } // @@ -258,7 +297,7 @@ class Renderer { async getArrayBuffer( attribute ) { - return await this._attributes.getArrayBuffer( attribute ); + return await this.backend.getArrayBuffer( attribute ); } @@ -384,19 +423,13 @@ class Renderer { } - copyFramebufferToRenderTarget( renderTarget ) { - - this.backend.copyFramebufferToRenderTarget( renderTarget ); - - } - getViewport( target ) { return target.copy( this._viewport ); } - setViewport( x, y, width, height /*minDepth = 0, maxDepth = 1*/ ) { + setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) { const viewport = this._viewport; @@ -410,6 +443,9 @@ class Renderer { } + viewport.minDepth = minDepth; + viewport.maxDepth = maxDepth; + } getClearColor( target ) { @@ -463,25 +499,27 @@ class Renderer { clear( color = true, depth = true, stencil = true ) { - this.backend.clear( color, depth, stencil ); + const renderContext = this._currentRenderContext || this._lastRenderContext; + + if ( renderContext ) this.backend.clear( renderContext, color, depth, stencil ); } clearColor() { - this.backend.clear( true, false, false ); + this.clear( true, false, false ); } clearDepth() { - this.backend.clear( false, true, false ); + this.clear( false, true, false ); } clearStencil() { - this.backend.clear( false, false, true ); + this.clear( false, false, true ); } @@ -513,7 +551,7 @@ class Renderer { if ( this._initialized === false ) await this.init(); const backend = this.backend; - const piplines = this._pipelines; + const pipelines = this._pipelines; const computeGroup = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ]; backend.beginCompute( computeGroup ); @@ -522,7 +560,7 @@ class Renderer { // onInit - if ( piplines.has( computeNode ) === false ) { + if ( pipelines.has( computeNode ) === false ) { computeNode.onInit( { renderer: this } ); @@ -531,7 +569,7 @@ class Renderer { this._nodes.updateForCompute( computeNode ); this._bindings.updateForCompute( computeNode ); - const computePipeline = piplines.getForCompute( computeNode ); + const computePipeline = pipelines.getForCompute( computeNode ); const computeBindings = this._bindings.getForCompute( computeNode ); backend.compute( computeGroup, computeNode, computeBindings, computePipeline ); @@ -554,6 +592,16 @@ class Renderer { } + copyFramebufferToTexture( framebufferTexture ) { + + const renderContext = this._currentRenderContext || this._lastRenderContext; + + this._textures.updateTexture( framebufferTexture ); + + this.backend.copyFramebufferToTexture( framebufferTexture, renderContext ); + + } + _projectObject( object, camera, groupOrder, renderList ) { if ( object.visible === false ) return; @@ -683,7 +731,12 @@ class Renderer { const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth; const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth; - this._currentRenderContext.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth ); + const viewportValue = this._currentRenderContext.viewportValue; + viewportValue.copy( vp ).multiplyScalar( this._pixelRatio ).floor(); + viewportValue.minDepth = minDepth; + viewportValue.maxDepth = maxDepth; + + this.backend.updateViewport( this._currentRenderContext ); this._renderObject( object, scene, camera2, geometry, material, group, lightsNode ); diff --git a/examples/jsm/renderers/universal/SampledTexture.js b/examples/jsm/renderers/universal/SampledTexture.js index be4b4c25118c24..f7956fe0e290ef 100644 --- a/examples/jsm/renderers/universal/SampledTexture.js +++ b/examples/jsm/renderers/universal/SampledTexture.js @@ -17,6 +17,14 @@ class SampledTexture extends Binding { } + get needsBindingsUpdate() { + + const { texture, version } = this; + + return texture.isVideoTexture ? true : version === 0 && texture.version > 0; + + } + update() { if ( this.version !== this.texture.version ) { @@ -33,7 +41,7 @@ class SampledTexture extends Binding { } -class WebGPUSampledArrayTexture extends SampledTexture { +class SampledArrayTexture extends SampledTexture { constructor( name, texture ) { @@ -45,7 +53,7 @@ class WebGPUSampledArrayTexture extends SampledTexture { } -class WebGPUSampled3DTexture extends SampledTexture { +class Sampled3DTexture extends SampledTexture { constructor( name, texture ) { @@ -57,7 +65,7 @@ class WebGPUSampled3DTexture extends SampledTexture { } -class WebGPUSampledCubeTexture extends SampledTexture { +class SampledCubeTexture extends SampledTexture { constructor( name, texture ) { @@ -69,4 +77,4 @@ class WebGPUSampledCubeTexture extends SampledTexture { } -export { SampledTexture as WebGPUSampledTexture, WebGPUSampledArrayTexture, WebGPUSampled3DTexture, WebGPUSampledCubeTexture }; +export { SampledTexture, SampledArrayTexture, Sampled3DTexture, SampledCubeTexture }; diff --git a/examples/jsm/renderers/universal/TextureRenderer.js b/examples/jsm/renderers/universal/TextureRenderer.js index 61c42ab83da43d..e28940b67eee0c 100644 --- a/examples/jsm/renderers/universal/TextureRenderer.js +++ b/examples/jsm/renderers/universal/TextureRenderer.js @@ -10,7 +10,7 @@ class TextureRenderer { } - getTexture() { + get texture() { return this.renderTarget.texture; diff --git a/examples/jsm/renderers/universal/Textures.js b/examples/jsm/renderers/universal/Textures.js index 07752ff9901248..16c4bd39765357 100644 --- a/examples/jsm/renderers/universal/Textures.js +++ b/examples/jsm/renderers/universal/Textures.js @@ -1,4 +1,5 @@ import DataMap from './DataMap.js'; +import { DepthTexture, DepthStencilFormat, UnsignedInt248Type } from 'three'; class Textures extends DataMap { @@ -11,74 +12,171 @@ class Textures extends DataMap { } - initTexture( texture ) { + updateRenderTarget( renderTarget ) { - const data = this.get( texture ); - if ( data.initialized ) return; + const renderTargetData = this.get( renderTarget ); - data.initialized = true; + const texture = renderTarget.texture; - this.backend.createSampler( texture ); - this.backend.createDefaultTexture( texture ); + let depthTexture = renderTarget.depthTexture || renderTargetData.depthTexture; - this.info.memory.textures ++; + if ( depthTexture === undefined ) { - // dispose + depthTexture = new DepthTexture(); + depthTexture.format = DepthStencilFormat; + depthTexture.type = UnsignedInt248Type; + depthTexture.image.width = texture.image.width; + depthTexture.image.height = texture.image.height; - const onDispose = () => { + } + + if ( renderTargetData.width !== texture.image.width || texture.image.height !== renderTargetData.height ) { + + texture.needsUpdate = true; + depthTexture.needsUpdate = true; + + depthTexture.image.width = texture.image.width; + depthTexture.image.height = texture.image.height; + + } + + renderTargetData.width = texture.image.width; + renderTargetData.height = texture.image.height; + renderTargetData.texture = texture; + renderTargetData.depthTexture = depthTexture; - texture.removeEventListener( 'dispose', onDispose ); + this.updateTexture( texture ); + this.updateTexture( depthTexture ); - this.backend.destroyTexture( texture ); + // dispose handler - this.delete( texture ); + if ( renderTargetData.initialized !== true ) { - this.info.memory.textures --; + renderTargetData.initialized = true; - }; + // dispose - texture.addEventListener( 'dispose', onDispose ); + const onDispose = () => { + + renderTarget.removeEventListener( 'dispose', onDispose ); + + this._destroyTexture( texture ); + this._destroyTexture( depthTexture ); + + }; + + renderTarget.addEventListener( 'dispose', onDispose ); + + } } updateTexture( texture ) { - let needsUpdate = false; + const textureData = this.get( texture ); + if ( textureData.initialized === true && textureData.version === texture.version ) return; - const data = this.get( texture ); + const isRenderTexture = texture.isRenderTargetTexture || texture.isDepthTexture || texture.isFramebufferTexture; + const backend = this.backend; - if ( texture.version > 0 && data.version !== texture.version ) { + if ( isRenderTexture && textureData.initialized === true ) { - const image = texture.image; + // it's a update - if ( image === undefined ) { + backend.destroySampler( texture ); + backend.destroyTexture( texture ); - console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' ); + } - } else if ( image.complete === false ) { + // - console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' ); + if ( isRenderTexture ) { - } else { + backend.createSampler( texture ); + backend.createTexture( texture ); + + } else { + + const needsCreate = textureData.initialized !== true; + + if ( needsCreate ) backend.createSampler( texture ); + + if ( texture.version > 0 ) { + + const image = texture.image; + + if ( image === undefined ) { + + console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' ); + + } else if ( image.complete === false ) { + + console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' ); + + } else { - // + if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) { - this.initTexture( texture ); + backend.createTexture( texture ); - // + textureData.isDefaultTexture = false; - this.backend.createTexture( texture ); - this.backend.updateTexture( texture ); + } - data.version = texture.version; + backend.updateTexture( texture ); - needsUpdate = true; + } + + } else { + + // async update + + backend.createDefaultTexture( texture ); + + textureData.isDefaultTexture = true; } } - return needsUpdate; + // dispose handler + + if ( textureData.initialized !== true ) { + + textureData.initialized = true; + + // + + this.info.memory.textures ++; + + // dispose + + const onDispose = () => { + + texture.removeEventListener( 'dispose', onDispose ); + + this._destroyTexture( texture ); + + this.info.memory.textures --; + + }; + + texture.addEventListener( 'dispose', onDispose ); + + } + + // + + textureData.version = texture.version; + + } + + _destroyTexture( texture ) { + + this.backend.destroySampler( texture ); + this.backend.destroyTexture( texture ); + + this.delete( texture ); } diff --git a/examples/jsm/renderers/universal/UniformsGroup.js b/examples/jsm/renderers/universal/UniformsGroup.js index a312159411e990..fad4c4acde34f7 100644 --- a/examples/jsm/renderers/universal/UniformsGroup.js +++ b/examples/jsm/renderers/universal/UniformsGroup.js @@ -37,17 +37,17 @@ class UniformsGroup extends UniformBuffer { } - getBuffer() { + get buffer() { - let buffer = this.buffer; + let buffer = this._buffer; if ( buffer === null ) { - const byteLength = this.getByteLength(); + const byteLength = this.byteLength; buffer = new Float32Array( new ArrayBuffer( byteLength ) ); - this.buffer = buffer; + this._buffer = buffer; } @@ -55,7 +55,7 @@ class UniformsGroup extends UniformBuffer { } - getByteLength() { + get byteLength() { let offset = 0; // global buffer offset in bytes @@ -130,7 +130,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const v = uniform.getValue(); const offset = uniform.offset; @@ -149,7 +149,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const v = uniform.getValue(); const offset = uniform.offset; @@ -170,7 +170,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const v = uniform.getValue(); const offset = uniform.offset; @@ -192,7 +192,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const v = uniform.getValue(); const offset = uniform.offset; @@ -215,7 +215,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const c = uniform.getValue(); const offset = uniform.offset; @@ -237,7 +237,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const e = uniform.getValue().elements; const offset = uniform.offset; @@ -267,7 +267,7 @@ class UniformsGroup extends UniformBuffer { let updated = false; - const a = this.getBuffer(); + const a = this.buffer; const e = uniform.getValue().elements; const offset = uniform.offset; diff --git a/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js b/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js index 3f0ffcf8288ee1..e8debe80a311dd 100644 --- a/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js +++ b/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js @@ -1,6 +1,6 @@ -import { WebGPUSampledTexture, WebGPUSampledCubeTexture } from '../SampledTexture.js'; +import { SampledTexture, SampledCubeTexture } from '../SampledTexture.js'; -class WebGPUNodeSampledTexture extends WebGPUSampledTexture { +class NodeSampledTexture extends SampledTexture { constructor( name, textureNode ) { @@ -18,7 +18,7 @@ class WebGPUNodeSampledTexture extends WebGPUSampledTexture { } -class WebGPUNodeSampledCubeTexture extends WebGPUSampledCubeTexture { +class NodeSampledCubeTexture extends SampledCubeTexture { constructor( name, textureNode ) { @@ -36,4 +36,4 @@ class WebGPUNodeSampledCubeTexture extends WebGPUSampledCubeTexture { } -export { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture }; +export { NodeSampledTexture, NodeSampledCubeTexture }; diff --git a/examples/jsm/renderers/universal/nodes/NodeSampler.js b/examples/jsm/renderers/universal/nodes/NodeSampler.js index 0b727582f55fcd..373e8c129724c1 100644 --- a/examples/jsm/renderers/universal/nodes/NodeSampler.js +++ b/examples/jsm/renderers/universal/nodes/NodeSampler.js @@ -1,6 +1,6 @@ import Sampler from '../Sampler.js'; -class WebGPUNodeSampler extends Sampler { +class NodeSampler extends Sampler { constructor( name, textureNode ) { @@ -18,4 +18,4 @@ class WebGPUNodeSampler extends Sampler { } -export default WebGPUNodeSampler; +export default NodeSampler; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 84fd988b45bf8c..3596669ecb3872 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -1,11 +1,25 @@ import Renderer from '../universal/Renderer.js'; import WebGPUBackend from './backends/webgpu/WebGPUBackend.js'; +/* +const debugHandler = { + get: function ( target, name ) { + + // Add |update + if ( /^(create|destroy)/.test( name ) ) console.log( 'WebGPUBackend.' + name ); + + return target[ name ]; + + } + +}; +*/ class WebGPURenderer extends Renderer { constructor( parameters = {} ) { const backend = new WebGPUBackend( parameters ); + //const backend = new Proxy( new WebGPUBackend( parameters ), debugHandler ); super( backend ); diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js index cfc3e66474c2c8..44d3fdf3ff0af7 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js @@ -1,11 +1,9 @@ -import { - GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat -} from './utils/WebGPUConstants.js'; +import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat } from './utils/WebGPUConstants.js'; import WebGPUNodeBuilder from './builder/WGSLNodeBuilder.js'; import Backend from '../../../universal/Backend.js'; -import { Matrix4, Frustum } from 'three'; +import { Matrix4, Frustum, DepthFormat } from 'three'; import WebGPUUtils from './utils/WebGPUUtils.js'; import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js'; @@ -15,21 +13,21 @@ import WebGPUTextureUtils from './utils/WebGPUTextureUtils.js'; // statics -let staticAdapter = null; +let _staticAdapter = null; if ( navigator.gpu !== undefined ) { - staticAdapter = await navigator.gpu.requestAdapter(); + _staticAdapter = await navigator.gpu.requestAdapter(); } // hacks -let initializedHack = false; +let _initializedHack = false; -function initWebGPUHack() { +function _initWebGPUHack() { - if ( initializedHack ) return; + if ( _initializedHack ) return; console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' ); @@ -93,10 +91,13 @@ function initWebGPUHack() { }; - initializedHack = true; + _initializedHack = true; } +_initWebGPUHack(); + +// class WebGPUBackend extends Backend { @@ -183,12 +184,16 @@ class WebGPUBackend extends Backend { this.device = device; this.context = context; - initWebGPUHack(); - this.updateSize(); } + async getArrayBuffer( attribute ) { + + return await this.attributeUtils.getArrayBuffer( attribute ); + + } + beginRender( renderContext ) { const renderContextData = this.get( renderContext ); @@ -207,19 +212,41 @@ class WebGPUBackend extends Backend { const colorAttachment = descriptor.colorAttachments[ 0 ]; const depthStencilAttachment = descriptor.depthStencilAttachment; - if ( this.parameters.antialias === true ) { + const antialias = this.parameters.antialias; - colorAttachment.view = this.colorBuffer.createView(); - colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + if ( renderContext.texture !== null ) { + + const textureData = this.get( renderContext.texture ); + const depthTextureData = this.get( renderContext.depthTexture ); + + // @TODO: Support RenderTarget with antialiasing. + + colorAttachment.view = textureData.texture.createView(); + depthStencilAttachment.view = depthTextureData.texture.createView(); + + if ( renderContext.stencil && renderContext.depthTexture.format === DepthFormat ) { + + renderContext.stencil = false; + + } } else { - colorAttachment.view = this.context.getCurrentTexture().createView(); - colorAttachment.resolveTarget = undefined; + if ( antialias === true ) { - } + colorAttachment.view = this.colorBuffer.createView(); + colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + + } else { - depthStencilAttachment.view = this.depthBuffer.createView(); + colorAttachment.view = this.context.getCurrentTexture().createView(); + colorAttachment.resolveTarget = undefined; + + } + + depthStencilAttachment.view = this.depthBuffer.createView(); + + } if ( renderContext.clearColor ) { @@ -234,6 +261,8 @@ class WebGPUBackend extends Backend { } + // + if ( renderContext.depth ) { if ( renderContext.clearDepth ) { @@ -268,20 +297,106 @@ class WebGPUBackend extends Backend { } - const encoder = device.createCommandEncoder( {} ); + // + + const encoder = device.createCommandEncoder( { label: 'renderContext_' + renderContext.id } ); const currentPass = encoder.beginRenderPass( descriptor ); + // + renderContextData.descriptor = descriptor; renderContextData.encoder = encoder; renderContextData.currentPass = currentPass; + // + + if ( renderContext.viewport ) { + + this.updateViewport( renderContext ); + + } + + if ( renderContext.scissor ) { + + const { x, y, width, height } = renderContext.scissorValue; + + currentPass.setScissorRect( x, y, width, height ); + + } + } finishRender( renderContext ) { const renderContextData = this.get( renderContext ); + renderContextData.currentPass.end(); + + this.device.queue.submit( [ renderContextData.encoder.finish() ] ); + + } + + updateViewport( renderContext ) { + + const { currentPass } = this.get( renderContext ); + const { x, y, width, height, minDepth, maxDepth } = renderContext.viewportValue; + + currentPass.setViewport( x, y, width, height, minDepth, maxDepth ); + + } + + clear( renderContext, color, depth, stencil ) { + const device = this.device; + const renderContextData = this.get( renderContext ); + + const { descriptor } = renderContextData; + + depth = depth && renderContext.depth; + stencil = stencil && renderContext.stencil; + + const colorAttachment = descriptor.colorAttachments[ 0 ]; + + const antialias = this.parameters.antialias; + + // @TODO: Include render target in clear operation. + if ( antialias === true ) { + + colorAttachment.view = this.colorBuffer.createView(); + colorAttachment.resolveTarget = this.context.getCurrentTexture().createView(); + + } else { + + colorAttachment.view = this.context.getCurrentTexture().createView(); + colorAttachment.resolveTarget = undefined; + + } + + descriptor.depthStencilAttachment.view = this.depthBuffer.createView(); + + if ( color ) { + + colorAttachment.loadOp = GPULoadOp.Clear; + colorAttachment.clearValue = renderContext.clearColorValue; + + } + + if ( depth ) { + + descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; + descriptor.depthStencilAttachment.depthClearValue = renderContext.clearDepthValue; + + } + + if ( stencil ) { + + descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; + descriptor.depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue; + + } + + renderContextData.encoder = device.createCommandEncoder( {} ); + renderContextData.currentPass = renderContextData.encoder.beginRenderPass( descriptor ); renderContextData.currentPass.end(); @@ -412,9 +527,9 @@ class WebGPUBackend extends Backend { const utils = this.utils; const sampleCount = utils.getSampleCount(); - const colorSpace = utils.getCurrentColorSpace(); - const colorFormat = utils.getCurrentColorFormat(); - const depthStencilFormat = utils.getCurrentDepthStencilFormat(); + const colorSpace = utils.getCurrentColorSpace( renderObject.context ); + const colorFormat = utils.getCurrentColorFormat( renderObject.context ); + const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context ); const primitiveTopology = utils.getPrimitiveTopology( object, material ); let needsUpdate = false; @@ -442,10 +557,11 @@ class WebGPUBackend extends Backend { const { object, material } = renderObject; const utils = this.utils; + const renderContext = renderObject.context; return [ utils.getSampleCount(), - utils.getCurrentColorSpace(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(), + utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ), utils.getPrimitiveTopology( object, material ) ].join(); @@ -459,6 +575,12 @@ class WebGPUBackend extends Backend { } + destroySampler( texture ) { + + this.textureUtils.destroySampler( texture ); + + } + createDefaultTexture( texture ) { this.textureUtils.createDefaultTexture( texture ); @@ -468,7 +590,6 @@ class WebGPUBackend extends Backend { createTexture( texture ) { this.textureUtils.createTexture( texture ); - this.textureUtils.updateTexture( texture ); } @@ -478,9 +599,9 @@ class WebGPUBackend extends Backend { } - destroyTexture( /*texture*/ ) { + destroyTexture( texture ) { - console.warn( '.destroyTexture() not implemented yet.' ); + this.textureUtils.destroyTexture( texture ); } @@ -571,9 +692,9 @@ class WebGPUBackend extends Backend { } - destroyAttribute( /*attribute*/ ) { + destroyAttribute( attribute ) { - console.warn( '.destroyAttribute() not implemented yet.' ); + this.attributeUtils.destroyAttribute( attribute ); } @@ -591,7 +712,7 @@ class WebGPUBackend extends Backend { hasFeature( name ) { - const adapter = this.adapter || staticAdapter; + const adapter = this.adapter || _staticAdapter; // @@ -609,6 +730,38 @@ class WebGPUBackend extends Backend { } + copyFramebufferToTexture( framebufferTexture, renderContext ) { + + const renderContextData = this.get( renderContext ); + + const { encoder, descriptor } = renderContextData; + + const sourceGPU = this.context.getCurrentTexture(); + const destinationGPU = this.get( framebufferTexture ).texture; + + renderContextData.currentPass.end(); + + encoder.copyTextureToTexture( + { + texture: sourceGPU + }, + { + texture: destinationGPU + }, + [ + framebufferTexture.image.width, + framebufferTexture.image.height + ] + ); + + descriptor.colorAttachments[ 0 ].loadOp = GPULoadOp.Load; + if ( renderContext.depth ) descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load; + if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; + + renderContextData.currentPass = encoder.beginRenderPass( descriptor ); + + } + // utils _configureContext() { @@ -627,6 +780,7 @@ class WebGPUBackend extends Backend { if ( this.colorBuffer ) this.colorBuffer.destroy(); const { width, height } = this.getDrawingBufferSize(); + //const format = navigator.gpu.getPreferredCanvasFormat(); // @TODO: Move to WebGPUUtils this.colorBuffer = this.device.createTexture( { label: 'colorBuffer', diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js index 73bfac3f38fe69..ece121e6d31e03 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js @@ -3,8 +3,8 @@ import { FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform } from '../../../../universal/nodes/NodeUniform.js'; -import WebGPUNodeSampler from '../../../../universal/nodes/NodeSampler.js'; -import { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture } from '../../../../universal/nodes/NodeSampledTexture.js'; +import NodeSampler from '../../../../universal/nodes/NodeSampler.js'; +import { NodeSampledTexture, NodeSampledCubeTexture } from '../../../../universal/nodes/NodeSampledTexture.js'; import UniformBuffer from '../../../../universal/UniformBuffer.js'; import StorageBuffer from '../../../../universal/StorageBuffer.js'; @@ -16,11 +16,13 @@ import { NodeBuilder, CodeNode, NodeMaterial } from '../../../../../nodes/Nodes. import WGSLNodeParser from './WGSLNodeParser.js'; +/* const gpuShaderStageLib = { 'vertex': GPUShaderStage.VERTEX, 'fragment': GPUShaderStage.FRAGMENT, 'compute': GPUShaderStage.COMPUTE }; +*/ const supports = { instance: true @@ -262,17 +264,17 @@ class WebGPUNodeBuilder extends NodeBuilder { if ( type === 'texture' || type === 'cubeTexture' ) { - const sampler = new WebGPUNodeSampler( `${uniformNode.name}_sampler`, uniformNode.node ); + const sampler = new NodeSampler( `${uniformNode.name}_sampler`, uniformNode.node ); let texture = null; if ( type === 'texture' ) { - texture = new WebGPUNodeSampledTexture( uniformNode.name, uniformNode.node ); + texture = new NodeSampledTexture( uniformNode.name, uniformNode.node ); } else if ( type === 'cubeTexture' ) { - texture = new WebGPUNodeSampledCubeTexture( uniformNode.name, uniformNode.node ); + texture = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node ); } diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js index 95f1e0c5c6fd78..749211073d0aa9 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js @@ -22,25 +22,25 @@ const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ class WebGPUAttributeUtils { - constructor( backend ) { + constructor( backend ) { - this.backend = backend; + this.backend = backend; - } + } - createAttribute( attribute, usage ) { + createAttribute( attribute, usage ) { - const bufferAttribute = this.getBufferAttribute( attribute ); + const bufferAttribute = this._getBufferAttribute( attribute ); - const backend = this.backend; - const device = backend.device; + const backend = this.backend; + const device = backend.device; const array = bufferAttribute.array; const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441 const buffer = device.createBuffer( { label: bufferAttribute.name, - size : size, + size: size, usage: usage, mappedAtCreation: true } ); @@ -53,22 +53,14 @@ class WebGPUAttributeUtils { } - getBufferAttribute( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - return attribute; + updateAttribute( attribute ) { - } + const bufferAttribute = this._getBufferAttribute( attribute ); - updateAttribute( attribute ) { + const backend = this.backend; + const device = backend.device; - const bufferAttribute = this.getBufferAttribute( attribute ); - - const backend = this.backend; - const device = backend.device; - - const buffer = backend.get( bufferAttribute ).buffer; + const buffer = backend.get( attribute ).buffer; const array = bufferAttribute.array; const updateRange = bufferAttribute.updateRange; @@ -98,9 +90,9 @@ class WebGPUAttributeUtils { } - } + } - createShaderAttributes( renderObject ) { + createShaderAttributes( renderObject ) { const attributes = renderObject.getAttributes(); const shaderAttributes = []; @@ -110,7 +102,7 @@ class WebGPUAttributeUtils { const geometryAttribute = attributes[ slot ]; const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT; - const format = this.getVertexFormat( geometryAttribute ); + const format = this._getVertexFormat( geometryAttribute ); let arrayStride = geometryAttribute.itemSize * bytesPerElement; let offset = 0; @@ -138,7 +130,70 @@ class WebGPUAttributeUtils { } - getVertexFormat( geometryAttribute ) { + destroyAttribute( attribute ) { + + const backend = this.backend; + const data = backend.get( attribute ); + + data.buffer.destroy(); + + backend.delete( attribute ); + + } + + async getArrayBuffer( attribute ) { + + const backend = this.backend; + const device = backend.device; + + const data = backend.get( attribute ); + + //const bufferAttribute = this._getBufferAttribute( attribute ); + + const bufferGPU = data.buffer; + const size = bufferGPU.size; + + let readBufferGPU = data.readBuffer; + let needsUnmap = true; + + if ( readBufferGPU === undefined ) { + + readBufferGPU = device.createBuffer( { + label: attribute.name, + size, + usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ + } ); + + needsUnmap = false; + + data.readBuffer = readBufferGPU; + + } + + const cmdEncoder = device.createCommandEncoder( {} ); + + cmdEncoder.copyBufferToBuffer( + bufferGPU, + 0, + readBufferGPU, + 0, + size + ); + + if ( needsUnmap ) readBufferGPU.unmap(); + + const gpuCommands = cmdEncoder.finish(); + device.queue.submit( [ gpuCommands ] ); + + await readBufferGPU.mapAsync( GPUMapMode.READ ); + + const arrayBuffer = readBufferGPU.getMappedRange(); + + return arrayBuffer; + + } + + _getVertexFormat( geometryAttribute ) { const { itemSize, normalized } = geometryAttribute; const ArrayType = geometryAttribute.array.constructor; @@ -183,6 +238,14 @@ class WebGPUAttributeUtils { } + _getBufferAttribute( attribute ) { + + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + + return attribute; + + } + } export default WebGPUAttributeUtils; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js index 9a1a358850558e..368c9489b146d1 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js @@ -1,49 +1,49 @@ -import { - GPUTextureAspect, GPUTextureViewDimension +import { + GPUTextureAspect, GPUTextureViewDimension } from './WebGPUConstants.js'; class WebGPUBindingUtils { - constructor( backend ) { + constructor( backend ) { - this.backend = backend; + this.backend = backend; - } + } - createBindings( bindings, pipeline ) { + createBindings( bindings, pipeline ) { - const backend = this.backend; - const bindingsData = backend.get( bindings ); + const backend = this.backend; + const bindingsData = backend.get( bindings ); - // setup (static) binding layout and (dynamic) binding group + // setup (static) binding layout and (dynamic) binding group - const pipelineGPU = backend.get( pipeline ).pipeline; + const pipelineGPU = backend.get( pipeline ).pipeline; - const bindLayoutGPU = pipelineGPU.getBindGroupLayout( 0 ); - const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU ); + const bindLayoutGPU = pipelineGPU.getBindGroupLayout( 0 ); + const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU ); - bindingsData.layout = bindLayoutGPU; + bindingsData.layout = bindLayoutGPU; bindingsData.group = bindGroupGPU; bindingsData.bindings = bindings; - } + } - updateBinding( binding ) { + updateBinding( binding ) { - const backend = this.backend; - const device = backend.device; + const backend = this.backend; + const device = backend.device; - const buffer = binding.getBuffer(); - const bufferGPU = backend.get( binding ).buffer; + const buffer = binding.buffer; + const bufferGPU = backend.get( binding ).buffer; - device.queue.writeBuffer( bufferGPU, 0, buffer, 0 ); + device.queue.writeBuffer( bufferGPU, 0, buffer, 0 ); } - createBindGroup( bindings, layoutGPU ) { + createBindGroup( bindings, layoutGPU ) { - const backend = this.backend; - const device = backend.device; + const backend = this.backend; + const device = backend.device; let bindingPoint = 0; const entriesGPU = []; @@ -56,7 +56,7 @@ class WebGPUBindingUtils { if ( bindingData.buffer === undefined ) { - const byteLength = binding.getByteLength(); + const byteLength = binding.byteLength; const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST; @@ -66,7 +66,7 @@ class WebGPUBindingUtils { usage: usage } ); - bindingData.buffer = bufferGPU; + bindingData.buffer = bufferGPU; } @@ -79,10 +79,10 @@ class WebGPUBindingUtils { if ( bindingData.buffer === undefined ) { const attribute = binding.attribute; - //const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST; + //const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST; //backend.attributeUtils.createAttribute( attribute, usage ); // @TODO: Move it to universal renderer - + bindingData.buffer = backend.get( attribute ).buffer; } @@ -99,20 +99,31 @@ class WebGPUBindingUtils { const textureData = backend.get( binding.texture ); - let dimensionGPU; + let dimensionViewGPU; if ( binding.isSampledCubeTexture ) { - dimensionGPU = GPUTextureViewDimension.Cube; + dimensionViewGPU = GPUTextureViewDimension.Cube; } else { - dimensionGPU = GPUTextureViewDimension.TwoD; + dimensionViewGPU = GPUTextureViewDimension.TwoD; } - const aspectGPU = GPUTextureAspect.All; - const resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionGPU } ); + let resourceGPU; + + if ( textureData.externalTexture !== undefined ) { + + resourceGPU = device.importExternalTexture( { source: textureData.externalTexture } ); + + } else { + + const aspectGPU = GPUTextureAspect.All; + + resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU } ); + + } entriesGPU.push( { binding: bindingPoint, resource: resourceGPU } ); diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js index e9296480ea948f..4e71bbddb71051 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js @@ -214,6 +214,12 @@ export const GPUAddressMode = { MirrorRepeat: 'mirror-repeat' }; +export const GPUSamplerBindingType = { + Filtering: 'filtering', + NonFiltering: 'non-filtering', + Comparison: 'comparison' +}; + export const GPUFilterMode = { Linear: 'linear', Nearest: 'nearest' diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js index 860e8075d315a6..2aec1dffbee026 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js @@ -1,31 +1,38 @@ -import { - GPUInputStepMode, GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat +import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../../../universal/Constants.js'; + +import { + GPUInputStepMode, GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation } from './WebGPUConstants.js'; import { FrontSide, BackSide, DoubleSide, - NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, - NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, + NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, + NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, + ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor, + OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor, + AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, + KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp, + NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc } from 'three'; class WebGPUPipelineUtils { - constructor( backend ) { + constructor( backend ) { - this.backend = backend; + this.backend = backend; - } + } - createRenderPipeline( renderObject ) { + createRenderPipeline( renderObject ) { - const { object, material, geometry, pipeline } = renderObject; - const { vertexProgram, fragmentProgram } = pipeline; + const { object, material, geometry, pipeline } = renderObject; + const { vertexProgram, fragmentProgram } = pipeline; - const backend = this.backend; - const device = backend.device; + const backend = this.backend; + const device = backend.device; const utils = backend.utils; - const pipelineGPU = backend.get( pipeline ); + const pipelineData = backend.get( pipeline ); // determine shader attributes @@ -55,8 +62,8 @@ class WebGPUPipelineUtils { if ( material.transparent === true && material.blending !== NoBlending ) { - alphaBlend = this.getAlphaBlend( material ); - colorBlend = this.getColorBlend( material ); + alphaBlend = this._getAlphaBlend( material ); + colorBlend = this._getColorBlend( material ); } @@ -67,27 +74,27 @@ class WebGPUPipelineUtils { if ( material.stencilWrite === true ) { stencilFront = { - compare: this.getStencilCompare( material ), - failOp: this.getStencilOperation( material.stencilFail ), - depthFailOp: this.getStencilOperation( material.stencilZFail ), - passOp: this.getStencilOperation( material.stencilZPass ) + compare: this._getStencilCompare( material ), + failOp: this._getStencilOperation( material.stencilFail ), + depthFailOp: this._getStencilOperation( material.stencilZFail ), + passOp: this._getStencilOperation( material.stencilZPass ) }; } // - const vertexModule = backend.get( vertexProgram ).module; - const fragmentModule = backend.get( fragmentProgram ).module; + const vertexModule = backend.get( vertexProgram ).module; + const fragmentModule = backend.get( fragmentProgram ).module; - const primitiveState = this.getPrimitiveState( object, geometry, material ); - const colorWriteMask = this.getColorWriteMask( material ); - const depthCompare = this.getDepthCompare( material ); - const colorFormat = utils.getCurrentColorFormat(); - const depthStencilFormat = utils.getCurrentDepthStencilFormat(); + const primitiveState = this._getPrimitiveState( object, geometry, material ); + const colorWriteMask = this._getColorWriteMask( material ); + const depthCompare = this._getDepthCompare( material ); + const colorFormat = utils.getCurrentColorFormat( renderObject.context ); + const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context ); const sampleCount = utils.getSampleCount(); - pipelineGPU.pipeline = device.createRenderPipeline( { + pipelineData.pipeline = device.createRenderPipeline( { vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ), fragment: Object.assign( {}, fragmentModule, { targets: [ { format: colorFormat, @@ -113,25 +120,25 @@ class WebGPUPipelineUtils { layout: 'auto' } ); - } + } - createComputePipeline( pipeline ) { + createComputePipeline( pipeline ) { - const backend = this.backend; - const device = backend.device; + const backend = this.backend; + const device = backend.device; - const computeProgram = backend.get( pipeline.computeProgram ).module; + const computeProgram = backend.get( pipeline.computeProgram ).module; - const pipelineGPU = backend.get( pipeline ); - - pipelineGPU.pipeline = device.createComputePipeline( { - compute: computeProgram, - layout: 'auto' - } ); + const pipelineGPU = backend.get( pipeline ); - } + pipelineGPU.pipeline = device.createComputePipeline( { + compute: computeProgram, + layout: 'auto' + } ); + + } - getAlphaBlend( material ) { + _getAlphaBlend( material ) { const blending = material.blending; const premultipliedAlpha = material.premultipliedAlpha; @@ -201,9 +208,9 @@ class WebGPUPipelineUtils { if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) { alphaBlend = { - srcFactor: this.getBlendFactor( blendSrcAlpha ), - dstFactor: this.getBlendFactor( blendDstAlpha ), - operation: this.getBlendOperation( blendEquationAlpha ) + srcFactor: this._getBlendFactor( blendSrcAlpha ), + dstFactor: this._getBlendFactor( blendDstAlpha ), + operation: this._getBlendOperation( blendEquationAlpha ) }; } @@ -219,7 +226,7 @@ class WebGPUPipelineUtils { } - getBlendFactor( blend ) { + _getBlendFactor( blend ) { let blendFactor; @@ -277,7 +284,6 @@ class WebGPUPipelineUtils { blendFactor = GPUBlendFactor.OneMinusBlendColor; break; - default: console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend ); @@ -287,7 +293,7 @@ class WebGPUPipelineUtils { } - getColorBlend( material ) { + _getColorBlend( material ) { const blending = material.blending; const premultipliedAlpha = material.premultipliedAlpha; @@ -325,9 +331,9 @@ class WebGPUPipelineUtils { break; case CustomBlending: - colorBlend.srcFactor = this.getBlendFactor( material.blendSrc ); - colorBlend.dstFactor = this.getBlendFactor( material.blendDst ); - colorBlend.operation = this.getBlendOperation( material.blendEquation ); + colorBlend.srcFactor = this._getBlendFactor( material.blendSrc ); + colorBlend.dstFactor = this._getBlendFactor( material.blendDst ); + colorBlend.operation = this._getBlendOperation( material.blendEquation ); break; default: @@ -339,7 +345,7 @@ class WebGPUPipelineUtils { } - getStencilCompare( material ) { + _getStencilCompare( material ) { let stencilCompare; @@ -388,7 +394,7 @@ class WebGPUPipelineUtils { } - getStencilOperation( op ) { + _getStencilOperation( op ) { let stencilOperation; @@ -435,7 +441,7 @@ class WebGPUPipelineUtils { } - getBlendOperation( blendEquation ) { + _getBlendOperation( blendEquation ) { let blendOperation; @@ -470,10 +476,10 @@ class WebGPUPipelineUtils { } - getPrimitiveState( object, geometry, material ) { + _getPrimitiveState( object, geometry, material ) { const descriptor = {}; - const utils = this.backend.utils; + const utils = this.backend.utils; descriptor.topology = utils.getPrimitiveTopology( object, material ); @@ -511,13 +517,13 @@ class WebGPUPipelineUtils { } - getColorWriteMask( material ) { + _getColorWriteMask( material ) { return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None; } - getDepthCompare( material ) { + _getDepthCompare( material ) { let depthCompare; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js index 69b316dc544704..40870d9e1d3e78 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js @@ -1,40 +1,52 @@ -import { - GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName +import { + GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName } from './WebGPUConstants.js'; -import { - VideoTexture, CubeTexture, Texture, - NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, - RepeatWrapping, MirroredRepeatWrapping, - RGB_ETC2_Format, RGBA_ETC2_EAC_Format, - RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat, DepthTexture, +import { + CubeTexture, Texture, + NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, + RepeatWrapping, MirroredRepeatWrapping, + RGB_ETC2_Format, RGBA_ETC2_EAC_Format, + RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, - RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type + RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type, + NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare } from 'three'; import WebGPUTextureMipmapUtils from './WebGPUTextureMipmapUtils.js'; +const _compareToWebGPU = { + [ NeverCompare ]: 'never', + [ AlwaysCompare ]: 'less', + [ LessCompare ]: 'equal', + [ LessEqualCompare ]: 'less-equal', + [ EqualCompare ]: 'greater', + [ GreaterEqualCompare ]: 'not-equal', + [ GreaterCompare ]: 'greater-equal', + [ NotEqualCompare ]: 'always' +}; + class WebGPUTextureUtils { - constructor( backend ) { + constructor( backend ) { - this.backend = backend; + this.backend = backend; - this.mipmapUtils = null; + this.mipmapUtils = null; - this.defaultTexture = null; - this.defaultCubeTexture = null; + this.defaultTexture = null; + this.defaultCubeTexture = null; - } + } - createSampler( texture ) { + createSampler( texture ) { - const backend = this.backend; - const device = backend.device; + const backend = this.backend; + const device = backend.device; const textureGPU = backend.get( texture ); - textureGPU.sampler = device.createSampler( { + const samplerDescriptorGPU = { addressModeU: this._convertAddressMode( texture.wrapS ), addressModeV: this._convertAddressMode( texture.wrapT ), addressModeW: this._convertAddressMode( texture.wrapR ), @@ -42,34 +54,46 @@ class WebGPUTextureUtils { minFilter: this._convertFilterMode( texture.minFilter ), mipmapFilter: this._convertFilterMode( texture.minFilter ), maxAnisotropy: texture.anisotropy - } ); + }; - } + if ( texture.isDepthTexture && texture.compareFunction !== null ) { + + samplerDescriptorGPU.compare = _compareToWebGPU[ texture.compareFunction ]; + + } - createDefaultTexture( texture ) { + textureGPU.sampler = device.createSampler( samplerDescriptorGPU ); - let textureGPU; + } - if ( texture.isCubeTexture ) { + createDefaultTexture( texture ) { - textureGPU = this._getDefaultCubeTextureGPU(); + let textureGPU; - } else { + if ( texture.isCubeTexture ) { - textureGPU = this._getDefaultTextureGPU(); + textureGPU = this._getDefaultCubeTextureGPU(); - } + } else { - this.backend.get( texture ).texture = textureGPU; + textureGPU = this._getDefaultTextureGPU(); - } + } - createTexture( texture ) { + this.backend.get( texture ).texture = textureGPU; - const backend = this.backend; + } + + createTexture( texture ) { + const backend = this.backend; const textureData = backend.get( texture ); - if ( textureData.initialized ) return; + + if ( textureData.initialized ) { + + throw new Error( 'WebGPUTexture: Texture already initialized.' ); + + } const { width, height, depth } = this._getSize( texture ); @@ -77,14 +101,16 @@ class WebGPUTextureUtils { const dimension = this._getDimension( texture ); const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps ); const format = texture.internalFormat || this._getFormat( texture ); + //const sampleCount = texture.isRenderTargetTexture || texture.isDepthTexture ? backend.utils.getSampleCount() : 1; + const sampleCount = 1; let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; - if ( texture.isCompressedTexture !== true ) { + if ( texture.isCompressedTexture !== true ) { - usage |= GPUTextureUsage.RENDER_ATTACHMENT; + usage |= GPUTextureUsage.RENDER_ATTACHMENT; - } + } const textureDescriptorGPU = { label: texture.name, @@ -94,7 +120,7 @@ class WebGPUTextureUtils { depthOrArrayLayers: depth, }, mipLevelCount: mipLevelCount, - sampleCount: 1, + sampleCount: sampleCount, dimension: dimension, format: format, usage: usage @@ -102,21 +128,58 @@ class WebGPUTextureUtils { // texture creation - textureData.texture = backend.device.createTexture( textureDescriptorGPU ); - textureData.initialized = true; + if ( texture.isVideoTexture ) { + + const video = texture.source.data; + const videoFrame = new VideoFrame( video ); + + textureDescriptorGPU.size.width = videoFrame.displayWidth; + textureDescriptorGPU.size.height = videoFrame.displayHeight; + + videoFrame.close(); + + textureData.externalTexture = video; + + } else { + + textureData.texture = backend.device.createTexture( textureDescriptorGPU ); + + } + + textureData.initialized = true; - textureData.needsMipmaps = needsMipmaps; - textureData.textureDescriptorGPU = textureDescriptorGPU; + textureData.needsMipmaps = needsMipmaps; + textureData.textureDescriptorGPU = textureDescriptorGPU; } - updateTexture( texture ) { + destroyTexture( texture ) { - const textureData = this.backend.get( texture ); + const backend = this.backend; + const textureData = backend.get( texture ); + + textureData.texture.destroy(); + + backend.delete( texture ); + + } + + destroySampler( texture ) { + + const backend = this.backend; + const textureData = backend.get( texture ); + + delete textureData.sampler; + + } - const { needsMipmaps, textureDescriptorGPU } = textureData; + updateTexture( texture ) { - // transfer texture data + const textureData = this.backend.get( texture ); + + const { needsMipmaps, textureDescriptorGPU } = textureData; + + // transfer texture data if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) { @@ -138,59 +201,69 @@ class WebGPUTextureUtils { if ( needsMipmaps === true ) this._generateMipmaps( textureData.texture, textureDescriptorGPU ); - } else if ( texture.isDepthTexture !== true && texture.image !== null ) { + } else if ( texture.isVideoTexture ) { + + const video = texture.source.data; + + textureData.externalTexture = video; + + } else if ( texture.image !== null ) { this._copyImageToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps ); - + + } else { + + console.warn( 'WebGPUTexture: Unable to update texture.' ); + } // textureData.version = texture.version; - } + } - _getDefaultTextureGPU() { + _getDefaultTextureGPU() { - let defaultTexture = this.defaultTexture; + let defaultTexture = this.defaultTexture; - if ( defaultTexture === null ) { + if ( defaultTexture === null ) { - const texture = new Texture(); + const texture = new Texture(); texture.minFilter = NearestFilter; texture.magFilter = NearestFilter; - this.createTexture( texture ); + this.createTexture( texture ); - this.defaultTexture = defaultTexture = texture; + this.defaultTexture = defaultTexture = texture; - } + } - return this.backend.get( defaultTexture ).texture; + return this.backend.get( defaultTexture ).texture; - } + } - _getDefaultCubeTextureGPU() { + _getDefaultCubeTextureGPU() { - let defaultCubeTexture = this.defaultTexture; + let defaultCubeTexture = this.defaultTexture; - if ( defaultCubeTexture === null ) { + if ( defaultCubeTexture === null ) { - const texture = new CubeTexture(); + const texture = new CubeTexture(); texture.minFilter = NearestFilter; texture.magFilter = NearestFilter; - this.createTexture( texture ); + this.createTexture( texture ); - this.defaultCubeTexture = defaultCubeTexture = texture; + this.defaultCubeTexture = defaultCubeTexture = texture; - } + } - return this.backend.get( defaultCubeTexture ).texture; + return this.backend.get( defaultCubeTexture ).texture; - } + } - _copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ) { + _copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ) { if ( this._isHTMLImage( image ) ) { @@ -210,13 +283,13 @@ class WebGPUTextureUtils { } - _isHTMLImage( image ) { + _isHTMLImage( image ) { return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ); } - _copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU, needsMipmaps ) { + _copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU, needsMipmaps ) { for ( let i = 0; i < 6; i ++ ) { @@ -236,9 +309,9 @@ class WebGPUTextureUtils { } - _copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) { + _copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) { - const device = this.backend.device; + const device = this.backend.device; device.queue.copyExternalImageToTexture( { @@ -258,7 +331,7 @@ class WebGPUTextureUtils { } - _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ) { + _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ) { if ( this.mipmapUtils === null ) { @@ -270,7 +343,7 @@ class WebGPUTextureUtils { } - _getImageBitmapFromHTML( image, texture ) { + _getImageBitmapFromHTML( image, texture ) { const width = image.width; const height = image.height; @@ -284,12 +357,12 @@ class WebGPUTextureUtils { } - _copyBufferToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) { + _copyBufferToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) { // @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture() // @TODO: Consider to support valid buffer layouts with other formats like RGB - const device = this.backend.device; + const device = this.backend.device; const data = image.data; @@ -317,11 +390,11 @@ class WebGPUTextureUtils { } - _copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) { + _copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) { // @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture() - const device = this.backend.device; + const device = this.backend.device; const blockData = this._getBlockData( textureDescriptorGPU.format ); @@ -355,7 +428,7 @@ class WebGPUTextureUtils { } - _getBlockData( format ) { + _getBlockData( format ) { // this method is only relevant for compressed texture formats @@ -392,7 +465,7 @@ class WebGPUTextureUtils { } - _convertAddressMode( value ) { + _convertAddressMode( value ) { let addressMode = GPUAddressMode.ClampToEdge; @@ -424,7 +497,7 @@ class WebGPUTextureUtils { } - _getSize( texture ) { + _getSize( texture ) { const image = texture.image; @@ -460,7 +533,7 @@ class WebGPUTextureUtils { } - _getBytesPerTexel( format ) { + _getBytesPerTexel( format ) { if ( format === GPUTextureFormat.R8Unorm ) return 1; if ( format === GPUTextureFormat.R16Float ) return 2; @@ -474,7 +547,7 @@ class WebGPUTextureUtils { } - _getDimension( texture ) { + _getDimension( texture ) { let dimension; @@ -492,7 +565,7 @@ class WebGPUTextureUtils { } - _getMipLevelCount( texture, width, height, needsMipmaps ) { + _getMipLevelCount( texture, width, height, needsMipmaps ) { let mipLevelCount; @@ -514,7 +587,7 @@ class WebGPUTextureUtils { } - _getFormat( texture ) { + _getFormat( texture ) { const format = texture.format; const type = texture.type; @@ -522,7 +595,11 @@ class WebGPUTextureUtils { let formatGPU; - if ( texture.isCompressedTexture === true ) { + if ( texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true ) { + + formatGPU = GPUTextureFormat.BGRA8Unorm; + + } else if ( texture.isCompressedTexture === true ) { switch ( format ) { diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js index 022fe3b955f36d..5a576591817411 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js @@ -2,22 +2,19 @@ import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js'; class WebGPUUtils { - constructor( backend ) { + constructor( backend ) { - this.backend = backend; + this.backend = backend; - } - - getCurrentDepthStencilFormat() { + } - const renderer = this.backend.renderer; - const renderTarget = renderer.getRenderTarget(); + getCurrentDepthStencilFormat( renderContext ) { - let format; + let format; - if ( renderTarget !== null ) { + if ( renderContext.depthTexture !== null ) { - format = this.get( renderTarget ).depthTextureFormat; + format = this.getTextureFormatGPU( renderContext.depthTexture ); } else { @@ -27,18 +24,21 @@ class WebGPUUtils { return format; - } + } - getCurrentColorFormat() { + getTextureFormatGPU( texture ) { - const renderer = this.backend.renderer; - const renderTarget = renderer.getRenderTarget(); + return this.backend.get( texture ).texture.format; - let format; + } - if ( renderTarget !== null ) { + getCurrentColorFormat( renderContext ) { - format = this.get( renderTarget ).colorTextureFormat; + let format; + + if ( renderContext.texture !== null ) { + + format = this.getTextureFormatGPU( renderContext.texture ); } else { @@ -50,22 +50,19 @@ class WebGPUUtils { } - getCurrentColorSpace() { - - const renderer = this.backend.renderer; - const renderTarget = renderer.getRenderTarget(); + getCurrentColorSpace( renderContext ) { - if ( renderTarget !== null ) { + if ( renderContext.texture !== null ) { - return renderTarget.texture.colorSpace; + return renderContext.texture.colorSpace; } - return renderer.outputColorSpace; + return this.backend.renderer.outputColorSpace; } - getPrimitiveTopology( object, material ) { + getPrimitiveTopology( object, material ) { if ( object.isPoints ) return GPUPrimitiveTopology.PointList; else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList; @@ -74,7 +71,7 @@ class WebGPUUtils { } - getSampleCount() { + getSampleCount() { return this.backend.parameters.sampleCount; diff --git a/examples/webgpu_backdrop.html b/examples/webgpu_backdrop.html index 54d35bb1eb842b..ee30b313607676 100644 --- a/examples/webgpu_backdrop.html +++ b/examples/webgpu_backdrop.html @@ -83,6 +83,8 @@ // portals + const geometry = new THREE.SphereGeometry( .3, 32, 16 ); + portals = new THREE.Group(); scene.add( portals ); @@ -92,8 +94,6 @@ const id = portals.children.length; const rotation = THREE.MathUtils.degToRad( id * 45 ); - const geometry = new THREE.SphereGeometry( .3, 32, 16 ); - const material = new MeshStandardNodeMaterial( { color: 0x0066ff } ); material.roughnessNode = float( .2 ); material.metalnessNode = float( 0 ); diff --git a/examples/webgpu_depth_texture.html b/examples/webgpu_depth_texture.html index 8caf557f1f7297..a8a6beedb7d7f6 100644 --- a/examples/webgpu_depth_texture.html +++ b/examples/webgpu_depth_texture.html @@ -30,7 +30,7 @@ import WebGPU from 'three/addons/capabilities/WebGPU.js'; import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; - import WebGPUTextureRenderer from 'three/addons/renderers/webgpu/WebGPUTextureRenderer.js'; + import TextureRenderer from 'three/addons/renderers/universal/TextureRenderer.js'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; @@ -92,8 +92,9 @@ document.body.appendChild( renderer.domElement ); const depthTexture = new THREE.DepthTexture(); + depthTexture.type = THREE.FloatType; - textureRenderer = new WebGPUTextureRenderer( renderer ); + textureRenderer = new TextureRenderer( renderer ); textureRenderer.renderTarget.depthTexture = depthTexture; textureRenderer.setSize( window.innerWidth * dpr, window.innerHeight * dpr ); diff --git a/examples/webgpu_rtt.html b/examples/webgpu_rtt.html index 1ed44a0c402fc6..23bbb65f507b11 100644 --- a/examples/webgpu_rtt.html +++ b/examples/webgpu_rtt.html @@ -30,7 +30,7 @@ import WebGPU from 'three/addons/capabilities/WebGPU.js'; import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; - import WebGPUTextureRenderer from 'three/addons/renderers/webgpu/WebGPUTextureRenderer.js'; + import TextureRenderer from 'three/addons/renderers/universal/TextureRenderer.js'; let camera, scene, renderer; const mouse = new THREE.Vector2(); @@ -81,7 +81,7 @@ renderer.setAnimationLoop( animate ); document.body.appendChild( renderer.domElement ); - textureRenderer = new WebGPUTextureRenderer( renderer ); + textureRenderer = new TextureRenderer( renderer ); textureRenderer.setSize( window.innerWidth * dpr, window.innerHeight * dpr ); window.addEventListener( 'mousemove', onWindowMouseMove ); @@ -99,7 +99,7 @@ const screenFXNode = uniform( mouse ).add( vec2( 0.5, 0.5 ) ); const materialFX = new MeshBasicNodeMaterial(); - materialFX.colorNode = texture( textureRenderer.getTexture() ).mul( screenFXNode ); + materialFX.colorNode = texture( textureRenderer.texture ).mul( screenFXNode ); const quad = new THREE.Mesh( geometryFX, materialFX ); sceneFX.add( quad ); From 988d2bd5cfbf68a3000f7c71db36c07f72f69628 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 00:08:08 -0300 Subject: [PATCH 05/26] cleanup --- examples/jsm/renderers/universal/RenderPipelines.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/jsm/renderers/universal/RenderPipelines.js b/examples/jsm/renderers/universal/RenderPipelines.js index 80a924e395e2d4..ea4a84677e159d 100644 --- a/examples/jsm/renderers/universal/RenderPipelines.js +++ b/examples/jsm/renderers/universal/RenderPipelines.js @@ -24,7 +24,7 @@ class RenderPipelines { get( renderObject ) { - const { backend, device } = this; + const { backend } = this; const cache = this._getCache( renderObject ); let currentPipeline = cache.currentPipeline; @@ -195,12 +195,12 @@ class RenderPipelines { } - _releaseProgram( stage ) { + _releaseProgram( program ) { - if ( -- stage.usedTimes === 0 ) { + if ( -- program.usedTimes === 0 ) { - const code = stage.code; - const stage = stage.stage; + const code = program.code; + const stage = program.stage; this.programs[ stage ].delete( code ); From bcd8d8fcd555f891b3382eaa6895acf15eb43404 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 00:20:26 -0300 Subject: [PATCH 06/26] cleanup --- examples/jsm/nodes/Nodes.js | 3 +- .../renderers/universal/ComputePipelines.js | 78 ------------------- 2 files changed, 1 insertion(+), 80 deletions(-) delete mode 100644 examples/jsm/renderers/universal/ComputePipelines.js diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index 2eb8401f31a3ef..9eaed9ad339f83 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -141,8 +141,7 @@ export { default as NodeObjectLoader } from './loaders/NodeObjectLoader.js'; export { default as NodeMaterialLoader } from './loaders/NodeMaterialLoader.js'; // parsers -//export { default as WGSLNodeParser } from './parsers/WGSLNodeParser.js'; -//export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js'; +export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js'; // @TODO: Move to jsm/renderers/webgl. // materials export * from './materials/Materials.js'; diff --git a/examples/jsm/renderers/universal/ComputePipelines.js b/examples/jsm/renderers/universal/ComputePipelines.js deleted file mode 100644 index d2a06e85e1657a..00000000000000 --- a/examples/jsm/renderers/universal/ComputePipelines.js +++ /dev/null @@ -1,78 +0,0 @@ -import ProgrammableStage from './ProgrammableStage.js'; - -class ComputePipelines { - - constructor( device, nodes ) { - - this.device = device; - this.nodes = nodes; - - this.pipelines = new WeakMap(); - this.stages = { - compute: new WeakMap() - }; - - } - - has( computeNode ) { - - return this.pipelines.get( computeNode ) !== undefined; - - } - - get( computeNode ) { - - let pipeline = this.pipelines.get( computeNode ); - - // @TODO: Reuse compute pipeline if possible, introduce WebGPUComputePipeline - - if ( pipeline === undefined ) { - - const device = this.device; - - // get shader - - const nodeBuilder = this.nodes.getForCompute( computeNode ); - const computeShader = nodeBuilder.computeShader; - - const shader = { - computeShader - }; - - // programmable stage - - let stageCompute = this.stages.compute.get( shader ); - - if ( stageCompute === undefined ) { - - stageCompute = new ProgrammableStage( device, computeShader, 'compute' ); - - this.stages.compute.set( shader, stageCompute ); - - } - - pipeline = device.createComputePipeline( { - compute: stageCompute.stage, - layout: 'auto' - } ); - - this.pipelines.set( computeNode, pipeline ); - - } - - return pipeline; - - } - - dispose() { - - this.pipelines = new WeakMap(); - this.stages = { - compute: new WeakMap() - }; - - } - -} - -export default ComputePipelines; From 10611dca95f7840a8ea6945908af6008983b695b Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 00:31:27 -0300 Subject: [PATCH 07/26] Rename /universal to /common --- .../renderers/{universal => common}/Animation.js | 0 .../{universal => common}/Attributes.js | 0 .../renderers/{universal => common}/Backend.js | 0 .../{universal => common}/Background.js | 0 .../renderers/{universal => common}/Binding.js | 0 .../renderers/{universal => common}/Bindings.js | 0 .../renderers/{universal => common}/Buffer.js | 0 .../{universal => common}/BufferUtils.js | 0 .../renderers/{universal => common}/ChainMap.js | 0 .../{universal => common}/ComputePipeline.js | 0 .../renderers/{universal => common}/Constants.js | 0 .../renderers/{universal => common}/DataMap.js | 0 .../{universal => common}/Geometries.js | 0 .../jsm/renderers/{universal => common}/Info.js | 0 .../renderers/{universal => common}/Pipeline.js | 0 .../renderers/{universal => common}/Pipelines.js | 0 .../{universal => common}/ProgrammableStage.js | 0 .../{universal => common}/RenderContext.js | 0 .../{universal => common}/RenderContexts.js | 0 .../{universal => common}/RenderList.js | 0 .../{universal => common}/RenderLists.js | 0 .../{universal => common}/RenderObject.js | 0 .../{universal => common}/RenderObjects.js | 0 .../{universal => common}/RenderPipeline.js | 0 .../{universal => common}/RenderPipelines.js | 0 .../{universal => common}/RenderTarget.js | 0 .../renderers/{universal => common}/Renderer.js | 0 .../{universal => common}/SampledTexture.js | 0 .../renderers/{universal => common}/Sampler.js | 0 .../{universal => common}/StorageBuffer.js | 0 .../{universal => common}/TextureRenderer.js | 0 .../renderers/{universal => common}/Textures.js | 0 .../renderers/{universal => common}/Uniform.js | 0 .../{universal => common}/UniformBuffer.js | 0 .../{universal => common}/UniformsGroup.js | 0 .../{universal => common}/nodes/NodeRender.js | 0 .../nodes/NodeSampledTexture.js | 0 .../{universal => common}/nodes/NodeSampler.js | 0 .../{universal => common}/nodes/NodeUniform.js | 0 .../{universal => common}/nodes/Nodes.js | 0 examples/jsm/renderers/webgpu/WebGPURenderer.js | 2 +- .../webgpu/backends/webgpu/WebGPUBackend.js | 2 +- .../backends/webgpu/builder/WGSLNodeBuilder.js | 16 ++++++++-------- .../backends/webgpu/utils/WebGPUPipelineUtils.js | 2 +- examples/webgpu_depth_texture.html | 2 +- examples/webgpu_rtt.html | 2 +- 46 files changed, 13 insertions(+), 13 deletions(-) rename examples/jsm/renderers/{universal => common}/Animation.js (100%) rename examples/jsm/renderers/{universal => common}/Attributes.js (100%) rename examples/jsm/renderers/{universal => common}/Backend.js (100%) rename examples/jsm/renderers/{universal => common}/Background.js (100%) rename examples/jsm/renderers/{universal => common}/Binding.js (100%) rename examples/jsm/renderers/{universal => common}/Bindings.js (100%) rename examples/jsm/renderers/{universal => common}/Buffer.js (100%) rename examples/jsm/renderers/{universal => common}/BufferUtils.js (100%) rename examples/jsm/renderers/{universal => common}/ChainMap.js (100%) rename examples/jsm/renderers/{universal => common}/ComputePipeline.js (100%) rename examples/jsm/renderers/{universal => common}/Constants.js (100%) rename examples/jsm/renderers/{universal => common}/DataMap.js (100%) rename examples/jsm/renderers/{universal => common}/Geometries.js (100%) rename examples/jsm/renderers/{universal => common}/Info.js (100%) rename examples/jsm/renderers/{universal => common}/Pipeline.js (100%) rename examples/jsm/renderers/{universal => common}/Pipelines.js (100%) rename examples/jsm/renderers/{universal => common}/ProgrammableStage.js (100%) rename examples/jsm/renderers/{universal => common}/RenderContext.js (100%) rename examples/jsm/renderers/{universal => common}/RenderContexts.js (100%) rename examples/jsm/renderers/{universal => common}/RenderList.js (100%) rename examples/jsm/renderers/{universal => common}/RenderLists.js (100%) rename examples/jsm/renderers/{universal => common}/RenderObject.js (100%) rename examples/jsm/renderers/{universal => common}/RenderObjects.js (100%) rename examples/jsm/renderers/{universal => common}/RenderPipeline.js (100%) rename examples/jsm/renderers/{universal => common}/RenderPipelines.js (100%) rename examples/jsm/renderers/{universal => common}/RenderTarget.js (100%) rename examples/jsm/renderers/{universal => common}/Renderer.js (100%) rename examples/jsm/renderers/{universal => common}/SampledTexture.js (100%) rename examples/jsm/renderers/{universal => common}/Sampler.js (100%) rename examples/jsm/renderers/{universal => common}/StorageBuffer.js (100%) rename examples/jsm/renderers/{universal => common}/TextureRenderer.js (100%) rename examples/jsm/renderers/{universal => common}/Textures.js (100%) rename examples/jsm/renderers/{universal => common}/Uniform.js (100%) rename examples/jsm/renderers/{universal => common}/UniformBuffer.js (100%) rename examples/jsm/renderers/{universal => common}/UniformsGroup.js (100%) rename examples/jsm/renderers/{universal => common}/nodes/NodeRender.js (100%) rename examples/jsm/renderers/{universal => common}/nodes/NodeSampledTexture.js (100%) rename examples/jsm/renderers/{universal => common}/nodes/NodeSampler.js (100%) rename examples/jsm/renderers/{universal => common}/nodes/NodeUniform.js (100%) rename examples/jsm/renderers/{universal => common}/nodes/Nodes.js (100%) diff --git a/examples/jsm/renderers/universal/Animation.js b/examples/jsm/renderers/common/Animation.js similarity index 100% rename from examples/jsm/renderers/universal/Animation.js rename to examples/jsm/renderers/common/Animation.js diff --git a/examples/jsm/renderers/universal/Attributes.js b/examples/jsm/renderers/common/Attributes.js similarity index 100% rename from examples/jsm/renderers/universal/Attributes.js rename to examples/jsm/renderers/common/Attributes.js diff --git a/examples/jsm/renderers/universal/Backend.js b/examples/jsm/renderers/common/Backend.js similarity index 100% rename from examples/jsm/renderers/universal/Backend.js rename to examples/jsm/renderers/common/Backend.js diff --git a/examples/jsm/renderers/universal/Background.js b/examples/jsm/renderers/common/Background.js similarity index 100% rename from examples/jsm/renderers/universal/Background.js rename to examples/jsm/renderers/common/Background.js diff --git a/examples/jsm/renderers/universal/Binding.js b/examples/jsm/renderers/common/Binding.js similarity index 100% rename from examples/jsm/renderers/universal/Binding.js rename to examples/jsm/renderers/common/Binding.js diff --git a/examples/jsm/renderers/universal/Bindings.js b/examples/jsm/renderers/common/Bindings.js similarity index 100% rename from examples/jsm/renderers/universal/Bindings.js rename to examples/jsm/renderers/common/Bindings.js diff --git a/examples/jsm/renderers/universal/Buffer.js b/examples/jsm/renderers/common/Buffer.js similarity index 100% rename from examples/jsm/renderers/universal/Buffer.js rename to examples/jsm/renderers/common/Buffer.js diff --git a/examples/jsm/renderers/universal/BufferUtils.js b/examples/jsm/renderers/common/BufferUtils.js similarity index 100% rename from examples/jsm/renderers/universal/BufferUtils.js rename to examples/jsm/renderers/common/BufferUtils.js diff --git a/examples/jsm/renderers/universal/ChainMap.js b/examples/jsm/renderers/common/ChainMap.js similarity index 100% rename from examples/jsm/renderers/universal/ChainMap.js rename to examples/jsm/renderers/common/ChainMap.js diff --git a/examples/jsm/renderers/universal/ComputePipeline.js b/examples/jsm/renderers/common/ComputePipeline.js similarity index 100% rename from examples/jsm/renderers/universal/ComputePipeline.js rename to examples/jsm/renderers/common/ComputePipeline.js diff --git a/examples/jsm/renderers/universal/Constants.js b/examples/jsm/renderers/common/Constants.js similarity index 100% rename from examples/jsm/renderers/universal/Constants.js rename to examples/jsm/renderers/common/Constants.js diff --git a/examples/jsm/renderers/universal/DataMap.js b/examples/jsm/renderers/common/DataMap.js similarity index 100% rename from examples/jsm/renderers/universal/DataMap.js rename to examples/jsm/renderers/common/DataMap.js diff --git a/examples/jsm/renderers/universal/Geometries.js b/examples/jsm/renderers/common/Geometries.js similarity index 100% rename from examples/jsm/renderers/universal/Geometries.js rename to examples/jsm/renderers/common/Geometries.js diff --git a/examples/jsm/renderers/universal/Info.js b/examples/jsm/renderers/common/Info.js similarity index 100% rename from examples/jsm/renderers/universal/Info.js rename to examples/jsm/renderers/common/Info.js diff --git a/examples/jsm/renderers/universal/Pipeline.js b/examples/jsm/renderers/common/Pipeline.js similarity index 100% rename from examples/jsm/renderers/universal/Pipeline.js rename to examples/jsm/renderers/common/Pipeline.js diff --git a/examples/jsm/renderers/universal/Pipelines.js b/examples/jsm/renderers/common/Pipelines.js similarity index 100% rename from examples/jsm/renderers/universal/Pipelines.js rename to examples/jsm/renderers/common/Pipelines.js diff --git a/examples/jsm/renderers/universal/ProgrammableStage.js b/examples/jsm/renderers/common/ProgrammableStage.js similarity index 100% rename from examples/jsm/renderers/universal/ProgrammableStage.js rename to examples/jsm/renderers/common/ProgrammableStage.js diff --git a/examples/jsm/renderers/universal/RenderContext.js b/examples/jsm/renderers/common/RenderContext.js similarity index 100% rename from examples/jsm/renderers/universal/RenderContext.js rename to examples/jsm/renderers/common/RenderContext.js diff --git a/examples/jsm/renderers/universal/RenderContexts.js b/examples/jsm/renderers/common/RenderContexts.js similarity index 100% rename from examples/jsm/renderers/universal/RenderContexts.js rename to examples/jsm/renderers/common/RenderContexts.js diff --git a/examples/jsm/renderers/universal/RenderList.js b/examples/jsm/renderers/common/RenderList.js similarity index 100% rename from examples/jsm/renderers/universal/RenderList.js rename to examples/jsm/renderers/common/RenderList.js diff --git a/examples/jsm/renderers/universal/RenderLists.js b/examples/jsm/renderers/common/RenderLists.js similarity index 100% rename from examples/jsm/renderers/universal/RenderLists.js rename to examples/jsm/renderers/common/RenderLists.js diff --git a/examples/jsm/renderers/universal/RenderObject.js b/examples/jsm/renderers/common/RenderObject.js similarity index 100% rename from examples/jsm/renderers/universal/RenderObject.js rename to examples/jsm/renderers/common/RenderObject.js diff --git a/examples/jsm/renderers/universal/RenderObjects.js b/examples/jsm/renderers/common/RenderObjects.js similarity index 100% rename from examples/jsm/renderers/universal/RenderObjects.js rename to examples/jsm/renderers/common/RenderObjects.js diff --git a/examples/jsm/renderers/universal/RenderPipeline.js b/examples/jsm/renderers/common/RenderPipeline.js similarity index 100% rename from examples/jsm/renderers/universal/RenderPipeline.js rename to examples/jsm/renderers/common/RenderPipeline.js diff --git a/examples/jsm/renderers/universal/RenderPipelines.js b/examples/jsm/renderers/common/RenderPipelines.js similarity index 100% rename from examples/jsm/renderers/universal/RenderPipelines.js rename to examples/jsm/renderers/common/RenderPipelines.js diff --git a/examples/jsm/renderers/universal/RenderTarget.js b/examples/jsm/renderers/common/RenderTarget.js similarity index 100% rename from examples/jsm/renderers/universal/RenderTarget.js rename to examples/jsm/renderers/common/RenderTarget.js diff --git a/examples/jsm/renderers/universal/Renderer.js b/examples/jsm/renderers/common/Renderer.js similarity index 100% rename from examples/jsm/renderers/universal/Renderer.js rename to examples/jsm/renderers/common/Renderer.js diff --git a/examples/jsm/renderers/universal/SampledTexture.js b/examples/jsm/renderers/common/SampledTexture.js similarity index 100% rename from examples/jsm/renderers/universal/SampledTexture.js rename to examples/jsm/renderers/common/SampledTexture.js diff --git a/examples/jsm/renderers/universal/Sampler.js b/examples/jsm/renderers/common/Sampler.js similarity index 100% rename from examples/jsm/renderers/universal/Sampler.js rename to examples/jsm/renderers/common/Sampler.js diff --git a/examples/jsm/renderers/universal/StorageBuffer.js b/examples/jsm/renderers/common/StorageBuffer.js similarity index 100% rename from examples/jsm/renderers/universal/StorageBuffer.js rename to examples/jsm/renderers/common/StorageBuffer.js diff --git a/examples/jsm/renderers/universal/TextureRenderer.js b/examples/jsm/renderers/common/TextureRenderer.js similarity index 100% rename from examples/jsm/renderers/universal/TextureRenderer.js rename to examples/jsm/renderers/common/TextureRenderer.js diff --git a/examples/jsm/renderers/universal/Textures.js b/examples/jsm/renderers/common/Textures.js similarity index 100% rename from examples/jsm/renderers/universal/Textures.js rename to examples/jsm/renderers/common/Textures.js diff --git a/examples/jsm/renderers/universal/Uniform.js b/examples/jsm/renderers/common/Uniform.js similarity index 100% rename from examples/jsm/renderers/universal/Uniform.js rename to examples/jsm/renderers/common/Uniform.js diff --git a/examples/jsm/renderers/universal/UniformBuffer.js b/examples/jsm/renderers/common/UniformBuffer.js similarity index 100% rename from examples/jsm/renderers/universal/UniformBuffer.js rename to examples/jsm/renderers/common/UniformBuffer.js diff --git a/examples/jsm/renderers/universal/UniformsGroup.js b/examples/jsm/renderers/common/UniformsGroup.js similarity index 100% rename from examples/jsm/renderers/universal/UniformsGroup.js rename to examples/jsm/renderers/common/UniformsGroup.js diff --git a/examples/jsm/renderers/universal/nodes/NodeRender.js b/examples/jsm/renderers/common/nodes/NodeRender.js similarity index 100% rename from examples/jsm/renderers/universal/nodes/NodeRender.js rename to examples/jsm/renderers/common/nodes/NodeRender.js diff --git a/examples/jsm/renderers/universal/nodes/NodeSampledTexture.js b/examples/jsm/renderers/common/nodes/NodeSampledTexture.js similarity index 100% rename from examples/jsm/renderers/universal/nodes/NodeSampledTexture.js rename to examples/jsm/renderers/common/nodes/NodeSampledTexture.js diff --git a/examples/jsm/renderers/universal/nodes/NodeSampler.js b/examples/jsm/renderers/common/nodes/NodeSampler.js similarity index 100% rename from examples/jsm/renderers/universal/nodes/NodeSampler.js rename to examples/jsm/renderers/common/nodes/NodeSampler.js diff --git a/examples/jsm/renderers/universal/nodes/NodeUniform.js b/examples/jsm/renderers/common/nodes/NodeUniform.js similarity index 100% rename from examples/jsm/renderers/universal/nodes/NodeUniform.js rename to examples/jsm/renderers/common/nodes/NodeUniform.js diff --git a/examples/jsm/renderers/universal/nodes/Nodes.js b/examples/jsm/renderers/common/nodes/Nodes.js similarity index 100% rename from examples/jsm/renderers/universal/nodes/Nodes.js rename to examples/jsm/renderers/common/nodes/Nodes.js diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 3596669ecb3872..148dcdc985580e 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -1,4 +1,4 @@ -import Renderer from '../universal/Renderer.js'; +import Renderer from '../common/Renderer.js'; import WebGPUBackend from './backends/webgpu/WebGPUBackend.js'; /* const debugHandler = { diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js index 44d3fdf3ff0af7..e92417d93314b7 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js @@ -1,7 +1,7 @@ import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat } from './utils/WebGPUConstants.js'; import WebGPUNodeBuilder from './builder/WGSLNodeBuilder.js'; -import Backend from '../../../universal/Backend.js'; +import Backend from '../../../common/Backend.js'; import { Matrix4, Frustum, DepthFormat } from 'three'; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js index ece121e6d31e03..f828a284e981af 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js @@ -1,16 +1,16 @@ -import UniformsGroup from '../../../../universal/UniformsGroup.js'; +import UniformsGroup from '../../../../common/UniformsGroup.js'; import { FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform -} from '../../../../universal/nodes/NodeUniform.js'; -import NodeSampler from '../../../../universal/nodes/NodeSampler.js'; -import { NodeSampledTexture, NodeSampledCubeTexture } from '../../../../universal/nodes/NodeSampledTexture.js'; +} from '../../../../common/nodes/NodeUniform.js'; +import NodeSampler from '../../../../common/nodes/NodeSampler.js'; +import { NodeSampledTexture, NodeSampledCubeTexture } from '../../../../common/nodes/NodeSampledTexture.js'; -import UniformBuffer from '../../../../universal/UniformBuffer.js'; -import StorageBuffer from '../../../../universal/StorageBuffer.js'; -import { getVectorLength, getStrideLength } from '../../../../universal/BufferUtils.js'; +import UniformBuffer from '../../../../common/UniformBuffer.js'; +import StorageBuffer from '../../../../common/StorageBuffer.js'; +import { getVectorLength, getStrideLength } from '../../../../common/BufferUtils.js'; -import RenderTarget from '../../../../universal/RenderTarget.js'; +import RenderTarget from '../../../../common/RenderTarget.js'; import { NodeBuilder, CodeNode, NodeMaterial } from '../../../../../nodes/Nodes.js'; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js index 2aec1dffbee026..32af25f51d3acc 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js @@ -1,4 +1,4 @@ -import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../../../universal/Constants.js'; +import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../../../common/Constants.js'; import { GPUInputStepMode, GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation diff --git a/examples/webgpu_depth_texture.html b/examples/webgpu_depth_texture.html index a8a6beedb7d7f6..4777067f4c419a 100644 --- a/examples/webgpu_depth_texture.html +++ b/examples/webgpu_depth_texture.html @@ -30,7 +30,7 @@ import WebGPU from 'three/addons/capabilities/WebGPU.js'; import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; - import TextureRenderer from 'three/addons/renderers/universal/TextureRenderer.js'; + import TextureRenderer from 'three/addons/renderers/common/TextureRenderer.js'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; diff --git a/examples/webgpu_rtt.html b/examples/webgpu_rtt.html index 23bbb65f507b11..b4de62066c4f18 100644 --- a/examples/webgpu_rtt.html +++ b/examples/webgpu_rtt.html @@ -30,7 +30,7 @@ import WebGPU from 'three/addons/capabilities/WebGPU.js'; import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; - import TextureRenderer from 'three/addons/renderers/universal/TextureRenderer.js'; + import TextureRenderer from 'three/addons/renderers/common/TextureRenderer.js'; let camera, scene, renderer; const mouse = new THREE.Vector2(); From 5f15a5f169fe62697870f07d09b61d46e075deff Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:07:22 -0300 Subject: [PATCH 08/26] Update examples/jsm/renderers/common/Renderer.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/renderers/common/Renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index 0e89c0611e88e9..bc1ef83f18fdcf 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -96,7 +96,7 @@ class Renderer { if ( this._initialized ) { - throw new Error( 'WebGPURenderer: Device has already been initialized.' ); + throw new Error( 'Renderer: Backend has already been initialized.' ); } From 121240adb7819ebc4a1634f82b03524ba08860ff Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:07:34 -0300 Subject: [PATCH 09/26] Update examples/jsm/renderers/common/Renderer.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/renderers/common/Renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index bc1ef83f18fdcf..e4f23ad61739da 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -401,7 +401,7 @@ class Renderer { if ( x.isVector4 ) { - scissor.set( x.x, x.y, x.z, x.w ); + scissor.copy( x ); } else { From 9cf8437ea2dcf9c2d959a6b84605ae1930fa4804 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:09:34 -0300 Subject: [PATCH 10/26] Update examples/jsm/renderers/common/Textures.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/renderers/common/Textures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/Textures.js b/examples/jsm/renderers/common/Textures.js index 16c4bd39765357..fc6b3c1c1ac44f 100644 --- a/examples/jsm/renderers/common/Textures.js +++ b/examples/jsm/renderers/common/Textures.js @@ -81,7 +81,7 @@ class Textures extends DataMap { if ( isRenderTexture && textureData.initialized === true ) { - // it's a update + // it's an update backend.destroySampler( texture ); backend.destroyTexture( texture ); From 727f9179251c8ac3bbab766dbe0622c5850b6d47 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:09:51 -0300 Subject: [PATCH 11/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUTextureUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js index 40870d9e1d3e78..0c8e5e033f30e2 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js @@ -91,7 +91,7 @@ class WebGPUTextureUtils { if ( textureData.initialized ) { - throw new Error( 'WebGPUTexture: Texture already initialized.' ); + throw new Error( 'WebGPUTextureUtils: Texture already initialized.' ); } From 2d4b8ed99951a9f937e365e35a52b24b21d7970d Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:10:02 -0300 Subject: [PATCH 12/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUTextureUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js index 0c8e5e033f30e2..a6425a0743b0b4 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js @@ -213,7 +213,7 @@ class WebGPUTextureUtils { } else { - console.warn( 'WebGPUTexture: Unable to update texture.' ); + console.warn( 'WebGPUTextureUtils: Unable to update texture.' ); } From 317dbd1055d0d11b2ad9e737aa06320aed4a45ac Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:10:17 -0300 Subject: [PATCH 13/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js index 32af25f51d3acc..c74988f8298388 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js @@ -570,7 +570,7 @@ class WebGPUPipelineUtils { break; default: - console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc ); + console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc ); } From 243a0d2b21f79f6b7af684fb6bd8e92795211bcd Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:10:25 -0300 Subject: [PATCH 14/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js index c74988f8298388..214787f6dae342 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js @@ -508,7 +508,7 @@ class WebGPUPipelineUtils { break; default: - console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side ); + console.error( 'THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side ); break; } From 007d995a541bfced06c2963fa5cad9293f6af998 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:10:38 -0300 Subject: [PATCH 15/26] Update examples/jsm/renderers/common/nodes/Nodes.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/renderers/common/nodes/Nodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/nodes/Nodes.js b/examples/jsm/renderers/common/nodes/Nodes.js index 21fad9ad63cabc..aae92dab2d40cb 100644 --- a/examples/jsm/renderers/common/nodes/Nodes.js +++ b/examples/jsm/renderers/common/nodes/Nodes.js @@ -242,7 +242,7 @@ class Nodes extends DataMap { } else { - console.error( 'WebGPUNodes: Unsupported environment configuration.', environment ); + console.error( 'Nodes: Unsupported environment configuration.', environment ); } From 3f2c9e4a1192a18dfa81c8ef9b584dceb7838119 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:10:47 -0300 Subject: [PATCH 16/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js index 214787f6dae342..d48439cf433e79 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js @@ -468,7 +468,7 @@ class WebGPUPipelineUtils { break; default: - console.error( 'THREE.WebGPURenderer: Blend equation not supported.', blendEquation ); + console.error( 'THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation ); } From 964dc50a925536f03f6f3db27985f842a86da2b0 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:10:56 -0300 Subject: [PATCH 17/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js index 749211073d0aa9..7166f6cb8467f1 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js @@ -230,7 +230,7 @@ class WebGPUAttributeUtils { if ( ! format ) { - console.error( 'THREE.WebGPURenderer: Vertex format not supported yet.' ); + console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' ); } From 1136afc692ad3df74e1815bd5897741affb1e08d Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:11:04 -0300 Subject: [PATCH 18/26] Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js Co-authored-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- .../webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js index 7166f6cb8467f1..08b8e3e87816bb 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js +++ b/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js @@ -218,7 +218,7 @@ class WebGPUAttributeUtils { if ( paddedItemSize % 1 ) { - throw new Error( 'THREE.WebGPURenderer: Bad vertex format item size.' ); + throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' ); } From f0739543447af0b56d007067ad47790f5c47b525 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 11:31:47 -0300 Subject: [PATCH 19/26] Simplifies paths. --- examples/jsm/renderers/common/Renderer.js | 2 +- .../{backends/webgpu => }/WebGPUBackend.js | 2 +- .../jsm/renderers/webgpu/WebGPURenderer.js | 2 +- .../webgpu => }/builder/WGSLNodeBuilder.js | 18 +++++++++--------- .../webgpu => }/builder/WGSLNodeFunction.js | 4 ++-- .../webgpu => }/builder/WGSLNodeParser.js | 2 +- .../webgpu => }/utils/WebGPUAttributeUtils.js | 0 .../webgpu => }/utils/WebGPUBindingUtils.js | 0 .../webgpu => }/utils/WebGPUConstants.js | 0 .../webgpu => }/utils/WebGPUPipelineUtils.js | 2 +- .../utils/WebGPUTextureMipmapUtils.js | 0 .../webgpu => }/utils/WebGPUTextureUtils.js | 0 .../{backends/webgpu => }/utils/WebGPUUtils.js | 0 13 files changed, 16 insertions(+), 16 deletions(-) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/WebGPUBackend.js (99%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/builder/WGSLNodeBuilder.js (96%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/builder/WGSLNodeFunction.js (92%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/builder/WGSLNodeParser.js (75%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUAttributeUtils.js (100%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUBindingUtils.js (100%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUConstants.js (100%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUPipelineUtils.js (99%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUTextureMipmapUtils.js (100%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUTextureUtils.js (100%) rename examples/jsm/renderers/webgpu/{backends/webgpu => }/utils/WebGPUUtils.js (100%) diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index e4f23ad61739da..10a9487e378129 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -645,7 +645,7 @@ class Renderer { } else if ( object.isLineLoop ) { - console.error( 'THREE.WebGPURenderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' ); + console.error( 'THREE.Renderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' ); } else if ( object.isMesh || object.isLine || object.isPoints ) { diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js similarity index 99% rename from examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js rename to examples/jsm/renderers/webgpu/WebGPUBackend.js index e92417d93314b7..6c4867e782cd53 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js @@ -1,7 +1,7 @@ import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat } from './utils/WebGPUConstants.js'; import WebGPUNodeBuilder from './builder/WGSLNodeBuilder.js'; -import Backend from '../../../common/Backend.js'; +import Backend from '../common/Backend.js'; import { Matrix4, Frustum, DepthFormat } from 'three'; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 148dcdc985580e..b886de248756d9 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -1,5 +1,5 @@ import Renderer from '../common/Renderer.js'; -import WebGPUBackend from './backends/webgpu/WebGPUBackend.js'; +import WebGPUBackend from './WebGPUBackend.js'; /* const debugHandler = { diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/builder/WGSLNodeBuilder.js similarity index 96% rename from examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js rename to examples/jsm/renderers/webgpu/builder/WGSLNodeBuilder.js index f828a284e981af..6c8c037b0b8cfd 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/builder/WGSLNodeBuilder.js @@ -1,18 +1,18 @@ -import UniformsGroup from '../../../../common/UniformsGroup.js'; +import UniformsGroup from '../../common/UniformsGroup.js'; import { FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform -} from '../../../../common/nodes/NodeUniform.js'; -import NodeSampler from '../../../../common/nodes/NodeSampler.js'; -import { NodeSampledTexture, NodeSampledCubeTexture } from '../../../../common/nodes/NodeSampledTexture.js'; +} from '../../common/nodes/NodeUniform.js'; +import NodeSampler from '../../common/nodes/NodeSampler.js'; +import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js'; -import UniformBuffer from '../../../../common/UniformBuffer.js'; -import StorageBuffer from '../../../../common/StorageBuffer.js'; -import { getVectorLength, getStrideLength } from '../../../../common/BufferUtils.js'; +import UniformBuffer from '../../common/UniformBuffer.js'; +import StorageBuffer from '../../common/StorageBuffer.js'; +import { getVectorLength, getStrideLength } from '../../common/BufferUtils.js'; -import RenderTarget from '../../../../common/RenderTarget.js'; +import RenderTarget from '../../common/RenderTarget.js'; -import { NodeBuilder, CodeNode, NodeMaterial } from '../../../../../nodes/Nodes.js'; +import { NodeBuilder, CodeNode, NodeMaterial } from '../../../nodes/Nodes.js'; import WGSLNodeParser from './WGSLNodeParser.js'; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeFunction.js b/examples/jsm/renderers/webgpu/builder/WGSLNodeFunction.js similarity index 92% rename from examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeFunction.js rename to examples/jsm/renderers/webgpu/builder/WGSLNodeFunction.js index 4e1740c8562656..074fba2d402240 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeFunction.js +++ b/examples/jsm/renderers/webgpu/builder/WGSLNodeFunction.js @@ -1,5 +1,5 @@ -import NodeFunction from '../../../../../nodes/core/NodeFunction.js'; -import NodeFunctionInput from '../../../../../nodes/core/NodeFunctionInput.js'; +import NodeFunction from '../../../nodes/core/NodeFunction.js'; +import NodeFunctionInput from '../../../nodes/core/NodeFunctionInput.js'; const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i; const propertiesRegexp = /[a-z_0-9]+/ig; diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeParser.js b/examples/jsm/renderers/webgpu/builder/WGSLNodeParser.js similarity index 75% rename from examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeParser.js rename to examples/jsm/renderers/webgpu/builder/WGSLNodeParser.js index a3f7b2bdab2652..5f9dfb8ca21053 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/builder/WGSLNodeParser.js +++ b/examples/jsm/renderers/webgpu/builder/WGSLNodeParser.js @@ -1,4 +1,4 @@ -import NodeParser from '../../../../../nodes/core/NodeParser.js'; +import NodeParser from '../../../nodes/core/NodeParser.js'; import WGSLNodeFunction from './WGSLNodeFunction.js'; class WGSLNodeParser extends NodeParser { diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js similarity index 100% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js similarity index 100% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUBindingUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js b/examples/jsm/renderers/webgpu/utils/WebGPUConstants.js similarity index 100% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUConstants.js rename to examples/jsm/renderers/webgpu/utils/WebGPUConstants.js diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js similarity index 99% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js index d48439cf433e79..7fc8ecf1c63a40 100644 --- a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -1,4 +1,4 @@ -import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../../../common/Constants.js'; +import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js'; import { GPUInputStepMode, GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureMipmapUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js similarity index 100% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureMipmapUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js similarity index 100% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js diff --git a/examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js similarity index 100% rename from examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUUtils.js From e94455b31262983f86e767e8155360f8decfc8e1 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 12:42:13 -0300 Subject: [PATCH 20/26] cleanup --- examples/jsm/renderers/common/Pipelines.js | 4 +- .../jsm/renderers/common/RenderPipelines.js | 258 ------------------ 2 files changed, 2 insertions(+), 260 deletions(-) delete mode 100644 examples/jsm/renderers/common/RenderPipelines.js diff --git a/examples/jsm/renderers/common/Pipelines.js b/examples/jsm/renderers/common/Pipelines.js index 5d36723c9da582..dd544c724548c2 100644 --- a/examples/jsm/renderers/common/Pipelines.js +++ b/examples/jsm/renderers/common/Pipelines.js @@ -143,8 +143,8 @@ class Pipelines extends DataMap { super.dispose(); - this.caches.clear(); - this.shaderModules = { + this.caches = new Map(); + this.programs = { vertex: new Map(), fragment: new Map(), compute: new Map() diff --git a/examples/jsm/renderers/common/RenderPipelines.js b/examples/jsm/renderers/common/RenderPipelines.js deleted file mode 100644 index ea4a84677e159d..00000000000000 --- a/examples/jsm/renderers/common/RenderPipelines.js +++ /dev/null @@ -1,258 +0,0 @@ -import RenderPipeline from './RenderPipeline.js'; -import ProgrammableStage from './ProgrammableStage.js'; - -class RenderPipelines { - - constructor( backend, device, nodes, utils ) { - - this.backend = backend; - this.device = device; - this.nodes = nodes; - this.utils = utils; - - this.bindings = null; - - this.pipelines = []; - this.cache = new WeakMap(); - - this.programs = { - vertex: new Map(), - fragment: new Map() - }; - - } - - get( renderObject ) { - - const { backend } = this; - const cache = this._getCache( renderObject ); - - let currentPipeline = cache.currentPipeline; - - if ( this._needsUpdate( renderObject ) ) { - - // release previous cache - - this._releasePipeline( renderObject ); - - // get shader - - const nodeBuilder = this.nodes.get( renderObject ); - - // programmable stages - - let stageVertex = this.programs.vertex.get( nodeBuilder.vertexShader ); - - if ( stageVertex === undefined ) { - - stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' ); - this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex ); - - backend.createProgram( stageVertex ); - - } - - let stageFragment = this.programs.fragment.get( nodeBuilder.fragmentShader ); - - if ( stageFragment === undefined ) { - - stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' ); - this.programs.fragment.set( nodeBuilder.fragmentShader, stageFragment ); - - backend.createProgram( stageFragment ); - - } - - // determine render pipeline - - currentPipeline = this._acquirePipeline( renderObject, stageVertex, stageFragment ); - cache.currentPipeline = currentPipeline; - - // keep track of all used times - - currentPipeline.usedTimes ++; - stageVertex.usedTimes ++; - stageFragment.usedTimes ++; - - } - - return currentPipeline; - - } - - remove( renderObject ) { - - this._releasePipeline( renderObject ); - - } - - dispose() { - - this.pipelines = []; - this.cache = new WeakMap(); - this.shaderModules = { - vertex: new Map(), - fragment: new Map() - }; - - } - - _acquirePipeline( renderObject, stageVertex, stageFragment ) { - - let pipeline; - const pipelines = this.pipelines; - - // check for existing pipeline - - const cacheKey = this._computeCacheKey( renderObject, stageVertex, stageFragment ); - - for ( let i = 0, il = pipelines.length; i < il; i ++ ) { - - const preexistingPipeline = pipelines[ i ]; - - if ( preexistingPipeline.cacheKey === cacheKey ) { - - pipeline = preexistingPipeline; - break; - - } - - } - - if ( pipeline === undefined ) { - - pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment ); - pipelines.push( pipeline ); - - renderObject.pipeline = pipeline; - - this.backend.createPipeline( renderObject ); - - } - - return pipeline; - - } - - _computeCacheKey( renderObject, stageVertex, stageFragment ) { - - const { material } = renderObject; - - const parameters = [ - stageVertex.id, stageFragment.id, - material.transparent, material.blending, material.premultipliedAlpha, - material.blendSrc, material.blendDst, material.blendEquation, - material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha, - material.colorWrite, - material.depthWrite, material.depthTest, material.depthFunc, - material.stencilWrite, material.stencilFunc, - material.stencilFail, material.stencilZFail, material.stencilZPass, - material.stencilFuncMask, material.stencilWriteMask, - material.side, - this.backend.getCacheKey( renderObject ) - ]; - - return parameters.join(); - - } - - _getCache( renderObject ) { - - let cache = this.cache.get( renderObject ); - - if ( cache === undefined ) { - - cache = {}; - this.cache.set( renderObject, cache ); - - } - - return cache; - - } - - _releasePipeline( renderObject ) { - - const cache = this._getCache( renderObject ); - - const pipeline = cache.currentPipeline; - delete cache.currentPipeline; - - this.bindings.remove( renderObject ); - - if ( pipeline && -- pipeline.usedTimes === 0 ) { - - const pipelines = this.pipelines; - - const i = pipelines.indexOf( pipeline ); - pipelines[ i ] = pipelines[ pipelines.length - 1 ]; - pipelines.pop(); - - this._releaseProgram( pipeline.stageVertex ); - this._releaseProgram( pipeline.stageFragment ); - - } - - } - - _releaseProgram( program ) { - - if ( -- program.usedTimes === 0 ) { - - const code = program.code; - const stage = program.stage; - - this.programs[ stage ].delete( code ); - - } - - } - - _needsUpdate( renderObject ) { - - const cache = this._getCache( renderObject ); - const material = renderObject.material; - - let needsUpdate = this.backend.needsUpdate( renderObject ); - - // check pipeline state - - if ( cache.currentPipeline === undefined ) needsUpdate = true; - - // check material state - - if ( needsUpdate === true || - cache.material !== material || cache.materialVersion !== material.version || - cache.transparent !== material.transparent || cache.blending !== material.blending || cache.premultipliedAlpha !== material.premultipliedAlpha || - cache.blendSrc !== material.blendSrc || cache.blendDst !== material.blendDst || cache.blendEquation !== material.blendEquation || - cache.blendSrcAlpha !== material.blendSrcAlpha || cache.blendDstAlpha !== material.blendDstAlpha || cache.blendEquationAlpha !== material.blendEquationAlpha || - cache.colorWrite !== material.colorWrite || - cache.depthWrite !== material.depthWrite || cache.depthTest !== material.depthTest || cache.depthFunc !== material.depthFunc || - cache.stencilWrite !== material.stencilWrite || cache.stencilFunc !== material.stencilFunc || - cache.stencilFail !== material.stencilFail || cache.stencilZFail !== material.stencilZFail || cache.stencilZPass !== material.stencilZPass || - cache.stencilFuncMask !== material.stencilFuncMask || cache.stencilWriteMask !== material.stencilWriteMask || - cache.side !== material.side - ) { - - cache.material = material; cache.materialVersion = material.version; - cache.transparent = material.transparent; cache.blending = material.blending; cache.premultipliedAlpha = material.premultipliedAlpha; - cache.blendSrc = material.blendSrc; cache.blendDst = material.blendDst; cache.blendEquation = material.blendEquation; - cache.blendSrcAlpha = material.blendSrcAlpha; cache.blendDstAlpha = material.blendDstAlpha; cache.blendEquationAlpha = material.blendEquationAlpha; - cache.colorWrite = material.colorWrite; - cache.depthWrite = material.depthWrite; cache.depthTest = material.depthTest; cache.depthFunc = material.depthFunc; - cache.stencilWrite = material.stencilWrite; cache.stencilFunc = material.stencilFunc; - cache.stencilFail = material.stencilFail; cache.stencilZFail = material.stencilZFail; cache.stencilZPass = material.stencilZPass; - cache.stencilFuncMask = material.stencilFuncMask; cache.stencilWriteMask = material.stencilWriteMask; - cache.side = material.side; - - needsUpdate = true; - - } - - return needsUpdate; - - } - -} - -export default RenderPipelines; From 4235b2a21ee5b32e5d54fce611c7b3937979e17d Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 13:11:22 -0300 Subject: [PATCH 21/26] cleanup --- examples/jsm/renderers/common/Backend.js | 8 ++++---- examples/jsm/renderers/common/BufferUtils.js | 4 ++-- examples/jsm/renderers/common/Constants.js | 2 +- examples/jsm/renderers/common/Renderer.js | 6 ++++-- examples/jsm/renderers/common/UniformsGroup.js | 10 +++++----- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/jsm/renderers/common/Backend.js b/examples/jsm/renderers/common/Backend.js index 128ea9a870af01..9a6e2635a41e81 100644 --- a/examples/jsm/renderers/common/Backend.js +++ b/examples/jsm/renderers/common/Backend.js @@ -8,7 +8,7 @@ class Backend { constructor( parameters = {} ) { this.parameters = Object.assign( {}, parameters ); - this.datas = new WeakMap(); + this.data = new WeakMap(); this.renderer = null; this.domElement = null; @@ -136,12 +136,12 @@ class Backend { get( object ) { - let map = this.datas.get( object ); + let map = this.data.get( object ); if ( map === undefined ) { map = {}; - this.datas.set( object, map ); + this.data.set( object, map ); } @@ -151,7 +151,7 @@ class Backend { delete( object ) { - this.datas.delete( object ); + this.data.delete( object ); } diff --git a/examples/jsm/renderers/common/BufferUtils.js b/examples/jsm/renderers/common/BufferUtils.js index b3e82b3072819b..0cbdd4c773d3cc 100644 --- a/examples/jsm/renderers/common/BufferUtils.js +++ b/examples/jsm/renderers/common/BufferUtils.js @@ -1,10 +1,10 @@ -import { GPUChunkBytes } from './Constants.js'; +import { GPU_CHUNK_BYTES } from './Constants.js'; function getFloatLength( floatLength ) { // ensure chunk size alignment (STD140 layout) - return floatLength + ( ( GPUChunkBytes - ( floatLength % GPUChunkBytes ) ) % GPUChunkBytes ); + return floatLength + ( ( GPU_CHUNK_BYTES - ( floatLength % GPU_CHUNK_BYTES ) ) % GPU_CHUNK_BYTES ); } diff --git a/examples/jsm/renderers/common/Constants.js b/examples/jsm/renderers/common/Constants.js index 4e15efefbf7220..034510a9994abe 100644 --- a/examples/jsm/renderers/common/Constants.js +++ b/examples/jsm/renderers/common/Constants.js @@ -6,7 +6,7 @@ export const AttributeType = { // size of a chunk in bytes (STD140 layout) -export const GPUChunkBytes = 16; +export const GPU_CHUNK_BYTES = 16; // @TODO: Move to src/constants.js diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index 10a9487e378129..38e64bf04d005b 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -190,11 +190,13 @@ class Renderer { let viewport = this._viewport; let scissor = this._scissor; + let pixelRatio = this._pixelRatio; if ( renderTarget !== null ) { viewport = renderTarget.viewport; scissor = renderTarget.scissor; + pixelRatio = 1; } @@ -205,12 +207,12 @@ class Renderer { const minDepth = ( viewport.minDepth === undefined ) ? 0 : viewport.minDepth; const maxDepth = ( viewport.maxDepth === undefined ) ? 1 : viewport.maxDepth; - renderContext.viewportValue.copy( viewport ).multiplyScalar( this._pixelRatio ).floor(); + renderContext.viewportValue.copy( viewport ).multiplyScalar( pixelRatio ).floor(); renderContext.viewportValue.minDepth = minDepth; renderContext.viewportValue.maxDepth = maxDepth; renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false; - renderContext.scissorValue.copy( scissor ).multiplyScalar( this._pixelRatio ).floor(); + renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor(); renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false; // diff --git a/examples/jsm/renderers/common/UniformsGroup.js b/examples/jsm/renderers/common/UniformsGroup.js index fad4c4acde34f7..58450ebb0f98df 100644 --- a/examples/jsm/renderers/common/UniformsGroup.js +++ b/examples/jsm/renderers/common/UniformsGroup.js @@ -1,5 +1,5 @@ import UniformBuffer from './UniformBuffer.js'; -import { GPUChunkBytes } from './Constants.js'; +import { GPU_CHUNK_BYTES } from './Constants.js'; class UniformsGroup extends UniformBuffer { @@ -65,8 +65,8 @@ class UniformsGroup extends UniformBuffer { // offset within a single chunk in bytes - const chunkOffset = offset % GPUChunkBytes; - const remainingSizeInChunk = GPUChunkBytes - chunkOffset; + const chunkOffset = offset % GPU_CHUNK_BYTES; + const remainingSizeInChunk = GPU_CHUNK_BYTES - chunkOffset; // conformance tests @@ -74,7 +74,7 @@ class UniformsGroup extends UniformBuffer { // check for chunk overflow - offset += ( GPUChunkBytes - chunkOffset ); + offset += ( GPU_CHUNK_BYTES - chunkOffset ); } else if ( chunkOffset % uniform.boundary !== 0 ) { @@ -90,7 +90,7 @@ class UniformsGroup extends UniformBuffer { } - return Math.ceil( offset / GPUChunkBytes ) * GPUChunkBytes; + return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES; } From 47f1e595fdb939bca86d8be3a7933edc9139f5db Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 13:37:02 -0300 Subject: [PATCH 22/26] fix resize --- examples/jsm/renderers/common/SampledTexture.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/SampledTexture.js b/examples/jsm/renderers/common/SampledTexture.js index f7956fe0e290ef..468815fbcd23c6 100644 --- a/examples/jsm/renderers/common/SampledTexture.js +++ b/examples/jsm/renderers/common/SampledTexture.js @@ -21,7 +21,7 @@ class SampledTexture extends Binding { const { texture, version } = this; - return texture.isVideoTexture ? true : version === 0 && texture.version > 0; + return texture.isVideoTexture ? true : version !== texture.version; // version === 0 && texture.version > 0 } From 3ec909b4b91a0ace394806023bb483be091272ff Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 13:38:57 -0300 Subject: [PATCH 23/26] update tip --- examples/jsm/renderers/common/SampledTexture.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/SampledTexture.js b/examples/jsm/renderers/common/SampledTexture.js index 468815fbcd23c6..1f386c915aac7f 100644 --- a/examples/jsm/renderers/common/SampledTexture.js +++ b/examples/jsm/renderers/common/SampledTexture.js @@ -21,7 +21,7 @@ class SampledTexture extends Binding { const { texture, version } = this; - return texture.isVideoTexture ? true : version !== texture.version; // version === 0 && texture.version > 0 + return texture.isVideoTexture ? true : version !== texture.version; // @TODO: version === 0 && texture.version > 0 ( need add it just to External Textures like PNG,JPG ) } From 9dd309948d1e9a5cac3583ba31f488c49d23ac33 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 13:39:18 -0300 Subject: [PATCH 24/26] update tip --- examples/jsm/renderers/common/SampledTexture.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/renderers/common/SampledTexture.js b/examples/jsm/renderers/common/SampledTexture.js index 1f386c915aac7f..e08215d4eceaea 100644 --- a/examples/jsm/renderers/common/SampledTexture.js +++ b/examples/jsm/renderers/common/SampledTexture.js @@ -21,7 +21,7 @@ class SampledTexture extends Binding { const { texture, version } = this; - return texture.isVideoTexture ? true : version !== texture.version; // @TODO: version === 0 && texture.version > 0 ( need add it just to External Textures like PNG,JPG ) + return texture.isVideoTexture ? true : version !== texture.version; // @TODO: version === 0 && texture.version > 0 ( add it just to External Textures like PNG,JPG ) } From a93dc620a9aa1a3ed218fae64af56c6b2c530b86 Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 13:46:38 -0300 Subject: [PATCH 25/26] Rename: webgpu/builder to webgpu/nodes --- examples/jsm/renderers/webgpu/WebGPUBackend.js | 2 +- .../jsm/renderers/webgpu/{builder => nodes}/WGSLNodeBuilder.js | 0 .../jsm/renderers/webgpu/{builder => nodes}/WGSLNodeFunction.js | 0 .../jsm/renderers/webgpu/{builder => nodes}/WGSLNodeParser.js | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename examples/jsm/renderers/webgpu/{builder => nodes}/WGSLNodeBuilder.js (100%) rename examples/jsm/renderers/webgpu/{builder => nodes}/WGSLNodeFunction.js (100%) rename examples/jsm/renderers/webgpu/{builder => nodes}/WGSLNodeParser.js (100%) diff --git a/examples/jsm/renderers/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js index 6c4867e782cd53..6eddba7ae0406f 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js @@ -1,6 +1,6 @@ import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat } from './utils/WebGPUConstants.js'; -import WebGPUNodeBuilder from './builder/WGSLNodeBuilder.js'; +import WebGPUNodeBuilder from './nodes/WGSLNodeBuilder.js'; import Backend from '../common/Backend.js'; import { Matrix4, Frustum, DepthFormat } from 'three'; diff --git a/examples/jsm/renderers/webgpu/builder/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js similarity index 100% rename from examples/jsm/renderers/webgpu/builder/WGSLNodeBuilder.js rename to examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js diff --git a/examples/jsm/renderers/webgpu/builder/WGSLNodeFunction.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js similarity index 100% rename from examples/jsm/renderers/webgpu/builder/WGSLNodeFunction.js rename to examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js diff --git a/examples/jsm/renderers/webgpu/builder/WGSLNodeParser.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js similarity index 100% rename from examples/jsm/renderers/webgpu/builder/WGSLNodeParser.js rename to examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js From 378e09df6d102120bb6d4c15d0fcf2049417bafa Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 23 May 2023 14:02:26 -0300 Subject: [PATCH 26/26] cleanup --- examples/jsm/renderers/common/DataMap.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/jsm/renderers/common/DataMap.js b/examples/jsm/renderers/common/DataMap.js index e08900296cccf6..3c03b6c876d5e1 100644 --- a/examples/jsm/renderers/common/DataMap.js +++ b/examples/jsm/renderers/common/DataMap.js @@ -2,18 +2,18 @@ class DataMap { constructor() { - this.datas = new WeakMap(); + this.data = new WeakMap(); } get( object ) { - let map = this.datas.get( object ); + let map = this.data.get( object ); if ( map === undefined ) { map = {}; - this.datas.set( object, map ); + this.data.set( object, map ); } @@ -25,11 +25,11 @@ class DataMap { let map; - if ( this.datas.has( object ) ) { + if ( this.data.has( object ) ) { - map = this.datas.get( object ); + map = this.data.get( object ); - this.datas.delete( object ); + this.data.delete( object ); } @@ -39,13 +39,13 @@ class DataMap { has( object ) { - return this.datas.has( object ); + return this.data.has( object ); } dispose() { - this.datas.clear(); + this.data.clear(); }