123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- /* global require */
- import defined from "../Core/defined.js";
- import Check from "../Core/Check.js";
- import PixelFormat from "../Core/PixelFormat.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import VulkanConstants from "../Core//VulkanConstants.js";
- import PixelDatatype from "../Renderer/PixelDatatype.js";
- import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
- import ktx_parse from "../ThirdParty/ktx-parse.js";
- const faceOrder = [
- "positiveX",
- "negativeX",
- "positiveY",
- "negativeY",
- "positiveZ",
- "negativeZ",
- ];
- // Flags
- const colorModelETC1S = 163;
- const colorModelUASTC = 166;
- let transcoderModule;
- function transcode(parameters, transferableObjects) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("transcoderModule", transcoderModule);
- //>>includeEnd('debug');
- const data = parameters.ktx2Buffer;
- const supportedTargetFormats = parameters.supportedTargetFormats;
- let header;
- try {
- header = ktx_parse(data);
- } catch (e) {
- throw new RuntimeError("Invalid KTX2 file.");
- }
- if (header.layerCount !== 0) {
- throw new RuntimeError("KTX2 texture arrays are not supported.");
- }
- if (header.pixelDepth !== 0) {
- throw new RuntimeError("KTX2 3D textures are unsupported.");
- }
- const dfd = header.dataFormatDescriptor[0];
- const result = new Array(header.levelCount);
- if (
- header.vkFormat === 0x0 &&
- (dfd.colorModel === colorModelETC1S || dfd.colorModel === colorModelUASTC)
- ) {
- // Compressed, initialize transcoder module
- transcodeCompressed(
- data,
- header,
- supportedTargetFormats,
- transcoderModule,
- transferableObjects,
- result
- );
- } else {
- transferableObjects.push(data.buffer);
- parseUncompressed(header, result);
- }
- return result;
- }
- // Parser for uncompressed
- function parseUncompressed(header, result) {
- const internalFormat =
- header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8_SRGB
- ? PixelFormat.RGB
- : PixelFormat.RGBA;
- let datatype;
- if (header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8A8_UNORM) {
- datatype = PixelDatatype.UNSIGNED_BYTE;
- } else if (
- header.vkFormat === VulkanConstants.VK_FORMAT_R16G16B16A16_SFLOAT
- ) {
- datatype = PixelDatatype.HALF_FLOAT;
- } else if (
- header.vkFormat === VulkanConstants.VK_FORMAT_R32G32B32A32_SFLOAT
- ) {
- datatype = PixelDatatype.FLOAT;
- }
- for (let i = 0; i < header.levels.length; ++i) {
- const level = {};
- result[i] = level;
- const levelBuffer = header.levels[i].levelData;
- const width = header.pixelWidth >> i;
- const height = header.pixelHeight >> i;
- const faceLength =
- width * height * PixelFormat.componentsLength(internalFormat);
- for (let j = 0; j < header.faceCount; ++j) {
- // multiply levelBuffer.byteOffset by the size in bytes of the pixel data type
- const faceByteOffset =
- levelBuffer.byteOffset + faceLength * header.typeSize * j;
- let faceView;
- if (!defined(datatype) || PixelDatatype.sizeInBytes(datatype) === 1) {
- faceView = new Uint8Array(
- levelBuffer.buffer,
- faceByteOffset,
- faceLength
- );
- } else if (PixelDatatype.sizeInBytes(datatype) === 2) {
- faceView = new Uint16Array(
- levelBuffer.buffer,
- faceByteOffset,
- faceLength
- );
- } else {
- faceView = new Float32Array(
- levelBuffer.buffer,
- faceByteOffset,
- faceLength
- );
- }
- level[faceOrder[j]] = {
- internalFormat: internalFormat,
- datatype: datatype,
- width: width,
- height: height,
- levelBuffer: faceView,
- };
- }
- }
- }
- function transcodeCompressed(
- data,
- header,
- supportedTargetFormats,
- transcoderModule,
- transferableObjects,
- result
- ) {
- const ktx2File = new transcoderModule.KTX2File(data);
- let width = ktx2File.getWidth();
- let height = ktx2File.getHeight();
- const levels = ktx2File.getLevels();
- const hasAlpha = ktx2File.getHasAlpha();
- if (!(width > 0) || !(height > 0) || !(levels > 0)) {
- ktx2File.close();
- ktx2File.delete();
- throw new RuntimeError("Invalid KTX2 file");
- }
- let internalFormat, transcoderFormat;
- const dfd = header.dataFormatDescriptor[0];
- const BasisFormat = transcoderModule.transcoder_texture_format;
- // Determine target format based on platform support
- if (dfd.colorModel === colorModelETC1S) {
- if (supportedTargetFormats.etc) {
- internalFormat = hasAlpha
- ? PixelFormat.RGBA8_ETC2_EAC
- : PixelFormat.RGB8_ETC2;
- transcoderFormat = hasAlpha
- ? BasisFormat.cTFETC2_RGBA
- : BasisFormat.cTFETC1_RGB;
- } else if (supportedTargetFormats.etc1 && !hasAlpha) {
- internalFormat = PixelFormat.RGB_ETC1;
- transcoderFormat = BasisFormat.cTFETC1_RGB;
- } else if (supportedTargetFormats.s3tc) {
- internalFormat = hasAlpha ? PixelFormat.RGBA_DXT5 : PixelFormat.RGB_DXT1;
- transcoderFormat = hasAlpha
- ? BasisFormat.cTFBC3_RGBA
- : BasisFormat.cTFBC1_RGB;
- } else if (supportedTargetFormats.pvrtc) {
- internalFormat = hasAlpha
- ? PixelFormat.RGBA_PVRTC_4BPPV1
- : PixelFormat.RGB_PVRTC_4BPPV1;
- transcoderFormat = hasAlpha
- ? BasisFormat.cTFPVRTC1_4_RGBA
- : BasisFormat.cTFPVRTC1_4_RGB;
- } else if (supportedTargetFormats.astc) {
- internalFormat = PixelFormat.RGBA_ASTC;
- transcoderFormat = BasisFormat.cTFASTC_4x4_RGBA;
- } else if (supportedTargetFormats.bc7) {
- internalFormat = PixelFormat.RGBA_BC7;
- transcoderFormat = BasisFormat.cTFBC7_RGBA;
- } else {
- throw new RuntimeError(
- "No transcoding format target available for ETC1S compressed ktx2."
- );
- }
- } else if (dfd.colorModel === colorModelUASTC) {
- if (supportedTargetFormats.astc) {
- internalFormat = PixelFormat.RGBA_ASTC;
- transcoderFormat = BasisFormat.cTFASTC_4x4_RGBA;
- } else if (supportedTargetFormats.bc7) {
- internalFormat = PixelFormat.RGBA_BC7;
- transcoderFormat = BasisFormat.cTFBC7_RGBA;
- } else if (supportedTargetFormats.s3tc) {
- internalFormat = hasAlpha ? PixelFormat.RGBA_DXT5 : PixelFormat.RGB_DXT1;
- transcoderFormat = hasAlpha
- ? BasisFormat.cTFBC3_RGBA
- : BasisFormat.cTFBC1_RGB;
- } else if (supportedTargetFormats.etc) {
- internalFormat = hasAlpha
- ? PixelFormat.RGBA8_ETC2_EAC
- : PixelFormat.RGB8_ETC2;
- transcoderFormat = hasAlpha
- ? BasisFormat.cTFETC2_RGBA
- : BasisFormat.cTFETC1_RGB;
- } else if (supportedTargetFormats.etc1 && !hasAlpha) {
- internalFormat = PixelFormat.RGB_ETC1;
- transcoderFormat = BasisFormat.cTFETC1_RGB;
- } else if (supportedTargetFormats.pvrtc) {
- internalFormat = hasAlpha
- ? PixelFormat.RGBA_PVRTC_4BPPV1
- : PixelFormat.RGB_PVRTC_4BPPV1;
- transcoderFormat = hasAlpha
- ? BasisFormat.cTFPVRTC1_4_RGBA
- : BasisFormat.cTFPVRTC1_4_RGB;
- } else {
- throw new RuntimeError(
- "No transcoding format target available for UASTC compressed ktx2."
- );
- }
- }
- if (!ktx2File.startTranscoding()) {
- ktx2File.close();
- ktx2File.delete();
- throw new RuntimeError("startTranscoding() failed");
- }
- for (let i = 0; i < header.levels.length; ++i) {
- const level = {};
- result[i] = level;
- width = header.pixelWidth >> i;
- height = header.pixelHeight >> i;
- // Since supercompressed cubemaps are unsupported, this function
- // does not iterate over KTX2 faces and assumes faceCount = 1.
- const dstSize = ktx2File.getImageTranscodedSizeInBytes(
- i, // level index
- 0, // layer index
- 0, // face index
- transcoderFormat.value
- );
- const dst = new Uint8Array(dstSize);
- const transcoded = ktx2File.transcodeImage(
- dst,
- i, // level index
- 0, // layer index
- 0, // face index
- transcoderFormat.value,
- 0, // get_alpha_for_opaque_formats
- -1, // channel0
- -1 // channel1
- );
- if (!defined(transcoded)) {
- throw new RuntimeError("transcodeImage() failed.");
- }
- transferableObjects.push(dst.buffer);
- level[faceOrder[0]] = {
- internalFormat: internalFormat,
- width: width,
- height: height,
- levelBuffer: dst,
- };
- }
- ktx2File.close();
- ktx2File.delete();
- return result;
- }
- function initWorker(compiledModule) {
- transcoderModule = compiledModule;
- transcoderModule.initializeBasis();
- self.onmessage = createTaskProcessorWorker(transcode);
- self.postMessage(true);
- }
- function transcodeKTX2(event) {
- const data = event.data;
- // Expect the first message to be to load a web assembly module
- const wasmConfig = data.webAssemblyConfig;
- if (defined(wasmConfig)) {
- // Require and compile WebAssembly module, or use fallback if not supported
- return require([wasmConfig.modulePath], function (mscBasisTranscoder) {
- if (defined(wasmConfig.wasmBinaryFile)) {
- if (!defined(mscBasisTranscoder)) {
- mscBasisTranscoder = self.MSC_TRANSCODER;
- }
- mscBasisTranscoder(wasmConfig).then(function (compiledModule) {
- initWorker(compiledModule);
- });
- } else {
- return mscBasisTranscoder().then(function (transcoder) {
- initWorker(transcoder);
- });
- }
- });
- }
- }
- export default transcodeKTX2;
|