-
-
Notifications
You must be signed in to change notification settings - Fork 341
Support ktx2 hdr #2587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support ktx2 hdr #2587
Changes from all commits
de4b004
b3369f6
bf83c46
acd2d3b
9c70cbb
0955bcc
f5b7fc1
dbee03a
89a228a
156d4a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add this e2e test to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The document might is obesolete, Would you please update it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
* @title HDR KTX2 | ||
* @category Texture | ||
*/ | ||
import { | ||
BloomEffect, | ||
Camera, | ||
DirectLight, | ||
Logger, | ||
MeshRenderer, | ||
PostProcess, | ||
PrimitiveMesh, | ||
Texture2D, | ||
TonemappingEffect, | ||
UnlitMaterial, | ||
Vector3, | ||
WebGLEngine | ||
} from "@galacean/engine"; | ||
import { OrbitControl } from "@galacean/engine-toolkit"; | ||
import { initScreenshot, updateForE2E } from "./.mockForE2E"; | ||
|
||
Logger.enable(); | ||
|
||
WebGLEngine.create({ canvas: "canvas", ktx2Loader: { workerCount: 0 } }).then((engine) => { | ||
engine.canvas.resizeByClientSize(2); | ||
const scene = engine.sceneManager.activeScene; | ||
const rootEntity = scene.createRootEntity(); | ||
|
||
// camera | ||
const cameraEntity = rootEntity.createChild("camera_node"); | ||
cameraEntity.transform.position = new Vector3(0, 1, 5); | ||
const camera = cameraEntity.addComponent(Camera); | ||
cameraEntity.addComponent(OrbitControl).target = new Vector3(0, 0, 0); | ||
|
||
const lightNode = rootEntity.createChild("light_node"); | ||
lightNode.addComponent(DirectLight).intensity = 0.6; | ||
lightNode.transform.lookAt(new Vector3(0, 0, 1)); | ||
lightNode.transform.rotate(new Vector3(0, 90, 0)); | ||
|
||
const planeEntity = rootEntity.createChild("plane"); | ||
const meshRenderer = planeEntity.addComponent(MeshRenderer); | ||
meshRenderer.mesh = PrimitiveMesh.createCuboid(engine); | ||
const mtl = new UnlitMaterial(engine); | ||
meshRenderer.setMaterial(mtl); | ||
const postProcess = rootEntity.addComponent(PostProcess); | ||
postProcess.addEffect(BloomEffect); | ||
postProcess.addEffect(TonemappingEffect); | ||
|
||
engine.resourceManager.load<Texture2D>("/autumn_field_puresky_1k.ktx2").then((tex) => { | ||
mtl.baseTexture = tex; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add Bloom or Tonemapping to test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
updateForE2E(engine); | ||
gz65555 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
initScreenshot(engine, camera); | ||
}); | ||
}); | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,32 +15,30 @@ | |
resourceLoader | ||
} from "@galacean/engine-core"; | ||
import { MathUtil } from "@galacean/engine-math"; | ||
import { DFDTransferFunction, KTX2Container } from "./KTX2Container"; | ||
import { DFDTransferFunction, KTX2Container, ColorModel } from "./KTX2Container"; | ||
import { KTX2TargetFormat } from "./KTX2TargetFormat"; | ||
import { TranscodeResult } from "./transcoder/AbstractTranscoder"; | ||
import { BinomialLLCTranscoder } from "./transcoder/BinomialLLCTranscoder"; | ||
import { KhronosTranscoder } from "./transcoder/KhronosTranscoder"; | ||
|
||
@resourceLoader(AssetType.KTX2, ["ktx2"]) | ||
export class KTX2Loader extends Loader<Texture2D | TextureCube> { | ||
private static _isBinomialInit: boolean = false; | ||
private static _binomialLLCTranscoder: BinomialLLCTranscoder; | ||
private static _khronosTranscoder: KhronosTranscoder; | ||
private static _priorityFormats = { | ||
etc1s: [ | ||
[ColorModel.ETC1S]: [ | ||
KTX2TargetFormat.ETC, | ||
KTX2TargetFormat.BC7, | ||
KTX2TargetFormat.ASTC, | ||
KTX2TargetFormat.BC1_BC3, | ||
KTX2TargetFormat.PVRTC | ||
], | ||
uastc: [ | ||
[ColorModel.UASTC_LDR_4X4]: [ | ||
gz65555 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
KTX2TargetFormat.ASTC, | ||
KTX2TargetFormat.BC7, | ||
KTX2TargetFormat.ETC, | ||
KTX2TargetFormat.BC1_BC3, | ||
KTX2TargetFormat.PVRTC | ||
] | ||
], | ||
[ColorModel.UASTC_HDR_4X4]: [KTX2TargetFormat.BC6H, KTX2TargetFormat.R16G16B16A16] | ||
}; | ||
private static _capabilityMap = { | ||
[KTX2TargetFormat.ASTC]: { | ||
|
@@ -59,6 +57,14 @@ | |
[DFDTransferFunction.linear]: [GLCapabilityType.s3tc], | ||
[DFDTransferFunction.sRGB]: [GLCapabilityType.s3tc_srgb] | ||
}, | ||
[KTX2TargetFormat.BC6H]: { | ||
[DFDTransferFunction.linear]: [GLCapabilityType.bptc], | ||
[DFDTransferFunction.sRGB]: [GLCapabilityType.bptc] | ||
}, | ||
[KTX2TargetFormat.R16G16B16A16]: { | ||
[DFDTransferFunction.linear]: [GLCapabilityType.textureHalfFloatLinear], | ||
[DFDTransferFunction.sRGB]: [GLCapabilityType.textureHalfFloat] | ||
}, | ||
[KTX2TargetFormat.PVRTC]: { [DFDTransferFunction.linear]: [GLCapabilityType.pvrtc, GLCapabilityType.pvrtc_webkit] } | ||
}; | ||
|
||
|
@@ -68,35 +74,27 @@ | |
*/ | ||
static release(): void { | ||
if (this._binomialLLCTranscoder) this._binomialLLCTranscoder.destroy(); | ||
if (this._khronosTranscoder) this._khronosTranscoder.destroy(); | ||
this._binomialLLCTranscoder = null; | ||
this._khronosTranscoder = null; | ||
this._isBinomialInit = false; | ||
} | ||
|
||
/** @internal */ | ||
static _parseBuffer(buffer: Uint8Array, engine: Engine, params?: KTX2Params) { | ||
const ktx2Container = new KTX2Container(buffer); | ||
const formatPriorities = | ||
params?.priorityFormats ?? KTX2Loader._priorityFormats[ktx2Container.isUASTC ? "uastc" : "etc1s"]; | ||
const formatPriorities = params?.priorityFormats ?? KTX2Loader._priorityFormats[ktx2Container.colorModel]; | ||
const targetFormat = KTX2Loader._decideTargetFormat(engine, ktx2Container, formatPriorities); | ||
let transcodeResultPromise: Promise<TranscodeResult>; | ||
if (KTX2Loader._isBinomialInit || !KhronosTranscoder.transcoderMap[targetFormat] || !ktx2Container.isUASTC) { | ||
const binomialLLCWorker = KTX2Loader._getBinomialLLCTranscoder(); | ||
transcodeResultPromise = binomialLLCWorker.init().then(() => binomialLLCWorker.transcode(buffer, targetFormat)); | ||
} else { | ||
const khronosWorker = KTX2Loader._getKhronosTranscoder(); | ||
transcodeResultPromise = khronosWorker.init().then(() => khronosWorker.transcode(ktx2Container)); | ||
} | ||
return transcodeResultPromise.then((result) => { | ||
return { | ||
ktx2Container, | ||
engine, | ||
result, | ||
targetFormat, | ||
params: ktx2Container.keyValue["GalaceanTextureParams"] as Uint8Array | ||
}; | ||
}); | ||
const binomialLLCWorker = KTX2Loader._getBinomialLLCTranscoder(); | ||
return binomialLLCWorker | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. delete There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
.init() | ||
.then(() => binomialLLCWorker.transcode(buffer, targetFormat)) | ||
.then((result) => { | ||
return { | ||
ktx2Container, | ||
engine, | ||
result, | ||
targetFormat, | ||
params: ktx2Container.keyValue["GalaceanTextureParams"] as Uint8Array | ||
}; | ||
}); | ||
} | ||
|
||
/** @internal */ | ||
|
@@ -190,14 +188,9 @@ | |
} | ||
|
||
private static _getBinomialLLCTranscoder(workerCount: number = 4) { | ||
KTX2Loader._isBinomialInit = true; | ||
return (this._binomialLLCTranscoder ??= new BinomialLLCTranscoder(workerCount)); | ||
} | ||
|
||
private static _getKhronosTranscoder(workerCount: number = 4) { | ||
return (this._khronosTranscoder ??= new KhronosTranscoder(workerCount, KTX2TargetFormat.ASTC)); | ||
} | ||
|
||
private static _getEngineTextureFormat( | ||
basisFormat: KTX2TargetFormat, | ||
transcodeResult: TranscodeResult | ||
|
@@ -216,6 +209,10 @@ | |
return hasAlpha ? TextureFormat.PVRTC_RGBA4 : TextureFormat.PVRTC_RGB4; | ||
case KTX2TargetFormat.R8G8B8A8: | ||
return TextureFormat.R8G8B8A8; | ||
case KTX2TargetFormat.BC6H: | ||
return TextureFormat.BC6H; | ||
case KTX2TargetFormat.R16G16B16A16: | ||
return TextureFormat.R16G16B16A16; | ||
Comment on lines
+212
to
+215
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAdded support for HDR texture formats but missing test coverage. The new texture formats for HDR support (BC6H and R16G16B16A16) have been properly added to the _getEngineTextureFormat method, but these lines lack test coverage according to the static analysis. Please ensure that you add unit tests to cover these new format mappings. Here's a suggestion for a test: // Add this to the appropriate test file
it("should map BC6H and R16G16B16A16 to the correct engine formats", () => {
// Mock transcodeResult
const transcodeResult = { hasAlpha: false } as TranscodeResult;
// Test
10000
BC6H mapping
const bc6hFormat = KTX2Loader["_getEngineTextureFormat"](KTX2TargetFormat.BC6H, transcodeResult);
expect(bc6hFormat).toBe(TextureFormat.BC6H);
// Test R16G16B16A16 mapping
const r16Format = KTX2Loader["_getEngineTextureFormat"](KTX2TargetFormat.R16G16B16A16, transcodeResult);
expect(r16Format).toBe(TextureFormat.R16G16B16A16);
}); 🏁 Script executed: #!/bin/bash
# Search for any existing tests referencing KTX2Loader
rg -n 'KTX2Loader' -g '*.spec.ts'
# Search for BC6H references in test files
rg -n 'BC6H' -g '*.spec.ts'
# Search for R16G16B16A16 references in test files
rg -n 'R16G16B16A16' -g '*.spec.ts' Length of output: 544 🏁 Script executed: #!/bin/bash
# List all test files in the repository
fd --extension ts --extension js | grep -E '\.(test|spec)\.(ts|js)$' || echo "No test files found" Length of output: 3321 🏁 Script executed: #!/bin/bash
rg -n 'BC6H' tests/src/loader/KTX2Loader.test.ts
rg -n 'R16G16B16A16' tests/src/loader/KTX2Loader.test.ts
rg -n '_getEngineTextureFormat' tests/src/loader/KTX2Loader.test.ts Length of output: 173 Add unit tests for HDR texture format mappings The new mappings for • File to update: tests/src/loader/KTX2Loader.test.ts Suggested test addition: import { KTX2Loader } from "../../../packages/loader/src/ktx2/KTX2Loader";
import { KTX2TargetFormat } from "../../../packages/loader/src/ktx2/KTX2Interfaces";
import { TextureFormat } from "@your-engine/core";
describe("KTX2Loader _getEngineTextureFormat", () => {
const dummyResult = { hasAlpha: false } as any;
it("maps BC6H to TextureFormat.BC6H", () => {
const format = (KTX2Loader as any)["_getEngineTextureFormat"](
KTX2TargetFormat.BC6H,
dummyResult,
);
expect(format).toBe(TextureFormat.BC6H);
});
it("maps R16G16B16A16 to TextureFormat.R16G16B16A16", () => {
const format = (KTX2Loader as any)["_getEngineTextureFormat"](
KTX2TargetFormat.R16G16B16A16,
dummyResult,
);
expect(format).toBe(TextureFormat.R16G16B16A16);
});
}); 🧰 Tools🪛 GitHub Check: codecov/patch[warning] 209-209: packages/loader/src/ktx2/KTX2Loader.ts#L209 [warning] 211-211: packages/loader/src/ktx2/KTX2Loader.ts#L211 |
||
} | ||
} | ||
|
||
|
@@ -227,11 +224,7 @@ | |
KTX2Loader._priorityFormats["uastc"] = options.priorityFormats; | ||
} | ||
|
||
if (options.transcoder === KTX2Transcoder.Khronos) { | ||
return KTX2Loader._getKhronosTranscoder(options.workerCount).init(); | ||
} else { | ||
return KTX2Loader._getBinomialLLCTranscoder(options.workerCount).init(); | ||
} | ||
return KTX2Loader._getBinomialLLCTranscoder(options.workerCount).init(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Simplified initialization but missing test coverage. The initialization now always uses Please add a unit test to verify the initialization behavior: // Add this to the appropriate test file
it("should properly initialize BinomialLLCTranscoder with the configured worker count", async () => {
const engine = {} as Engine;
const config = {
ktx2Loader: {
workerCount: 2
}
} as EngineConfiguration;
const getBinomialSpy = jest.spyOn(KTX2Loader as any, '_getBinomialLLCTranscoder');
const mockTranscoder = { init: jest.fn().mockResolvedValue(undefined) };
getBinomialSpy.mockReturnValue(mockTranscoder);
await loader.initialize(engine, config);
expect(getBinomialSpy).toHaveBeenCalledWith(2);
expect(mockTranscoder.init).toHaveBeenCalled();
getBinomialSpy.mockRestore();
}); 🧰 Tools🪛 GitHub Check: codecov/patch[warning] 223-223: packages/loader/src/ktx2/KTX2Loader.ts#L223 |
||
} | ||
} | ||
|
||
|
@@ -268,14 +261,6 @@ | |
priorityFormats: KTX2TargetFormat[]; | ||
} | ||
|
||
/** Used for initialize KTX2 transcoder. */ | ||
export enum KTX2Transcoder { | ||
/** BinomialLLC transcoder. */ | ||
BinomialLLC, | ||
/** Khronos transcoder. */ | ||
Khronos | ||
} | ||
|
||
declare module "@galacean/engine-core" { | ||
interface EngineConfiguration { | ||
/** KTX2 loader options. If set this option and workCount is great than 0, workers will be created. */ | ||
|
@@ -285,8 +270,6 @@ | |
/** Global transcoding format queue which will be used if not specified in per-instance param, default is BC7/ASTC/BC3_BC1/ETC/PVRTC/R8G8B8A8. */ | ||
/** @deprecated */ | ||
priorityFormats?: KTX2TargetFormat[]; | ||
/** Used for initialize KTX2 transcoder, default is BinomialLLC. */ | ||
transcoder?: KTX2Transcoder; | ||
}; | ||
} | ||
A076 | } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC @luzhuang