فهرست منبع

WebGPURenderer: Initial support for S3TC texture compression.

Mugen87 4 سال پیش
والد
کامیت
cd95aa4ea6
3فایلهای تغییر یافته به همراه137 افزوده شده و 37 حذف شده
  1. 120 33
      examples/jsm/renderers/webgpu/WebGPUTextures.js
  2. 1 1
      examples/jsm/renderers/webgpu/constants.js
  3. 16 3
      examples/webgpu_sandbox.html

+ 120 - 33
examples/jsm/renderers/webgpu/WebGPUTextures.js

@@ -1,5 +1,7 @@
 import { GPUTextureFormat, GPUAddressMode, GPUFilterMode } from './constants.js';
-import { Texture, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, RepeatWrapping, MirroredRepeatWrapping, FloatType, HalfFloatType } from '../../../../build/three.module.js';
+import { Texture, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, RepeatWrapping, MirroredRepeatWrapping,
+	RGBFormat, RGBAFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType
+} from '../../../../build/three.module.js';
 import WebGPUTextureUtils from './WebGPUTextureUtils.js';
 
 class WebGPUTextures {
@@ -279,17 +281,50 @@ class WebGPUTextures {
 
 	}
 
-	_convertFormat( type ) {
+	_convertFormat( format, type ) {
 
-		let formatGPU = GPUTextureFormat.RGBA8Unorm;
+		let formatGPU;
 
-		if ( type === FloatType ) {
+		switch ( format ) {
 
-			formatGPU = GPUTextureFormat.RGBA32Float;
+			case RGBA_S3TC_DXT1_Format:
+				formatGPU = GPUTextureFormat.BC1RGBAUnorm;
+				break;
 
-		} else if ( type === HalfFloatType ) {
+			case RGBA_S3TC_DXT3_Format:
+				formatGPU = GPUTextureFormat.BC2RGBAUnorm;
+				break;
 
-			formatGPU = GPUTextureFormat.RGBA16Float;
+			case RGBA_S3TC_DXT5_Format:
+				formatGPU = GPUTextureFormat.BC3RGBAUnorm;
+				break;
+
+			case RGBFormat:
+			case RGBAFormat:
+
+				switch ( type ) {
+
+					case UnsignedByteType:
+						formatGPU = GPUTextureFormat.RGBA8Unorm;
+						break;
+
+					case FloatType:
+						formatGPU = GPUTextureFormat.RGBA32Float;
+						break;
+
+					case HalfFloatType:
+						formatGPU = GPUTextureFormat.RGBA16Float;
+						break;
+
+					default:
+						console.error( 'WebGPURenderer: Unsupported texture type with RGBAFormat.', type );
+
+				}
+
+				break;
+
+			default:
+				console.error( 'WebGPURenderer: Unsupported texture format.', format );
 
 		}
 
@@ -305,9 +340,26 @@ class WebGPUTextures {
 		const width = ( image !== undefined ) ? image.width : 1;
 		const height = ( image !== undefined ) ? image.height : 1;
 
-		const format = this._convertFormat( texture.type );
-		const needsMipmaps = this._needsMipmaps( texture );
-		const mipLevelCount = ( needsMipmaps === true ) ? this._computeMipLevelCount( width, height ) : undefined;
+		const format = this._convertFormat( texture.format, texture.type );
+
+		let needsMipmaps;
+		let mipLevelCount;
+
+		if ( texture.isCompressedTexture ) {
+
+			mipLevelCount = texture.mipmaps.length;
+
+		} else {
+
+			needsMipmaps = this._needsMipmaps( texture );
+
+			if ( needsMipmaps === true ) {
+
+				mipLevelCount = this._computeMipLevelCount( width, height );
+
+			}
+
+		}
 
 		let usage = GPUTextureUsage.SAMPLED | GPUTextureUsage.COPY_DST;
 
@@ -337,6 +389,10 @@ class WebGPUTextures {
 
 			this._copyBufferToTexture( image, format, textureGPU );
 
+		} else if ( texture.isCompressedTexture ) {
+
+			this._copyCompressedTextureDataToTexture( texture.mipmaps, format, textureGPU );
+
 		} else {
 
 			// convert HTML iamges and canvas elements to ImageBitmap before copy
@@ -375,42 +431,30 @@ class WebGPUTextures {
 
 	_copyBufferToTexture( image, format, textureGPU ) {
 
-		// this code assumes data textures in RGBA format
-
+		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
 		// @TODO: Consider to support valid buffer layouts with other formats like RGB
-		// @TODO: Support mipmaps
 
-		const device = this.device;
 		const data = image.data;
 
 		const bytesPerTexel = this._getBytesPerTexel( format );
 		const bytesPerRow = Math.ceil( image.width * bytesPerTexel / 256 ) * 256;
 
-		const textureDataBuffer = device.createBuffer( {
-			size: data.byteLength,
-			usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
-			mappedAtCreation: true,
-		} );
-
-		new data.constructor( textureDataBuffer.getMappedRange() ).set( data );
-		textureDataBuffer.unmap();
-
-		const commandEncoder = device.createCommandEncoder( {} );
-		commandEncoder.copyBufferToTexture(
+		this.device.defaultQueue.writeTexture(
+			{
+				texture: textureGPU,
+				mipLevel: 0 // @TODO: Support mipmaps
+			},
+			data,
+			{
+				offset: 0,
+				bytesPerRow
+			},
 			{
-				buffer: textureDataBuffer,
-				bytesPerRow: bytesPerRow
-			}, {
-				texture: textureGPU
-			}, {
 				width: image.width,
 				height: image.height,
 				depth: 1
 			} );
 
-		device.defaultQueue.submit( [ commandEncoder.finish() ] );
-		textureDataBuffer.destroy();
-
 	}
 
 	_copyImageBitmapToTexture( imageBitmap, textureGPU, needsMipmaps, textureGPUDescriptor ) {
@@ -443,6 +487,41 @@ class WebGPUTextures {
 
 	}
 
+	_copyCompressedTextureDataToTexture( mipmaps, format, textureGPU ) {
+
+		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
+
+		const blockData = this._getBlockData( format );
+
+		for ( let i = 0; i < mipmaps.length; i ++ ) {
+
+			const mipmap = mipmaps[ i ];
+
+			const width = mipmap.width;
+			const height = mipmap.height;
+
+			const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength;
+
+			this.device.defaultQueue.writeTexture(
+				{
+					texture: textureGPU,
+					mipLevel: i
+				},
+				mipmap.data,
+				{
+					offset: 0,
+					bytesPerRow
+				},
+				{
+					width: Math.ceil( width / blockData.width ) * blockData.width,
+					height: Math.ceil( height / blockData.width ) * blockData.width,
+					depth: 1,
+				} );
+
+		}
+
+	}
+
 	_getBytesPerTexel( format ) {
 
 		if ( format === GPUTextureFormat.RGBA8Unorm ) return 4;
@@ -451,6 +530,14 @@ class WebGPUTextures {
 
 	}
 
+	_getBlockData( format ) {
+
+		if ( format === GPUTextureFormat.BC1RGBAUnorm ) return { byteLength: 8, width: 4, height: 4 };
+		if ( format === GPUTextureFormat.BC2RGBAUnorm ) return { byteLength: 16, width: 4, height: 4 };
+		if ( format === GPUTextureFormat.BC3RGBAUnorm ) return { byteLength: 16, width: 4, height: 4 };
+
+	}
+
 	_needsMipmaps( texture ) {
 
 		return ( texture.generateMipmaps === true ) && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );

+ 1 - 1
examples/jsm/renderers/webgpu/constants.js

@@ -220,7 +220,7 @@ export const GPUStencilOperation = {
 	DecrementWrap: 'decrement-wrap'
 };
 
-// @TODO Move to src/constants.js
+// @TODO: Move to src/constants.js
 
 export const BlendColorFactor = 211;
 export const OneMinusBlendColorFactor = 212;

+ 16 - 3
examples/webgpu_sandbox.html

@@ -14,6 +14,8 @@
 
 			import * as THREE from '../build/three.module.js';
 
+			import { DDSLoader } from './jsm/loaders/DDSLoader.js';
+
 			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
 			import WebGPU from './jsm/renderers/webgpu/WebGPU.js';
 
@@ -41,8 +43,8 @@
 
 				// textured mesh
 
-				const loader = new THREE.TextureLoader();
-				const texture = loader.load( './textures/uv_grid_opengl.jpg' );
+				const textureLoader = new THREE.TextureLoader();
+				const texture = textureLoader.load( './textures/uv_grid_opengl.jpg' );
 
 				const geometryBox = new THREE.BoxBufferGeometry();
 				const materialBox = new THREE.MeshBasicMaterial( { map: texture } );
@@ -60,6 +62,17 @@
 				plane.position.set( - 1, - 1, 0 );
 				scene.add( plane );
 
+				// compress texture
+
+				const ddsLoader = new DDSLoader();
+				const dxt5Texture = ddsLoader.load( './textures/compressed/explosion_dxt5_mip.dds' );
+
+				const materialBoxCompressed = new THREE.MeshBasicMaterial( { map: dxt5Texture, transparent: true } );
+
+				box = new THREE.Mesh( geometryBox, materialBoxCompressed );
+				box.position.set( - 3, 1, 0 );
+				scene.add( box );
+
 				// points
 
 				const geometryPoints = new THREE.BufferGeometry().setFromPoints( [
@@ -88,7 +101,7 @@
 
 				//
 
-				renderer = new WebGPURenderer();
+				renderer = new WebGPURenderer( { extensions: [ 'texture-compression-bc' ] } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				document.body.appendChild( renderer.domElement );