Path: blob/master/thirdparty/basis_universal/encoder/basisu_comp.h
9902 views
// basisu_comp.h1// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14#pragma once15#include "basisu_frontend.h"16#include "basisu_backend.h"17#include "basisu_basis_file.h"18#include "../transcoder/basisu_transcoder.h"19#include "basisu_uastc_enc.h"20#include "basisu_uastc_hdr_4x4_enc.h"21#include "basisu_astc_hdr_6x6_enc.h"2223#define BASISU_LIB_VERSION 16024#define BASISU_LIB_VERSION_STRING "1.60"2526#ifndef BASISD_SUPPORT_KTX227#error BASISD_SUPPORT_KTX2 is undefined28#endif29#ifndef BASISD_SUPPORT_KTX2_ZSTD30#error BASISD_SUPPORT_KTX2_ZSTD is undefined31#endif3233#if !BASISD_SUPPORT_KTX234#error BASISD_SUPPORT_KTX2 must be enabled when building the encoder. To reduce code size if KTX2 support is not needed, set BASISD_SUPPORT_KTX2_ZSTD to 035#endif3637namespace basisu38{39struct opencl_context;40typedef opencl_context* opencl_context_ptr;4142const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;4344// Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.45const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f;4647// Allow block's color distance to increase by 1.25 while searching the selector history buffer for a close enough match.48const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f;4950const int BASISU_DEFAULT_QUALITY = 128;51const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;5253const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384;54const uint32_t BASISU_QUALITY_MIN = 1;55const uint32_t BASISU_QUALITY_MAX = 255;56const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;57const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;5859const uint32_t BASISU_MAX_SLICES = 0xFFFFFF;6061const int BASISU_RDO_UASTC_DICT_SIZE_DEFAULT = 4096; // 32768;62const int BASISU_RDO_UASTC_DICT_SIZE_MIN = 64;63const int BASISU_RDO_UASTC_DICT_SIZE_MAX = 65536;6465struct image_stats66{67image_stats()68{69clear();70}7172void clear()73{74m_filename.clear();75m_width = 0;76m_height = 0;7778m_basis_rgb_avg_psnr = 0.0f;79m_basis_rgb_avg_log2_psnr = 0.0f;8081m_basis_rgba_avg_psnr = 0.0f;82m_basis_a_avg_psnr = 0.0f;83m_basis_luma_709_psnr = 0.0f;84m_basis_luma_601_psnr = 0.0f;85m_basis_luma_709_ssim = 0.0f;8687m_basis_rgb_avg_bc6h_psnr = 0.0f;88m_basis_rgb_avg_bc6h_log2_psnr = 0.0f;8990m_bc7_rgb_avg_psnr = 0.0f;91m_bc7_rgba_avg_psnr = 0.0f;92m_bc7_a_avg_psnr = 0.0f;93m_bc7_luma_709_psnr = 0.0f;94m_bc7_luma_601_psnr = 0.0f;95m_bc7_luma_709_ssim = 0.0f;9697m_best_etc1s_rgb_avg_psnr = 0.0f;98m_best_etc1s_luma_709_psnr = 0.0f;99m_best_etc1s_luma_601_psnr = 0.0f;100m_best_etc1s_luma_709_ssim = 0.0f;101102m_opencl_failed = false;103}104105std::string m_filename;106uint32_t m_width;107uint32_t m_height;108109// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)110float m_basis_rgb_avg_psnr;111float m_basis_rgb_avg_log2_psnr;112113float m_basis_rgba_avg_psnr;114float m_basis_a_avg_psnr;115float m_basis_luma_709_psnr;116float m_basis_luma_601_psnr;117float m_basis_luma_709_ssim;118119// UASTC HDR only.120float m_basis_rgb_avg_bc6h_psnr;121float m_basis_rgb_avg_bc6h_log2_psnr;122123// LDR: BC7 statistics124float m_bc7_rgb_avg_psnr;125float m_bc7_rgba_avg_psnr;126float m_bc7_a_avg_psnr;127float m_bc7_luma_709_psnr;128float m_bc7_luma_601_psnr;129float m_bc7_luma_709_ssim;130131// LDR: Highest achievable quality ETC1S statistics132float m_best_etc1s_rgb_avg_psnr;133float m_best_etc1s_luma_709_psnr;134float m_best_etc1s_luma_601_psnr;135float m_best_etc1s_luma_709_ssim;136137bool m_opencl_failed;138};139140enum class hdr_modes141{142// standard but constrained ASTC HDR 4x4 tex data that can be rapidly transcoded to BC6H143cUASTC_HDR_4X4,144// standard RDO optimized or non-RDO (highest quality) ASTC HDR 6x6 tex data that can be rapidly re-encoded to BC6H145cASTC_HDR_6X6,146// a custom intermediate format based off ASTC HDR that can be rapidly decoded straight to ASTC HDR or re-encoded to BC6H147cASTC_HDR_6X6_INTERMEDIATE,148cTotal149};150151template<bool def>152struct bool_param153{154bool_param() :155m_value(def),156m_changed(false)157{158}159160void clear()161{162m_value = def;163m_changed = false;164}165166operator bool() const167{168return m_value;169}170171bool operator= (bool v)172{173m_value = v;174m_changed = true;175return m_value;176}177178bool was_changed() const { return m_changed; }179void set_changed(bool flag) { m_changed = flag; }180181bool m_value;182bool m_changed;183};184185template<typename T>186struct param187{188param(T def, T min_v, T max_v) :189m_value(def),190m_def(def),191m_min(min_v),192m_max(max_v),193m_changed(false)194{195}196197void clear()198{199m_value = m_def;200m_changed = false;201}202203operator T() const204{205return m_value;206}207208T operator= (T v)209{210m_value = clamp<T>(v, m_min, m_max);211m_changed = true;212return m_value;213}214215T operator *= (T v)216{217m_value *= v;218m_changed = true;219return m_value;220}221222bool was_changed() const { return m_changed; }223void set_changed(bool flag) { m_changed = flag; }224225T m_value;226T m_def;227T m_min;228T m_max;229bool m_changed;230};231232struct basis_compressor_params233{234basis_compressor_params() :235m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),236m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),237m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),238m_mip_scale(1.0f, .000125f, 4.0f),239m_mip_smallest_dimension(1, 1, 16384),240m_etc1s_max_endpoint_clusters(512),241m_etc1s_max_selector_clusters(512),242m_etc1s_quality_level(-1),243m_pack_uastc_ldr_4x4_flags(cPackUASTCLevelDefault),244m_rdo_uastc_ldr_4x4_quality_scalar(1.0f, 0.001f, 50.0f),245m_rdo_uastc_ldr_4x4_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),246m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),247m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),248m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),249m_rdo_uastc_ldr_4x4_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),250m_resample_width(0, 1, 16384),251m_resample_height(0, 1, 16384),252m_resample_factor(0.0f, .00125f, 100.0f),253m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE),254m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX),255m_ldr_hdr_upconversion_nit_multiplier(0.0f, 0.0f, basist::MAX_HALF_FLOAT),256m_ldr_hdr_upconversion_black_bias(0.0f, 0.0f, 1.0f),257m_pJob_pool(nullptr)258{259clear();260}261262void clear()263{264m_uastc.clear();265m_hdr.clear();266m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;267268m_use_opencl.clear();269m_status_output.clear();270271m_source_filenames.clear();272m_source_alpha_filenames.clear();273274m_source_images.clear();275m_source_mipmap_images.clear();276277m_out_filename.clear();278279m_y_flip.clear();280m_debug.clear();281m_validate_etc1s.clear();282m_debug_images.clear();283m_perceptual.clear();284m_no_selector_rdo.clear();285m_selector_rdo_thresh.clear();286m_read_source_images.clear();287m_write_output_basis_or_ktx2_files.clear();288m_compression_level.clear();289m_compute_stats.clear();290m_print_stats.clear();291m_check_for_alpha.clear();292m_force_alpha.clear();293m_multithreading.clear();294m_swizzle[0] = 0;295m_swizzle[1] = 1;296m_swizzle[2] = 2;297m_swizzle[3] = 3;298m_renormalize.clear();299m_disable_hierarchical_endpoint_codebooks.clear();300301m_no_endpoint_rdo.clear();302m_endpoint_rdo_thresh.clear();303304m_mip_gen.clear();305m_mip_scale.clear();306m_mip_filter = "kaiser";307m_mip_scale = 1.0f;308m_mip_srgb.clear();309m_mip_premultiplied.clear();310m_mip_renormalize.clear();311m_mip_wrapping.clear();312m_mip_fast.clear();313m_mip_smallest_dimension.clear();314315m_etc1s_max_endpoint_clusters = 0;316m_etc1s_max_selector_clusters = 0;317m_etc1s_quality_level = -1;318319m_tex_type = basist::cBASISTexType2D;320m_userdata0 = 0;321m_userdata1 = 0;322m_us_per_frame = 0;323324m_pack_uastc_ldr_4x4_flags = cPackUASTCLevelDefault;325m_rdo_uastc_ldr_4x4.clear();326m_rdo_uastc_ldr_4x4_quality_scalar.clear();327m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale.clear();328m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev.clear();329m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio.clear();330m_rdo_uastc_ldr_4x4_skip_block_rms_thresh.clear();331m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode.clear();332m_rdo_uastc_ldr_4x4_multithreading.clear();333334m_resample_width.clear();335m_resample_height.clear();336m_resample_factor.clear();337338m_pGlobal_codebooks = nullptr;339340m_create_ktx2_file.clear();341m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE;342m_ktx2_key_values.clear();343m_ktx2_zstd_supercompression_level.clear();344m_ktx2_srgb_transfer_func.clear();345346m_validate_output_data.clear();347348m_ldr_hdr_upconversion_srgb_to_linear.clear();349350m_hdr_favor_astc.clear();351352m_uastc_hdr_4x4_options.init();353m_astc_hdr_6x6_options.clear();354355m_ldr_hdr_upconversion_nit_multiplier.clear();356m_ldr_hdr_upconversion_black_bias.clear();357358m_pJob_pool = nullptr;359}360361// Configures the compressor's mode by setting the proper parameters (which were preserved for backwards compatibility with old code).362void set_format_mode(basist::basis_tex_format m)363{364switch (m)365{366case basist::basis_tex_format::cETC1S:367{368m_hdr = false;369m_uastc = false;370m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter371break;372}373case basist::basis_tex_format::cUASTC4x4:374{375m_hdr = false;376m_uastc = true;377m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter378break;379}380case basist::basis_tex_format::cUASTC_HDR_4x4:381{382m_hdr = true;383m_uastc = true;384m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;385break;386}387case basist::basis_tex_format::cASTC_HDR_6x6:388{389m_hdr = true;390m_uastc = true;391m_hdr_mode = hdr_modes::cASTC_HDR_6X6;392break;393}394case basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:395{396m_hdr = true;397m_uastc = true;398m_hdr_mode = hdr_modes::cASTC_HDR_6X6_INTERMEDIATE;399break;400}401default:402assert(0);403break;404}405}406407// By default we generate LDR ETC1S data.408// if m_uastc is true but m_hdr is not true, we generate UASTC 4x4 LDR data (8bpp with or without RDO).409// if m_uastc is true and m_hdr is true, we generate 4x4 or 6x6 HDR data (either standard ASTC, constrained ASTC, RDO ASTC, or intermediate), controlled by m_hdr_mode.410411// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.412// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, and ASTC 6x6 HDR intermediate).413bool_param<false> m_uastc;414415// Set m_hdr to true to switch to UASTC HDR mode. m_hdr_mode then controls which format is output.416// m_hdr_mode then controls which format is output (4x4, 6x6, or 6x6 intermediate).417bool_param<false> m_hdr;418419// If m_hdr is true, this specifies which mode we operate in (currently UASTC 4x4 HDR or ASTC 6x6 HDR). Defaults to UASTC 4x4 HDR for backwards compatibility.420hdr_modes m_hdr_mode;421422bool_param<false> m_use_opencl;423424// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG etc. images to read.425// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.426basisu::vector<std::string> m_source_filenames;427basisu::vector<std::string> m_source_alpha_filenames;428429basisu::vector<image> m_source_images;430431basisu::vector<imagef> m_source_images_hdr;432433// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.434// If m_source_mipmaps isn't empty, automatic mipmap generation isn't done. m_source_mipmaps.size() MUST equal m_source_images.size() or the compressor returns an error.435// The compressor applies the user-provided swizzling (in m_swizzle) to these images.436basisu::vector< basisu::vector<image> > m_source_mipmap_images;437438basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;439440// Filename of the output basis/ktx2 file441std::string m_out_filename;442443// The params are done this way so we can detect when the user has explictly changed them.444445// Flip images across Y axis446bool_param<false> m_y_flip;447448// If true, the compressor will print basis status to stdout during compression.449bool_param<true> m_status_output;450451// Output debug information during compression452bool_param<false> m_debug;453bool_param<false> m_validate_etc1s;454455// m_debug_images is pretty slow456bool_param<false> m_debug_images;457458// ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower).459// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.460// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.461param<int> m_compression_level;462463// Use perceptual sRGB colorspace metrics instead of linear464bool_param<true> m_perceptual;465466// Disable selector RDO, for faster compression but larger files467bool_param<false> m_no_selector_rdo;468param<float> m_selector_rdo_thresh;469470bool_param<false> m_no_endpoint_rdo;471param<float> m_endpoint_rdo_thresh;472473// Read source images from m_source_filenames/m_source_alpha_filenames474bool_param<false> m_read_source_images;475476// Write the output basis/ktx2 file to disk using m_out_filename477bool_param<false> m_write_output_basis_or_ktx2_files;478479// Compute and display image metrics480bool_param<false> m_compute_stats;481482// Print stats to stdout, if m_compute_stats is true.483bool_param<true> m_print_stats;484485// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels486bool_param<true> m_check_for_alpha;487488// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha489bool_param<false> m_force_alpha;490bool_param<true> m_multithreading;491492// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels493uint8_t m_swizzle[4];494495bool_param<false> m_renormalize;496497// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.498// Note some m_compression_level's disable this automatically.499bool_param<false> m_disable_hierarchical_endpoint_codebooks;500501// mipmap generation parameters502bool_param<false> m_mip_gen;503param<float> m_mip_scale;504std::string m_mip_filter;505bool_param<false> m_mip_srgb;506bool_param<true> m_mip_premultiplied; // not currently supported507bool_param<false> m_mip_renormalize;508bool_param<true> m_mip_wrapping;509bool_param<true> m_mip_fast;510param<int> m_mip_smallest_dimension;511512// ETC1S codebook size (quality) control.513// If m_etc1s_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].514// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.515uint32_t m_etc1s_max_endpoint_clusters;516uint32_t m_etc1s_max_selector_clusters;517int m_etc1s_quality_level;518519// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the .basis file header.520basist::basis_texture_type m_tex_type;521uint32_t m_userdata0;522uint32_t m_userdata1;523uint32_t m_us_per_frame;524525// UASTC LDR 4x4 parameters526// cPackUASTCLevelDefault, etc.527uint32_t m_pack_uastc_ldr_4x4_flags;528bool_param<false> m_rdo_uastc_ldr_4x4;529param<float> m_rdo_uastc_ldr_4x4_quality_scalar;530param<int> m_rdo_uastc_ldr_4x4_dict_size;531param<float> m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale;532param<float> m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev;533param<float> m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio;534param<float> m_rdo_uastc_ldr_4x4_skip_block_rms_thresh;535bool_param<true> m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode;536bool_param<true> m_rdo_uastc_ldr_4x4_multithreading;537538param<int> m_resample_width;539param<int> m_resample_height;540param<float> m_resample_factor;541542const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;543544// KTX2 specific parameters.545// Internally, the compressor always creates a .basis file then it converts that lossless to KTX2.546bool_param<false> m_create_ktx2_file;547basist::ktx2_supercompression m_ktx2_uastc_supercompression;548basist::ktx2_transcoder::key_value_vec m_ktx2_key_values;549param<int> m_ktx2_zstd_supercompression_level;550bool_param<false> m_ktx2_srgb_transfer_func;551552uastc_hdr_4x4_codec_options m_uastc_hdr_4x4_options;553astc_6x6_hdr::astc_hdr_6x6_global_config m_astc_hdr_6x6_options;554555bool_param<false> m_validate_output_data;556557// LDR->HDR upconversion parameters.558//559// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion), or absolute luminance (nits or candelas per meter squared), and then processed as HDR.560// Otherwise, LDR images are assumed to already be in linear light (i.e. they don't use the sRGB transfer function).561bool_param<true> m_ldr_hdr_upconversion_srgb_to_linear;562563// m_ldr_hdr_upconversion_nit_multiplier is only used when loading SDR/LDR images and compressing to an HDR output format.564// By default m_ldr_hdr_upconversion_nit_multiplier is 0. It's an override for the default.565// When loading LDR images, a default multiplier of 1.0 will be used in UASTC 4x4 HDR mode. Partially for backwards compatibility with previous library releases, and also because it doesn't really matter with this encoder what the multiplier is.566// With the 6x6 HDR encoder it does matter because it expects inputs in absolute nits, so the LDR upconversion luminance multiplier default will be 100 nits. (Most SDR monitors were/are 80-100 nits or so.)567param<float> m_ldr_hdr_upconversion_nit_multiplier;568569// The optional sRGB space bias to use during LDR->HDR upconversion. Should be between [0,.49] or so. Only applied on black (0.0) color components.570// Defaults to no bias (0.0f).571param<float> m_ldr_hdr_upconversion_black_bias;572573// If true, ASTC HDR quality is favored more than BC6H quality. Otherwise it's a rough balance.574bool_param<false> m_hdr_favor_astc;575576job_pool *m_pJob_pool;577};578579// Important: basisu_encoder_init() MUST be called first before using this class.580class basis_compressor581{582BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);583584public:585basis_compressor();586~basis_compressor();587588// Note it *should* be possible to call init() multiple times with different inputs, but this scenario isn't well tested. Ideally, create 1 object, compress, then delete it.589bool init(const basis_compressor_params ¶ms);590591enum error_code592{593cECSuccess = 0,594cECFailedInitializing,595cECFailedReadingSourceImages,596cECFailedValidating,597cECFailedEncodeUASTC,598cECFailedFrontEnd,599cECFailedFontendExtract,600cECFailedBackend,601cECFailedCreateBasisFile,602cECFailedWritingOutput,603cECFailedUASTCRDOPostProcess,604cECFailedCreateKTX2File605};606607error_code process();608609// The output .basis file will always be valid of process() succeeded.610const uint8_vec &get_output_basis_file() const { return m_output_basis_file; }611612// The output .ktx2 file will only be valid if m_create_ktx2_file was true and process() succeeded.613const uint8_vec& get_output_ktx2_file() const { return m_output_ktx2_file; }614615const basisu::vector<image_stats> &get_stats() const { return m_stats; }616617uint32_t get_basis_file_size() const { return m_basis_file_size; }618double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }619620bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }621622bool get_opencl_failed() const { return m_opencl_failed; }623624private:625basis_compressor_params m_params;626627opencl_context_ptr m_pOpenCL_context;628629basist::basis_tex_format m_fmt_mode;630631basisu::vector<image> m_slice_images;632basisu::vector<imagef> m_slice_images_hdr;633634basisu::vector<image_stats> m_stats;635636uint32_t m_basis_file_size;637double m_basis_bits_per_texel;638639basisu_backend_slice_desc_vec m_slice_descs;640641uint32_t m_total_blocks;642643basisu_frontend m_frontend;644645// These are 4x4 blocks.646pixel_block_vec m_source_blocks;647pixel_block_hdr_vec m_source_blocks_hdr;648649basisu::vector<gpu_image> m_frontend_output_textures;650651basisu::vector<gpu_image> m_best_etc1s_images;652basisu::vector<image> m_best_etc1s_images_unpacked;653654basisu_backend m_backend;655656basisu_file m_basis_file;657658basisu::vector<gpu_image> m_decoded_output_textures; // BC6H in HDR mode659basisu::vector<image> m_decoded_output_textures_unpacked;660661basisu::vector<gpu_image> m_decoded_output_textures_bc7;662basisu::vector<image> m_decoded_output_textures_unpacked_bc7;663664basisu::vector<imagef> m_decoded_output_textures_bc6h_hdr_unpacked; // BC6H in HDR mode665666basisu::vector<gpu_image> m_decoded_output_textures_astc_hdr;667basisu::vector<imagef> m_decoded_output_textures_astc_hdr_unpacked;668669uint8_vec m_output_basis_file;670uint8_vec m_output_ktx2_file;671672basisu::vector<gpu_image> m_uastc_slice_textures;673basisu_backend_output m_uastc_backend_output;674675// The amount the HDR input has to be scaled up in case it had to be rescaled to fit into half floats.676float m_hdr_image_scale;677678// The upconversion multiplier used to load LDR images in HDR mode.679float m_ldr_to_hdr_upconversion_nit_multiplier;680681// True if any loaded source images were LDR and upconverted to HDR.682bool m_upconverted_any_ldr_images;683684bool m_any_source_image_has_alpha;685686bool m_opencl_failed;687688void check_for_hdr_inputs();689bool sanity_check_input_params();690void clean_hdr_image(imagef& src_img);691bool read_dds_source_images();692bool read_source_images();693bool extract_source_blocks();694bool process_frontend();695bool extract_frontend_texture_data();696bool process_backend();697bool create_basis_file_and_transcode();698bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);699bool write_output_files_and_compute_stats();700error_code encode_slices_to_astc_6x6_hdr();701error_code encode_slices_to_uastc_4x4_hdr();702error_code encode_slices_to_uastc_4x4_ldr();703bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);704bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);705bool validate_texture_type_constraints();706bool validate_ktx2_constraints();707bool get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);708bool create_ktx2_file();709void pick_format_mode();710711uint32_t get_block_width() const712{713if (m_params.m_hdr)714{715switch (m_params.m_hdr_mode)716{717case hdr_modes::cASTC_HDR_6X6:718case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:719return 6;720default:721break;722}723}724return 4;725}726727uint32_t get_block_height() const728{729if (m_params.m_hdr)730{731switch (m_params.m_hdr_mode)732{733case hdr_modes::cASTC_HDR_6X6:734case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:735return 6;736default:737break;738}739}740return 4;741}742};743744// Alternative simple C-style wrapper API around the basis_compressor class.745// This doesn't expose every encoder feature, but it's enough to get going.746// Important: basisu_encoder_init() MUST be called first before calling these functions.747//748// Input parameters:749// source_images: Array of "image" objects, one per mipmap level, largest mipmap level first.750// OR751// pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.752// width/height/pitch_in_pixels: dimensions of pImageRGBA753//754// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC level, i.e. "cFlagSRGB | cFlagGenMipsClamp | cFlagThreaded | 128" or "cFlagSRGB | cFlagGenMipsClamp | cFlagUASTC | cFlagThreaded | cPackUASTCLevelDefault".755// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)756// In UASTC mode, the lower 8-bits are the UASTC LDR/HDR pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly. Valid values are [0,4] for both LDR/HDR.757// In UASTC mode, be sure to set this, otherwise it defaults to 0 (fastest/lowest quality).758//759// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)760//761// pSize: Returns the output data's compressed size in bytes762//763// Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it.764enum765{766cFlagUseOpenCL = 1 << 8, // use OpenCL if available767cFlagThreaded = 1 << 9, // use multiple threads for compression768cFlagDebug = 1 << 10, // enable debug output769770cFlagKTX2 = 1 << 11, // generate a KTX2 file771cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files772773cFlagSRGB = 1 << 13, // input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2 output transfer func to sRGB774cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing775cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing776777cFlagYFlip = 1 << 16, // flip source image on Y axis before compression778779cFlagUASTCRDO = 1 << 17, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)780781cFlagPrintStats = 1 << 18, // print image stats to stdout782cFlagPrintStatus = 1 << 19, // print status to stdout783784cFlagDebugImages = 1 << 20, // enable status output785786cFlagREC2020 = 1 << 21 // ASTC 6x6 modes: treat input as REC 2020 vs. the default 709787};788789// This function accepts an array of source images.790// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.791// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.792// Important: The returned block MUST be manually freed using basis_free_data().793// basisu_encoder_init() MUST be called first!794// LDR version. To compress the LDR source image as HDR: Use the cFlagHDR flag.795void* basis_compress(796basist::basis_tex_format mode,797const basisu::vector<image> &source_images,798uint32_t flags_and_quality, float uastc_rdo_quality,799size_t* pSize,800image_stats* pStats = nullptr);801802// HDR-only version.803// Important: The returned block MUST be manually freed using basis_free_data().804void* basis_compress(805basist::basis_tex_format mode,806const basisu::vector<imagef>& source_images_hdr,807uint32_t flags_and_quality, float lambda,808size_t* pSize,809image_stats* pStats = nullptr);810811// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.812// Important: The returned block MUST be manually freed using basis_free_data().813void* basis_compress(814basist::basis_tex_format mode,815const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,816uint32_t flags_and_quality, float uastc_rdo_quality,817size_t* pSize,818image_stats* pStats = nullptr);819820// Frees the dynamically allocated file data returned by basis_compress().821// This MUST be called on the pointer returned by basis_compress() when you're done with it.822void basis_free_data(void* p);823824// Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled.825// Returns true if opencl is worth using on this system, otherwise false.826// If pOpenCL_failed is not null, it will be set to true if OpenCL encoding failed *on this particular machine/driver/BasisU version* and the encoder falled back to CPU encoding.827// basisu_encoder_init() MUST be called first. If OpenCL support wasn't enabled this always returns false.828bool basis_benchmark_etc1s_opencl(bool *pOpenCL_failed = nullptr);829830// Parallel compression API831struct parallel_results832{833double m_total_time;834basis_compressor::error_code m_error_code;835uint8_vec m_basis_file;836uint8_vec m_ktx2_file;837basisu::vector<image_stats> m_stats;838double m_basis_bits_per_texel;839bool m_any_source_image_has_alpha;840841parallel_results()842{843clear();844}845846void clear()847{848m_total_time = 0.0f;849m_error_code = basis_compressor::cECFailedInitializing;850m_basis_file.clear();851m_ktx2_file.clear();852m_stats.clear();853m_basis_bits_per_texel = 0.0f;854m_any_source_image_has_alpha = false;855}856};857858// Compresses an array of input textures across total_threads threads using the basis_compressor class.859// Compressing multiple textures at a time is substantially more efficient than just compressing one at a time.860// total_threads must be >= 1.861bool basis_parallel_compress(862uint32_t total_threads,863const basisu::vector<basis_compressor_params> ¶ms_vec,864basisu::vector< parallel_results > &results_vec);865866} // namespace basisu867868869870