From 9b26582be62cd808453a8d8bda49a6daf9c559f7 Mon Sep 17 00:00:00 2001 From: Renaud Rohlinger Date: Tue, 9 Jan 2024 08:47:47 +0900 Subject: [PATCH] WebGPURenderer: Add Offscreen Support (#27520) * add offscreen support * refactor statics webgpu * navigator.gpu should be enough for initial check * fix webgl context ktx2loader * use isAvailable in webgpurenderer * cleanup and should fix pupeeter * return promise * feedbacks * remove unecessary async * removed oversight * cleanup --------- --- examples/jsm/capabilities/WebGPU.js | 22 +++++++------ examples/jsm/loaders/KTX2Loader.js | 15 +++++++++ examples/jsm/renderers/common/Backend.js | 2 ++ examples/jsm/renderers/common/Renderer.js | 6 ++++ examples/jsm/renderers/webgl/WebGLBackend.js | 6 ++++ .../jsm/renderers/webgpu/WebGPUBackend.js | 31 +++++++++++-------- .../jsm/renderers/webgpu/WebGPURenderer.js | 3 +- .../renderers/webgpu/nodes/WGSLNodeBuilder.js | 2 +- examples/webgpu_instance_mesh.html | 2 +- examples/webgpu_loader_gltf_compressed.html | 4 +-- examples/webgpu_morphtargets_face.html | 6 ++-- examples/webgpu_sandbox.html | 4 +-- 12 files changed, 71 insertions(+), 32 deletions(-) diff --git a/examples/jsm/capabilities/WebGPU.js b/examples/jsm/capabilities/WebGPU.js index 3587718e259ccb..53397f44f5cc4a 100644 --- a/examples/jsm/capabilities/WebGPU.js +++ b/examples/jsm/capabilities/WebGPU.js @@ -1,20 +1,17 @@ -if ( window.GPUShaderStage === undefined ) { +if ( self.GPUShaderStage === undefined ) { - window.GPUShaderStage = { VERTEX: 1, FRAGMENT: 2, COMPUTE: 4 }; + self.GPUShaderStage = { VERTEX: 1, FRAGMENT: 2, COMPUTE: 4 }; } -let isAvailable = false; +// statics -if ( navigator.gpu !== undefined ) { +let isAvailable = navigator.gpu !== undefined; - const adapter = await navigator.gpu.requestAdapter(); - if ( adapter !== null ) { +if ( typeof window !== 'undefined' && isAvailable ) { - isAvailable = true; - - } + isAvailable = await navigator.gpu.requestAdapter(); } @@ -22,6 +19,12 @@ class WebGPU { static isAvailable() { + return Boolean( isAvailable ); + + } + + static getStaticAdapter() { + return isAvailable; } @@ -50,4 +53,5 @@ class WebGPU { } + export default WebGPU; diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index 69d343495e339a..64ea89132bace2 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -120,6 +120,21 @@ class KTX2Loader extends Loader { } + async detectSupportAsync( renderer ) { + + this.workerConfig = { + astcSupported: await renderer.hasFeatureAsync( 'texture-compression-astc' ), + etc1Supported: await renderer.hasFeatureAsync( 'texture-compression-etc1' ), + etc2Supported: await renderer.hasFeatureAsync( 'texture-compression-etc2' ), + dxtSupported: await renderer.hasFeatureAsync( 'texture-compression-bc' ), + bptcSupported: await renderer.hasFeatureAsync( 'texture-compression-bptc' ), + pvrtcSupported: await renderer.hasFeatureAsync( 'texture-compression-pvrtc' ) + }; + + return this; + + } + detectSupport( renderer ) { if ( renderer.isWebGPURenderer === true ) { diff --git a/examples/jsm/renderers/common/Backend.js b/examples/jsm/renderers/common/Backend.js index 5d6e24c6ae9fad..3a46be0ffa8fe6 100644 --- a/examples/jsm/renderers/common/Backend.js +++ b/examples/jsm/renderers/common/Backend.js @@ -90,6 +90,8 @@ class Backend { // utils + hasFeatureAsync( name ) { } // return Boolean + hasFeature( name ) { } // return Boolean getInstanceCount( renderObject ) { diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index 6f8d2b33a68b8e..d7f91a2804edaa 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -770,6 +770,12 @@ class Renderer { } + hasFeatureAsync( name ) { + + return this.backend.hasFeatureAsync( name ); + + } + hasFeature( name ) { return this.backend.hasFeature( name ); diff --git a/examples/jsm/renderers/webgl/WebGLBackend.js b/examples/jsm/renderers/webgl/WebGLBackend.js index f1a1217d825078..26b8ffccb5f990 100644 --- a/examples/jsm/renderers/webgl/WebGLBackend.js +++ b/examples/jsm/renderers/webgl/WebGLBackend.js @@ -837,6 +837,12 @@ class WebGLBackend extends Backend { } + async hasFeatureAsync( name ) { + + return this.hasFeature( name ); + + } + hasFeature( name ) { const keysMatching = Object.keys( GLFeatureName ).filter( key => GLFeatureName[ key ] === name ); diff --git a/examples/jsm/renderers/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js index 09bbe6127b9bb7..0b954b1a2c9b96 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js @@ -14,16 +14,7 @@ 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(); - -} +import WebGPU from '../../capabilities/WebGPU.js'; // @@ -1070,10 +1061,10 @@ class WebGPUBackend extends Backend { return 16; } + + async hasFeatureAsync( name ) { - hasFeature( name ) { - - const adapter = this.adapter || _staticAdapter; + const adapter = this.adapter || await WebGPU.getStaticAdapter(); // @@ -1081,6 +1072,20 @@ class WebGPUBackend extends Backend { } + hasFeature( name ) { + + if ( !this.adapter ) { + + console.warn( 'WebGPUBackend: WebGPU adapter has not been initialized yet. Please use detectSupportAsync instead' ); + + return false; + + } + + return this.adapter.features.has( name ); + + } + copyFramebufferToTexture( texture, renderContext ) { const renderContextData = this.get( renderContext ); diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 8c855e2ffa3b33..2bfcc908db58d2 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -1,7 +1,8 @@ +import WebGPU from '../../capabilities/WebGPU.js'; + import Renderer from '../common/Renderer.js'; import WebGLBackend from '../webgl/WebGLBackend.js'; import WebGPUBackend from './WebGPUBackend.js'; -import WebGPU from '../../capabilities/WebGPU.js'; /* const debugHandler = { diff --git a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js index a49e3f09069c8b..11ca8830809bf5 100644 --- a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -16,7 +16,7 @@ import { getFormat } from '../utils/WebGPUTextureUtils.js'; import WGSLNodeParser from './WGSLNodeParser.js'; // GPUShaderStage is not defined in browsers not supporting WebGPU -const GPUShaderStage = window.GPUShaderStage; +const GPUShaderStage = self.GPUShaderStage; const gpuShaderStageLib = { 'vertex': GPUShaderStage ? GPUShaderStage.VERTEX : 1, diff --git a/examples/webgpu_instance_mesh.html b/examples/webgpu_instance_mesh.html index c672657c934502..d90c55acf63649 100644 --- a/examples/webgpu_instance_mesh.html +++ b/examples/webgpu_instance_mesh.html @@ -44,7 +44,7 @@ init(); - function init() { + async function init() { if ( WebGPU.isAvailable() === false && WebGL.isWebGL2Available() === false ) { diff --git a/examples/webgpu_loader_gltf_compressed.html b/examples/webgpu_loader_gltf_compressed.html index 54d149cf72c79c..a51424f7dc6f4c 100644 --- a/examples/webgpu_loader_gltf_compressed.html +++ b/examples/webgpu_loader_gltf_compressed.html @@ -67,9 +67,9 @@ controls.maxDistance = 6; controls.update(); - const ktx2Loader = new KTX2Loader() + const ktx2Loader = await new KTX2Loader() .setTranscoderPath( 'jsm/libs/basis/' ) - .detectSupport( renderer ); + .detectSupportAsync( renderer ); const loader = new GLTFLoader(); loader.setKTX2Loader( ktx2Loader ); diff --git a/examples/webgpu_morphtargets_face.html b/examples/webgpu_morphtargets_face.html index 3cca8de03e5a8b..8575eb2edb2c33 100644 --- a/examples/webgpu_morphtargets_face.html +++ b/examples/webgpu_morphtargets_face.html @@ -48,7 +48,7 @@ init(); - function init() { + async function init() { if ( WebGPU.isAvailable() === false && WebGL.isWebGL2Available() === false ) { @@ -79,9 +79,9 @@ container.appendChild( renderer.domElement ); - const ktx2Loader = new KTX2Loader() + const ktx2Loader = await new KTX2Loader() .setTranscoderPath( 'jsm/libs/basis/' ) - .detectSupport( renderer ); + .detectSupportAsync( renderer ); new GLTFLoader() .setKTX2Loader( ktx2Loader ) diff --git a/examples/webgpu_sandbox.html b/examples/webgpu_sandbox.html index 89b5d291d2a9bc..f8e81690c1831b 100644 --- a/examples/webgpu_sandbox.html +++ b/examples/webgpu_sandbox.html @@ -74,9 +74,9 @@ textureDisplace.wrapS = THREE.RepeatWrapping; textureDisplace.wrapT = THREE.RepeatWrapping; - const ktxLoader = new KTX2Loader() + const ktxLoader = await new KTX2Loader() .setTranscoderPath( 'jsm/libs/basis/' ) - .detectSupport( renderer ); + .detectSupportAsync( renderer ); const ktxTexture = await ktxLoader.loadAsync( './textures/compressed/sample_uastc_zstd.ktx2' );