ソースを参照

WebGPURenderer: Support KTX Compressed texture in the WebGL Backend (#27463)

* wip

* cleanup webgl backend

* cleanup and add silentError

* cleanup WebGLTextureUtils

* move textures to textureutils

* remove handling error in hasFeatre
Renaud Rohlinger 1 年間 前
コミット
3f19a059c8

+ 3 - 3
examples/jsm/loaders/KTX2Loader.js

@@ -126,11 +126,11 @@ class KTX2Loader extends Loader {
 
 			this.workerConfig = {
 				astcSupported: renderer.hasFeature( 'texture-compression-astc' ),
-				etc1Supported: false,
+				etc1Supported: renderer.hasFeature( 'texture-compression-etc1' ),
 				etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ),
 				dxtSupported: renderer.hasFeature( 'texture-compression-bc' ),
-				bptcSupported: false,
-				pvrtcSupported: false
+				bptcSupported: renderer.hasFeature( 'texture-compression-bptc' ),
+				pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' )
 			};
 
 		} else {

+ 31 - 143
examples/jsm/renderers/webgl/WebGLBackend.js

@@ -9,7 +9,7 @@ import WebGLUtils from './utils/WebGLUtils.js';
 import WebGLTextureUtils from './utils/WebGLTextureUtils.js';
 import WebGLExtensions from './utils/WebGLExtensions.js';
 import WebGLCapabilities from './utils/WebGLCapabilities.js';
-
+import { GLFeatureName } from './utils/WebGLConstants.js';
 //
 
 class WebGLBackend extends Backend {
@@ -22,9 +22,9 @@ class WebGLBackend extends Backend {
 
 	}
 
-	async init( renderer ) {
+	init( renderer ) {
 
-		await super.init( renderer );
+		super.init( renderer );
 
 		//
 
@@ -40,7 +40,6 @@ class WebGLBackend extends Backend {
 		this.textureUtils = new WebGLTextureUtils( this );
 		this.state = new WebGLState( this );
 		this.utils = new WebGLUtils( this );
-		this.defaultTextures = {};
 
 		this.extensions.get( 'EXT_color_buffer_float' );
 		this._currentContext = null;
@@ -445,7 +444,7 @@ class WebGLBackend extends Backend {
 
 	}
 
-	needsRenderUpdate( renderObject ) {
+	needsRenderUpdate( /*renderObject*/ ) {
 
 		return false;
 
@@ -459,173 +458,50 @@ class WebGLBackend extends Backend {
 
 	// textures
 
-	createSampler( /*texture*/ ) {
-
-		//console.warn( 'Abstract class.' );
-
-	}
-
 	createDefaultTexture( texture ) {
 
-		const { gl, textureUtils, defaultTextures } = this;
-
-		const glTextureType = textureUtils.getGLTextureType( texture );
-
-		let textureGPU = defaultTextures[ glTextureType ];
-
-		if ( textureGPU === undefined ) {
-
-			textureGPU = gl.createTexture();
-
-			gl.bindTexture( glTextureType, textureGPU );
-			gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
-			gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
-
-			//gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
-
-			defaultTextures[ glTextureType ] = textureGPU;
-
-		}
-
-		this.set( texture, {
-			textureGPU,
-			glTextureType,
-			isDefault: true
-		} );
+		this.textureUtils.createDefaultTexture( texture );
 
 	}
 
 	createTexture( texture, options ) {
 
-		const { gl, utils, textureUtils } = this;
-		const { levels, width, height, depth } = options;
-
-		const glFormat = utils.convert( texture.format, texture.colorSpace );
-		const glType = utils.convert( texture.type );
-		const glInternalFormat = textureUtils.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );
-
-		const textureGPU = gl.createTexture();
-		const glTextureType = textureUtils.getGLTextureType( texture );
-
-		gl.bindTexture( glTextureType, textureGPU );
-
-		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
-		gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
-		gl.pixelStorei( gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
-		gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE );
-
-		textureUtils.setTextureParameters( glTextureType, texture );
-
-		gl.bindTexture( glTextureType, textureGPU );
-
-		if ( texture.isDataArrayTexture ) {
-
-			gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
-
-		} else if ( ! texture.isVideoTexture ) {
-
-			gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
-
-		}
-
-		this.set( texture, {
-			textureGPU,
-			glTextureType,
-			glFormat,
-			glType,
-			glInternalFormat
-		} );
+		this.textureUtils.createTexture( texture, options );
 
 	}
 
 	updateTexture( texture, options ) {
 
-		const { gl } = this;
-		const { width, height } = options;
-		const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.get( texture );
-
-		const getImage = ( source ) => {
-
-			if ( source.isDataTexture ) {
-
-				return source.image.data;
-
-			} else if ( source instanceof ImageBitmap || source instanceof OffscreenCanvas || source instanceof HTMLImageElement || source instanceof HTMLCanvasElement ) {
-
-				return source;
-
-			}
-
-			return source.data;
-
-		};
-
-		gl.bindTexture( glTextureType, textureGPU );
-
-		if ( texture.isCubeTexture ) {
-
-			const images = options.images;
-
-			for ( let i = 0; i < 6; i ++ ) {
-
-				const image = getImage( images[ i ] );
-
-				gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image );
-
-			}
-
-		} else if ( texture.isDataArrayTexture ) {
-
-			const image = options.image;
-
-			gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
-
-		} else if ( texture.isVideoTexture ) {
-
-			texture.update();
-
-			gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
-
-
-		} else {
-
-			const image = getImage( options.image );
-
-			gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image );
-
-		}
+		this.textureUtils.updateTexture( texture, options );
 
 	}
 
 	generateMipmaps( texture ) {
 
-		const { gl } = this;
-		const { textureGPU, glTextureType } = this.get( texture );
-
-		gl.bindTexture( glTextureType, textureGPU );
-		gl.generateMipmap( glTextureType );
+		this.textureUtils.generateMipmaps( texture );
 
 	}
 
 	destroyTexture( texture ) {
 
-		const { gl } = this;
-		const { textureGPU } = this.get( texture );
-
-		gl.deleteTexture( textureGPU );
-
-		this.delete( texture );
+		this.textureUtils.destroyTexture( texture );
 
 	}
 
-	destroySampler() {}
-
 	copyTextureToBuffer( texture, x, y, width, height ) {
 
 		return this.textureUtils.copyTextureToBuffer( texture, x, y, width, height );
 
 	}
 
+	createSampler( /*texture*/ ) {
+
+		//console.warn( 'Abstract class.' );
+
+	}
+
+	destroySampler() {}
+
 	// node builder
 
 	createNodeBuilder( object, renderer, scene = null ) {
@@ -892,12 +768,24 @@ class WebGLBackend extends Backend {
 
 	}
 
-	hasFeature( /*name*/ ) {
+	hasFeature( name ) {
+
+		const keysMatching = Object.keys( GLFeatureName ).filter( key => GLFeatureName[ key ] === name );
+
+		const extensions = this.extensions;
+
+		for ( let i = 0; i < keysMatching.length; i ++ ) {
+
 
-		return true;
+			if ( extensions.has( keysMatching[ i ] ) ) return true;
+
+		}
+
+		return false;
 
 	}
 
+
 	getMaxAnisotropy() {
 
 		return this.capabilities.getMaxAnisotropy();

+ 11 - 0
examples/jsm/renderers/webgl/utils/WebGLConstants.js

@@ -0,0 +1,11 @@
+export const GLFeatureName = {
+
+	'WEBGL_compressed_texture_astc': 'texture-compression-astc',
+	'WEBGL_compressed_texture_etc': 'texture-compression-etc2',
+	'WEBGL_compressed_texture_etc1': 'texture-compression-etc1',
+	'WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc',
+	'WEBKIT_WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc',
+	'WEBGL_compressed_texture_s3tc': 'texture-compression-bc',
+	'EXT_texture_compression_bptc': 'texture-compression-bptc',
+
+};

+ 205 - 0
examples/jsm/renderers/webgl/utils/WebGLTextureUtils.js

@@ -10,6 +10,7 @@ class WebGLTextureUtils {
 
 		this.gl = backend.gl;
 		this.extensions = backend.extensions;
+		this.defaultTextures = {};
 
 		if ( initialized === false ) {
 
@@ -211,6 +212,210 @@ class WebGLTextureUtils {
 
 	}
 
+	createDefaultTexture( texture ) {
+
+		const { gl, backend, defaultTextures } = this;
+
+
+		const glTextureType = this.getGLTextureType( texture );
+
+		let textureGPU = defaultTextures[ glTextureType ];
+
+		if ( textureGPU === undefined ) {
+
+			textureGPU = gl.createTexture();
+
+			gl.bindTexture( glTextureType, textureGPU );
+			gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+			gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+
+			//gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
+
+			defaultTextures[ glTextureType ] = textureGPU;
+
+		}
+
+		backend.set( texture, {
+			textureGPU,
+			glTextureType,
+			isDefault: true
+		} );
+
+	}
+
+	createTexture( texture, options ) {
+
+		const { gl, backend } = this;
+		const { levels, width, height, depth } = options;
+
+		const glFormat = backend.utils.convert( texture.format, texture.colorSpace );
+		const glType = backend.utils.convert( texture.type );
+		const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );
+
+		const textureGPU = gl.createTexture();
+		const glTextureType = this.getGLTextureType( texture );
+
+		gl.bindTexture( glTextureType, textureGPU );
+
+		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+		gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
+		gl.pixelStorei( gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
+		gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE );
+
+		this.setTextureParameters( glTextureType, texture );
+
+		gl.bindTexture( glTextureType, textureGPU );
+
+		if ( texture.isDataArrayTexture ) {
+
+			gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
+
+		} else if ( ! texture.isVideoTexture ) {
+
+			gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
+
+		}
+
+		backend.set( texture, {
+			textureGPU,
+			glTextureType,
+			glFormat,
+			glType,
+			glInternalFormat
+		} );
+
+	}
+
+	updateTexture( texture, options ) {
+
+		const { gl } = this;
+		const { width, height } = options;
+		const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.backend.get( texture );
+
+		if ( texture.isRenderTargetTexture || ( textureGPU === undefined /* unsupported texture format */ ) )
+			return;
+
+		const getImage = ( source ) => {
+
+			if ( source.isDataTexture ) {
+
+				return source.image.data;
+
+			} else if ( source instanceof ImageBitmap || source instanceof OffscreenCanvas || source instanceof HTMLImageElement || source instanceof HTMLCanvasElement ) {
+
+				return source;
+
+			}
+
+			return source.data;
+
+		};
+
+		gl.bindTexture( glTextureType, textureGPU );
+
+		if ( texture.isCompressedTexture ) {
+
+			const mipmaps = texture.mipmaps;
+
+			for ( let i = 0; i < mipmaps.length; i ++ ) {
+
+				const mipmap = mipmaps[ i ];
+
+				if ( texture.isCompressedArrayTexture ) {
+
+					const image = options.image;
+
+					if ( texture.format !== gl.RGBA ) {
+
+						if ( glFormat !== null ) {
+
+							gl.compressedTexSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 );
+
+
+						} else {
+
+							console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
+
+						}
+
+					} else {
+
+						gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );
+
+					}
+
+				} else {
+
+					if ( glFormat !== null ) {
+
+						gl.compressedTexSubImage2D( gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );
+
+					} else {
+
+						console.warn( 'Unsupported compressed texture format' );
+
+					}
+
+				}
+
+			}
+
+		} else if ( texture.isCubeTexture ) {
+
+			const images = options.images;
+
+			for ( let i = 0; i < 6; i ++ ) {
+
+				const image = getImage( images[ i ] );
+
+				gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image );
+
+			}
+
+		} else if ( texture.isDataArrayTexture ) {
+
+			const image = options.image;
+
+			gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
+
+		} else if ( texture.isVideoTexture ) {
+
+			texture.update();
+
+			gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
+
+
+		} else {
+
+			const image = getImage( options.image );
+
+			gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image );
+
+		}
+
+	}
+
+	generateMipmaps( texture ) {
+
+		const { gl, backend } = this;
+		const { textureGPU, glTextureType } = backend.get( texture );
+
+		gl.bindTexture( glTextureType, textureGPU );
+		gl.generateMipmap( glTextureType );
+
+	}
+
+	destroyTexture( texture ) {
+
+		const { gl, backend } = this;
+		const { textureGPU } = backend.get( texture );
+
+		gl.deleteTexture( textureGPU );
+
+		backend.delete( texture );
+
+	}
+
 	async copyTextureToBuffer( texture, x, y, width, height ) {
 
 		const { backend, gl } = this;

+ 0 - 10
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -1077,16 +1077,6 @@ class WebGPUBackend extends Backend {
 
 		//
 
-		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 );
 
 	}

+ 0 - 9
examples/webgpu_loader_gltf_compressed.html

@@ -31,7 +31,6 @@
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
-			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
 
 			let camera, scene, renderer;
@@ -40,14 +39,6 @@
 
 			async function init() {
 
-				if ( WebGPU.isAvailable() === false ) {
-
-					document.body.appendChild( WebGPU.getErrorMessage() );
-
-					throw new Error( 'No WebGPU support' );
-
-				}
-
 				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 20 );
 				camera.position.set( 2, 2, 2 );