123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- /* -*- tab-width: 4; -*- */
- /* vi: set sw=2 ts=4 expandtab: */
- /*
- * Copyright 2019-2020 The Khronos Group Inc.
- * SPDX-License-Identifier: Apache-2.0
- */
- /**
- * @internal
- * @file basis_transcode.cpp
- * @~English
- *
- * @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures.
- *
- * Two worlds collide here too. More uglyness!
- *
- * @author Mark Callow, www.edgewise-consulting.com
- */
- #include <inttypes.h>
- #include <stdio.h>
- #include <KHR/khr_df.h>
- #include "dfdutils/dfd.h"
- #include "ktx.h"
- #include "ktxint.h"
- #include "texture2.h"
- #include "vkformat_enum.h"
- #include "vk_format.h"
- #include "basis_sgd.h"
- #include "transcoder/basisu_file_headers.h"
- #include "transcoder/basisu_transcoder.h"
- #include "transcoder/basisu_transcoder_internal.h"
- #undef DECLARE_PRIVATE
- #undef DECLARE_PROTECTED
- #define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private)
- #define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected)
- using namespace basisu;
- using namespace basist;
- inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
- inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
- KTX_error_code
- ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
- alpha_content_e alphaContent,
- ktxTexture2* prototype,
- ktx_transcode_fmt_e outputFormat,
- ktx_transcode_flags transcodeFlags);
- KTX_error_code
- ktxTexture2_transcodeUastc(ktxTexture2* This,
- alpha_content_e alphaContent,
- ktxTexture2* prototype,
- ktx_transcode_fmt_e outputFormat,
- ktx_transcode_flags transcodeFlags);
- /**
- * @memberof ktxTexture2
- * @ingroup reader
- * @~English
- * @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.
- *
- * If the texture contains BasisLZ supercompressed images, Inflates them from
- * back to ETC1S then transcodes them to the specified block-compressed
- * format. If the texture contains UASTC images, inflates them, if they have been
- * supercompressed with zstd, then transcodes then to the specified format, The
- * transcoded images replace the original images and the texture's fields including
- * the DFD are modified to reflect the new format.
- *
- * These types of textures must be transcoded to a desired target
- * block-compressed format before they can be uploaded to a GPU via a
- * graphics API.
- *
- * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
- * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
- * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
- * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
- * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
- * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
- * @c KTX_TTF_BC1_OR_3.
- *
- * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
- * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
- * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
- * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
- * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
- *
- * Transcoding to ATC & FXT1 formats is not supported by libktx as there
- * are no equivalent Vulkan formats.
- *
- * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
- * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
- *
- * The following @p transcodeFlags are available.
- *
- * @sa ktxtexture2_CompressBasis().
- *
- * @param[in] This pointer to the ktxTexture2 object of interest.
- * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
- * specifying the target format.
- * @param[in] transcodeFlags bitfield of flags modifying the transcode
- * operation. @sa ktx_texture_decode_flags_e.
- *
- * @return KTX_SUCCESS on success, other KTX_* enum values on error.
- *
- * @exception KTX_FILE_DATA_ERROR
- * Supercompression global data is corrupted.
- * @exception KTX_INVALID_OPERATION
- * The texture's format is not transcodable (not
- * ETC1S/BasisLZ or UASTC).
- * @exception KTX_INVALID_OPERATION
- * Supercompression global data is missing, i.e.,
- * the texture object is invalid.
- * @exception KTX_INVALID_OPERATION
- * Image data is missing, i.e., the texture object
- * is invalid.
- * @exception KTX_INVALID_OPERATION
- * @p outputFormat is PVRTC1 but the texture does
- * does not have power-of-two dimensions.
- * @exception KTX_INVALID_VALUE @p outputFormat is invalid.
- * @exception KTX_TRANSCODE_FAILED
- * Something went wrong during transcoding.
- * @exception KTX_UNSUPPORTED_FEATURE
- * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
- * or the specified transcode target has not been
- * included in the library being used.
- * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
- */
- KTX_error_code
- ktxTexture2_TranscodeBasis(ktxTexture2* This,
- ktx_transcode_fmt_e outputFormat,
- ktx_transcode_flags transcodeFlags)
- {
- uint32_t* BDB = This->pDfd + 1;
- khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);
- if (colorModel != KHR_DF_MODEL_UASTC
- // Constructor has checked color model matches BASIS_LZ.
- && This->supercompressionScheme != KTX_SS_BASIS_LZ)
- {
- return KTX_INVALID_OPERATION; // Not in a transcodable format.
- }
- DECLARE_PRIVATE(priv, This);
- if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {
- if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0)
- return KTX_INVALID_OPERATION;
- }
- if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) {
- debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n");
- return KTX_UNSUPPORTED_FEATURE;
- }
- if (outputFormat == KTX_TTF_PVRTC1_4_RGB
- || outputFormat == KTX_TTF_PVRTC1_4_RGBA) {
- if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) {
- debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n");
- return KTX_INVALID_OPERATION;
- }
- }
- const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB);
- alpha_content_e alphaContent = eNone;
- if (colorModel == KHR_DF_MODEL_ETC1S) {
- if (KHR_DFDSAMPLECOUNT(BDB) == 2) {
- uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID);
- if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) {
- alphaContent = eAlpha;
- } else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){
- alphaContent = eGreen;
- } else {
- return KTX_FILE_DATA_ERROR;
- }
- }
- } else {
- uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID);
- if (channelId == KHR_DF_CHANNEL_UASTC_RGBA)
- alphaContent = eAlpha;
- else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG)
- alphaContent = eGreen;
- }
- VkFormat vkFormat;
- // Do some format mapping.
- switch (outputFormat) {
- case KTX_TTF_BC1_OR_3:
- outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA
- : KTX_TTF_BC1_RGB;
- break;
- case KTX_TTF_ETC:
- outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA
- : KTX_TTF_ETC1_RGB;
- break;
- case KTX_TTF_PVRTC1_4_RGBA:
- // This transcoder does not write opaque alpha blocks.
- outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC1_4_RGBA
- : KTX_TTF_PVRTC1_4_RGB;
- break;
- case KTX_TTF_PVRTC2_4_RGBA:
- // This transcoder does not write opaque alpha blocks.
- outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA
- : KTX_TTF_PVRTC2_4_RGB;
- break;
- default:
- /*NOP*/;
- }
- switch (outputFormat) {
- case KTX_TTF_ETC1_RGB:
- vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK
- : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
- break;
- case KTX_TTF_ETC2_RGBA:
- vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK
- : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
- break;
- case KTX_TTF_ETC2_EAC_R11:
- vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
- break;
- case KTX_TTF_ETC2_EAC_RG11:
- vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
- break;
- case KTX_TTF_BC1_RGB:
- // Transcoding doesn't support BC1 alpha.
- vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK
- : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
- break;
- case KTX_TTF_BC3_RGBA:
- vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK
- : VK_FORMAT_BC3_UNORM_BLOCK;
- break;
- case KTX_TTF_BC4_R:
- vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
- break;
- case KTX_TTF_BC5_RG:
- vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
- break;
- case KTX_TTF_PVRTC1_4_RGB:
- case KTX_TTF_PVRTC1_4_RGBA:
- vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG
- : VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
- break;
- case KTX_TTF_PVRTC2_4_RGB:
- case KTX_TTF_PVRTC2_4_RGBA:
- vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG
- : VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
- break;
- case KTX_TTF_BC7_RGBA:
- vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK
- : VK_FORMAT_BC7_UNORM_BLOCK;
- break;
- case KTX_TTF_ASTC_4x4_RGBA:
- vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK
- : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
- break;
- case KTX_TTF_RGB565:
- vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
- break;
- case KTX_TTF_BGR565:
- vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16;
- break;
- case KTX_TTF_RGBA4444:
- vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
- break;
- case KTX_TTF_RGBA32:
- vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB
- : VK_FORMAT_R8G8B8A8_UNORM;
- break;
- default:
- return KTX_INVALID_VALUE;
- }
- basis_tex_format textureFormat;
- if (colorModel == KHR_DF_MODEL_UASTC)
- textureFormat = basis_tex_format::cUASTC4x4;
- else
- textureFormat = basis_tex_format::cETC1S;
- if (!basis_is_format_supported((transcoder_texture_format)outputFormat,
- textureFormat)) {
- return KTX_UNSUPPORTED_FEATURE;
- }
- // Create a prototype texture to use for calculating sizes in the target
- // format and, as useful side effects, provide us with a properly sized
- // data allocation and the DFD for the target format.
- ktxTextureCreateInfo createInfo;
- createInfo.glInternalformat = 0;
- createInfo.vkFormat = vkFormat;
- createInfo.baseWidth = This->baseWidth;
- createInfo.baseHeight = This->baseHeight;
- createInfo.baseDepth = This->baseDepth;
- createInfo.generateMipmaps = This->generateMipmaps;
- createInfo.isArray = This->isArray;
- createInfo.numDimensions = This->numDimensions;
- createInfo.numFaces = This->numFaces;
- createInfo.numLayers = This->numLayers;
- createInfo.numLevels = This->numLevels;
- createInfo.pDfd = nullptr;
- KTX_error_code result;
- ktxTexture2* prototype;
- result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE,
- &prototype);
- if (result != KTX_SUCCESS) {
- assert(result == KTX_OUT_OF_MEMORY); // The only run time error
- return result;
- }
- if (!This->pData) {
- if (ktxTexture_isActiveStream((ktxTexture*)This)) {
- // Load pending. Complete it.
- result = ktxTexture2_LoadImageData(This, NULL, 0);
- if (result != KTX_SUCCESS)
- {
- ktxTexture2_Destroy(prototype);
- return result;
- }
- } else {
- // No data to transcode.
- ktxTexture2_Destroy(prototype);
- return KTX_INVALID_OPERATION;
- }
- }
- // Transcoder global initialization. Requires ~9 milliseconds when compiled
- // and executed natively on a Core i7 2.2 GHz. If this is too slow, the
- // tables it computes can easily be moved to be compiled in.
- static bool transcoderInitialized;
- if (!transcoderInitialized) {
- basisu_transcoder_init();
- transcoderInitialized = true;
- }
- if (textureFormat == basis_tex_format::cETC1S) {
- result = ktxTexture2_transcodeLzEtc1s(This, alphaContent,
- prototype, outputFormat,
- transcodeFlags);
- } else {
- result = ktxTexture2_transcodeUastc(This, alphaContent,
- prototype, outputFormat,
- transcodeFlags);
- }
- if (result == KTX_SUCCESS) {
- // Fix up the current texture
- DECLARE_PROTECTED(thisPrtctd, This);
- DECLARE_PRIVATE(protoPriv, prototype);
- DECLARE_PROTECTED(protoPrtctd, prototype);
- memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize,
- sizeof(ktxFormatSize));
- This->vkFormat = vkFormat;
- This->isCompressed = prototype->isCompressed;
- This->supercompressionScheme = KTX_SS_NONE;
- priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment;
- // Copy the levelIndex from the prototype to This.
- memcpy(priv._levelIndex, protoPriv._levelIndex,
- This->numLevels * sizeof(ktxLevelIndexEntry));
- // Move the DFD and data from the prototype to This.
- free(This->pDfd);
- This->pDfd = prototype->pDfd;
- prototype->pDfd = 0;
- free(This->pData);
- This->pData = prototype->pData;
- This->dataSize = prototype->dataSize;
- prototype->pData = 0;
- prototype->dataSize = 0;
- }
- ktxTexture2_Destroy(prototype);
- return result;
- }
- /**
- * @memberof ktxTexture2 @private
- * @ingroup reader
- * @~English
- * @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images.
- *
- * Inflates the images from BasisLZ supercompression back to ETC1S
- * then transcodes them to the specified block-compressed format. The
- * transcoded images replace the original images and the texture's fields
- * including the DFD are modified to reflect the new format.
- *
- * BasisLZ supercompressed textures must be transcoded to a desired target
- * block-compressed format before they can be uploaded to a GPU via a graphics
- * API.
- *
- * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
- * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
- * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
- * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
- * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
- * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
- * @c KTX_TTF_BC1_OR_3.
- *
- * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
- * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
- * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
- * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
- * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
- *
- * ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats.
- *
- * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
- * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
- *
- * The following @p transcodeFlags are available.
- *
- * @sa ktxtexture2_CompressBasis().
- *
- * @param[in] This pointer to the ktxTexture2 object of interest.
- * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
- * specifying the target format.
- * @param[in] transcodeFlags bitfield of flags modifying the transcode
- * operation. @sa ktx_texture_decode_flags_e.
- *
- * @return KTX_SUCCESS on success, other KTX_* enum values on error.
- *
- * @exception KTX_FILE_DATA_ERROR
- * Supercompression global data is corrupted.
- * @exception KTX_INVALID_OPERATION
- * The texture's format is not transcodable (not
- * ETC1S/BasisLZ or UASTC).
- * @exception KTX_INVALID_OPERATION
- * Supercompression global data is missing, i.e.,
- * the texture object is invalid.
- * @exception KTX_INVALID_OPERATION
- * Image data is missing, i.e., the texture object
- * is invalid.
- * @exception KTX_INVALID_OPERATION
- * @p outputFormat is PVRTC1 but the texture does
- * does not have power-of-two dimensions.
- * @exception KTX_INVALID_VALUE @p outputFormat is invalid.
- * @exception KTX_TRANSCODE_FAILED
- * Something went wrong during transcoding. The
- * texture object will be corrupted.
- * @exception KTX_UNSUPPORTED_FEATURE
- * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
- * or the specified transcode target has not been
- * included in the library being used.
- * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
- */
- KTX_error_code
- ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
- alpha_content_e alphaContent,
- ktxTexture2* prototype,
- ktx_transcode_fmt_e outputFormat,
- ktx_transcode_flags transcodeFlags)
- {
- DECLARE_PRIVATE(priv, This);
- DECLARE_PRIVATE(protoPriv, prototype);
- KTX_error_code result = KTX_SUCCESS;
- assert(This->supercompressionScheme == KTX_SS_BASIS_LZ);
- uint8_t* bgd = priv._supercompressionGlobalData;
- ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);
- if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) {
- debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables");
- return KTX_FILE_DATA_ERROR;
- }
- // Compute some helpful numbers.
- //
- // firstImages contains the indices of the first images for each level to
- // ease finding the correct slice description when iterating from smallest
- // level to largest or when randomly accessing them (t.b.c). The last array
- // entry contains the total number of images, for calculating the offsets
- // of the endpoints, etc.
- uint32_t* firstImages = new uint32_t[This->numLevels+1];
- // Temporary invariant value
- uint32_t layersFaces = This->numLayers * This->numFaces;
- firstImages[0] = 0;
- for (uint32_t level = 1; level <= This->numLevels; level++) {
- // NOTA BENE: numFaces * depth is only reasonable because they can't
- // both be > 1. I.e there are no 3d cubemaps.
- firstImages[level] = firstImages[level - 1]
- + layersFaces * MAX(This->baseDepth >> (level - 1), 1);
- }
- uint32_t& imageCount = firstImages[This->numLevels];
- if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) {
- return KTX_FILE_DATA_ERROR;
- }
- // FIXME: Do more validation.
- // Prepare low-level transcoder for transcoding slices.
- basist::basisu_lowlevel_etc1s_transcoder bit;
- // basisu_transcoder_state is used to find the previous frame when
- // decoding a video P-Frame. It tracks the previous frame for each mip
- // level. For cube map array textures we need to find the previous frame
- // for each face so we a state per face. Although providing this is only
- // needed for video, it is easier to always pass our own.
- std::vector<basisu_transcoder_state> xcoderStates;
- xcoderStates.resize(This->isVideo ? This->numFaces : 1);
- bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount),
- bgdh.endpointsByteLength,
- bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount),
- bgdh.selectorsByteLength);
- bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount),
- bgdh.tablesByteLength);
- // Find matching VkFormat and calculate output sizes.
- const bool isVideo = This->isVideo;
- ktx_uint8_t* pXcodedData = prototype->pData;
- // Inconveniently, the output buffer size parameter of transcode_image
- // has to be in pixels for uncompressed output and in blocks for
- // compressed output. The only reason for humouring the API is so
- // its buffer size tests provide a real check. An alternative is to
- // always provide the size in bytes which will always pass.
- ktx_uint32_t outputBlockByteLength
- = prototype->_protected->_formatSize.blockSizeInBits / 8;
- ktx_size_t xcodedDataLength
- = prototype->dataSize / outputBlockByteLength;
- ktxLevelIndexEntry* protoLevelIndex;
- uint64_t levelOffsetWrite;
- const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd);
- // Finally we're ready to transcode the slices.
- // FIXME: Iframe flag needs to be queryable by the application. In Basis
- // the app can query file_info and image_info from the transcoder which
- // returns a structure with lots of info about the image.
- protoLevelIndex = protoPriv._levelIndex;
- levelOffsetWrite = 0;
- for (int32_t level = This->numLevels - 1; level >= 0; level--) {
- uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level);
- uint64_t writeOffset = levelOffsetWrite;
- uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
- uint32_t levelWidth = MAX(1, This->baseWidth >> level);
- uint32_t levelHeight = MAX(1, This->baseHeight >> level);
- // ETC1S texel block dimensions
- const uint32_t bw = 4, bh = 4;
- uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
- uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
- uint32_t depth = MAX(1, This->baseDepth >> level);
- //uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;
- uint32_t faceSlices = This->numFaces * depth;
- uint32_t numImages = This->numLayers * faceSlices;
- uint32_t image = firstImages[level];
- uint32_t endImage = image + numImages;
- ktx_size_t levelImageSizeOut, levelSizeOut;
- uint32_t stateIndex = 0;
- levelSizeOut = 0;
- // FIXME: Figure out a way to get the size out of the transcoder.
- levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level);
- for (; image < endImage; image++) {
- const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image];
- basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
- // We have face0 [face1 ...] within each layer. Use `stateIndex`
- // rather than a double loop of layers and faceSlices as this
- // works for 3d texture and non-array cube maps as well as
- // cube map arrays without special casing.
- if (++stateIndex == xcoderStates.size())
- stateIndex = 0;
- if (alphaContent != eNone)
- {
- // The slice descriptions should have alpha information.
- if (imageDesc.alphaSliceByteOffset == 0
- || imageDesc.alphaSliceByteLength == 0)
- return KTX_FILE_DATA_ERROR;
- }
- bool status;
- status = bit.transcode_image(
- (transcoder_texture_format)outputFormat,
- pXcodedData + writeOffset,
- (uint32_t)(xcodedDataLength - writeOffsetBlocks),
- This->pData,
- (uint32_t)This->dataSize,
- levelBlocksX,
- levelBlocksY,
- levelWidth,
- levelHeight,
- level,
- (uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset),
- imageDesc.rgbSliceByteLength,
- (uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset),
- imageDesc.alphaSliceByteLength,
- transcodeFlags,
- alphaContent != eNone,
- isVideo,
- // Our P-Frame flag is in the same bit as
- // cSliceDescFlagsFrameIsIFrame. We have to
- // invert it to make it an I-Frame flag.
- //
- // API currently doesn't have any way to pass
- // the I-Frame flag.
- //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
- 0, // output_row_pitch_in_blocks_or_pixels
- &xcoderState,
- 0 // output_rows_in_pixels
- );
- if (!status) {
- result = KTX_TRANSCODE_FAILED;
- goto cleanup;
- }
- writeOffset += levelImageSizeOut;
- levelSizeOut += levelImageSizeOut;
- } // end images loop
- protoLevelIndex[level].byteOffset = levelOffsetWrite;
- protoLevelIndex[level].byteLength = levelSizeOut;
- protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
- levelOffsetWrite += levelSizeOut;
- assert(levelOffsetWrite == writeOffset);
- // In case of transcoding to uncompressed.
- levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
- levelOffsetWrite);
- } // level loop
- result = KTX_SUCCESS;
- cleanup:
- delete[] firstImages;
- return result;
- }
- KTX_error_code
- ktxTexture2_transcodeUastc(ktxTexture2* This,
- alpha_content_e alphaContent,
- ktxTexture2* prototype,
- ktx_transcode_fmt_e outputFormat,
- ktx_transcode_flags transcodeFlags)
- {
- assert(This->supercompressionScheme != KTX_SS_BASIS_LZ);
- ktx_uint8_t* pXcodedData = prototype->pData;
- ktx_uint32_t outputBlockByteLength
- = prototype->_protected->_formatSize.blockSizeInBits / 8;
- ktx_size_t xcodedDataLength
- = prototype->dataSize / outputBlockByteLength;
- DECLARE_PRIVATE(protoPriv, prototype);
- ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex;
- ktx_size_t levelOffsetWrite = 0;
- basisu_lowlevel_uastc_transcoder uit;
- // See comment on same declaration in transcodeEtc1s.
- std::vector<basisu_transcoder_state> xcoderStates;
- xcoderStates.resize(This->isVideo ? This->numFaces : 1);
- for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
- {
- ktx_uint32_t depth;
- uint64_t writeOffset = levelOffsetWrite;
- uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
- ktx_size_t levelImageSizeIn, levelImageOffsetIn;
- ktx_size_t levelImageSizeOut, levelSizeOut;
- ktx_uint32_t levelImageCount;
- uint32_t levelWidth = MAX(1, This->baseWidth >> level);
- uint32_t levelHeight = MAX(1, This->baseHeight >> level);
- // UASTC texel block dimensions
- const uint32_t bw = 4, bh = 4;
- uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
- uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
- uint32_t stateIndex = 0;
- depth = MAX(1, This->baseDepth >> level);
- levelImageCount = This->numLayers * This->numFaces * depth;
- levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level,
- KTX_FORMAT_VERSION_TWO);
- levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype),
- level,
- KTX_FORMAT_VERSION_TWO);
- levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level);
- levelSizeOut = 0;
- bool status;
- for (uint32_t image = 0; image < levelImageCount; image++) {
- basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
- // See comment before same lines in transcodeEtc1s.
- if (++stateIndex == xcoderStates.size())
- stateIndex = 0;
- status = uit.transcode_image(
- (transcoder_texture_format)outputFormat,
- pXcodedData + writeOffset,
- (uint32_t)(xcodedDataLength - writeOffsetBlocks),
- This->pData,
- (uint32_t)This->dataSize,
- levelBlocksX,
- levelBlocksY,
- levelWidth,
- levelHeight,
- level,
- (uint32_t)levelImageOffsetIn,
- (uint32_t)levelImageSizeIn,
- transcodeFlags,
- alphaContent != eNone,
- This->isVideo, // is_video
- //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
- 0, // output_row_pitch_in_blocks_or_pixels
- &xcoderState, // pState
- 0, // output_rows_in_pixels,
- -1, // channel0
- -1 // channel1
- );
- if (!status)
- return KTX_TRANSCODE_FAILED;
- writeOffset += levelImageSizeOut;
- levelSizeOut += levelImageSizeOut;
- levelImageOffsetIn += levelImageSizeIn;
- }
- protoLevelIndex[level].byteOffset = levelOffsetWrite;
- // writeOffset will be equal to total size of the images in the level.
- protoLevelIndex[level].byteLength = levelSizeOut;
- protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
- levelOffsetWrite += levelSizeOut;
- }
- // In case of transcoding to uncompressed.
- levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
- levelOffsetWrite);
- return KTX_SUCCESS;
- }
|