summaryrefslogtreecommitdiff
path: root/thirdparty/basis_universal/encoder/basisu_comp.h
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/basis_universal/encoder/basisu_comp.h')
-rw-r--r--thirdparty/basis_universal/encoder/basisu_comp.h555
1 files changed, 555 insertions, 0 deletions
diff --git a/thirdparty/basis_universal/encoder/basisu_comp.h b/thirdparty/basis_universal/encoder/basisu_comp.h
new file mode 100644
index 0000000000..2c3af968f7
--- /dev/null
+++ b/thirdparty/basis_universal/encoder/basisu_comp.h
@@ -0,0 +1,555 @@
+// basisu_comp.h
+// Copyright (C) 2019-2021 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.
+// 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.
+#pragma once
+#include "basisu_frontend.h"
+#include "basisu_backend.h"
+#include "basisu_basis_file.h"
+#include "../transcoder/basisu_global_selector_palette.h"
+#include "../transcoder/basisu_transcoder.h"
+#include "basisu_uastc_enc.h"
+
+#define BASISU_LIB_VERSION 115
+#define BASISU_LIB_VERSION_STRING "1.15"
+
+#ifndef BASISD_SUPPORT_KTX2
+ #error BASISD_SUPPORT_KTX2 is undefined
+#endif
+#ifndef BASISD_SUPPORT_KTX2_ZSTD
+ #error BASISD_SUPPORT_KTX2_ZSTD is undefined
+#endif
+
+#if !BASISD_SUPPORT_KTX2
+ #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 0
+#endif
+
+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;
+
+ // 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 int BASISU_DEFAULT_QUALITY = 128;
+ const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;
+
+ 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_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;
+ const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;
+
+ const uint32_t BASISU_MAX_SLICES = 0xFFFFFF;
+
+ const int BASISU_RDO_UASTC_DICT_SIZE_DEFAULT = 4096; // 32768;
+ const int BASISU_RDO_UASTC_DICT_SIZE_MIN = 64;
+ const int BASISU_RDO_UASTC_DICT_SIZE_MAX = 65536;
+
+ struct image_stats
+ {
+ image_stats()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ m_filename.clear();
+ m_width = 0;
+ m_height = 0;
+
+ m_basis_rgb_avg_psnr = 0.0f;
+ m_basis_rgba_avg_psnr = 0.0f;
+ m_basis_a_avg_psnr = 0.0f;
+ m_basis_luma_709_psnr = 0.0f;
+ m_basis_luma_601_psnr = 0.0f;
+ m_basis_luma_709_ssim = 0.0f;
+
+ m_bc7_rgb_avg_psnr = 0.0f;
+ m_bc7_rgba_avg_psnr = 0.0f;
+ m_bc7_a_avg_psnr = 0.0f;
+ 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;
+ m_best_etc1s_luma_709_ssim = 0.0f;
+ }
+
+ std::string m_filename;
+ uint32_t m_width;
+ uint32_t m_height;
+
+ // .basis compressed (ETC1S or UASTC statistics)
+ float m_basis_rgb_avg_psnr;
+ float m_basis_rgba_avg_psnr;
+ float m_basis_a_avg_psnr;
+ float m_basis_luma_709_psnr;
+ float m_basis_luma_601_psnr;
+ float m_basis_luma_709_ssim;
+
+ // BC7 statistics
+ float m_bc7_rgb_avg_psnr;
+ float m_bc7_rgba_avg_psnr;
+ float m_bc7_a_avg_psnr;
+ float m_bc7_luma_709_psnr;
+ float m_bc7_luma_601_psnr;
+ float m_bc7_luma_709_ssim;
+
+ // Highest achievable quality ETC1S statistics
+ float m_best_etc1s_rgb_avg_psnr;
+ float m_best_etc1s_luma_709_psnr;
+ float m_best_etc1s_luma_601_psnr;
+ float m_best_etc1s_luma_709_ssim;
+ };
+
+ template<bool def>
+ struct bool_param
+ {
+ bool_param() :
+ m_value(def),
+ m_changed(false)
+ {
+ }
+
+ void clear()
+ {
+ m_value = def;
+ m_changed = false;
+ }
+
+ operator bool() const
+ {
+ return m_value;
+ }
+
+ bool operator= (bool v)
+ {
+ m_value = v;
+ m_changed = true;
+ return m_value;
+ }
+
+ bool was_changed() const { return m_changed; }
+ void set_changed(bool flag) { m_changed = flag; }
+
+ bool m_value;
+ bool m_changed;
+ };
+
+ template<typename T>
+ struct param
+ {
+ param(T def, T min_v, T max_v) :
+ m_value(def),
+ m_def(def),
+ m_min(min_v),
+ m_max(max_v),
+ m_changed(false)
+ {
+ }
+
+ void clear()
+ {
+ m_value = m_def;
+ m_changed = false;
+ }
+
+ operator T() const
+ {
+ return m_value;
+ }
+
+ T operator= (T v)
+ {
+ m_value = clamp<T>(v, m_min, m_max);
+ m_changed = true;
+ return m_value;
+ }
+
+ T operator *= (T v)
+ {
+ m_value *= v;
+ m_changed = true;
+ return m_value;
+ }
+
+ bool was_changed() const { return m_changed; }
+ void set_changed(bool flag) { m_changed = flag; }
+
+ T m_value;
+ T m_def;
+ T m_min;
+ T m_max;
+ bool m_changed;
+ };
+
+ struct basis_compressor_params
+ {
+ basis_compressor_params() :
+ m_pSel_codebook(NULL),
+ m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_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_hybrid_sel_cb_quality_thresh(BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH, 0.0f, 1e+10f),
+ m_global_pal_bits(8, 0, ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS),
+ m_global_mod_bits(8, 0, basist::etc1_global_palette_entry_modifier::cTotalBits),
+ m_mip_scale(1.0f, .000125f, 4.0f),
+ m_mip_smallest_dimension(1, 1, 16384),
+ m_max_endpoint_clusters(512),
+ m_max_selector_clusters(512),
+ m_quality_level(-1),
+ m_pack_uastc_flags(cPackUASTCLevelDefault),
+ m_rdo_uastc_quality_scalar(1.0f, 0.001f, 50.0f),
+ m_rdo_uastc_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
+ m_rdo_uastc_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
+ m_rdo_uastc_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
+ m_rdo_uastc_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
+ m_rdo_uastc_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
+ m_resample_width(0, 1, 16384),
+ m_resample_height(0, 1, 16384),
+ 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_pJob_pool(nullptr)
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ m_pSel_codebook = NULL;
+
+ m_uastc.clear();
+ m_status_output.clear();
+
+ m_source_filenames.clear();
+ m_source_alpha_filenames.clear();
+
+ m_source_images.clear();
+ m_source_mipmap_images.clear();
+
+ m_out_filename.clear();
+
+ m_y_flip.clear();
+ m_debug.clear();
+ m_validate.clear();
+ m_debug_images.clear();
+ m_global_sel_pal.clear();
+ m_auto_global_sel_pal.clear();
+ m_no_hybrid_sel_cb.clear();
+ m_perceptual.clear();
+ m_no_selector_rdo.clear();
+ m_selector_rdo_thresh.clear();
+ m_read_source_images.clear();
+ m_write_output_basis_files.clear();
+ m_compression_level.clear();
+ m_compute_stats.clear();
+ m_check_for_alpha.clear();
+ m_force_alpha.clear();
+ m_multithreading.clear();
+ m_swizzle[0] = 0;
+ m_swizzle[1] = 1;
+ m_swizzle[2] = 2;
+ m_swizzle[3] = 3;
+ m_renormalize.clear();
+ m_hybrid_sel_cb_quality_thresh.clear();
+ m_global_pal_bits.clear();
+ m_global_mod_bits.clear();
+ m_disable_hierarchical_endpoint_codebooks.clear();
+
+ m_no_endpoint_rdo.clear();
+ m_endpoint_rdo_thresh.clear();
+
+ m_mip_gen.clear();
+ m_mip_scale.clear();
+ m_mip_filter = "kaiser";
+ m_mip_scale = 1.0f;
+ m_mip_srgb.clear();
+ m_mip_premultiplied.clear();
+ m_mip_renormalize.clear();
+ m_mip_wrapping.clear();
+ m_mip_fast.clear();
+ m_mip_smallest_dimension.clear();
+
+ m_max_endpoint_clusters = 0;
+ m_max_selector_clusters = 0;
+ m_quality_level = -1;
+
+ m_tex_type = basist::cBASISTexType2D;
+ m_userdata0 = 0;
+ m_userdata1 = 0;
+ m_us_per_frame = 0;
+
+ m_pack_uastc_flags = cPackUASTCLevelDefault;
+ m_rdo_uastc.clear();
+ m_rdo_uastc_quality_scalar.clear();
+ m_rdo_uastc_max_smooth_block_error_scale.clear();
+ m_rdo_uastc_smooth_block_max_std_dev.clear();
+ m_rdo_uastc_max_allowed_rms_increase_ratio.clear();
+ m_rdo_uastc_skip_block_rms_thresh.clear();
+ m_rdo_uastc_favor_simpler_modes_in_rdo_mode.clear();
+ m_rdo_uastc_multithreading.clear();
+
+ m_resample_width.clear();
+ m_resample_height.clear();
+ m_resample_factor.clear();
+
+ m_pGlobal_codebooks = nullptr;
+
+ m_create_ktx2_file.clear();
+ 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_pJob_pool = nullptr;
+ }
+
+ // Pointer to the global selector codebook, or nullptr to not use a global selector codebook
+ const basist::etc1_global_selector_codebook *m_pSel_codebook;
+
+ // True to generate UASTC .basis file data, otherwise ETC1S.
+ bool_param<false> m_uastc;
+
+ // If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read.
+ // Otherwise, the compressor processes the images in m_source_images.
+ basisu::vector<std::string> m_source_filenames;
+ basisu::vector<std::string> m_source_alpha_filenames;
+
+ basisu::vector<image> m_source_images;
+
+ // 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;
+
+ // Filename of the output basis file
+ std::string m_out_filename;
+
+ // The params are done this way so we can detect when the user has explictly changed them.
+
+ // Flip images across Y axis
+ bool_param<false> m_y_flip;
+
+ // 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;
+ bool_param<false> m_validate;
+
+ // m_debug_images is pretty slow
+ bool_param<false> m_debug_images;
+
+ // Compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower)
+ param<int> m_compression_level;
+
+ bool_param<false> m_global_sel_pal;
+ bool_param<false> m_auto_global_sel_pal;
+
+ // Frontend/backend codec parameters
+ bool_param<false> m_no_hybrid_sel_cb;
+
+ // Use perceptual sRGB colorspace metrics instead of linear
+ bool_param<true> m_perceptual;
+
+ // Disable selector RDO, for faster compression but larger files
+ bool_param<false> m_no_selector_rdo;
+ param<float> m_selector_rdo_thresh;
+
+ bool_param<false> m_no_endpoint_rdo;
+ param<float> m_endpoint_rdo_thresh;
+
+ // Read source images from m_source_filenames/m_source_alpha_filenames
+ bool_param<false> m_read_source_images;
+
+ // Write the output basis file to disk using m_out_filename
+ bool_param<false> m_write_output_basis_files;
+
+ // Compute and display image metrics
+ bool_param<false> m_compute_stats;
+
+ // Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels
+ bool_param<true> m_check_for_alpha;
+
+ // Always put alpha slices in the output basis file, even when the input doesn't have alpha
+ bool_param<false> m_force_alpha;
+ bool_param<true> m_multithreading;
+
+ // Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels
+ char m_swizzle[4];
+
+ bool_param<false> m_renormalize;
+
+ bool_param<false> m_disable_hierarchical_endpoint_codebooks;
+
+ // Global/hybrid selector codebook parameters
+ param<float> m_hybrid_sel_cb_quality_thresh;
+ param<int> m_global_pal_bits;
+ param<int> m_global_mod_bits;
+
+ // 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<true> m_mip_wrapping;
+ bool_param<true> m_mip_fast;
+ param<int> m_mip_smallest_dimension;
+
+ // Codebook size (quality) control.
+ // If m_quality_level != -1, it controls the quality level. It ranges from [0,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_max_endpoint_clusters;
+ uint32_t m_max_selector_clusters;
+ 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;
+ uint32_t m_userdata1;
+ uint32_t m_us_per_frame;
+
+ // cPackUASTCLevelDefault, etc.
+ uint32_t m_pack_uastc_flags;
+ bool_param<false> m_rdo_uastc;
+ param<float> m_rdo_uastc_quality_scalar;
+ param<int> m_rdo_uastc_dict_size;
+ param<float> m_rdo_uastc_max_smooth_block_error_scale;
+ param<float> m_rdo_uastc_smooth_block_max_std_dev;
+ param<float> m_rdo_uastc_max_allowed_rms_increase_ratio;
+ param<float> m_rdo_uastc_skip_block_rms_thresh;
+ bool_param<true> m_rdo_uastc_favor_simpler_modes_in_rdo_mode;
+ bool_param<true> m_rdo_uastc_multithreading;
+
+ param<int> m_resample_width;
+ param<int> m_resample_height;
+ param<float> m_resample_factor;
+ const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
+
+ // KTX2 specific parameters.
+ // Internally, the compressor always creates a .basis file then it converts that lossless to KTX2.
+ bool_param<false> m_create_ktx2_file;
+ 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;
+
+ job_pool *m_pJob_pool;
+ };
+
+ class basis_compressor
+ {
+ BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);
+
+ public:
+ basis_compressor();
+
+ bool init(const basis_compressor_params &params);
+
+ enum error_code
+ {
+ cECSuccess = 0,
+ cECFailedReadingSourceImages,
+ cECFailedValidating,
+ cECFailedEncodeUASTC,
+ cECFailedFrontEnd,
+ cECFailedFontendExtract,
+ cECFailedBackend,
+ cECFailedCreateBasisFile,
+ cECFailedWritingOutput,
+ cECFailedUASTCRDOPostProcess,
+ cECFailedCreateKTX2File
+ };
+
+ 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; }
+ double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
+
+ bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
+
+ private:
+ basis_compressor_params m_params;
+
+ basisu::vector<image> m_slice_images;
+
+ basisu::vector<image_stats> m_stats;
+
+ uint32_t m_basis_file_size;
+ double m_basis_bits_per_texel;
+
+ basisu_backend_slice_desc_vec m_slice_descs;
+
+ uint32_t m_total_blocks;
+ bool m_auto_global_sel_pal;
+
+ basisu_frontend m_frontend;
+ pixel_block_vec m_source_blocks;
+
+ basisu::vector<gpu_image> m_frontend_output_textures;
+
+ basisu::vector<gpu_image> m_best_etc1s_images;
+ basisu::vector<image> m_best_etc1s_images_unpacked;
+
+ basisu_backend m_backend;
+
+ basisu_file m_basis_file;
+
+ basisu::vector<gpu_image> m_decoded_output_textures;
+ 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;
+
+ 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;
+
+ bool m_any_source_image_has_alpha;
+
+ bool read_source_images();
+ bool extract_source_blocks();
+ bool process_frontend();
+ bool extract_frontend_texture_data();
+ bool process_backend();
+ bool create_basis_file_and_transcode();
+ bool write_output_files_and_compute_stats();
+ error_code encode_slices_to_uastc();
+ bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
+ bool validate_texture_type_constraints();
+ bool validate_ktx2_constraints();
+ void get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
+ bool create_ktx2_file();
+ };
+
+} // namespace basisu
+