Path: blob/master/thirdparty/libktx/lib/basis_transcode.cpp
9902 views
/* -*- tab-width: 4; -*- */1/* vi: set sw=2 ts=4 expandtab: */23/*4* Copyright 2019-2020 The Khronos Group Inc.5* SPDX-License-Identifier: Apache-2.06*/78/**9* @internal10* @file11* @~English12*13* @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures.14*15* Two worlds collide here too. More uglyness!16*17* @author Mark Callow, www.edgewise-consulting.com18*/1920#include <inttypes.h>21#include <stdio.h>22#include <KHR/khr_df.h>2324#include "dfdutils/dfd.h"25#include "ktx.h"26#include "ktxint.h"27#include "texture2.h"28#include "vkformat_enum.h"29#include "vk_format.h"30#include "basis_sgd.h"31#include "transcoder/basisu_file_headers.h"32#include "transcoder/basisu_transcoder.h"33#include "transcoder/basisu_transcoder_internal.h"3435#undef DECLARE_PRIVATE36#undef DECLARE_PROTECTED37#define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private)38#define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected)3940using namespace basisu;41using namespace basist;4243inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }4445inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }4647KTX_error_code48ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,49alpha_content_e alphaContent,50ktxTexture2* prototype,51ktx_transcode_fmt_e outputFormat,52ktx_transcode_flags transcodeFlags);53KTX_error_code54ktxTexture2_transcodeUastc(ktxTexture2* This,55alpha_content_e alphaContent,56ktxTexture2* prototype,57ktx_transcode_fmt_e outputFormat,58ktx_transcode_flags transcodeFlags);5960/**61* @memberof ktxTexture262* @ingroup reader63* @~English64* @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.65*66* If the texture contains BasisLZ supercompressed images, Inflates them from67* back to ETC1S then transcodes them to the specified block-compressed68* format. If the texture contains UASTC images, inflates them, if they have been69* supercompressed with zstd, then transcodes then to the specified format, The70* transcoded images replace the original images and the texture's fields including71* the DFD are modified to reflect the new format.72*73* These types of textures must be transcoded to a desired target74* block-compressed format before they can be uploaded to a GPU via a75* graphics API.76*77* The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,78* @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,79* @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,80* @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,81* @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,82* @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and83* @c KTX_TTF_BC1_OR_3.84*85* @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and86* @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_387* does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if88* @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha89* channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.90*91* Transcoding to ATC & FXT1 formats is not supported by libktx as there92* are no equivalent Vulkan formats.93*94* The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,95* @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.96*97* The following @p transcodeFlags are available.98*99* @sa ktxtexture2_CompressBasis().100*101* @param[in] This pointer to the ktxTexture2 object of interest.102* @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum103* specifying the target format.104* @param[in] transcodeFlags bitfield of flags modifying the transcode105* operation. @sa ktx_texture_decode_flags_e.106*107* @return KTX_SUCCESS on success, other KTX_* enum values on error.108*109* @exception KTX_FILE_DATA_ERROR110* Supercompression global data is corrupted.111* @exception KTX_INVALID_OPERATION112* The texture's format is not transcodable (not113* ETC1S/BasisLZ or UASTC).114* @exception KTX_INVALID_OPERATION115* Supercompression global data is missing, i.e.,116* the texture object is invalid.117* @exception KTX_INVALID_OPERATION118* Image data is missing, i.e., the texture object119* is invalid.120* @exception KTX_INVALID_OPERATION121* @p outputFormat is PVRTC1 but the texture does122* does not have power-of-two dimensions.123* @exception KTX_INVALID_VALUE @p outputFormat is invalid.124* @exception KTX_TRANSCODE_FAILED125* Something went wrong during transcoding.126* @exception KTX_UNSUPPORTED_FEATURE127* KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested128* or the specified transcode target has not been129* included in the library being used.130* @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.131*/132KTX_error_code133ktxTexture2_TranscodeBasis(ktxTexture2* This,134ktx_transcode_fmt_e outputFormat,135ktx_transcode_flags transcodeFlags)136{137uint32_t* BDB = This->pDfd + 1;138khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);139if (colorModel != KHR_DF_MODEL_UASTC140// Constructor has checked color model matches BASIS_LZ.141&& This->supercompressionScheme != KTX_SS_BASIS_LZ)142{143return KTX_INVALID_OPERATION; // Not in a transcodable format.144}145146DECLARE_PRIVATE(priv, This);147if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {148if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0)149return KTX_INVALID_OPERATION;150}151152if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) {153debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n");154return KTX_UNSUPPORTED_FEATURE;155}156157if (outputFormat == KTX_TTF_PVRTC1_4_RGB158|| outputFormat == KTX_TTF_PVRTC1_4_RGBA) {159if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) {160debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n");161return KTX_INVALID_OPERATION;162}163}164165const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB);166alpha_content_e alphaContent = eNone;167if (colorModel == KHR_DF_MODEL_ETC1S) {168if (KHR_DFDSAMPLECOUNT(BDB) == 2) {169uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID);170if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) {171alphaContent = eAlpha;172} else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){173alphaContent = eGreen;174} else {175return KTX_FILE_DATA_ERROR;176}177}178} else {179uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID);180if (channelId == KHR_DF_CHANNEL_UASTC_RGBA)181alphaContent = eAlpha;182else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG)183alphaContent = eGreen;184}185186VkFormat vkFormat;187188// Do some format mapping.189switch (outputFormat) {190case KTX_TTF_BC1_OR_3:191outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA192: KTX_TTF_BC1_RGB;193break;194case KTX_TTF_ETC:195outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA196: KTX_TTF_ETC1_RGB;197break;198case KTX_TTF_PVRTC1_4_RGBA:199// This transcoder does not write opaque alpha blocks.200outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC1_4_RGBA201: KTX_TTF_PVRTC1_4_RGB;202break;203case KTX_TTF_PVRTC2_4_RGBA:204// This transcoder does not write opaque alpha blocks.205outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA206: KTX_TTF_PVRTC2_4_RGB;207break;208default:209/*NOP*/;210}211212switch (outputFormat) {213case KTX_TTF_ETC1_RGB:214vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK215: VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;216break;217case KTX_TTF_ETC2_RGBA:218vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK219: VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;220break;221case KTX_TTF_ETC2_EAC_R11:222vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;223break;224case KTX_TTF_ETC2_EAC_RG11:225vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;226break;227case KTX_TTF_BC1_RGB:228// Transcoding doesn't support BC1 alpha.229vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK230: VK_FORMAT_BC1_RGB_UNORM_BLOCK;231break;232case KTX_TTF_BC3_RGBA:233vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK234: VK_FORMAT_BC3_UNORM_BLOCK;235break;236case KTX_TTF_BC4_R:237vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;238break;239case KTX_TTF_BC5_RG:240vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;241break;242case KTX_TTF_PVRTC1_4_RGB:243case KTX_TTF_PVRTC1_4_RGBA:244vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG245: VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;246break;247case KTX_TTF_PVRTC2_4_RGB:248case KTX_TTF_PVRTC2_4_RGBA:249vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG250: VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;251break;252case KTX_TTF_BC7_RGBA:253vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK254: VK_FORMAT_BC7_UNORM_BLOCK;255break;256case KTX_TTF_ASTC_4x4_RGBA:257vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK258: VK_FORMAT_ASTC_4x4_UNORM_BLOCK;259break;260case KTX_TTF_RGB565:261vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;262break;263case KTX_TTF_BGR565:264vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16;265break;266case KTX_TTF_RGBA4444:267vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;268break;269case KTX_TTF_RGBA32:270vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB271: VK_FORMAT_R8G8B8A8_UNORM;272break;273default:274return KTX_INVALID_VALUE;275}276277basis_tex_format textureFormat;278if (colorModel == KHR_DF_MODEL_UASTC)279textureFormat = basis_tex_format::cUASTC4x4;280else281textureFormat = basis_tex_format::cETC1S;282283if (!basis_is_format_supported((transcoder_texture_format)outputFormat,284textureFormat)) {285return KTX_UNSUPPORTED_FEATURE;286}287288289// Create a prototype texture to use for calculating sizes in the target290// format and, as useful side effects, provide us with a properly sized291// data allocation and the DFD for the target format.292ktxTextureCreateInfo createInfo;293createInfo.glInternalformat = 0;294createInfo.vkFormat = vkFormat;295createInfo.baseWidth = This->baseWidth;296createInfo.baseHeight = This->baseHeight;297createInfo.baseDepth = This->baseDepth;298createInfo.generateMipmaps = This->generateMipmaps;299createInfo.isArray = This->isArray;300createInfo.numDimensions = This->numDimensions;301createInfo.numFaces = This->numFaces;302createInfo.numLayers = This->numLayers;303createInfo.numLevels = This->numLevels;304createInfo.pDfd = nullptr;305306KTX_error_code result;307ktxTexture2* prototype;308result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE,309&prototype);310311if (result != KTX_SUCCESS) {312assert(result == KTX_OUT_OF_MEMORY); // The only run time error313return result;314}315316if (!This->pData) {317if (ktxTexture_isActiveStream((ktxTexture*)This)) {318// Load pending. Complete it.319result = ktxTexture2_LoadImageData(This, NULL, 0);320if (result != KTX_SUCCESS)321{322ktxTexture2_Destroy(prototype);323return result;324}325} else {326// No data to transcode.327ktxTexture2_Destroy(prototype);328return KTX_INVALID_OPERATION;329}330}331332// Transcoder global initialization. Requires ~9 milliseconds when compiled333// and executed natively on a Core i7 2.2 GHz. If this is too slow, the334// tables it computes can easily be moved to be compiled in.335static bool transcoderInitialized;336if (!transcoderInitialized) {337basisu_transcoder_init();338transcoderInitialized = true;339}340341if (textureFormat == basis_tex_format::cETC1S) {342result = ktxTexture2_transcodeLzEtc1s(This, alphaContent,343prototype, outputFormat,344transcodeFlags);345} else {346result = ktxTexture2_transcodeUastc(This, alphaContent,347prototype, outputFormat,348transcodeFlags);349}350351if (result == KTX_SUCCESS) {352// Fix up the current texture353DECLARE_PROTECTED(thisPrtctd, This);354DECLARE_PRIVATE(protoPriv, prototype);355DECLARE_PROTECTED(protoPrtctd, prototype);356memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize,357sizeof(ktxFormatSize));358This->vkFormat = vkFormat;359This->isCompressed = prototype->isCompressed;360This->supercompressionScheme = KTX_SS_NONE;361priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment;362// Copy the levelIndex from the prototype to This.363memcpy(priv._levelIndex, protoPriv._levelIndex,364This->numLevels * sizeof(ktxLevelIndexEntry));365// Move the DFD and data from the prototype to This.366free(This->pDfd);367This->pDfd = prototype->pDfd;368prototype->pDfd = 0;369free(This->pData);370This->pData = prototype->pData;371This->dataSize = prototype->dataSize;372prototype->pData = 0;373prototype->dataSize = 0;374// Free SGD data375This->_private->_sgdByteLength = 0;376if (This->_private->_supercompressionGlobalData) {377free(This->_private->_supercompressionGlobalData);378This->_private->_supercompressionGlobalData = NULL;379}380}381ktxTexture2_Destroy(prototype);382return result;383}384385/**386* @memberof ktxTexture2 @private387* @ingroup reader388* @~English389* @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images.390*391* Inflates the images from BasisLZ supercompression back to ETC1S392* then transcodes them to the specified block-compressed format. The393* transcoded images replace the original images and the texture's fields394* including the DFD are modified to reflect the new format.395*396* BasisLZ supercompressed textures must be transcoded to a desired target397* block-compressed format before they can be uploaded to a GPU via a graphics398* API.399*400* The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,401* @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,402* @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,403* @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,404* @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,405* @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and406* @c KTX_TTF_BC1_OR_3.407*408* @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and409* @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3410* does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if411* @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha412* channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.413*414* ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats.415*416* The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,417* @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.418*419* The following @p transcodeFlags are available.420*421* @sa ktxtexture2_CompressBasis().422*423* @param[in] This pointer to the ktxTexture2 object of interest.424* @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum425* specifying the target format.426* @param[in] transcodeFlags bitfield of flags modifying the transcode427* operation. @sa ktx_texture_decode_flags_e.428*429* @return KTX_SUCCESS on success, other KTX_* enum values on error.430*431* @exception KTX_FILE_DATA_ERROR432* Supercompression global data is corrupted.433* @exception KTX_INVALID_OPERATION434* The texture's format is not transcodable (not435* ETC1S/BasisLZ or UASTC).436* @exception KTX_INVALID_OPERATION437* Supercompression global data is missing, i.e.,438* the texture object is invalid.439* @exception KTX_INVALID_OPERATION440* Image data is missing, i.e., the texture object441* is invalid.442* @exception KTX_INVALID_OPERATION443* @p outputFormat is PVRTC1 but the texture does444* does not have power-of-two dimensions.445* @exception KTX_INVALID_VALUE @p outputFormat is invalid.446* @exception KTX_TRANSCODE_FAILED447* Something went wrong during transcoding. The448* texture object will be corrupted.449* @exception KTX_UNSUPPORTED_FEATURE450* KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested451* or the specified transcode target has not been452* included in the library being used.453* @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.454*/455KTX_error_code456ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,457alpha_content_e alphaContent,458ktxTexture2* prototype,459ktx_transcode_fmt_e outputFormat,460ktx_transcode_flags transcodeFlags)461{462DECLARE_PRIVATE(priv, This);463DECLARE_PRIVATE(protoPriv, prototype);464KTX_error_code result = KTX_SUCCESS;465466assert(This->supercompressionScheme == KTX_SS_BASIS_LZ);467468uint8_t* bgd = priv._supercompressionGlobalData;469ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);470if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) {471debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables");472return KTX_FILE_DATA_ERROR;473}474475// Compute some helpful numbers.476//477// firstImages contains the indices of the first images for each level to478// ease finding the correct slice description when iterating from smallest479// level to largest or when randomly accessing them (t.b.c). The last array480// entry contains the total number of images, for calculating the offsets481// of the endpoints, etc.482uint32_t* firstImages = new uint32_t[This->numLevels+1];483484// Temporary invariant value485uint32_t layersFaces = This->numLayers * This->numFaces;486firstImages[0] = 0;487for (uint32_t level = 1; level <= This->numLevels; level++) {488// NOTA BENE: numFaces * depth is only reasonable because they can't489// both be > 1. I.e there are no 3d cubemaps.490firstImages[level] = firstImages[level - 1]491+ layersFaces * MAX(This->baseDepth >> (level - 1), 1);492}493uint32_t& imageCount = firstImages[This->numLevels];494495if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) {496// Compiler will not allow `goto cleanup;` because "jump bypasses variable initialization."497// The static initializations below this and before the loop are presumably the issue498// as the compiler is,presumably, inserting code to destruct those at the end of the499// function.500delete[] firstImages;501return KTX_FILE_DATA_ERROR;502}503// FIXME: Do more validation.504505// Prepare low-level transcoder for transcoding slices.506basist::basisu_lowlevel_etc1s_transcoder bit;507508// basisu_transcoder_state is used to find the previous frame when509// decoding a video P-Frame. It tracks the previous frame for each mip510// level. For cube map array textures we need to find the previous frame511// for each face so we a state per face. Although providing this is only512// needed for video, it is easier to always pass our own.513std::vector<basisu_transcoder_state> xcoderStates;514xcoderStates.resize(This->isVideo ? This->numFaces : 1);515516bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount),517bgdh.endpointsByteLength,518bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount),519bgdh.selectorsByteLength);520521bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount),522bgdh.tablesByteLength);523524// Find matching VkFormat and calculate output sizes.525526const bool isVideo = This->isVideo;527528ktx_uint8_t* pXcodedData = prototype->pData;529// Inconveniently, the output buffer size parameter of transcode_image530// has to be in pixels for uncompressed output and in blocks for531// compressed output. The only reason for humouring the API is so532// its buffer size tests provide a real check. An alternative is to533// always provide the size in bytes which will always pass.534ktx_uint32_t outputBlockByteLength535= prototype->_protected->_formatSize.blockSizeInBits / 8;536ktx_size_t xcodedDataLength537= prototype->dataSize / outputBlockByteLength;538ktxLevelIndexEntry* protoLevelIndex;539uint64_t levelOffsetWrite;540const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd);541542// Finally we're ready to transcode the slices.543544// FIXME: Iframe flag needs to be queryable by the application. In Basis545// the app can query file_info and image_info from the transcoder which546// returns a structure with lots of info about the image.547548protoLevelIndex = protoPriv._levelIndex;549levelOffsetWrite = 0;550for (int32_t level = This->numLevels - 1; level >= 0; level--) {551uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level);552uint64_t writeOffset = levelOffsetWrite;553uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;554uint32_t levelWidth = MAX(1, This->baseWidth >> level);555uint32_t levelHeight = MAX(1, This->baseHeight >> level);556// ETC1S texel block dimensions557const uint32_t bw = 4, bh = 4;558uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;559uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;560uint32_t depth = MAX(1, This->baseDepth >> level);561//uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;562uint32_t faceSlices = This->numFaces * depth;563uint32_t numImages = This->numLayers * faceSlices;564uint32_t image = firstImages[level];565uint32_t endImage = image + numImages;566ktx_size_t levelImageSizeOut, levelSizeOut;567uint32_t stateIndex = 0;568569levelSizeOut = 0;570// FIXME: Figure out a way to get the size out of the transcoder.571levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level);572for (; image < endImage; image++) {573const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image];574575basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];576// We have face0 [face1 ...] within each layer. Use `stateIndex`577// rather than a double loop of layers and faceSlices as this578// works for 3d texture and non-array cube maps as well as579// cube map arrays without special casing.580if (++stateIndex == xcoderStates.size())581stateIndex = 0;582583if (alphaContent != eNone)584{585// The slice descriptions should have alpha information.586if (imageDesc.alphaSliceByteOffset == 0587|| imageDesc.alphaSliceByteLength == 0)588return KTX_FILE_DATA_ERROR;589}590591bool status;592status = bit.transcode_image(593(transcoder_texture_format)outputFormat,594pXcodedData + writeOffset,595(uint32_t)(xcodedDataLength - writeOffsetBlocks),596This->pData,597(uint32_t)This->dataSize,598levelBlocksX,599levelBlocksY,600levelWidth,601levelHeight,602level,603(uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset),604imageDesc.rgbSliceByteLength,605(uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset),606imageDesc.alphaSliceByteLength,607transcodeFlags,608alphaContent != eNone,609isVideo,610// Our P-Frame flag is in the same bit as611// cSliceDescFlagsFrameIsIFrame. We have to612// invert it to make it an I-Frame flag.613//614// API currently doesn't have any way to pass615// the I-Frame flag.616//imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,6170, // output_row_pitch_in_blocks_or_pixels618&xcoderState,6190 // output_rows_in_pixels620);621if (!status) {622result = KTX_TRANSCODE_FAILED;623goto cleanup;624}625626writeOffset += levelImageSizeOut;627levelSizeOut += levelImageSizeOut;628} // end images loop629protoLevelIndex[level].byteOffset = levelOffsetWrite;630protoLevelIndex[level].byteLength = levelSizeOut;631protoLevelIndex[level].uncompressedByteLength = levelSizeOut;632levelOffsetWrite += levelSizeOut;633assert(levelOffsetWrite == writeOffset);634// In case of transcoding to uncompressed.635levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,636levelOffsetWrite);637} // level loop638639result = KTX_SUCCESS;640641cleanup:642delete[] firstImages;643return result;644}645646647KTX_error_code648ktxTexture2_transcodeUastc(ktxTexture2* This,649alpha_content_e alphaContent,650ktxTexture2* prototype,651ktx_transcode_fmt_e outputFormat,652ktx_transcode_flags transcodeFlags)653{654assert(This->supercompressionScheme != KTX_SS_BASIS_LZ);655656ktx_uint8_t* pXcodedData = prototype->pData;657ktx_uint32_t outputBlockByteLength658= prototype->_protected->_formatSize.blockSizeInBits / 8;659ktx_size_t xcodedDataLength660= prototype->dataSize / outputBlockByteLength;661DECLARE_PRIVATE(protoPriv, prototype);662ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex;663ktx_size_t levelOffsetWrite = 0;664665basisu_lowlevel_uastc_ldr_4x4_transcoder uit;666// See comment on same declaration in transcodeEtc1s.667std::vector<basisu_transcoder_state> xcoderStates;668xcoderStates.resize(This->isVideo ? This->numFaces : 1);669670for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)671{672ktx_uint32_t depth;673uint64_t writeOffset = levelOffsetWrite;674uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;675ktx_size_t levelImageSizeIn, levelImageOffsetIn;676ktx_size_t levelImageSizeOut, levelSizeOut;677ktx_uint32_t levelImageCount;678uint32_t levelWidth = MAX(1, This->baseWidth >> level);679uint32_t levelHeight = MAX(1, This->baseHeight >> level);680// UASTC texel block dimensions681const uint32_t bw = 4, bh = 4;682uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;683uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;684uint32_t stateIndex = 0;685686depth = MAX(1, This->baseDepth >> level);687688levelImageCount = This->numLayers * This->numFaces * depth;689levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level,690KTX_FORMAT_VERSION_TWO);691levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype),692level,693KTX_FORMAT_VERSION_TWO);694695levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level);696levelSizeOut = 0;697bool status;698for (uint32_t image = 0; image < levelImageCount; image++) {699basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];700// See comment before same lines in transcodeEtc1s.701if (++stateIndex == xcoderStates.size())702stateIndex = 0;703704status = uit.transcode_image(705(transcoder_texture_format)outputFormat,706pXcodedData + writeOffset,707(uint32_t)(xcodedDataLength - writeOffsetBlocks),708This->pData,709(uint32_t)This->dataSize,710levelBlocksX,711levelBlocksY,712levelWidth,713levelHeight,714level,715(uint32_t)levelImageOffsetIn,716(uint32_t)levelImageSizeIn,717transcodeFlags,718alphaContent != eNone,719This->isVideo, // is_video720//imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,7210, // output_row_pitch_in_blocks_or_pixels722&xcoderState, // pState7230, // output_rows_in_pixels,724-1, // channel0725-1 // channel1726);727if (!status)728return KTX_TRANSCODE_FAILED;729writeOffset += levelImageSizeOut;730levelSizeOut += levelImageSizeOut;731levelImageOffsetIn += levelImageSizeIn;732}733protoLevelIndex[level].byteOffset = levelOffsetWrite;734// writeOffset will be equal to total size of the images in the level.735protoLevelIndex[level].byteLength = levelSizeOut;736protoLevelIndex[level].uncompressedByteLength = levelSizeOut;737levelOffsetWrite += levelSizeOut;738}739// In case of transcoding to uncompressed.740levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,741levelOffsetWrite);742return KTX_SUCCESS;743}744745746