Download third_party/ktx-src/lib/basis_transcode.cpp from kimjunhan/vkgs-sim: direct link, hf CLI and curl.
- Browser
- Download file 32.1 kB
-
https://huggingface.co/kimjunhan/vkgs-sim/resolve/main/third_party/ktx-src/lib/basis_transcode.cpp
- Command line
-
hf download hf://kimjunhan/vkgs-sim/third_party/ktx-src/lib/basis_transcode.cpp
-
curl -L -o basis_transcode.cpp https://huggingface.co/kimjunhan/vkgs-sim/resolve/main/third_party/ktx-src/lib/basis_transcode.cpp
32.1 kB
| /* -*- 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 | |
| * @~English | |
| * | |
| * @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures. | |
| * | |
| * Two worlds collide here too. More uglyness! | |
| * | |
| * @author Mark Callow, github.com/MarkCallow | |
| */ | |
| 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; | |
| // Free SGD data | |
| This->_private->_sgdByteLength = 0; | |
| if (This->_private->_supercompressionGlobalData) { | |
| free(This->_private->_supercompressionGlobalData); | |
| This->_private->_supercompressionGlobalData = NULL; | |
| } | |
| } | |
| 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) { | |
| // Compiler will not allow `goto cleanup;` because "jump bypasses variable initialization." | |
| // The static initializations below this and before the loop are presumably the issue | |
| // as the compiler is,presumably, inserting code to destruct those at the end of the | |
| // function. | |
| delete[] firstImages; | |
| 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; | |
| } | |