merging v2.0 from xuastc_private

This commit is contained in:
Richard Geldreich
2026-01-19 01:59:54 -05:00
parent ea6778b2b5
commit e93d3beb36
49 changed files with 72432 additions and 26002 deletions

View File

@@ -1,5 +1,5 @@
// basisu_comp.h
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,9 +20,10 @@
#include "basisu_uastc_enc.h"
#include "basisu_uastc_hdr_4x4_enc.h"
#include "basisu_astc_hdr_6x6_enc.h"
#include "basisu_astc_ldr_encode.h"
#define BASISU_LIB_VERSION 160
#define BASISU_LIB_VERSION_STRING "1.60"
#define BASISU_LIB_VERSION 200
#define BASISU_LIB_VERSION_STRING "2.00"
#ifndef BASISD_SUPPORT_KTX2
#error BASISD_SUPPORT_KTX2 is undefined
@@ -43,10 +44,10 @@ namespace basisu
const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;
// Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.
const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f;
const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f;
// Allow block's color distance to increase by 1.25 while searching the selector history buffer for a close enough match.
const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f;
const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f;
const int BASISU_DEFAULT_QUALITY = 128;
const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;
@@ -54,6 +55,8 @@ namespace basisu
const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384;
const uint32_t BASISU_QUALITY_MIN = 1;
const uint32_t BASISU_QUALITY_MAX = 255;
const uint32_t BASISU_XUASTC_QUALITY_MIN = 1;
const uint32_t BASISU_XUASTC_QUALITY_MAX = 100;
const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;
const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;
@@ -75,7 +78,7 @@ namespace basisu
m_filename.clear();
m_width = 0;
m_height = 0;
m_basis_rgb_avg_psnr = 0.0f;
m_basis_rgb_avg_log2_psnr = 0.0f;
@@ -94,7 +97,7 @@ namespace basisu
m_bc7_luma_709_psnr = 0.0f;
m_bc7_luma_601_psnr = 0.0f;
m_bc7_luma_709_ssim = 0.0f;
m_best_etc1s_rgb_avg_psnr = 0.0f;
m_best_etc1s_luma_709_psnr = 0.0f;
m_best_etc1s_luma_601_psnr = 0.0f;
@@ -128,8 +131,8 @@ namespace basisu
float m_bc7_luma_709_psnr;
float m_bc7_luma_601_psnr;
float m_bc7_luma_709_ssim;
// LDR: Highest achievable quality ETC1S statistics
// LDR: Highest achievable quality ETC1S statistics, for development/comparison
float m_best_etc1s_rgb_avg_psnr;
float m_best_etc1s_luma_709_psnr;
float m_best_etc1s_luma_601_psnr;
@@ -141,11 +144,11 @@ namespace basisu
enum class hdr_modes
{
// standard but constrained ASTC HDR 4x4 tex data that can be rapidly transcoded to BC6H
cUASTC_HDR_4X4,
cUASTC_HDR_4X4,
// standard RDO optimized or non-RDO (highest quality) ASTC HDR 6x6 tex data that can be rapidly re-encoded to BC6H
cASTC_HDR_6X6,
// a custom intermediate format based off ASTC HDR that can be rapidly decoded straight to ASTC HDR or re-encoded to BC6H
cASTC_HDR_6X6_INTERMEDIATE,
cUASTC_HDR_6X6_INTERMEDIATE,
cTotal
};
@@ -230,17 +233,21 @@ namespace basisu
bool m_changed;
};
// Low-level direct compressor parameters.
// Also see basis_compress() below for a simplified C-style interface.
struct basis_compressor_params
{
basis_compressor_params() :
m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
m_xuastc_or_astc_ldr_basis_tex_format(-1, -1, INT_MAX),
// Note the ETC1S default compression/effort level is 2, not the command line default of 1.
m_etc1s_compression_level((int)BASISU_DEFAULT_ETC1S_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_ETC1S_COMPRESSION_LEVEL),
m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
m_mip_scale(1.0f, .000125f, 4.0f),
m_mip_smallest_dimension(1, 1, 16384),
m_etc1s_max_endpoint_clusters(512),
m_etc1s_max_selector_clusters(512),
m_etc1s_quality_level(-1),
m_quality_level(-1),
m_pack_uastc_ldr_4x4_flags(cPackUASTCLevelDefault),
m_rdo_uastc_ldr_4x4_quality_scalar(1.0f, 0.001f, 50.0f),
m_rdo_uastc_ldr_4x4_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
@@ -253,8 +260,14 @@ namespace basisu
m_resample_factor(0.0f, .00125f, 100.0f),
m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE),
m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX),
m_transcode_flags(0, 0, UINT32_MAX),
m_ldr_hdr_upconversion_nit_multiplier(0.0f, 0.0f, basist::MAX_HALF_FLOAT),
m_ldr_hdr_upconversion_black_bias(0.0f, 0.0f, 1.0f),
m_xuastc_ldr_effort_level(astc_ldr::EFFORT_LEVEL_DEF, astc_ldr::EFFORT_LEVEL_MIN, astc_ldr::EFFORT_LEVEL_MAX),
m_xuastc_ldr_syntax((int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullZStd, (int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullArith, (int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullZStd),
m_ls_min_psnr(35.0f, 0.0f, 100.0f), m_ls_min_alpha_psnr(38.0f, 0.0f, 100.0f),
m_ls_thresh_psnr(1.5f, 0.0f, 100.0f), m_ls_thresh_alpha_psnr(0.75f, 0.0f, 100.0f),
m_ls_thresh_edge_psnr(1.0f, 0.0f, 100.00f), m_ls_thresh_edge_alpha_psnr(0.5f, 0.0f, 100.00f),
m_pJob_pool(nullptr)
{
clear();
@@ -262,9 +275,12 @@ namespace basisu
void clear()
{
m_format_mode = basist::basis_tex_format::cETC1S;
m_uastc.clear();
m_hdr.clear();
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_use_opencl.clear();
m_status_output.clear();
@@ -286,7 +302,7 @@ namespace basisu
m_selector_rdo_thresh.clear();
m_read_source_images.clear();
m_write_output_basis_or_ktx2_files.clear();
m_compression_level.clear();
m_etc1s_compression_level.clear();
m_compute_stats.clear();
m_print_stats.clear();
m_check_for_alpha.clear();
@@ -301,7 +317,7 @@ namespace basisu
m_no_endpoint_rdo.clear();
m_endpoint_rdo_thresh.clear();
m_mip_gen.clear();
m_mip_scale.clear();
m_mip_filter = "kaiser";
@@ -315,7 +331,7 @@ namespace basisu
m_etc1s_max_endpoint_clusters = 0;
m_etc1s_max_selector_clusters = 0;
m_etc1s_quality_level = -1;
m_quality_level = -1;
m_tex_type = basist::cBASISTexType2D;
m_userdata0 = 0;
@@ -342,37 +358,62 @@ namespace basisu
m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE;
m_ktx2_key_values.clear();
m_ktx2_zstd_supercompression_level.clear();
m_ktx2_srgb_transfer_func.clear();
m_ktx2_and_basis_srgb_transfer_function.clear();
m_validate_output_data.clear();
m_transcode_flags.clear();
m_ldr_hdr_upconversion_srgb_to_linear.clear();
m_hdr_favor_astc.clear();
m_uastc_hdr_4x4_options.init();
m_astc_hdr_6x6_options.clear();
m_ldr_hdr_upconversion_nit_multiplier.clear();
m_ldr_hdr_upconversion_black_bias.clear();
m_xuastc_ldr_effort_level.clear();
m_xuastc_ldr_use_dct.clear();
m_xuastc_ldr_use_lossy_supercompression.clear();
m_xuastc_ldr_force_disable_subsets.clear();
m_xuastc_ldr_force_disable_rgb_dual_plane.clear();
m_xuastc_ldr_syntax.clear();
m_ls_min_psnr.clear();
m_ls_min_alpha_psnr.clear();
m_ls_thresh_psnr.clear();
m_ls_thresh_alpha_psnr.clear();
m_ls_thresh_edge_psnr.clear();
m_ls_thresh_edge_alpha_psnr.clear();
for (uint32_t i = 0; i < 4; i++)
m_xuastc_ldr_channel_weights[i] = 1;
m_xuastc_ldr_blurring.clear();
m_pJob_pool = nullptr;
}
// Configures the compressor's mode by setting the proper parameters (which were preserved for backwards compatibility with old code).
void set_format_mode(basist::basis_tex_format m)
// This is by far the preferred way of controlling which codec mode the compressor will select.
void set_format_mode(basist::basis_tex_format mode)
{
switch (m)
m_format_mode = mode;
switch (mode)
{
case basist::basis_tex_format::cETC1S:
{
// ETC1S
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = false;
m_uastc = false;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
case basist::basis_tex_format::cUASTC4x4:
case basist::basis_tex_format::cUASTC_LDR_4x4:
{
// UASTC LDR 4x4
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = false;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
@@ -380,6 +421,8 @@ namespace basisu
}
case basist::basis_tex_format::cUASTC_HDR_4x4:
{
// UASTC HDR 4x4
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
@@ -387,16 +430,56 @@ namespace basisu
}
case basist::basis_tex_format::cASTC_HDR_6x6:
{
// ASTC HDR 6x6
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cASTC_HDR_6X6;
break;
}
case basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:
case basist::basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE:
{
// UASTC HDR 6x6
m_xuastc_or_astc_ldr_basis_tex_format = -1;
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cASTC_HDR_6X6_INTERMEDIATE;
m_hdr_mode = hdr_modes::cUASTC_HDR_6X6_INTERMEDIATE;
break;
}
case basist::basis_tex_format::cXUASTC_LDR_4x4:
case basist::basis_tex_format::cXUASTC_LDR_5x4:
case basist::basis_tex_format::cXUASTC_LDR_5x5:
case basist::basis_tex_format::cXUASTC_LDR_6x5:
case basist::basis_tex_format::cXUASTC_LDR_6x6:
case basist::basis_tex_format::cXUASTC_LDR_8x5:
case basist::basis_tex_format::cXUASTC_LDR_8x6:
case basist::basis_tex_format::cXUASTC_LDR_10x5:
case basist::basis_tex_format::cXUASTC_LDR_10x6:
case basist::basis_tex_format::cXUASTC_LDR_8x8:
case basist::basis_tex_format::cXUASTC_LDR_10x8:
case basist::basis_tex_format::cXUASTC_LDR_10x10:
case basist::basis_tex_format::cXUASTC_LDR_12x10:
case basist::basis_tex_format::cXUASTC_LDR_12x12:
case basist::basis_tex_format::cASTC_LDR_4x4:
case basist::basis_tex_format::cASTC_LDR_5x4:
case basist::basis_tex_format::cASTC_LDR_5x5:
case basist::basis_tex_format::cASTC_LDR_6x5:
case basist::basis_tex_format::cASTC_LDR_6x6:
case basist::basis_tex_format::cASTC_LDR_8x5:
case basist::basis_tex_format::cASTC_LDR_8x6:
case basist::basis_tex_format::cASTC_LDR_10x5:
case basist::basis_tex_format::cASTC_LDR_10x6:
case basist::basis_tex_format::cASTC_LDR_8x8:
case basist::basis_tex_format::cASTC_LDR_10x8:
case basist::basis_tex_format::cASTC_LDR_10x10:
case basist::basis_tex_format::cASTC_LDR_12x10:
case basist::basis_tex_format::cASTC_LDR_12x12:
{
// ASTC LDR 4x4-12x12 or XUASTC LDR 4x4-12x12
m_xuastc_or_astc_ldr_basis_tex_format = (int)mode;
m_hdr = false;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
default:
@@ -405,39 +488,84 @@ namespace basisu
}
}
// By default we generate LDR ETC1S data.
// if m_uastc is true but m_hdr is not true, we generate UASTC 4x4 LDR data (8bpp with or without RDO).
// 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.
// Like set_format_mode() but also sets the effort and quality parameters appropriately for the selected mode.
// "Effort" (perf. vs. highest achievable quality) and "quality" (quality vs. bitrate) parameters are now mode dependent.
// Effort ranges from [0,10] and quality ranges from [1,100], unless they are -1 in which case you get the codec's default settings.
bool set_format_mode_and_effort(basist::basis_tex_format mode, int effort = -1, bool set_defaults = true);
bool set_format_mode_and_quality_effort(basist::basis_tex_format mode, int quality = -1, int effort = -1, bool set_defaults = true);
// Sets all the sRGB-related options (m_perceptual, m_mip_srgb, m_ktx2_and_basis_srgb_transfer_function) to the specified value.
void set_srgb_options(bool srgb_flag)
{
m_perceptual = srgb_flag;
m_mip_srgb = srgb_flag;
m_ktx2_and_basis_srgb_transfer_function = srgb_flag;
}
// Simpler helpers - I wish this was easier, but backwards API compat is also valuable.
bool is_etc1s() const
{
return !m_uastc;
}
bool is_uastc_ldr_4x4() const
{
return m_uastc && !m_hdr && (m_xuastc_or_astc_ldr_basis_tex_format == -1);
}
bool is_uastc_hdr_4x4() const
{
return m_uastc && m_hdr && (m_hdr_mode == hdr_modes::cUASTC_HDR_4X4);
}
// By default we generate LDR ETC1S data.
// Ideally call set_format_mode() above instead of directly manipulating the below fields. These individual parameters are for backwards API compatibility.
// - If m_uastc is false you get ETC1S (the default).
// - If m_uastc is true, and m_hdr is not true, and m_xuastc_or_astc_ldr_basis_tex_format==-1, we generate UASTC 4x4 LDR data (8bpp with or without RDO).
// - If m_uastc is true, and m_hdr is not true, and m_xuastc_or_astc_ldr_basis_tex_format!=-1, we generate XUASTC 4x4-12x12 or ASTC 4x4-12x12 LDR data.
// - If m_uastc is true and m_hdr is true, we generate 4x4 or 6x6 HDR data, controlled by m_hdr_mode.
// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, and ASTC 6x6 HDR intermediate).
// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, UASTC 6x6 HDR, or ASTC/XUASTC LDR 4x4-12x12).
// Note: Ideally call set_format_mode() or set_format_mode_and_quality_effort() above instead.
// Many of these individual parameters are for backwards API compatibility.
bool_param<false> m_uastc;
// Set m_hdr to true to switch to UASTC HDR mode. m_hdr_mode then controls which format is output.
// m_hdr_mode then controls which format is output (4x4, 6x6, or 6x6 intermediate).
// Note: Ideally call set_format_mode() instead. This is for backwards API compatibility.
bool_param<false> m_hdr;
// 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.
// Note: Ideally call set_format_mode() instead. This is for backwards API compatibility.
hdr_modes m_hdr_mode;
// If not -1: Generate XUASTC or ASTC LDR 4x4-12x12 files in the specified basis_tex_format (which also sets the ASTC block size). If -1 (the default), don't generate XUASTC/ASTC LDR files.
// m_uastc must also be set to true if this is not -1.
// Note: Ideally call set_format_mode() instead.
param<int> m_xuastc_or_astc_ldr_basis_tex_format; // enum basis_tex_format
// True to enable OpenCL if it's available. The compressor will fall back to CPU encoding if something goes wrong.
bool_param<false> m_use_opencl;
// 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.
// 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.
// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.
basisu::vector<std::string> m_source_filenames;
basisu::vector<std::string> m_source_alpha_filenames;
// An array of 2D LDR/SDR source images.
basisu::vector<image> m_source_images;
// An array of 2D HDR source images.
basisu::vector<imagef> m_source_images_hdr;
// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.
// 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.
// The compressor applies the user-provided swizzling (in m_swizzle) to these images.
basisu::vector< basisu::vector<image> > m_source_mipmap_images;
basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;
// Filename of the output basis/ktx2 file
std::string m_out_filename;
@@ -448,20 +576,24 @@ namespace basisu
// If true, the compressor will print basis status to stdout during compression.
bool_param<true> m_status_output;
// Output debug information during compression
bool_param<false> m_debug;
// Low-level ETC1S data validation during encoding (slower/development).
bool_param<false> m_validate_etc1s;
// m_debug_images is pretty slow
bool_param<false> m_debug_images;
// ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower).
// ETC1S compression effort level, from 0 to BASISU_MAX_ETC1S_COMPRESSION_LEVEL (higher is slower).
// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.
// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.
param<int> m_compression_level;
// Use perceptual sRGB colorspace metrics instead of linear
param<int> m_etc1s_compression_level;
// Use perceptual sRGB colorspace metrics instead of linear.
// Note: You probably also want to set m_ktx2_srgb_transfer_func to match.
// Note: This member variable was previously called "m_perceptual".
bool_param<true> m_perceptual;
// Disable selector RDO, for faster compression but larger files
@@ -476,47 +608,53 @@ namespace basisu
// Write the output basis/ktx2 file to disk using m_out_filename
bool_param<false> m_write_output_basis_or_ktx2_files;
// Compute and display image metrics
// Compute and display image metrics
bool_param<false> m_compute_stats;
// Print stats to stdout, if m_compute_stats is true.
bool_param<true> m_print_stats;
// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels
bool_param<true> m_check_for_alpha;
// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha
bool_param<false> m_force_alpha;
bool_param<true> m_multithreading;
bool_param<false> m_force_alpha;
// True to enable multithreading in various compressors.
// Note currently, some compressors (like ASTC/XUASTC LDR) will utilize threading anyway if the job pool is more than one thread.
bool_param<true> m_multithreading;
// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels
uint8_t m_swizzle[4];
// Renormalize normal map normals after loading image
bool_param<false> m_renormalize;
// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.
// Note some m_compression_level's disable this automatically.
// Note some m_etc1s_compression_level's disable this automatically.
bool_param<false> m_disable_hierarchical_endpoint_codebooks;
// mipmap generation parameters
bool_param<false> m_mip_gen;
param<float> m_mip_scale;
std::string m_mip_filter;
bool_param<false> m_mip_srgb;
bool_param<true> m_mip_premultiplied; // not currently supported
bool_param<false> m_mip_renormalize;
bool_param<false> m_mip_renormalize;
bool_param<true> m_mip_wrapping;
bool_param<true> m_mip_fast;
param<int> m_mip_smallest_dimension;
// ETC1S codebook size (quality) control.
// ETC1S codebook size (quality) control.
// If m_etc1s_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
uint32_t m_etc1s_max_endpoint_clusters;
uint32_t m_etc1s_max_selector_clusters;
int m_etc1s_quality_level;
// Quality level (bitrate vs. distortion tradeoff) control for ETC1S or XUASTC LDR 4x4-12x12 (must not be -1 for DCT to be used in XUASTC LDR 4x4 mode)
int m_quality_level;
// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the .basis file header.
basist::basis_texture_type m_tex_type;
uint32_t m_userdata0;
@@ -527,7 +665,7 @@ namespace basisu
// cPackUASTCLevelDefault, etc.
uint32_t m_pack_uastc_ldr_4x4_flags;
bool_param<false> m_rdo_uastc_ldr_4x4;
param<float> m_rdo_uastc_ldr_4x4_quality_scalar;
param<float> m_rdo_uastc_ldr_4x4_quality_scalar; // RDO lambda for UASTC 4x4 LDR
param<int> m_rdo_uastc_ldr_4x4_dict_size;
param<float> m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale;
param<float> m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev;
@@ -536,10 +674,12 @@ namespace basisu
bool_param<true> m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode;
bool_param<true> m_rdo_uastc_ldr_4x4_multithreading;
// Resample input texture after loading
param<int> m_resample_width;
param<int> m_resample_height;
param<float> m_resample_factor;
// ETC1S global codebook control
const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
// KTX2 specific parameters.
@@ -548,19 +688,28 @@ namespace basisu
basist::ktx2_supercompression m_ktx2_uastc_supercompression;
basist::ktx2_transcoder::key_value_vec m_ktx2_key_values;
param<int> m_ktx2_zstd_supercompression_level;
bool_param<false> m_ktx2_srgb_transfer_func;
// Note: The default for this parameter (which used to be "m_ktx2_srgb_transfer_func") used to be false, now setting this to true and renaming to m_ktx2_and_basis_srgb_transfer_function.
// Also see m_perceptual and m_mip_srgb, which should in most uses be the same.
// This also controls the XUASTC LDR ASTC decode profile (linear vs. sRGB) in the simulated decoder block.
// For XUASTC LDR, it's also still used when generating .basis files vs. .KTX2.
bool_param<true> m_ktx2_and_basis_srgb_transfer_function; // false = linear transfer function, true = sRGB transfer function
uastc_hdr_4x4_codec_options m_uastc_hdr_4x4_options;
astc_6x6_hdr::astc_hdr_6x6_global_config m_astc_hdr_6x6_options;
// True to try transcoding the generated output after compression to a few formats.
bool_param<false> m_validate_output_data;
// The flags to use while transcoding if m_validate_output_data
param<uint32_t> m_transcode_flags;
// LDR->HDR upconversion parameters.
//
// 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.
//
// 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.
// Otherwise, LDR images are assumed to already be in linear light (i.e. they don't use the sRGB transfer function).
bool_param<true> m_ldr_hdr_upconversion_srgb_to_linear;
// m_ldr_hdr_upconversion_nit_multiplier is only used when loading SDR/LDR images and compressing to an HDR output format.
// By default m_ldr_hdr_upconversion_nit_multiplier is 0. It's an override for the default.
// 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.
@@ -571,10 +720,35 @@ namespace basisu
// Defaults to no bias (0.0f).
param<float> m_ldr_hdr_upconversion_black_bias;
// If true, ASTC HDR quality is favored more than BC6H quality. Otherwise it's a rough balance.
// If true, ASTC HDR quality is favored more than BC6H quality by the dual target encoder. Otherwise it's a rough balance.
// UASTC HDR 4x4
bool_param<false> m_hdr_favor_astc;
// XUASTC LDR 4x4-12x12 specific options
param<int> m_xuastc_ldr_effort_level;
bool_param<false> m_xuastc_ldr_use_dct; // set the DCT quality above using m_quality_level, [1,100]
bool_param<false> m_xuastc_ldr_use_lossy_supercompression; // allows the compressor to introduce a bounded amount of distortion if doing so would make smaller files (actually ASTC or XUASTC)
bool_param<false> m_xuastc_ldr_force_disable_subsets; // disable 2-3 subset usage in all effort levels, faster encoding, faster transcoding to BC7, but lower quality)
bool_param<false> m_xuastc_ldr_force_disable_rgb_dual_plane; // disable RGB dual plane usage (still can use dual plane on alpha blocks), for faster transcoding to BC7 but lower quality
param<int> m_xuastc_ldr_syntax; // favor faster decompression over ratio, default is basist::astc_ldr_t::xuastc_ldr_syntax::cFullZstd (fastest transcoding but lower ratio)
uint32_t m_xuastc_ldr_channel_weights[4];
bool_param<false> m_xuastc_ldr_blurring; // experimental, not recommended, very slow
// XUASTC Lossy supercompression PSNR threshold parameters
param<float> m_ls_min_psnr, m_ls_min_alpha_psnr;
param<float> m_ls_thresh_psnr, m_ls_thresh_alpha_psnr;
param<float> m_ls_thresh_edge_psnr, m_ls_thresh_edge_alpha_psnr;
// Job pool, MUST not be nullptr;
job_pool *m_pJob_pool;
// Returns the current format mode as set by set_format_mode() above.
// Because of backwards API compatibility we don't use this directly yet, it's just here to aid the transition to the new API.
basist::basis_tex_format get_format_mode() const { return m_format_mode; }
private:
// This is set by set_format_mode() above. For backwards API compat we don't use it directly, it's just here to aid the transition to the new API.
basist::basis_tex_format m_format_mode;
};
// Important: basisu_encoder_init() MUST be called first before using this class.
@@ -588,7 +762,7 @@ namespace basisu
// 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.
bool init(const basis_compressor_params &params);
enum error_code
{
cECSuccess = 0,
@@ -602,45 +776,63 @@ namespace basisu
cECFailedCreateBasisFile,
cECFailedWritingOutput,
cECFailedUASTCRDOPostProcess,
cECFailedCreateKTX2File
cECFailedCreateKTX2File,
cECFailedInvalidParameters
};
error_code process();
// The output .basis file will always be valid of process() succeeded.
const uint8_vec &get_output_basis_file() const { return m_output_basis_file; }
// The output .ktx2 file will only be valid if m_create_ktx2_file was true and process() succeeded.
const uint8_vec& get_output_ktx2_file() const { return m_output_ktx2_file; }
const basisu::vector<image_stats> &get_stats() const { return m_stats; }
uint32_t get_basis_file_size() const { return m_basis_file_size; }
// Sum of all slice orig pixels. Intended for statistics display.
uint64_t get_total_slice_orig_texels() const { return m_total_slice_orig_texels; }
uint64_t get_basis_file_size() const { return m_basis_file_size; }
double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
uint64_t get_ktx2_file_size() const { return m_ktx2_file_size; }
double get_ktx2_bits_per_texel() const { return m_ktx2_bits_per_texel; }
bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
bool get_opencl_failed() const { return m_opencl_failed; }
private:
basis_compressor_params m_params;
opencl_context_ptr m_pOpenCL_context;
basist::basis_tex_format m_fmt_mode;
// the output mode/codec
basist::basis_tex_format m_fmt_mode;
// the output mode/codec's block width/height
uint32_t m_fmt_mode_block_width;
uint32_t m_fmt_mode_block_height;
// Note these images are expanded if necessary (duplicating cols/rows) to account for block dimensions.
basisu::vector<image> m_slice_images;
basisu::vector<imagef> m_slice_images_hdr;
basisu::vector<image_stats> m_stats;
uint32_t m_basis_file_size;
uint64_t m_total_slice_orig_texels;
uint64_t m_basis_file_size;
double m_basis_bits_per_texel;
uint64_t m_ktx2_file_size;
double m_ktx2_bits_per_texel;
basisu_backend_slice_desc_vec m_slice_descs;
uint32_t m_total_blocks;
basisu_frontend m_frontend;
// These are 4x4 blocks.
@@ -658,7 +850,7 @@ namespace basisu
basisu::vector<gpu_image> m_decoded_output_textures; // BC6H in HDR mode
basisu::vector<image> m_decoded_output_textures_unpacked;
basisu::vector<gpu_image> m_decoded_output_textures_bc7;
basisu::vector<image> m_decoded_output_textures_unpacked_bc7;
@@ -669,16 +861,16 @@ namespace basisu
uint8_vec m_output_basis_file;
uint8_vec m_output_ktx2_file;
basisu::vector<gpu_image> m_uastc_slice_textures;
basisu_backend_output m_uastc_backend_output;
// The amount the HDR input has to be scaled up in case it had to be rescaled to fit into half floats.
float m_hdr_image_scale;
float m_hdr_image_scale;
// The upconversion multiplier used to load LDR images in HDR mode.
float m_ldr_to_hdr_upconversion_nit_multiplier;
// True if any loaded source images were LDR and upconverted to HDR.
bool m_upconverted_any_ldr_images;
@@ -701,48 +893,20 @@ namespace basisu
error_code encode_slices_to_astc_6x6_hdr();
error_code encode_slices_to_uastc_4x4_hdr();
error_code encode_slices_to_uastc_4x4_ldr();
error_code encode_slices_to_xuastc_or_astc_ldr();
bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
bool validate_texture_type_constraints();
bool validate_ktx2_constraints();
bool get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
bool create_ktx2_file();
void pick_format_mode();
bool pick_format_mode();
uint32_t get_block_width() const
{
if (m_params.m_hdr)
{
switch (m_params.m_hdr_mode)
{
case hdr_modes::cASTC_HDR_6X6:
case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:
return 6;
default:
break;
}
}
return 4;
}
uint32_t get_block_height() const
{
if (m_params.m_hdr)
{
switch (m_params.m_hdr_mode)
{
case hdr_modes::cASTC_HDR_6X6:
case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:
return 6;
default:
break;
}
}
return 4;
}
uint32_t get_block_width() const { return m_fmt_mode_block_width; }
uint32_t get_block_height() const { return m_fmt_mode_block_height; }
};
// Alternative simple C-style wrapper API around the basis_compressor class.
// Alternative simple C-style wrapper API around the basis_compressor class.
// This doesn't expose every encoder feature, but it's enough to get going.
// Important: basisu_encoder_init() MUST be called first before calling these functions.
//
@@ -751,16 +915,22 @@ namespace basisu
// OR
// pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.
// width/height/pitch_in_pixels: dimensions of pImageRGBA
//
// 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".
//
// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC quality or effort level.
// Note: basis_compress2() variants below accept the new-style "quality_level" (0-100) and "effort_level" (0-10) parameters instead of packing them into flags_and_quality.
// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
// 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.
// In UASTC mode, be sure to set this, otherwise it defaults to 0 (fastest/lowest quality).
//
// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
//
// In UASTC LDR 4x4 mode, the lower 8-bits are the UASTC LDR/HDR pack or effort level (see cPackUASTCLevelFastest to cPackUASTCLevelVerySlow). Fastest/lowest quality is 0, so be sure to set it correctly. Valid values are [0,4] for both LDR/HDR.
// In UASTC HDR 4x4 mode, the lower 8-bits are the codec's effort level. Valid range is [uastc_hdr_4x4_codec_options::cMinLevel, uastc_hdr_4x4_codec_options::cMaxLevel]. Higher=better quality, but slower.
// In RDO ASTC HDR 6x6/UASTC HDR 6x6 mode, the lower 8-bits are the codec's effort level. Valid range is [0,astc_6x6_hdr::ASTC_HDR_6X6_MAX_USER_COMP_LEVEL]. Higher levels=better quality, but slower.
// In XUASTC/ASTC LDR 4x4-12x12 mode, the lower 8-bits are the compressor's effort level from [0,10] (astc_ldr_t::EFFORT_LEVEL_MIN, astc_ldr_t::EFFORT_LEVEL_MAX).
//
// float uastc_rdo_or_dct_quality:
// UASTC LDR 4x4 RDO quality level: RDO lambda setting - 0=no change/highest quality. Higher values lower quality but increase compressibility, initially try .5-1.5.
// RDO ASTC 6x6 HDR/UASTC 6x6 HDR: RDO lambda setting. 0=no change/highest quality. Higher values lower quality but increase compressibility, initially try 250-2000 (HDR) or 1000-10000 (LDR/SDR inputs upconverted to HDR).
// In XUASTC/ASTC LDR 4x4-12x12 mode, this is the [1,100] weight grid DCT quality level.
//
// pSize: Returns the output data's compressed size in bytes
//
//
// Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it.
enum
{
@@ -769,36 +939,63 @@ namespace basisu
cFlagDebug = 1 << 10, // enable debug output
cFlagKTX2 = 1 << 11, // generate a KTX2 file
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on non-supercompressed formats that support it.
cFlagSRGB = 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 sRGB
cFlagSRGB = 1 << 13, // input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2/.basis output transfer func to sRGB
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
cFlagUASTCRDO = 1 << 17, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
// Note 11/18/2025: cFlagUASTCRDO flag is now ignored. Now if uastc_rdo_or_dct_quality>0 in UASTC LDR 4x4 mode, you automatically get RDO.
//cFlagUASTCRDO = 1 << 17, // use RDO postprocessing when generating UASTC LDR 4x4 files (must set uastc_rdo_or_dct_quality to the quality scalar)
cFlagPrintStats = 1 << 18, // print image stats to stdout
cFlagPrintStatus = 1 << 19, // print status to stdout
cFlagDebugImages = 1 << 20, // enable debug image generation (for development, slower)
cFlagDebugImages = 1 << 20, // enable status output
cFlagREC2020 = 1 << 21, // ASTC 6x6 modes: treat input as REC 2020 vs. the default 709
cFlagREC2020 = 1 << 21, // treat input as REC 2020 vs. the default 709 (for codecs that support this, currently UASTC HDR and ASTC 6x6), bit is always placed into KTX2 DFD
cFlagValidateOutput = 1 << 22, // transcode the output after encoding for testing
// XUASTC LDR profile: full arith, hybrid or full zstd (see basist::astc_ldr_t::xuastc_ldr_syntax)
cFlagXUASTCLDRSyntaxFullArith = 0 << 23,
cFlagXUASTCLDRSyntaxHybrid = 1 << 23,
cFlagXUASTCLDRSyntaxFullZStd = 2 << 23,
cFlagXUASTCLDRSyntaxShift = 23,
cFlagXUASTCLDRSyntaxMask = 3,
// Texture Type: 2D, 2D Array, Cubemap Array, or Texture Video (see enum basis_texture_type). Defaults to plain 2D.
cFlagTextureType2D = 0 << 25,
cFlagTextureType2DArray = 1 << 25,
cFlagTextureTypeCubemapArray = 2 << 25,
cFlagTextureTypeVideoFrames = 3 << 25,
cFlagTextureTypeShift = 25,
cFlagTextureTypeMask = 3,
};
// This function accepts an array of source images.
void* basis_compress_internal(
basist::basis_tex_format mode,
const basisu::vector<image>* pSource_images,
const basisu::vector<imagef>* pSource_images_hdr,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats,
int quality_level = -1, int effort_level = -1);
// This function accepts an array of source images.
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.
// Important: The returned block MUST be manually freed using basis_free_data().
// basisu_encoder_init() MUST be called first!
// LDR version. To compress the LDR source image as HDR: Use the cFlagHDR flag.
// LDR version.
void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_quality,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
@@ -807,7 +1004,7 @@ namespace basisu
void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, float lambda,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
@@ -816,7 +1013,30 @@ namespace basisu
void* basis_compress(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_quality,
uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// basis_compress2() variants accept the new unified quality_level and effort_level parameters instead of the old flags/float uastc_rdo_or_dct_quality parameter.
// quality_level must be [0,100], effort_level [0,10].
void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<image>& source_images,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
void* basis_compress2(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
void* basis_compress2(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, int quality_level, int effort_level,
size_t* pSize,
image_stats* pStats = nullptr);
@@ -841,7 +1061,7 @@ namespace basisu
double m_basis_bits_per_texel;
bool m_any_source_image_has_alpha;
parallel_results()
parallel_results()
{
clear();
}
@@ -857,7 +1077,7 @@ namespace basisu
m_any_source_image_has_alpha = false;
}
};
// Compresses an array of input textures across total_threads threads using the basis_compressor class.
// Compressing multiple textures at a time is substantially more efficient than just compressing one at a time.
// total_threads must be >= 1.
@@ -865,5 +1085,6 @@ namespace basisu
uint32_t total_threads,
const basisu::vector<basis_compressor_params> &params_vec,
basisu::vector< parallel_results > &results_vec);
} // namespace basisu