From 0f5ecf68f189447af3f68b5b247db6888b436067 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Thu, 31 Oct 2019 12:24:51 +0100 Subject: [PATCH 01/13] WIP: Basis support --- h3d/impl/GlDriver.hx | 74 ++++++- hxd/PixelFormat.hx | 26 ++- hxd/Pixels.hx | 5 +- hxd/net/BinaryLoader.hx | 4 +- hxd/res/BasisTextureLoader.hx | 402 ++++++++++++++++++++++++++++++++++ 5 files changed, 494 insertions(+), 17 deletions(-) create mode 100644 hxd/res/BasisTextureLoader.hx diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 82dc241bb5..9dac702f54 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -127,6 +127,7 @@ class GlDriver extends Driver { static var UID = 0; public var gl : GL2; public static var ALLOW_WEBGL2 = true; + public var textureSupport:hxd.PixelFormat; #end #if (hlsdl||usegl) @@ -783,7 +784,10 @@ class GlDriver extends Driver { case GL2.RED, GL2.R8, GL2.R16F, GL2.R32F: GL2.RED; case GL2.RG, GL2.RG8, GL2.RG16F, GL2.RG32F: GL2.RG; case GL2.RGB16F, GL2.RGB32F: GL.RGB; - case 0x83F1, 0x83F2, 0x83F3: GL.RGBA; + case hxd.PixelFormat.DXT_FORMAT.RGBA_DXT1,hxd.PixelFormat.DXT_FORMAT.RGBA_DXT3, + hxd.PixelFormat.DXT_FORMAT.RGBA_DXT5,hxd.PixelFormat.ASTC_FORMAT.RGBA_4x4, + hxd.PixelFormat.PVRTC_FORMAT.RGBA_4BPPV1: GL.RGBA; + case hxd.PixelFormat.PVRTC_FORMAT.RGB_4BPPV1, hxd.PixelFormat.ETC_FORMAT.RGB_ETC1: GL.RGB; default: throw "Invalid format " + t.internalFmt; } } @@ -856,12 +860,29 @@ class GlDriver extends Driver { tt.internalFmt = GL2.R11F_G11F_B10F; tt.pixelFmt = GL2.UNSIGNED_INT_10F_11F_11F_REV; case S3TC(n) if( n <= maxCompressedTexturesSupport ): - if( t.width&3 != 0 || t.height&3 != 0 ) - throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + checkMult4(t); switch( n ) { - case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT - case 2: tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT - case 3: tt.internalFmt = 0x83F3; // COMPRESSED_RGBA_S3TC_DXT5_EXT + case 1: tt.internalFmt = hxd.PixelFormat.DXT_FORMAT.RGBA_DXT1; + case 2: tt.internalFmt = hxd.PixelFormat.DXT_FORMAT.RGBA_DXT3; + case 3: tt.internalFmt = hxd.PixelFormat.DXT_FORMAT.RGBA_DXT5; + default: throw "Unsupported texture format "+t.format; + } + case ASTC(n): + checkMult4(t); + switch( n ) { + case 10: tt.internalFmt = hxd.PixelFormat.ASTC_FORMAT.RGBA_4x4; + default: throw "Unsupported texture format "+t.format; + } + case ETC(n): + checkMult4(t); + switch( n ) { + case 0: tt.internalFmt = hxd.PixelFormat.ETC_FORMAT.RGB_ETC1; + default: throw "Unsupported texture format "+t.format; + } + case PVRTC(n): + checkMult4(t); + switch(n) { + case 9: tt.internalFmt = hxd.PixelFormat.PVRTC_FORMAT.RGBA_4BPPV1; default: throw "Unsupported texture format "+t.format; } default: @@ -908,6 +929,11 @@ class GlDriver extends Driver { return tt; } + inline function checkMult4(t) { + if( t.width&3 != 0 || t.height&3 != 0 ) + throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + } + function restoreBind() { var t = boundTextures[lastActiveIndex]; if( t == null ) @@ -1129,10 +1155,12 @@ class GlDriver extends Driver { case RGB10A2, RG11B10UF: new Uint32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, bufLen>>2); default: new Uint8Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, bufLen); } - if( t.format.match(S3TC(_)) ) - gl.compressedTexImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, buffer); - else - gl.texImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, getChannels(t.t), t.t.pixelFmt, buffer); + switch (t.format) { + case S3TC(_), ASTC(_), PVRTC(_), ETC(_): + gl.compressedTexImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, buffer); + default: + gl.texImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, getChannels(t.t), t.t.pixelFmt, buffer); + } #else throw "Not implemented"; #end @@ -1526,12 +1554,34 @@ class GlDriver extends Driver { } #if js + function checkTextureSupport():hxd.PixelFormat { + var astcSupported = gl.getExtension('WEBGL_compressed_texture_astc') != null; + var dxtSupported = gl.getExtension('WEBGL_compressed_texture_s3tc') != null; + var pvrtcSupported = gl.getExtension('WEBGL_compressed_texture_pvrtc') != null + || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc') != null; + var etcSupported = gl.getExtension('WEBGL_compressed_texture_etc1') != null; + + return if(astcSupported) { + hxd.PixelFormat.ASTC(); + } else if(dxtSupported){ + hxd.PixelFormat.S3TC(); + } else if(etcSupported){ + hxd.PixelFormat.ETC(); + } else if(pvrtcSupported){ + hxd.PixelFormat.PVRTC(); + } else { + null; + } + } var features : Map = new Map(); function makeFeatures() { for( f in Type.allEnums(Feature) ) features.set(f,checkFeature(f)); - if( gl.getExtension("WEBGL_compressed_texture_s3tc") != null ) - maxCompressedTexturesSupport = 3; + textureSupport = checkTextureSupport(); + maxCompressedTexturesSupport = switch (textureSupport) { + case hxd.PixelFormat.S3TC(_), hxd.PixelFormat.ASTC(_), hxd.PixelFormat.ETC(_), hxd.PixelFormat.PVRTC(_): 3; + default: 0; + } } function checkFeature( f : Feature ) { return switch( f ) { diff --git a/hxd/PixelFormat.hx b/hxd/PixelFormat.hx index c7ed0743e5..d9decd2439 100644 --- a/hxd/PixelFormat.hx +++ b/hxd/PixelFormat.hx @@ -19,5 +19,27 @@ enum PixelFormat { SRGB_ALPHA; RGB10A2; RG11B10UF; // unsigned float - S3TC( v : Int ); -} \ No newline at end of file + ASTC( ?v : Int ); + ETC( ?v : Int ); + S3TC( ?v : Int ); + PVRTC( ?v : Int); +} + +enum abstract ASTC_FORMAT(Int) from Int to Int { + final RGBA_4x4 = 0x93B0; +} + +enum abstract DXT_FORMAT(Int) from Int to Int { + final RGBA_DXT1 = 0x83F1; + final RGBA_DXT3 = 0x83F2; + final RGBA_DXT5 = 0x83F3; +} + +enum abstract ETC_FORMAT(Int) from Int to Int { + final RGB_ETC1 = 0x8D64; +} + +enum abstract PVRTC_FORMAT(Int) from Int to Int { + final RGB_4BPPV1 = 0x8C00; + final RGBA_4BPPV1 = 0x8C02; +} diff --git a/hxd/Pixels.hx b/hxd/Pixels.hx index 6d0168262a..d18d440513 100644 --- a/hxd/Pixels.hx +++ b/hxd/Pixels.hx @@ -420,6 +420,9 @@ class Pixels { case RGB32F: 12; case RGB10A2: 4; case RG11B10UF: 4; + case ASTC(n): 1; + case ETC(n): 0; + case PVRTC(n): 0; case S3TC(n): if( n == 1 || n == 4 ) return width >> 1; @@ -455,7 +458,7 @@ class Pixels { channel.toInt() * 4; case RGB10A2, RG11B10UF: throw "Bit packed format"; - case S3TC(_): + case S3TC(_), ASTC(_), ETC(_), PVRTC(_): throw "Not supported"; } } diff --git a/hxd/net/BinaryLoader.hx b/hxd/net/BinaryLoader.hx index c70dc23e21..8990bb9ddd 100644 --- a/hxd/net/BinaryLoader.hx +++ b/hxd/net/BinaryLoader.hx @@ -21,7 +21,7 @@ class BinaryLoader { throw msg; } - public function load() { + public function load(raw = false) { #if flash loader = new flash.net.URLLoader(); loader.dataFormat = flash.net.URLLoaderDataFormat.BINARY; @@ -43,7 +43,7 @@ class BinaryLoader { onError(xhr.statusText); return; } - onLoaded(haxe.io.Bytes.ofData(xhr.response)); + onLoaded(raw ? xhr.response : haxe.io.Bytes.ofData(xhr.response)); } xhr.onprogress = function(e) { diff --git a/hxd/res/BasisTextureLoader.hx b/hxd/res/BasisTextureLoader.hx new file mode 100644 index 0000000000..755c3d7077 --- /dev/null +++ b/hxd/res/BasisTextureLoader.hx @@ -0,0 +1,402 @@ +package hxd.res; + +import hxd.PixelFormat; +import js.html.ImageData; +import h3d.mat.Texture; +import h3d.impl.GlDriver; +import h3d.mat.Data; + +class BasisTextureLoader { + public static var workerLimit = 4; + public static var transcoderPath = 'vendor/basis_transcoder.js'; + public static var wasmPath = 'vendor/basis_transcoder.wasm'; + + static var _workerNextTaskID = 1; + static var _workerSourceURL:String; + static var _workerConfig = { + format: 0, + astcSupported: false, + etc1Supported: false, + etc2Supported: false, + dxtSupported: false, + pvrtcSupported: false, + }; + static var _workerPool:Array = []; + static var _transcoderPending:js.lib.Promise; + static var _transcoderBinary:Dynamic; + + public static function getTexture(bytes:haxe.io.BytesData) { + detectSupport(); + return createTexture(bytes); + } + + static function detectSupport() { + final driver:GlDriver = cast h3d.Engine.getCurrent().driver; + final context = driver.gl; + + final extASTC = context.getExtension('WEBGL_compressed_texture_astc'); + final extETC1 = context.getExtension('WEBGL_compressed_texture_etc1'); + final extDXT = context.getExtension('WEBGL_compressed_texture_s3tc'); + final extPVRTC = context.getExtension('WEBGL_compressed_texture_pvrtc') != null ? context.getExtension('WEBGL_compressed_texture_pvrtc') : context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); + + _workerConfig.astcSupported = extASTC != null; + _workerConfig.etc1Supported = extETC1 != null; + _workerConfig.dxtSupported = extDXT != null; + _workerConfig.pvrtcSupported = extPVRTC != null; + + if (_workerConfig.etc1Supported) { + _workerConfig.format = BASIS_FORMAT.cTFETC1; + } else if (_workerConfig.astcSupported) { + _workerConfig.format = BASIS_FORMAT.cTFASTC_4x4; + } else if (_workerConfig.dxtSupported) { + _workerConfig.format = BASIS_FORMAT.cTFBC3; + } else if (_workerConfig.pvrtcSupported) { + _workerConfig.format = BASIS_FORMAT.cTFPVRTC1_4_RGBA; + } else { + throw 'No suitable compressed texture format found.'; + }; + } + + static function createTexture(buffer:haxe.io.BytesData):js.lib.Promise { + var worker:js.html.Worker; + var workerTask:WorkerTask; + var taskID:Int; + var texturePending = getWorker().then((task) -> { + workerTask = task; + worker = workerTask.worker; + taskID = _workerNextTaskID++; + return new js.lib.Promise((resolve, reject) -> { + workerTask.callbacks.set(taskID, { + resolve: resolve, + reject: reject, + }); + workerTask.taskCosts.set(taskID, buffer.byteLength); + workerTask.taskLoad += workerTask.taskCosts.get(taskID); + worker.postMessage({type: 'transcode', id: taskID, buffer: buffer}, [buffer]); + }); + }).then((message) -> { + final w = message.width; + final h = message.height; + final mipmaps:Array = message.mipmaps; + final format = message.format; + final create = (fmt) -> { + final texture = new h3d.mat.Texture(w, h, null, fmt); + var level = 0; + for (mipmap in mipmaps) { + final pixels = new hxd.Pixels(mipmap.width, mipmap.height, haxe.io.Bytes.ofData(cast mipmap.data), fmt); + texture.uploadPixels(pixels, level); + level++; + } + return texture; + } + var texture:h3d.mat.Texture; + switch (format) { + case BASIS_FORMAT.cTFASTC_4x4: + texture = create(hxd.PixelFormat.ASTC(format)); + case BASIS_FORMAT.cTFBC1, BASIS_FORMAT.cTFBC3: + texture = create(hxd.PixelFormat.S3TC(format)); + case BASIS_FORMAT.cTFETC1: + texture = create(hxd.PixelFormat.ETC(format)); + case BASIS_FORMAT.cTFPVRTC1_4_RGB, BASIS_FORMAT.cTFPVRTC1_4_RGBA: + texture = create(hxd.PixelFormat.PVRTC(format)); + default: + throw 'BasisTextureLoader: No supported format available.'; + } + if (mipmaps.length > 1) { + texture.flags.set(MipMapped); + } + return texture; + }).then((tex) -> { + if (workerTask != null && taskID > 0) { + workerTask.taskLoad -= workerTask.taskCosts.get(taskID); + workerTask.callbacks.remove(taskID); + workerTask.taskCosts.remove(taskID); + } + return tex; + }); + return texturePending; + } + + static function initTranscoder() { + if (_transcoderBinary == null) { + // Load transcoder wrapper. + final jsLoader = new hxd.net.BinaryLoader(transcoderPath); + final jsContent = new js.lib.Promise((resolve, reject) -> { + jsLoader.onLoaded = resolve; + jsLoader.onError = reject; + jsLoader.load(); + }); + // Load transcoder WASM binary. + final binaryLoader = new hxd.net.BinaryLoader(wasmPath); + final binaryContent = new js.lib.Promise((resolve, reject) -> { + binaryLoader.onLoaded = resolve; + binaryLoader.onError = reject; + binaryLoader.load(true); + }); + + _transcoderPending = js.lib.Promise.all([jsContent, binaryContent]).then((arr) -> { + final transcoder = arr[0]; + final wasm = arr[1]; + var fn = BasisWorker.func; + + var body = [ + '/* basis_transcoder.js */', + transcoder, + '/* worker */', + fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}')) + ].join('\n'); + + _workerSourceURL = js.html.URL.createObjectURL(new js.html.Blob([body])); + _transcoderBinary = wasm; + }); + } + + return _transcoderPending; + } + + static function getWorker() { + return initTranscoder().then((val) -> { + if (_workerPool.length < workerLimit) { + final worker = new js.html.Worker(_workerSourceURL); + final workerTask:WorkerTask = { + worker: worker, + callbacks: new haxe.ds.IntMap(), + taskCosts: new haxe.ds.IntMap(), + taskLoad: 0, + } + + worker.postMessage({ + type: 'init', + config: _workerConfig, + transcoderBinary: _transcoderBinary, + }); + + worker.onmessage = function(e) { + var message = e.data; + + switch (message.type) { + case 'transcode': + workerTask.callbacks.get(message.id).resolve(message); + case 'error': + workerTask.callbacks.get(message.id).reject(message); + default: + throw 'BasisTextureLoader: Unexpected message, "' + message.type + '"'; + } + }; + + _workerPool.push(workerTask); + } else { + _workerPool.sort(function(a, b) { + return a.taskLoad > b.taskLoad ? -1 : 1; + }); + } + + return _workerPool[_workerPool.length - 1]; + }); + } +} + +enum abstract BASIS_FORMAT(Int) from Int to Int { + final cTFETC1 = 0; + final cTFETC2 = 1; + final cTFBC1 = 2; + final cTFBC3 = 3; + final cTFBC4 = 4; + final cTFBC5 = 5; + final cTFBC7_M6_OPAQUE_ONLY = 6; + final cTFBC7_M5 = 7; + final cTFPVRTC1_4_RGB = 8; + final cTFPVRTC1_4_RGBA = 9; + final cTFASTC_4x4 = 10; + final cTFATC_RGB = 11; + final cTFATC_RGBA_INTERPOLATED_ALPHA = 12; + final cTFRGBA32 = 13; + final cTFRGB565 = 14; + final cTFBGR565 = 15; + final cTFRGBA4444 = 16; +} + +enum abstract FORMAT(Int) from Int to Int { + final RGB_S3TC_DXT1_Format = 33776; + final RGBA_S3TC_DXT1_Format = 33777; + final RGBA_S3TC_DXT3_Format = 33778; + final RGBA_S3TC_DXT5_Format = 33779; + final RGB_PVRTC_4BPPV1_Format = 35840; + final RGB_PVRTC_2BPPV1_Format = 35841; + final RGBA_PVRTC_4BPPV1_Format = 35842; + final RGBA_PVRTC_2BPPV1_Format = 35843; + final RGB_ETC1_Format = 36196; + final RGBA_ASTC_4x4_Format = 37808; + final RGBA_ASTC_5x4_Format = 37809; + final RGBA_ASTC_5x5_Format = 37810; + final RGBA_ASTC_6x5_Format = 37811; + final RGBA_ASTC_6x6_Format = 37812; + final RGBA_ASTC_8x5_Format = 37813; + final RGBA_ASTC_8x6_Format = 37814; + final RGBA_ASTC_8x8_Format = 37815; + final RGBA_ASTC_10x5_Format = 37816; + final RGBA_ASTC_10x6_Format = 37817; + final RGBA_ASTC_10x8_Format = 37818; + final RGBA_ASTC_10x10_Format = 37819; + final RGBA_ASTC_12x10_Format = 37820; + final RGBA_ASTC_12x12_Format = 37821; +} + +typedef WorkerTask = { + worker:js.html.Worker, + callbacks:haxe.ds.IntMap<{resolve:(value:Dynamic) -> Void, reject:(reason:Dynamic) -> Void}>, + taskCosts:haxe.ds.IntMap, + taskLoad:Int, +} + +class BasisWorker { + static public final func = "function () { + + var config; + var transcoderPending; + var _BasisFile; + + onmessage = function ( e ) { + + var message = e.data; + + switch ( message.type ) { + + case 'init': + config = message.config; + init( message.transcoderBinary ); + break; + + case 'transcode': + transcoderPending.then( () => { + + try { + + var { width, height, hasAlpha, mipmaps, format } = transcode( message.buffer ); + + var buffers = []; + + for ( var i = 0; i < mipmaps.length; ++ i ) { + + buffers.push( mipmaps[ i ].data.buffer ); + + } + console.log('transcode', buffers.length); + self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, buffers ); + + } catch ( error ) { + + console.error( error ); + + self.postMessage( { type: 'error', id: message.id, error: error.message } ); + + } + + } ); + break; + + } + + }; + + function init( wasmBinary ) { + + var BasisModule; + transcoderPending = new Promise( ( resolve ) => { + + BasisModule = { wasmBinary, onRuntimeInitialized: resolve }; + BASIS( BasisModule ); + + } ).then( () => { + + var { BasisFile, initializeBasis } = BasisModule; + + _BasisFile = BasisFile; + + initializeBasis(); + + } ); + + } + + function transcode( buffer ) { + + var basisFile = new _BasisFile( new Uint8Array( buffer ) ); + + var width = basisFile.getImageWidth( 0, 0 ); + var height = basisFile.getImageHeight( 0, 0 ); + var levels = basisFile.getNumLevels( 0 ); + var hasAlpha = basisFile.getHasAlpha(); + + function cleanup() { + + basisFile.close(); + basisFile.delete(); + + } + + if ( ! hasAlpha ) { + + switch ( config.format ) { + + case 9: // Hardcoded: BASIS_FORMAT.cTFPVRTC1_4_RGBA + config.format = 8; // BASIS_FORMAT.cTFPVRTC1_4_RGB; + break; + default: + break; + + } + + } + + if ( ! width || ! height || ! levels ) { + + cleanup(); + throw new Error( 'BasisTextureLoader: Invalid .basis file' ); + + } + + if ( ! basisFile.startTranscoding() ) { + + cleanup(); + throw new Error( 'BasisTextureLoader: .startTranscoding failed' ); + + } + + var mipmaps = []; + + for ( var mip = 0; mip < levels; mip ++ ) { + + var mipWidth = basisFile.getImageWidth( 0, mip ); + var mipHeight = basisFile.getImageHeight( 0, mip ); + var dst = new Uint8Array( basisFile.getImageTranscodedSizeInBytes( 0, mip, config.format ) ); + + var status = basisFile.transcodeImage( + dst, + 0, + mip, + config.format, + 0, + hasAlpha + ); + + if ( ! status ) { + + cleanup(); + throw new Error( 'BasisTextureLoader: .transcodeImage failed.' ); + + } + + mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } ); + + } + + cleanup(); + + return { width, height, hasAlpha, mipmaps, format: config.format }; + + } + + }"; +} + From e6f75d9d2dc7b09d32fcaa32ccc10e7c0dd4218a Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Thu, 31 Oct 2019 12:31:58 +0100 Subject: [PATCH 02/13] Add all compressed texture formats to isSupportedFormat --- h3d/impl/GlDriver.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 9dac702f54..2cad78a810 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -799,6 +799,7 @@ class GlDriver extends Driver { case SRGB, SRGB_ALPHA: hasFeature(SRGBTextures); case R8, RG8, RGB8, R16F, RG16F, RGB16F, R32F, RG32F, RGB32F, RG11B10UF, RGB10A2: #if js glES >= 3 #else true #end; case S3TC(n): n <= maxCompressedTexturesSupport; + case ASTC(_), ETC(_), S3TC(_), PVRTC(_): #if js true #else false #end; default: false; } } From c8b85c41adc1754de28b9f4cc6f7f20c37890195 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Thu, 31 Oct 2019 15:44:10 +0100 Subject: [PATCH 03/13] Remove unused pattern for supported format --- h3d/impl/GlDriver.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 2cad78a810..5216fd5a24 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -799,7 +799,7 @@ class GlDriver extends Driver { case SRGB, SRGB_ALPHA: hasFeature(SRGBTextures); case R8, RG8, RGB8, R16F, RG16F, RGB16F, R32F, RG32F, RGB32F, RG11B10UF, RGB10A2: #if js glES >= 3 #else true #end; case S3TC(n): n <= maxCompressedTexturesSupport; - case ASTC(_), ETC(_), S3TC(_), PVRTC(_): #if js true #else false #end; + case ASTC(_), ETC(_), PVRTC(_): #if js true #else false #end; default: false; } } From 1f50d202cbc58d3ad003ecc3b883002a7bb7badb Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Thu, 31 Oct 2019 17:10:41 +0100 Subject: [PATCH 04/13] Make BasisTextureLoader js only --- hxd/res/BasisTextureLoader.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hxd/res/BasisTextureLoader.hx b/hxd/res/BasisTextureLoader.hx index 755c3d7077..6bf070b047 100644 --- a/hxd/res/BasisTextureLoader.hx +++ b/hxd/res/BasisTextureLoader.hx @@ -1,5 +1,5 @@ package hxd.res; - +#if js import hxd.PixelFormat; import js.html.ImageData; import h3d.mat.Texture; @@ -400,3 +400,4 @@ class BasisWorker { }"; } +#end \ No newline at end of file From 599adb5797135208dbcb306a77dae0ed636c56c3 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Mon, 18 Nov 2019 11:01:47 +0100 Subject: [PATCH 05/13] Add bsis support --- h3d/impl/GlDriver.hx | 6 +++--- hxd/Pixels.hx | 3 +++ hxd/res/BasisTextureLoader.hx | 6 ++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 5216fd5a24..601cd635c5 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -907,7 +907,7 @@ class GlDriver extends Driver { if( t.flags.has(Cube) ) { for( i in 0...6 ) { - gl.texImage2D(CUBE_FACES[i], 0, tt.internalFmt, tt.width, tt.height, 0, getChannels(tt), tt.pixelFmt, null); + gl.texImage2D(CUBE_FACES[i], 0, tt.internalFmt, tt.width, tt.height, 0, getChannels(tt), tt.pixelFmt, null); if( checkError() ) break; } } else if( t.flags.has(IsArray) ) { @@ -915,7 +915,7 @@ class GlDriver extends Driver { checkError(); } else { #if js - if( !t.format.match(S3TC(_)) ) + if( !t.format.match(S3TC(_)) && !t.format.match(ETC(_)) && !t.format.match(ASTC(_)) && !t.format.match(PVRTC(_))) #end gl.texImage2D(bind, 0, tt.internalFmt, tt.width, tt.height, 0, getChannels(tt), tt.pixelFmt, null); checkError(); @@ -1555,7 +1555,7 @@ class GlDriver extends Driver { } #if js - function checkTextureSupport():hxd.PixelFormat { + public function checkTextureSupport():hxd.PixelFormat { var astcSupported = gl.getExtension('WEBGL_compressed_texture_astc') != null; var dxtSupported = gl.getExtension('WEBGL_compressed_texture_s3tc') != null; var pvrtcSupported = gl.getExtension('WEBGL_compressed_texture_pvrtc') != null diff --git a/hxd/Pixels.hx b/hxd/Pixels.hx index d18d440513..c4ac5a9092 100644 --- a/hxd/Pixels.hx +++ b/hxd/Pixels.hx @@ -321,6 +321,9 @@ class Pixels { } case [S3TC(a),S3TC(b)] if( a == b ): + case [ASTC(a),ASTC(b)] if( a == b ): + case [ETC(a),ETC(b)] if( a == b ): + case [PVRTC(a),PVRTC(b)] if( a == b ): // nothing #if (hl && hl_ver >= "1.10") diff --git a/hxd/res/BasisTextureLoader.hx b/hxd/res/BasisTextureLoader.hx index 6bf070b047..29bfc9ecc5 100644 --- a/hxd/res/BasisTextureLoader.hx +++ b/hxd/res/BasisTextureLoader.hx @@ -38,7 +38,7 @@ class BasisTextureLoader { final extETC1 = context.getExtension('WEBGL_compressed_texture_etc1'); final extDXT = context.getExtension('WEBGL_compressed_texture_s3tc'); final extPVRTC = context.getExtension('WEBGL_compressed_texture_pvrtc') != null ? context.getExtension('WEBGL_compressed_texture_pvrtc') : context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); - + _workerConfig.astcSupported = extASTC != null; _workerConfig.etc1Supported = extETC1 != null; _workerConfig.dxtSupported = extDXT != null; @@ -281,7 +281,6 @@ class BasisWorker { buffers.push( mipmaps[ i ].data.buffer ); } - console.log('transcode', buffers.length); self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, buffers ); } catch ( error ) { @@ -399,5 +398,4 @@ class BasisWorker { }"; } - -#end \ No newline at end of file +#end From 0d4b759b9f7940e7405f318137d6acfe24f07318 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Mon, 18 Nov 2019 11:02:08 +0100 Subject: [PATCH 06/13] Get image width/height from basis bytes --- hxd/res/Image.hx | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/hxd/res/Image.hx b/hxd/res/Image.hx index 9ebefc8fa8..d95d9f624d 100644 --- a/hxd/res/Image.hx +++ b/hxd/res/Image.hx @@ -7,6 +7,7 @@ package hxd.res; var Gif = 2; var Tga = 3; var Dds = 4; + var Basis = 5; /* Tells if we might not be able to directly decode the image without going through a loadBitmap async call. @@ -128,7 +129,20 @@ class Image extends Resource { if( bc == 0 ) throw entry.path+" has unsupported 4CC "+String.fromCharCode(fourCC&0xFF)+String.fromCharCode((fourCC>>8)&0xFF)+String.fromCharCode((fourCC>>16)&0xFF)+String.fromCharCode(fourCC>>>24); - + case 0x4273: + format = Basis; + f.skip(12); + var slices = f.readUInt24(); + var images = f.readUInt24(); + var bFormat = f.readInt8(); + f.skip(44); + var slicesPos = f.readInt32(); + f.skip(slicesPos-48); + var imageIndex = f.readUInt24(); + var levelIndex = f.readInt8(); + var flags = f.readInt8(); + width = f.readUInt16(); + height = f.readUInt16(); case _ if( entry.extension == "tga" ): format = Tga; f.skip(10); @@ -203,7 +217,19 @@ class Image extends Resource { case Dds: var bytes = entry.getBytes(); pixels = new hxd.Pixels(inf.width, inf.height, bytes, S3TC(inf.bc), 128 + (inf.bc >= 6 ? 20 : 0)); + case Basis: + var bytes = entry.getBytes(); + var driver:h3d.impl.GlDriver = cast h3d.Engine.getCurrent().driver; + var f = switch(driver.checkTextureSupport()) { + case hxd.PixelFormat.S3TC(_): hxd.PixelFormat.S3TC(inf.bc); + case hxd.PixelFormat.ETC(_): hxd.PixelFormat.ETC(0); + case hxd.PixelFormat.ASTC(_): hxd.PixelFormat.ASTC(10); + case hxd.PixelFormat.PVRTC(_): hxd.PixelFormat.PVRTC(9); + default: throw 'Unsupported basis texture'; + } + pixels = new hxd.Pixels(inf.width, inf.height, bytes, f); } + if( fmt != null ) pixels.convert(fmt); if( flipY != null ) pixels.setFlip(flipY); return pixels; From d3fc2f7176a76a5557c3cf72465a38ea887131d1 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Mon, 18 Nov 2019 12:00:27 +0100 Subject: [PATCH 07/13] Throw when basis texture on non-js target --- hxd/res/Image.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hxd/res/Image.hx b/hxd/res/Image.hx index d95d9f624d..bf25b71b3b 100644 --- a/hxd/res/Image.hx +++ b/hxd/res/Image.hx @@ -218,6 +218,7 @@ class Image extends Resource { var bytes = entry.getBytes(); pixels = new hxd.Pixels(inf.width, inf.height, bytes, S3TC(inf.bc), 128 + (inf.bc >= 6 ? 20 : 0)); case Basis: + #if js var bytes = entry.getBytes(); var driver:h3d.impl.GlDriver = cast h3d.Engine.getCurrent().driver; var f = switch(driver.checkTextureSupport()) { @@ -228,6 +229,9 @@ class Image extends Resource { default: throw 'Unsupported basis texture'; } pixels = new hxd.Pixels(inf.width, inf.height, bytes, f); + #else + throw 'Basis only supported on js target'; + #end } if( fmt != null ) pixels.convert(fmt); From 3bb8c546ee65a0e30da286dfe1411930ee766ea9 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Wed, 20 Nov 2019 08:15:43 +0100 Subject: [PATCH 08/13] Cleanup basis width detection --- hxd/res/Image.hx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hxd/res/Image.hx b/hxd/res/Image.hx index bf25b71b3b..391db3c1d6 100644 --- a/hxd/res/Image.hx +++ b/hxd/res/Image.hx @@ -131,16 +131,9 @@ class Image extends Resource { throw entry.path+" has unsupported 4CC "+String.fromCharCode(fourCC&0xFF)+String.fromCharCode((fourCC>>8)&0xFF)+String.fromCharCode((fourCC>>16)&0xFF)+String.fromCharCode(fourCC>>>24); case 0x4273: format = Basis; - f.skip(12); - var slices = f.readUInt24(); - var images = f.readUInt24(); - var bFormat = f.readInt8(); - f.skip(44); + f.skip(63); var slicesPos = f.readInt32(); - f.skip(slicesPos-48); - var imageIndex = f.readUInt24(); - var levelIndex = f.readInt8(); - var flags = f.readInt8(); + f.skip(slicesPos-42); width = f.readUInt16(); height = f.readUInt16(); case _ if( entry.extension == "tga" ): From 49d6a2d8b3304170060a31e019bfee2f0f029de0 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Wed, 20 Nov 2019 08:16:00 +0100 Subject: [PATCH 09/13] Remove duplicate compressed texture detection --- hxd/res/BasisTextureLoader.hx | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/hxd/res/BasisTextureLoader.hx b/hxd/res/BasisTextureLoader.hx index 29bfc9ecc5..1dd8fdc634 100644 --- a/hxd/res/BasisTextureLoader.hx +++ b/hxd/res/BasisTextureLoader.hx @@ -32,29 +32,14 @@ class BasisTextureLoader { static function detectSupport() { final driver:GlDriver = cast h3d.Engine.getCurrent().driver; - final context = driver.gl; - - final extASTC = context.getExtension('WEBGL_compressed_texture_astc'); - final extETC1 = context.getExtension('WEBGL_compressed_texture_etc1'); - final extDXT = context.getExtension('WEBGL_compressed_texture_s3tc'); - final extPVRTC = context.getExtension('WEBGL_compressed_texture_pvrtc') != null ? context.getExtension('WEBGL_compressed_texture_pvrtc') : context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); - - _workerConfig.astcSupported = extASTC != null; - _workerConfig.etc1Supported = extETC1 != null; - _workerConfig.dxtSupported = extDXT != null; - _workerConfig.pvrtcSupported = extPVRTC != null; - - if (_workerConfig.etc1Supported) { - _workerConfig.format = BASIS_FORMAT.cTFETC1; - } else if (_workerConfig.astcSupported) { - _workerConfig.format = BASIS_FORMAT.cTFASTC_4x4; - } else if (_workerConfig.dxtSupported) { - _workerConfig.format = BASIS_FORMAT.cTFBC3; - } else if (_workerConfig.pvrtcSupported) { - _workerConfig.format = BASIS_FORMAT.cTFPVRTC1_4_RGBA; - } else { - throw 'No suitable compressed texture format found.'; - }; + final fmt = driver.textureSupport; + _workerConfig.format = switch(fmt) { + case ETC(_): BASIS_FORMAT.cTFETC1; + case ASTC(_): BASIS_FORMAT.cTFASTC_4x4; + case S3TC(_): BASIS_FORMAT.cTFBC3; + case PVRTC(_): BASIS_FORMAT.cTFPVRTC1_4_RGBA; + default: throw 'No suitable compressed texture format found.'; + } } static function createTexture(buffer:haxe.io.BytesData):js.lib.Promise { @@ -83,7 +68,8 @@ class BasisTextureLoader { final texture = new h3d.mat.Texture(w, h, null, fmt); var level = 0; for (mipmap in mipmaps) { - final pixels = new hxd.Pixels(mipmap.width, mipmap.height, haxe.io.Bytes.ofData(cast mipmap.data), fmt); + final bytes = haxe.io.Bytes.ofData(cast mipmap.data); + final pixels = new hxd.Pixels(mipmap.width, mipmap.height, bytes, fmt); texture.uploadPixels(pixels, level); level++; } From 7779b539eaa766421e56a75f752593976c486487 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Wed, 20 Nov 2019 08:17:24 +0100 Subject: [PATCH 10/13] Use stride 1 with ETC and PBRTC --- hxd/Pixels.hx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hxd/Pixels.hx b/hxd/Pixels.hx index c4ac5a9092..9a60a6867b 100644 --- a/hxd/Pixels.hx +++ b/hxd/Pixels.hx @@ -423,9 +423,7 @@ class Pixels { case RGB32F: 12; case RGB10A2: 4; case RG11B10UF: 4; - case ASTC(n): 1; - case ETC(n): 0; - case PVRTC(n): 0; + case ASTC(n), ETC(n), PVRTC(n): 1; case S3TC(n): if( n == 1 || n == 4 ) return width >> 1; From 42fa2b7fc261aaddd646b62d16775916e790ea87 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Wed, 20 Nov 2019 08:18:04 +0100 Subject: [PATCH 11/13] Prioritize ETC textures last --- h3d/impl/GlDriver.hx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 601cd635c5..5e7b95a3aa 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -1,4 +1,5 @@ package h3d.impl; +import js.lib.Uint8ClampedArray; import h3d.impl.Driver; import h3d.mat.Pass; import h3d.mat.Stencil; @@ -1154,6 +1155,7 @@ class GlDriver extends Driver { case RGBA32F, R32F, RG32F, RGB32F: new Float32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, bufLen>>2); case RGBA16F, R16F, RG16F, RGB16F: new Uint16Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, bufLen>>1); case RGB10A2, RG11B10UF: new Uint32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, bufLen>>2); + case ETC(_), PVRTC(_): new Uint8Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset); default: new Uint8Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, bufLen); } switch (t.format) { @@ -1561,15 +1563,14 @@ class GlDriver extends Driver { var pvrtcSupported = gl.getExtension('WEBGL_compressed_texture_pvrtc') != null || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc') != null; var etcSupported = gl.getExtension('WEBGL_compressed_texture_etc1') != null; - return if(astcSupported) { hxd.PixelFormat.ASTC(); } else if(dxtSupported){ hxd.PixelFormat.S3TC(); + } else if(pvrtcSupported){ + hxd.PixelFormat.PVRTC(); } else if(etcSupported){ hxd.PixelFormat.ETC(); - } else if(pvrtcSupported){ - hxd.PixelFormat.PVRTC(); } else { null; } From 1c6f8ed9332550289ebd6d6c39769d3d92941c26 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Wed, 20 Nov 2019 09:01:52 +0100 Subject: [PATCH 12/13] Remove unused import --- h3d/impl/GlDriver.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 5e7b95a3aa..2d63e4dd97 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -1,5 +1,4 @@ package h3d.impl; -import js.lib.Uint8ClampedArray; import h3d.impl.Driver; import h3d.mat.Pass; import h3d.mat.Stencil; From e8067c37fd24912dda93a30587f0a33549599d3f Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Wed, 20 Nov 2019 12:46:52 +0100 Subject: [PATCH 13/13] Support non alpha PVRTC --- h3d/impl/GlDriver.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 2d63e4dd97..848d640fce 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -883,6 +883,7 @@ class GlDriver extends Driver { case PVRTC(n): checkMult4(t); switch(n) { + case 8: tt.internalFmt = hxd.PixelFormat.PVRTC_FORMAT.RGB_4BPPV1; case 9: tt.internalFmt = hxd.PixelFormat.PVRTC_FORMAT.RGBA_4BPPV1; default: throw "Unsupported texture format "+t.format; }