// SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- // Copyright 2020-2024 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at: // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // ---------------------------------------------------------------------------- /** * @brief The core astcenc codec library interface. * * This interface is the entry point to the core astcenc codec. It aims to be easy to use for * non-experts, but also to allow experts to have fine control over the compressor heuristics if * needed. The core codec only handles compression and decompression, transferring all inputs and * outputs via memory buffers. To catch obvious input/output buffer sizing issues, which can cause * security and stability problems, all transfer buffers are explicitly sized. * * While the aim is that we keep this interface mostly stable, it should be viewed as a mutable * interface tied to a specific source version. We are not trying to maintain backwards * compatibility across codec versions. * * The API state management is based around an explicit context object, which is the context for all * allocated memory resources needed to compress and decompress a single image. A context can be * used to sequentially compress multiple images using the same configuration, allowing setup * overheads to be amortized over multiple images, which is particularly important when images are * small. * * Multi-threading can be used two ways. * * * An application wishing to process multiple images in parallel can allocate multiple * contexts and assign each context to a thread. * * An application wishing to process a single image in using multiple threads can configure * contexts for multi-threaded use, and invoke astcenc_compress/decompress() once per thread * for faster processing. The caller is responsible for creating the worker threads, and * synchronizing between images. * * Extended instruction set support * ================================ * * This library supports use of extended instruction sets, such as SSE4.1 and AVX2. These are * enabled at compile time when building the library. There is no runtime checking in the core * library that the instruction sets used are actually available. Checking compatibility is the * responsibility of the calling code. * * Threading * ========= * * In pseudo-code, the usage for manual user threading looks like this: * * // Configure the compressor run * astcenc_config my_config; * astcenc_config_init(..., &my_config); * * // Power users can tweak <my_config> settings here ... * * // Allocate working state given config and thread_count * astcenc_context* my_context; * astcenc_context_alloc(&my_config, thread_count, &my_context); * * // Compress each image using these config settings * foreach image: * // For each thread in the thread pool * for i in range(0, thread_count): * astcenc_compress_image(my_context, &my_input, my_output, i); * * astcenc_compress_reset(my_context); * * // Clean up * astcenc_context_free(my_context); * * Images * ====== * * The codec supports compressing single images, which can be either 2D images or volumetric 3D * images. Calling code is responsible for any handling of aggregate types, such as mipmap chains, * texture arrays, or sliced 3D textures. * * Images are passed in as an astcenc_image structure. Inputs can be either 8-bit unorm, 16-bit * half-float, or 32-bit float, as indicated by the data_type field. * * Images can be any dimension; there is no requirement to be a multiple of the ASTC block size. * * Data is always passed in as 4 color components, and accessed as an array of 2D image slices. Data * within an image slice is always tightly packed without padding. Addressing looks like this: * * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 ] // Red * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 + 1] // Green * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 + 2] // Blue * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 + 3] // Alpha * * Common compressor usage * ======================= * * One of the most important things for coding image quality is to align the input data component * count with the ASTC color endpoint mode. This avoids wasting bits encoding components you don't * actually need in the endpoint colors. * * | Input data | Encoding swizzle | Sampling swizzle | * | ------------ | ---------------- | ---------------- | * | 1 component | RRR1 | .[rgb] | * | 2 components | RRRG | .[rgb]a | * | 3 components | RGB1 | .rgb | * | 4 components | RGBA | .rgba | * * The 1 and 2 component modes recommend sampling from "g" to recover the luminance value as this * provide best compatibility with other texture formats where the green component may be stored at * higher precision than the others, such as RGB565. For ASTC any of the RGB components can be used; * the luminance endpoint component will be returned for all three. * * When using the normal map compression mode ASTC will store normals as a two component X+Y map. * Input images must contain unit-length normalized and should be passed in using a two component * swizzle. The astcenc command line tool defaults to an RRRG swizzle, but some developers prefer * to use GGGR for compatability with BC5n which will work just as well. The Z component can be * recovered programmatically in shader code, using knowledge that the vector is unit length and * that Z must be positive for a tangent-space normal map. * * Decompress-only usage * ===================== * * For some use cases it is useful to have a cut-down context and/or library which supports * decompression but not compression. * * A context can be made decompress-only using the ASTCENC_FLG_DECOMPRESS_ONLY flag when the context * is allocated. These contexts have lower dynamic memory footprint than a full context. * * The entire library can be made decompress-only by building the files with the define * ASTCENC_DECOMPRESS_ONLY set. In this build the context will be smaller, and the library will * exclude the functionality which is only needed for compression. This reduces the binary size by * ~180KB. For these builds contexts must be created with the ASTCENC_FLG_DECOMPRESS_ONLY flag. * * Note that context structures returned by a library built as decompress-only are incompatible with * a library built with compression included, and visa versa, as they have different sizes and * memory layout. * * Self-decompress-only usage * ========================== * * ASTC is a complex format with a large search space. The parts of this search space that are * searched is determined by heuristics that are, in part, tied to the quality level used when * creating the context. * * A normal context is capable of decompressing any ASTC texture, including those generated by other * compressors with unknown heuristics. This is the most flexible implementation, but forces the * data tables used by the codec to include entries that are not needed during compression. This * can slow down context creation by a significant amount, especially for the faster compression * modes where few data table entries are actually used. To optimize this use case the context can * be created with the ASTCENC_FLG_SELF_DECOMPRESS_ONLY flag. This tells the compressor that it will * only be asked to decompress images that it compressed itself, allowing the data tables to * exclude entries that are not needed by the current compression configuration. This reduces the * size of the context data tables in memory and improves context creation performance. Note that, * as of the 3.6 release, this flag no longer affects compression performance. * * Using this flag while attempting to decompress an valid image which was created by another * compressor, or even another astcenc compressor version or configuration, may result in blocks * returning as solid magenta or NaN value error blocks. */ #ifndef ASTCENC_INCLUDED #define ASTCENC_INCLUDED #include <cstddef> #include <cstdint> #if defined(ASTCENC_DYNAMIC_LIBRARY) #if defined(_MSC_VER) #define ASTCENC_PUBLIC … #else #define ASTCENC_PUBLIC … #endif #else #define ASTCENC_PUBLIC #endif /* ============================================================================ Data declarations ============================================================================ */ /** * @brief An opaque structure; see astcenc_internal.h for definition. */ struct astcenc_context; /** * @brief A codec API error code. */ enum astcenc_error { … }; /** * @brief A codec color profile. */ enum astcenc_profile { … }; /** @brief The fastest, lowest quality, search preset. */ static const float ASTCENC_PRE_FASTEST = …; /** @brief The fast search preset. */ static const float ASTCENC_PRE_FAST = …; /** @brief The medium quality search preset. */ static const float ASTCENC_PRE_MEDIUM = …; /** @brief The thorough quality search preset. */ static const float ASTCENC_PRE_THOROUGH = …; /** @brief The thorough quality search preset. */ static const float ASTCENC_PRE_VERYTHOROUGH = …; /** @brief The exhaustive, highest quality, search preset. */ static const float ASTCENC_PRE_EXHAUSTIVE = …; /** * @brief A codec component swizzle selector. */ enum astcenc_swz { … }; /** * @brief A texel component swizzle. */ struct astcenc_swizzle { … }; /** * @brief A texel component data format. */ enum astcenc_type { … }; /** * @brief Function pointer type for compression progress reporting callback. */ extern "C" astcenc_progress_callback; /** * @brief Enable normal map compression. * * Input data will be treated a two component normal map, storing X and Y, and the codec will * optimize for angular error rather than simple linear PSNR. In this mode the input swizzle should * be e.g. rrrg (the default ordering for ASTC normals on the command line) or gggr (the ordering * used by BC5n). */ static const unsigned int ASTCENC_FLG_MAP_NORMAL = …; /** * @brief Enable compression heuristics that assume use of decode_unorm8 decode mode. * * The decode_unorm8 decode mode rounds differently to the decode_fp16 decode mode, so enabling this * flag during compression will allow the compressor to use the correct rounding when selecting * encodings. This will improve the compressed image quality if your application is using the * decode_unorm8 decode mode, but will reduce image quality if using decode_fp16. * * Note that LDR_SRGB images will always use decode_unorm8 for the RGB channels, irrespective of * this setting. */ static const unsigned int ASTCENC_FLG_USE_DECODE_UNORM8 = …; /** * @brief Enable alpha weighting. * * The input alpha value is used for transparency, so errors in the RGB components are weighted by * the transparency level. This allows the codec to more accurately encode the alpha value in areas * where the color value is less significant. */ static const unsigned int ASTCENC_FLG_USE_ALPHA_WEIGHT = …; /** * @brief Enable perceptual error metrics. * * This mode enables perceptual compression mode, which will optimize for perceptual error rather * than best PSNR. Only some input modes support perceptual error metrics. */ static const unsigned int ASTCENC_FLG_USE_PERCEPTUAL = …; /** * @brief Create a decompression-only context. * * This mode disables support for compression. This enables context allocation to skip some * transient buffer allocation, resulting in lower memory usage. */ static const unsigned int ASTCENC_FLG_DECOMPRESS_ONLY = …; /** * @brief Create a self-decompression context. * * This mode configures the compressor so that it is only guaranteed to be able to decompress images * that were actually created using the current context. This is the common case for compression use * cases, and setting this flag enables additional optimizations, but does mean that the context * cannot reliably decompress arbitrary ASTC images. */ static const unsigned int ASTCENC_FLG_SELF_DECOMPRESS_ONLY = …; /** * @brief Enable RGBM map compression. * * Input data will be treated as HDR data that has been stored in an LDR RGBM-encoded wrapper * format. Data must be preprocessed by the user to be in LDR RGBM format before calling the * compression function, this flag is only used to control the use of RGBM-specific heuristics and * error metrics. * * IMPORTANT: The ASTC format is prone to bad failure modes with unconstrained RGBM data; very small * M values can round to zero due to quantization and result in black or white pixels. It is highly * recommended that the minimum value of M used in the encoding is kept above a lower threshold (try * 16 or 32). Applying this threshold reduces the number of very dark colors that can be * represented, but is still higher precision than 8-bit LDR. * * When this flag is set the value of @c rgbm_m_scale in the context must be set to the RGBM scale * factor used during reconstruction. This defaults to 5 when in RGBM mode. * * It is recommended that the value of @c cw_a_weight is set to twice the value of the multiplier * scale, ensuring that the M value is accurately encoded. This defaults to 10 when in RGBM mode, * matching the default scale factor. */ static const unsigned int ASTCENC_FLG_MAP_RGBM = …; /** * @brief The bit mask of all valid flags. */ static const unsigned int ASTCENC_ALL_FLAGS = …; /** * @brief The config structure. * * This structure will initially be populated by a call to astcenc_config_init, but power users may * modify it before calling astcenc_context_alloc. See astcenccli_toplevel_help.cpp for full user * documentation of the power-user settings. * * Note for any settings which are associated with a specific color component, the value in the * config applies to the component that exists after any compression data swizzle is applied. */ struct astcenc_config { … }; /** * @brief An uncompressed 2D or 3D image. * * 3D image are passed in as an array of 2D slices. Each slice has identical * size and color format. */ struct astcenc_image { … }; /** * @brief A block encoding metadata query result. * * If the block is an error block or a constant color block or an error block all fields other than * the profile, block dimensions, and error/constant indicator will be zero. */ struct astcenc_block_info { … }; /** * Populate a codec config based on default settings. * * Power users can edit the returned config struct to fine tune before allocating the context. * * @param profile Color profile. * @param block_x ASTC block size X dimension. * @param block_y ASTC block size Y dimension. * @param block_z ASTC block size Z dimension. * @param quality Search quality preset / effort level. Either an * @c ASTCENC_PRE_* value, or a effort level between 0 * and 100. Performance is not linear between 0 and 100. * @param flags A valid set of @c ASTCENC_FLG_* flag bits. * @param[out] config Output config struct to populate. * * @return @c ASTCENC_SUCCESS on success, or an error if the inputs are invalid * either individually, or in combination. */ ASTCENC_PUBLIC astcenc_error astcenc_config_init( astcenc_profile profile, unsigned int block_x, unsigned int block_y, unsigned int block_z, float quality, unsigned int flags, astcenc_config* config); /** * @brief Allocate a new codec context based on a config. * * This function allocates all of the memory resources and threads needed by the codec. This can be * slow, so it is recommended that contexts are reused to serially compress or decompress multiple * images to amortize setup cost. * * Contexts can be allocated to support only decompression using the @c ASTCENC_FLG_DECOMPRESS_ONLY * flag when creating the configuration. The compression functions will fail if invoked. For a * decompress-only library build the @c ASTCENC_FLG_DECOMPRESS_ONLY flag must be set when creating * any context. * * @param[in] config Codec config. * @param thread_count Thread count to configure for. * @param[out] context Location to store an opaque context pointer. * * @return @c ASTCENC_SUCCESS on success, or an error if context creation failed. */ ASTCENC_PUBLIC astcenc_error astcenc_context_alloc( const astcenc_config* config, unsigned int thread_count, astcenc_context** context); /** * @brief Compress an image. * * A single context can only compress or decompress a single image at a time. * * For a context configured for multi-threading, any set of the N threads can call this function. * Work will be dynamically scheduled across the threads available. Each thread must have a unique * @c thread_index. * * @param context Codec context. * @param[in,out] image An input image, in 2D slices. * @param swizzle Compression data swizzle, applied before compression. * @param[out] data_out Pointer to output data array. * @param data_len Length of the output data array. * @param thread_index Thread index [0..N-1] of calling thread. * * @return @c ASTCENC_SUCCESS on success, or an error if compression failed. */ ASTCENC_PUBLIC astcenc_error astcenc_compress_image( astcenc_context* context, astcenc_image* image, const astcenc_swizzle* swizzle, uint8_t* data_out, size_t data_len, unsigned int thread_index); /** * @brief Reset the codec state for a new compression. * * The caller is responsible for synchronizing threads in the worker thread pool. This function must * only be called when all threads have exited the @c astcenc_compress_image() function for image N, * but before any thread enters it for image N + 1. * * Calling this is not required (but won't hurt), if the context is created for single threaded use. * * @param context Codec context. * * @return @c ASTCENC_SUCCESS on success, or an error if reset failed. */ ASTCENC_PUBLIC astcenc_error astcenc_compress_reset( astcenc_context* context); /** * @brief Decompress an image. * * @param context Codec context. * @param[in] data Pointer to compressed data. * @param data_len Length of the compressed data, in bytes. * @param[in,out] image_out Output image. * @param swizzle Decompression data swizzle, applied after decompression. * @param thread_index Thread index [0..N-1] of calling thread. * * @return @c ASTCENC_SUCCESS on success, or an error if decompression failed. */ ASTCENC_PUBLIC astcenc_error astcenc_decompress_image( astcenc_context* context, const uint8_t* data, size_t data_len, astcenc_image* image_out, const astcenc_swizzle* swizzle, unsigned int thread_index); /** * @brief Reset the codec state for a new decompression. * * The caller is responsible for synchronizing threads in the worker thread pool. This function must * only be called when all threads have exited the @c astcenc_decompress_image() function for image * N, but before any thread enters it for image N + 1. * * Calling this is not required (but won't hurt), if the context is created for single threaded use. * * @param context Codec context. * * @return @c ASTCENC_SUCCESS on success, or an error if reset failed. */ ASTCENC_PUBLIC astcenc_error astcenc_decompress_reset( astcenc_context* context); /** * Free the compressor context. * * @param context The codec context. */ ASTCENC_PUBLIC void astcenc_context_free( astcenc_context* context); /** * @brief Provide a high level summary of a block's encoding. * * This feature is primarily useful for codec developers but may be useful for developers building * advanced content packaging pipelines. * * @param context Codec context. * @param data One block of compressed ASTC data. * @param info The output info structure to populate. * * @return @c ASTCENC_SUCCESS if the block was decoded, or an error otherwise. Note that this * function will return success even if the block itself was an error block encoding, as the * decode was correctly handled. */ ASTCENC_PUBLIC astcenc_error astcenc_get_block_info( astcenc_context* context, const uint8_t data[16], astcenc_block_info* info); /** * @brief Get a printable string for specific status code. * * @param status The status value. * * @return A human readable nul-terminated string. */ ASTCENC_PUBLIC const char* astcenc_get_error_string( astcenc_error status); #endif