Skip to content

Commit

Permalink
WebGPURenderer: Initial support for S3TC texture compression.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mugen87 committed Sep 17, 2020
1 parent 1893c9d commit cd95aa4
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 37 deletions.
153 changes: 120 additions & 33 deletions examples/jsm/renderers/webgpu/WebGPUTextures.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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 );

}

Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ) {
Expand Down Expand Up @@ -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;
Expand All @@ -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 );
Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/renderers/webgpu/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
19 changes: 16 additions & 3 deletions examples/webgpu_sandbox.html
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 } );
Expand All @@ -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( [
Expand Down Expand Up @@ -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 );
Expand Down

0 comments on commit cd95aa4

Please sign in to comment.