/* 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";
|
|
var faceOrder = [
|
"positiveX",
|
"negativeX",
|
"positiveY",
|
"negativeY",
|
"positiveZ",
|
"negativeZ",
|
];
|
|
// Flags
|
var colorModelETC1S = 163;
|
var colorModelUASTC = 166;
|
|
var transcoderModule;
|
function transcode(parameters, transferableObjects) {
|
//>>includeStart('debug', pragmas.debug);
|
Check.typeOf.object("transcoderModule", transcoderModule);
|
//>>includeEnd('debug');
|
|
var data = parameters.ktx2Buffer;
|
var supportedTargetFormats = parameters.supportedTargetFormats;
|
var 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.");
|
}
|
|
var dfd = header.dataFormatDescriptor[0];
|
var 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) {
|
var internalFormat =
|
header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8_SRGB
|
? PixelFormat.RGB
|
: PixelFormat.RGBA;
|
var 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 (var i = 0; i < header.levels.length; ++i) {
|
var level = {};
|
result[i] = level;
|
var levelBuffer = header.levels[i].levelData;
|
|
var width = header.pixelWidth >> i;
|
var height = header.pixelHeight >> i;
|
var faceLength =
|
width * height * PixelFormat.componentsLength(internalFormat);
|
|
for (var j = 0; j < header.faceCount; ++j) {
|
// multiply levelBuffer.byteOffset by the size in bytes of the pixel data type
|
var faceByteOffset =
|
levelBuffer.byteOffset + faceLength * header.typeSize * j;
|
var 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
|
) {
|
var ktx2File = new transcoderModule.KTX2File(data);
|
var width = ktx2File.getWidth();
|
var height = ktx2File.getHeight();
|
var levels = ktx2File.getLevels();
|
var hasAlpha = ktx2File.getHasAlpha();
|
|
if (!(width > 0) || !(height > 0) || !(levels > 0)) {
|
ktx2File.close();
|
ktx2File.delete();
|
throw new RuntimeError("Invalid KTX2 file");
|
}
|
|
var internalFormat, transcoderFormat;
|
var dfd = header.dataFormatDescriptor[0];
|
var 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 (var i = 0; i < header.levels.length; ++i) {
|
var 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.
|
|
var dstSize = ktx2File.getImageTranscodedSizeInBytes(
|
i, // level index
|
0, // layer index
|
0, // face index
|
transcoderFormat.value
|
);
|
var dst = new Uint8Array(dstSize);
|
|
var 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) {
|
var data = event.data;
|
|
// Expect the first message to be to load a web assembly module
|
var 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;
|