From 20446470fce7329e983db7d22af94276f18230b2 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Thu, 30 Oct 2025 14:52:58 +0000 Subject: [PATCH 1/2] chore: Update version --- haxelib.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/haxelib.json b/haxelib.json index 9a5524608..e0d7cdc99 100644 --- a/haxelib.json +++ b/haxelib.json @@ -3,7 +3,7 @@ "url": "http://heaps.io", "license": "BSD", "description": "The GPU Game Framework", - "version": "2.1.4", + "version": "2.1.5", "releasenote": "See CHANGELOG.md", "contributors": [ "ncannasse" diff --git a/package.json b/package.json index bce3bf46e..af0f2fee0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hacksawstudios/heaps", - "version": "2.1.4", + "version": "2.1.5", "description": "_High Performance Game Framework_", "main": "index.js", "devDependencies": { From 0a4d5a23d1fcde05822584808f8e892519631a10 Mon Sep 17 00:00:00 2001 From: Leo Bergman Date: Sun, 1 Mar 2026 14:07:27 +0000 Subject: [PATCH 2/2] fix: support res ktx2 formats and bump 2.1.11 --- bun.lockb | Bin 3276 -> 3252 bytes haxelib.json | 6 +-- hxd/res/Image.hx | 136 +++++++++++++++++++++++++++-------------------- package.json | 7 ++- 4 files changed, 83 insertions(+), 66 deletions(-) diff --git a/bun.lockb b/bun.lockb index cfd09658e5383acac748746d48a37f80bf5ceb12..297cb3a55c44763e02056277d590bee2bd91e10a 100755 GIT binary patch delta 439 zcmX>jxkYkTV%^k=lA^?;j0_AsKz>$neqLH;PUE zdDQ3F`p2#de5UQab<=G6x`{g!CO+_BYGatJ#bh>FfsupBlyR~aqaD*l#>rVgDwb*T zDn>iThm&72nos<|!U;5n0gNZ7GAZ){IS?L*28m2Qz?3_=m&JK<0ki02U1o1a=E;@J z;gdC3cqiWl@>nNpviMJYECRHMMIafdNfhX$3Lwn~q$giwN!T30s=+w9h0S2{2DTVR zpUG$0lvP32F#$0v5VHU=GZcgP4eUSy2+Sr&va5q6CfBoT^Dr?Q8tNJ8889&HVw}8< z-N+0U8h@^PF}a4E>tJGx14@Gw;FbYumE(|5Z{`47zyPDUY>HEJ%TkNE?W}GlMLKVh|rB-T-m~5ClvPWmlivz^=u^ z!DwixXQXGq!0>`$@-}uOGg#>Sx$ec}8gj0Ki7^f+4N`zx2B=kzLt^qQb}5xv91t5| zG?xv?nVEUTC5bsXdc~=^WvNAzV>#3~Pe2u#Ozz=OnY^AOb#fC|+GZcF3yhP6IWJ9a HWSs#3T?B4; diff --git a/haxelib.json b/haxelib.json index 4bcd9ffc4..8543c63fd 100644 --- a/haxelib.json +++ b/haxelib.json @@ -3,7 +3,7 @@ "url": "http://heaps.io", "license": "BSD", "description": "The GPU Game Framework", - "version": "2.1.8", + "version": "2.1.11", "releasenote": "See CHANGELOG.md", "contributors": [ "ncannasse" @@ -11,6 +11,4 @@ "dependencies": { "format": "" } - -} - +} diff --git a/hxd/res/Image.hx b/hxd/res/Image.hx index e9028fd2f..ac74f4b04 100644 --- a/hxd/res/Image.hx +++ b/hxd/res/Image.hx @@ -257,23 +257,26 @@ class Image extends Resource { #if js case 0x4273: throw 'Use .ktx2 files for GPU compressed textures instead of .basis'; - case 0x4BAB: - final ktx2 = hxd.res.Ktx2.readFile(new haxe.io.BytesInput(@:privateAccess f.cache)); - final basisFormat = switch ktx2.dfd.colorModel { - case hxd.res.Ktx2.DFDModel.ETC1S: BasisFormat.ETC1S; - case hxd.res.Ktx2.DFDModel.UASTC: BasisFormat.UASTC; - default: throw 'Unsupported colorModel in ktx2 file: ${ktx2.dfd.colorModel}'; - } - final formatInfo = hxd.res.Ktx2.Ktx2Decoder.getTranscoderFormat(basisFormat, ktx2.header.pixelWidth, ktx2.header.pixelHeight, ktx2.dfd.hasAlpha()); - inf.pixelFormat = switch formatInfo.transcoderFormat { - case TranscoderFormat.ASTC_4x4: hxd.PixelFormat.ASTC(10); - case TranscoderFormat.BC7_M5: hxd.PixelFormat.S3TC(7); - case TranscoderFormat.BC3: hxd.PixelFormat.S3TC(3); - case TranscoderFormat.ETC1: hxd.PixelFormat.ETC(0); - case TranscoderFormat.ETC2: hxd.PixelFormat.ETC(1); - default: - throw 'Unsupported transcoder format: ${formatInfo.transcoderFormat}'; - } + case 0x4BAB: + final ktx2 = hxd.res.Ktx2.readFile(new haxe.io.BytesInput(@:privateAccess f.cache)); + final basisFormat = switch ktx2.dfd.colorModel { + case hxd.res.Ktx2.DFDModel.ETC1S: BasisFormat.ETC1S; + case hxd.res.Ktx2.DFDModel.UASTC: BasisFormat.UASTC; + default: throw 'Unsupported colorModel in ktx2 file: ${ktx2.dfd.colorModel}'; + } + final formatInfo = hxd.res.Ktx2.Ktx2Decoder.getTranscoderFormat(basisFormat, ktx2.header.pixelWidth, ktx2.header.pixelHeight, ktx2.dfd.hasAlpha()); + inf.pixelFormat = switch formatInfo.transcoderFormat { + case TranscoderFormat.ASTC_4x4: hxd.PixelFormat.ASTC(10); + case TranscoderFormat.BC7_M5: hxd.PixelFormat.S3TC(7); + case TranscoderFormat.BC1: hxd.PixelFormat.S3TC(1); + case TranscoderFormat.BC3: hxd.PixelFormat.S3TC(3); + case TranscoderFormat.ETC1: hxd.PixelFormat.ETC(0); + case TranscoderFormat.ETC2: hxd.PixelFormat.ETC(1); + case TranscoderFormat.RGBA32: hxd.PixelFormat.RGBA; + case TranscoderFormat.RGBA_HALF: hxd.PixelFormat.RGBA16F; + default: + throw 'Unsupported transcoder format: ${formatInfo.transcoderFormat}'; + } inf.mipLevels = ktx2.header.levelCount; inf.width = ktx2.header.pixelWidth; inf.height = ktx2.header.pixelHeight; @@ -552,9 +555,9 @@ class Image extends Resource { return bmp; } - function watchCallb() { - var prevInfo = inf; - inf = null; + function watchCallb() { + var prevInfo = inf; + inf = null; try { getInfo(); } catch ( e : Dynamic ) { @@ -564,9 +567,34 @@ class Image extends Resource { var s = getSize(); if (prevInfo.width != s.width || prevInfo.height != s.height) tex.resize(s.width, s.height); - tex.realloc = null; - loadTexture(); - } + tex.realloc = null; + loadTexture(); + } + + function resolveTextureWaitLoads() { + @:privateAccess if (tex.waitLoads != null) { + var arr = tex.waitLoads; + tex.waitLoads = null; + for (f in arr) + f(); + } + } + + static function pixelFormatFromKtx2Face(faceFormat:Int, faceType:Int):hxd.PixelFormat { + return switch faceFormat { + case hxd.CompressedTextureFormat.BPTC_FORMAT.RGBA_BPTC: hxd.PixelFormat.S3TC(7); + case hxd.CompressedTextureFormat.ASTC_FORMAT.RGBA_4x4: hxd.PixelFormat.ASTC(10); + case hxd.CompressedTextureFormat.DXT_FORMAT.RGB_DXT1, hxd.CompressedTextureFormat.DXT_FORMAT.RGBA_DXT1: hxd.PixelFormat.S3TC(1); + case hxd.CompressedTextureFormat.DXT_FORMAT.RGBA_DXT3: hxd.PixelFormat.S3TC(2); + case hxd.CompressedTextureFormat.DXT_FORMAT.RGBA_DXT5: hxd.PixelFormat.S3TC(3); + case hxd.CompressedTextureFormat.ETC_FORMAT.RGB_ETC1: hxd.PixelFormat.ETC(0); + case hxd.CompressedTextureFormat.ETC_FORMAT.RGBA_ETC2: hxd.PixelFormat.ETC(1); + case hxd.res.Ktx2.EngineFormat.RGBAFormat: + faceType == hxd.res.Ktx2.EngineType.HalfFloatType ? hxd.PixelFormat.RGBA16F : hxd.PixelFormat.RGBA; + default: + throw 'No compressed texture format found for ${StringTools.hex(faceFormat)}'; + } + } static var BLACK_1x1 = Pixels.alloc(1, 1, RGBA); public static var ASYNC_LOADER:hxd.impl.AsyncLoader; @@ -605,19 +633,14 @@ class Image extends Resource { if (getFormat().useLoadBitmap) { // use native decoding tex.flags.set(Loading); - entry.loadBitmap(function(bmp) { - var bmp = bmp.toBitmap(); - tex.alloc(); - tex.uploadBitmap(bmp); - bmp.dispose(); - tex.realloc = () -> loadTexture(); - tex.flags.unset(Loading); - @:privateAccess if (tex.waitLoads != null) { - var arr = tex.waitLoads; - tex.waitLoads = null; - for (f in arr) - f(); - } + entry.loadBitmap(function(bmp) { + var bmp = bmp.toBitmap(); + tex.alloc(); + tex.uploadBitmap(bmp); + bmp.dispose(); + tex.realloc = () -> loadTexture(); + tex.flags.unset(Loading); + resolveTextureWaitLoads(); if (ENABLE_AUTO_WATCH && !watchRegistered) { watchRegistered = true; @@ -681,22 +704,18 @@ class Image extends Resource { pos += size; } } - case Ktx2ETC1S, Ktx2UASTC: - for (layer in 0...asyncMessage.faces.length) { - final face = asyncMessage.faces[layer]; - for (mip in 0...face.mipmaps.length) { - final w = inf.width >> mip; - final h = inf.height >> mip; - inf.pixelFormat = switch face.format { - case hxd.CompressedTextureFormat.BPTC_FORMAT.RGBA_BPTC: hxd.PixelFormat.S3TC(7); - case hxd.CompressedTextureFormat.ASTC_FORMAT.RGBA_4x4: hxd.PixelFormat.ASTC(10); - default: throw 'No compressed texture format found for ${StringTools.hex(face.format)}'; - } - final pixels = new hxd.Pixels(w, h, haxe.io.Bytes.ofData(face.mipmaps[mip].data.buffer), inf.pixelFormat, 0); - tex.uploadPixels(pixels, mip, layer); - pixels.dispose(); - } - } + case Ktx2ETC1S, Ktx2UASTC: + for (layer in 0...asyncMessage.faces.length) { + final face = asyncMessage.faces[layer]; + for (mip in 0...face.mipmaps.length) { + final w = hxd.Math.imax(1, inf.width >> mip); + final h = hxd.Math.imax(1, inf.height >> mip); + inf.pixelFormat = pixelFormatFromKtx2Face(face.format, face.type); + final pixels = new hxd.Pixels(w, h, haxe.io.Bytes.ofData(face.mipmaps[mip].data.buffer), inf.pixelFormat, 0); + tex.uploadPixels(pixels, mip, layer); + pixels.dispose(); + } + } default: for (layer in 0...tex.layerCount) { for (mip in 0...inf.mipLevels) { @@ -712,12 +731,13 @@ class Image extends Resource { #if hl Sys.println #else trace #end (fmtStr + " " + Std.int(time) + "." + (Std.int(time * 10) % 10) + "ms " + inf.width + "x" + inf.height + " " + entry.path); } - tex.realloc = () -> loadTexture(); - if (ENABLE_AUTO_WATCH && !watchRegistered) { - watchRegistered = true; - watch(watchCallb); - } - } + tex.realloc = () -> loadTexture(); + if (ENABLE_AUTO_WATCH && !watchRegistered) { + watchRegistered = true; + watch(watchCallb); + } + resolveTextureWaitLoads(); + } if (entry.isAvailable) load(); else diff --git a/package.json b/package.json index 1415b6812..984ed18f5 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,10 @@ { "name": "@hacksawstudios/heaps", - "version": "2.1.8", + "version": "2.1.11", "description": "_High Performance Game Framework_", "main": "index.js", "devDependencies": { - "@hacksawstudios/haxe-module-installer": "1.2.12", - "fs-extra": "11.2.0" + "@hacksawstudios/haxe-module-installer": "1.2.12" }, "scripts": { "postinstall": "npx @hacksawstudios/haxe-module-installer", @@ -21,4 +20,4 @@ "url": "https://github.com/HacksawStudios/heaps/issues" }, "homepage": "https://github.com/HacksawStudios/heaps#readme" -} \ No newline at end of file +}