| 1 | // basisu_comp.h | 
|---|
| 2 | // Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved. | 
|---|
| 3 | // | 
|---|
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|---|
| 5 | // you may not use this file except in compliance with the License. | 
|---|
| 6 | // You may obtain a copy of the License at | 
|---|
| 7 | // | 
|---|
| 8 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|---|
| 9 | // | 
|---|
| 10 | // Unless required by applicable law or agreed to in writing, software | 
|---|
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|---|
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|---|
| 13 | // See the License for the specific language governing permissions and | 
|---|
| 14 | // limitations under the License. | 
|---|
| 15 | #pragma once | 
|---|
| 16 | #include "basisu_frontend.h" | 
|---|
| 17 | #include "basisu_backend.h" | 
|---|
| 18 | #include "basisu_basis_file.h" | 
|---|
| 19 | #include "../transcoder/basisu_transcoder.h" | 
|---|
| 20 | #include "basisu_uastc_enc.h" | 
|---|
| 21 |  | 
|---|
| 22 | #define BASISU_LIB_VERSION 116 | 
|---|
| 23 | #define BASISU_LIB_VERSION_STRING "1.16" | 
|---|
| 24 |  | 
|---|
| 25 | #ifndef BASISD_SUPPORT_KTX2 | 
|---|
| 26 | #error BASISD_SUPPORT_KTX2 is undefined | 
|---|
| 27 | #endif | 
|---|
| 28 | #ifndef BASISD_SUPPORT_KTX2_ZSTD | 
|---|
| 29 | #error BASISD_SUPPORT_KTX2_ZSTD is undefined | 
|---|
| 30 | #endif | 
|---|
| 31 |  | 
|---|
| 32 | #if !BASISD_SUPPORT_KTX2 | 
|---|
| 33 | #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 | 
|---|
| 34 | #endif | 
|---|
| 35 |  | 
|---|
| 36 | namespace basisu | 
|---|
| 37 | { | 
|---|
| 38 | struct opencl_context; | 
|---|
| 39 | typedef opencl_context* opencl_context_ptr; | 
|---|
| 40 |  | 
|---|
| 41 | const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384; | 
|---|
| 42 |  | 
|---|
| 43 | // Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint. | 
|---|
| 44 | const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f; | 
|---|
| 45 |  | 
|---|
| 46 | // Allow block's color distance to increase by 1.25 while searching the selector history buffer for a close enough match. | 
|---|
| 47 | const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f; | 
|---|
| 48 |  | 
|---|
| 49 | const int BASISU_DEFAULT_QUALITY = 128; | 
|---|
| 50 | const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f; | 
|---|
| 51 |  | 
|---|
| 52 | const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384; | 
|---|
| 53 | const uint32_t BASISU_QUALITY_MIN = 1; | 
|---|
| 54 | const uint32_t BASISU_QUALITY_MAX = 255; | 
|---|
| 55 | const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters; | 
|---|
| 56 | const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters; | 
|---|
| 57 |  | 
|---|
| 58 | const uint32_t BASISU_MAX_SLICES = 0xFFFFFF; | 
|---|
| 59 |  | 
|---|
| 60 | const int BASISU_RDO_UASTC_DICT_SIZE_DEFAULT = 4096; // 32768; | 
|---|
| 61 | const int BASISU_RDO_UASTC_DICT_SIZE_MIN = 64; | 
|---|
| 62 | const int BASISU_RDO_UASTC_DICT_SIZE_MAX = 65536; | 
|---|
| 63 |  | 
|---|
| 64 | struct image_stats | 
|---|
| 65 | { | 
|---|
| 66 | image_stats() | 
|---|
| 67 | { | 
|---|
| 68 | clear(); | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | void clear() | 
|---|
| 72 | { | 
|---|
| 73 | m_filename.clear(); | 
|---|
| 74 | m_width = 0; | 
|---|
| 75 | m_height = 0; | 
|---|
| 76 |  | 
|---|
| 77 | m_basis_rgb_avg_psnr = 0.0f; | 
|---|
| 78 | m_basis_rgba_avg_psnr = 0.0f; | 
|---|
| 79 | m_basis_a_avg_psnr = 0.0f; | 
|---|
| 80 | m_basis_luma_709_psnr = 0.0f; | 
|---|
| 81 | m_basis_luma_601_psnr = 0.0f; | 
|---|
| 82 | m_basis_luma_709_ssim = 0.0f; | 
|---|
| 83 |  | 
|---|
| 84 | m_bc7_rgb_avg_psnr = 0.0f; | 
|---|
| 85 | m_bc7_rgba_avg_psnr = 0.0f; | 
|---|
| 86 | m_bc7_a_avg_psnr = 0.0f; | 
|---|
| 87 | m_bc7_luma_709_psnr = 0.0f; | 
|---|
| 88 | m_bc7_luma_601_psnr = 0.0f; | 
|---|
| 89 | m_bc7_luma_709_ssim = 0.0f; | 
|---|
| 90 |  | 
|---|
| 91 | m_best_etc1s_rgb_avg_psnr = 0.0f; | 
|---|
| 92 | m_best_etc1s_luma_709_psnr = 0.0f; | 
|---|
| 93 | m_best_etc1s_luma_601_psnr = 0.0f; | 
|---|
| 94 | m_best_etc1s_luma_709_ssim = 0.0f; | 
|---|
| 95 |  | 
|---|
| 96 | m_opencl_failed = false; | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | std::string m_filename; | 
|---|
| 100 | uint32_t m_width; | 
|---|
| 101 | uint32_t m_height; | 
|---|
| 102 |  | 
|---|
| 103 | // .basis compressed (ETC1S or UASTC statistics) | 
|---|
| 104 | float m_basis_rgb_avg_psnr; | 
|---|
| 105 | float m_basis_rgba_avg_psnr; | 
|---|
| 106 | float m_basis_a_avg_psnr; | 
|---|
| 107 | float m_basis_luma_709_psnr; | 
|---|
| 108 | float m_basis_luma_601_psnr; | 
|---|
| 109 | float m_basis_luma_709_ssim; | 
|---|
| 110 |  | 
|---|
| 111 | // BC7 statistics | 
|---|
| 112 | float m_bc7_rgb_avg_psnr; | 
|---|
| 113 | float m_bc7_rgba_avg_psnr; | 
|---|
| 114 | float m_bc7_a_avg_psnr; | 
|---|
| 115 | float m_bc7_luma_709_psnr; | 
|---|
| 116 | float m_bc7_luma_601_psnr; | 
|---|
| 117 | float m_bc7_luma_709_ssim; | 
|---|
| 118 |  | 
|---|
| 119 | // Highest achievable quality ETC1S statistics | 
|---|
| 120 | float m_best_etc1s_rgb_avg_psnr; | 
|---|
| 121 | float m_best_etc1s_luma_709_psnr; | 
|---|
| 122 | float m_best_etc1s_luma_601_psnr; | 
|---|
| 123 | float m_best_etc1s_luma_709_ssim; | 
|---|
| 124 |  | 
|---|
| 125 | bool m_opencl_failed; | 
|---|
| 126 | }; | 
|---|
| 127 |  | 
|---|
| 128 | template<bool def> | 
|---|
| 129 | struct bool_param | 
|---|
| 130 | { | 
|---|
| 131 | bool_param() : | 
|---|
| 132 | m_value(def), | 
|---|
| 133 | m_changed(false) | 
|---|
| 134 | { | 
|---|
| 135 | } | 
|---|
| 136 |  | 
|---|
| 137 | void clear() | 
|---|
| 138 | { | 
|---|
| 139 | m_value = def; | 
|---|
| 140 | m_changed = false; | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | operator bool() const | 
|---|
| 144 | { | 
|---|
| 145 | return m_value; | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | bool operator= (bool v) | 
|---|
| 149 | { | 
|---|
| 150 | m_value = v; | 
|---|
| 151 | m_changed = true; | 
|---|
| 152 | return m_value; | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | bool was_changed() const { return m_changed; } | 
|---|
| 156 | void set_changed(bool flag) { m_changed = flag; } | 
|---|
| 157 |  | 
|---|
| 158 | bool m_value; | 
|---|
| 159 | bool m_changed; | 
|---|
| 160 | }; | 
|---|
| 161 |  | 
|---|
| 162 | template<typename T> | 
|---|
| 163 | struct param | 
|---|
| 164 | { | 
|---|
| 165 | param(T def, T min_v, T max_v) : | 
|---|
| 166 | m_value(def), | 
|---|
| 167 | m_def(def), | 
|---|
| 168 | m_min(min_v), | 
|---|
| 169 | m_max(max_v), | 
|---|
| 170 | m_changed(false) | 
|---|
| 171 | { | 
|---|
| 172 | } | 
|---|
| 173 |  | 
|---|
| 174 | void clear() | 
|---|
| 175 | { | 
|---|
| 176 | m_value = m_def; | 
|---|
| 177 | m_changed = false; | 
|---|
| 178 | } | 
|---|
| 179 |  | 
|---|
| 180 | operator T() const | 
|---|
| 181 | { | 
|---|
| 182 | return m_value; | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | T operator= (T v) | 
|---|
| 186 | { | 
|---|
| 187 | m_value = clamp<T>(v, m_min, m_max); | 
|---|
| 188 | m_changed = true; | 
|---|
| 189 | return m_value; | 
|---|
| 190 | } | 
|---|
| 191 |  | 
|---|
| 192 | T operator *= (T v) | 
|---|
| 193 | { | 
|---|
| 194 | m_value *= v; | 
|---|
| 195 | m_changed = true; | 
|---|
| 196 | return m_value; | 
|---|
| 197 | } | 
|---|
| 198 |  | 
|---|
| 199 | bool was_changed() const { return m_changed; } | 
|---|
| 200 | void set_changed(bool flag) { m_changed = flag; } | 
|---|
| 201 |  | 
|---|
| 202 | T m_value; | 
|---|
| 203 | T m_def; | 
|---|
| 204 | T m_min; | 
|---|
| 205 | T m_max; | 
|---|
| 206 | bool m_changed; | 
|---|
| 207 | }; | 
|---|
| 208 |  | 
|---|
| 209 | struct basis_compressor_params | 
|---|
| 210 | { | 
|---|
| 211 | basis_compressor_params() : | 
|---|
| 212 | m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL), | 
|---|
| 213 | m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f), | 
|---|
| 214 | m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f), | 
|---|
| 215 | m_mip_scale(1.0f, .000125f, 4.0f), | 
|---|
| 216 | m_mip_smallest_dimension(1, 1, 16384), | 
|---|
| 217 | m_max_endpoint_clusters(512), | 
|---|
| 218 | m_max_selector_clusters(512), | 
|---|
| 219 | m_quality_level(-1), | 
|---|
| 220 | m_pack_uastc_flags(cPackUASTCLevelDefault), | 
|---|
| 221 | m_rdo_uastc_quality_scalar(1.0f, 0.001f, 50.0f), | 
|---|
| 222 | m_rdo_uastc_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX), | 
|---|
| 223 | m_rdo_uastc_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f), | 
|---|
| 224 | m_rdo_uastc_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f), | 
|---|
| 225 | m_rdo_uastc_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f), | 
|---|
| 226 | m_rdo_uastc_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f), | 
|---|
| 227 | m_resample_width(0, 1, 16384), | 
|---|
| 228 | m_resample_height(0, 1, 16384), | 
|---|
| 229 | m_resample_factor(0.0f, .00125f, 100.0f), | 
|---|
| 230 | m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE), | 
|---|
| 231 | m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX), | 
|---|
| 232 | m_pJob_pool(nullptr) | 
|---|
| 233 | { | 
|---|
| 234 | clear(); | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | void clear() | 
|---|
| 238 | { | 
|---|
| 239 | m_uastc.clear(); | 
|---|
| 240 | m_use_opencl.clear(); | 
|---|
| 241 | m_status_output.clear(); | 
|---|
| 242 |  | 
|---|
| 243 | m_source_filenames.clear(); | 
|---|
| 244 | m_source_alpha_filenames.clear(); | 
|---|
| 245 |  | 
|---|
| 246 | m_source_images.clear(); | 
|---|
| 247 | m_source_mipmap_images.clear(); | 
|---|
| 248 |  | 
|---|
| 249 | m_out_filename.clear(); | 
|---|
| 250 |  | 
|---|
| 251 | m_y_flip.clear(); | 
|---|
| 252 | m_debug.clear(); | 
|---|
| 253 | m_validate_etc1s.clear(); | 
|---|
| 254 | m_debug_images.clear(); | 
|---|
| 255 | m_perceptual.clear(); | 
|---|
| 256 | m_no_selector_rdo.clear(); | 
|---|
| 257 | m_selector_rdo_thresh.clear(); | 
|---|
| 258 | m_read_source_images.clear(); | 
|---|
| 259 | m_write_output_basis_files.clear(); | 
|---|
| 260 | m_compression_level.clear(); | 
|---|
| 261 | m_compute_stats.clear(); | 
|---|
| 262 | m_print_stats.clear(); | 
|---|
| 263 | m_check_for_alpha.clear(); | 
|---|
| 264 | m_force_alpha.clear(); | 
|---|
| 265 | m_multithreading.clear(); | 
|---|
| 266 | m_swizzle[0] = 0; | 
|---|
| 267 | m_swizzle[1] = 1; | 
|---|
| 268 | m_swizzle[2] = 2; | 
|---|
| 269 | m_swizzle[3] = 3; | 
|---|
| 270 | m_renormalize.clear(); | 
|---|
| 271 | m_disable_hierarchical_endpoint_codebooks.clear(); | 
|---|
| 272 |  | 
|---|
| 273 | m_no_endpoint_rdo.clear(); | 
|---|
| 274 | m_endpoint_rdo_thresh.clear(); | 
|---|
| 275 |  | 
|---|
| 276 | m_mip_gen.clear(); | 
|---|
| 277 | m_mip_scale.clear(); | 
|---|
| 278 | m_mip_filter = "kaiser"; | 
|---|
| 279 | m_mip_scale = 1.0f; | 
|---|
| 280 | m_mip_srgb.clear(); | 
|---|
| 281 | m_mip_premultiplied.clear(); | 
|---|
| 282 | m_mip_renormalize.clear(); | 
|---|
| 283 | m_mip_wrapping.clear(); | 
|---|
| 284 | m_mip_fast.clear(); | 
|---|
| 285 | m_mip_smallest_dimension.clear(); | 
|---|
| 286 |  | 
|---|
| 287 | m_max_endpoint_clusters = 0; | 
|---|
| 288 | m_max_selector_clusters = 0; | 
|---|
| 289 | m_quality_level = -1; | 
|---|
| 290 |  | 
|---|
| 291 | m_tex_type = basist::cBASISTexType2D; | 
|---|
| 292 | m_userdata0 = 0; | 
|---|
| 293 | m_userdata1 = 0; | 
|---|
| 294 | m_us_per_frame = 0; | 
|---|
| 295 |  | 
|---|
| 296 | m_pack_uastc_flags = cPackUASTCLevelDefault; | 
|---|
| 297 | m_rdo_uastc.clear(); | 
|---|
| 298 | m_rdo_uastc_quality_scalar.clear(); | 
|---|
| 299 | m_rdo_uastc_max_smooth_block_error_scale.clear(); | 
|---|
| 300 | m_rdo_uastc_smooth_block_max_std_dev.clear(); | 
|---|
| 301 | m_rdo_uastc_max_allowed_rms_increase_ratio.clear(); | 
|---|
| 302 | m_rdo_uastc_skip_block_rms_thresh.clear(); | 
|---|
| 303 | m_rdo_uastc_favor_simpler_modes_in_rdo_mode.clear(); | 
|---|
| 304 | m_rdo_uastc_multithreading.clear(); | 
|---|
| 305 |  | 
|---|
| 306 | m_resample_width.clear(); | 
|---|
| 307 | m_resample_height.clear(); | 
|---|
| 308 | m_resample_factor.clear(); | 
|---|
| 309 |  | 
|---|
| 310 | m_pGlobal_codebooks = nullptr; | 
|---|
| 311 |  | 
|---|
| 312 | m_create_ktx2_file.clear(); | 
|---|
| 313 | m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE; | 
|---|
| 314 | m_ktx2_key_values.clear(); | 
|---|
| 315 | m_ktx2_zstd_supercompression_level.clear(); | 
|---|
| 316 | m_ktx2_srgb_transfer_func.clear(); | 
|---|
| 317 |  | 
|---|
| 318 | m_validate_output_data.clear(); | 
|---|
| 319 |  | 
|---|
| 320 | m_pJob_pool = nullptr; | 
|---|
| 321 | } | 
|---|
| 322 |  | 
|---|
| 323 | // True to generate UASTC .basis file data, otherwise ETC1S. | 
|---|
| 324 | bool_param<false> m_uastc; | 
|---|
| 325 |  | 
|---|
| 326 | bool_param<false> m_use_opencl; | 
|---|
| 327 |  | 
|---|
| 328 | // If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read. | 
|---|
| 329 | // Otherwise, the compressor processes the images in m_source_images. | 
|---|
| 330 | basisu::vector<std::string> m_source_filenames; | 
|---|
| 331 | basisu::vector<std::string> m_source_alpha_filenames; | 
|---|
| 332 |  | 
|---|
| 333 | basisu::vector<image> m_source_images; | 
|---|
| 334 |  | 
|---|
| 335 | // Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual. | 
|---|
| 336 | // 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. | 
|---|
| 337 | // The compressor applies the user-provided swizzling (in m_swizzle) to these images. | 
|---|
| 338 | basisu::vector< basisu::vector<image> > m_source_mipmap_images; | 
|---|
| 339 |  | 
|---|
| 340 | // Filename of the output basis file | 
|---|
| 341 | std::string m_out_filename; | 
|---|
| 342 |  | 
|---|
| 343 | // The params are done this way so we can detect when the user has explictly changed them. | 
|---|
| 344 |  | 
|---|
| 345 | // Flip images across Y axis | 
|---|
| 346 | bool_param<false> m_y_flip; | 
|---|
| 347 |  | 
|---|
| 348 | // If true, the compressor will print basis status to stdout during compression. | 
|---|
| 349 | bool_param<true> m_status_output; | 
|---|
| 350 |  | 
|---|
| 351 | // Output debug information during compression | 
|---|
| 352 | bool_param<false> m_debug; | 
|---|
| 353 | bool_param<false> m_validate_etc1s; | 
|---|
| 354 |  | 
|---|
| 355 | // m_debug_images is pretty slow | 
|---|
| 356 | bool_param<false> m_debug_images; | 
|---|
| 357 |  | 
|---|
| 358 | // ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower). | 
|---|
| 359 | // This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs. | 
|---|
| 360 | // Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this. | 
|---|
| 361 | param<int> m_compression_level; | 
|---|
| 362 |  | 
|---|
| 363 | // Use perceptual sRGB colorspace metrics instead of linear | 
|---|
| 364 | bool_param<true> m_perceptual; | 
|---|
| 365 |  | 
|---|
| 366 | // Disable selector RDO, for faster compression but larger files | 
|---|
| 367 | bool_param<false> m_no_selector_rdo; | 
|---|
| 368 | param<float> m_selector_rdo_thresh; | 
|---|
| 369 |  | 
|---|
| 370 | bool_param<false> m_no_endpoint_rdo; | 
|---|
| 371 | param<float> m_endpoint_rdo_thresh; | 
|---|
| 372 |  | 
|---|
| 373 | // Read source images from m_source_filenames/m_source_alpha_filenames | 
|---|
| 374 | bool_param<false> m_read_source_images; | 
|---|
| 375 |  | 
|---|
| 376 | // Write the output basis file to disk using m_out_filename | 
|---|
| 377 | bool_param<false> m_write_output_basis_files; | 
|---|
| 378 |  | 
|---|
| 379 | // Compute and display image metrics | 
|---|
| 380 | bool_param<false> m_compute_stats; | 
|---|
| 381 |  | 
|---|
| 382 | // Print stats to stdout, if m_compute_stats is true. | 
|---|
| 383 | bool_param<true> m_print_stats; | 
|---|
| 384 |  | 
|---|
| 385 | // Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels | 
|---|
| 386 | bool_param<true> m_check_for_alpha; | 
|---|
| 387 |  | 
|---|
| 388 | // Always put alpha slices in the output basis file, even when the input doesn't have alpha | 
|---|
| 389 | bool_param<false> m_force_alpha; | 
|---|
| 390 | bool_param<true> m_multithreading; | 
|---|
| 391 |  | 
|---|
| 392 | // Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels | 
|---|
| 393 | char m_swizzle[4]; | 
|---|
| 394 |  | 
|---|
| 395 | bool_param<false> m_renormalize; | 
|---|
| 396 |  | 
|---|
| 397 | // If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution. | 
|---|
| 398 | // Note some m_compression_level's disable this automatically. | 
|---|
| 399 | bool_param<false> m_disable_hierarchical_endpoint_codebooks; | 
|---|
| 400 |  | 
|---|
| 401 | // mipmap generation parameters | 
|---|
| 402 | bool_param<false> m_mip_gen; | 
|---|
| 403 | param<float> m_mip_scale; | 
|---|
| 404 | std::string m_mip_filter; | 
|---|
| 405 | bool_param<false> m_mip_srgb; | 
|---|
| 406 | bool_param<true> m_mip_premultiplied; // not currently supported | 
|---|
| 407 | bool_param<false> m_mip_renormalize; | 
|---|
| 408 | bool_param<true> m_mip_wrapping; | 
|---|
| 409 | bool_param<true> m_mip_fast; | 
|---|
| 410 | param<int> m_mip_smallest_dimension; | 
|---|
| 411 |  | 
|---|
| 412 | // Codebook size (quality) control. | 
|---|
| 413 | // If m_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX]. | 
|---|
| 414 | // Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly. | 
|---|
| 415 | uint32_t m_max_endpoint_clusters; | 
|---|
| 416 | uint32_t m_max_selector_clusters; | 
|---|
| 417 | int m_quality_level; | 
|---|
| 418 |  | 
|---|
| 419 | // m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the Basis file header. | 
|---|
| 420 | basist::basis_texture_type m_tex_type; | 
|---|
| 421 | uint32_t m_userdata0; | 
|---|
| 422 | uint32_t m_userdata1; | 
|---|
| 423 | uint32_t m_us_per_frame; | 
|---|
| 424 |  | 
|---|
| 425 | // cPackUASTCLevelDefault, etc. | 
|---|
| 426 | uint32_t m_pack_uastc_flags; | 
|---|
| 427 | bool_param<false> m_rdo_uastc; | 
|---|
| 428 | param<float> m_rdo_uastc_quality_scalar; | 
|---|
| 429 | param<int> m_rdo_uastc_dict_size; | 
|---|
| 430 | param<float> m_rdo_uastc_max_smooth_block_error_scale; | 
|---|
| 431 | param<float> m_rdo_uastc_smooth_block_max_std_dev; | 
|---|
| 432 | param<float> m_rdo_uastc_max_allowed_rms_increase_ratio; | 
|---|
| 433 | param<float> m_rdo_uastc_skip_block_rms_thresh; | 
|---|
| 434 | bool_param<true> m_rdo_uastc_favor_simpler_modes_in_rdo_mode; | 
|---|
| 435 | bool_param<true> m_rdo_uastc_multithreading; | 
|---|
| 436 |  | 
|---|
| 437 | param<int> m_resample_width; | 
|---|
| 438 | param<int> m_resample_height; | 
|---|
| 439 | param<float> m_resample_factor; | 
|---|
| 440 |  | 
|---|
| 441 | const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks; | 
|---|
| 442 |  | 
|---|
| 443 | // KTX2 specific parameters. | 
|---|
| 444 | // Internally, the compressor always creates a .basis file then it converts that lossless to KTX2. | 
|---|
| 445 | bool_param<false> m_create_ktx2_file; | 
|---|
| 446 | basist::ktx2_supercompression m_ktx2_uastc_supercompression; | 
|---|
| 447 | basist::ktx2_transcoder::key_value_vec m_ktx2_key_values; | 
|---|
| 448 | param<int> m_ktx2_zstd_supercompression_level; | 
|---|
| 449 | bool_param<false> m_ktx2_srgb_transfer_func; | 
|---|
| 450 |  | 
|---|
| 451 | bool_param<false> m_validate_output_data; | 
|---|
| 452 |  | 
|---|
| 453 | job_pool *m_pJob_pool; | 
|---|
| 454 | }; | 
|---|
| 455 |  | 
|---|
| 456 | // Important: basisu_encoder_init() MUST be called first before using this class. | 
|---|
| 457 | class basis_compressor | 
|---|
| 458 | { | 
|---|
| 459 | BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor); | 
|---|
| 460 |  | 
|---|
| 461 | public: | 
|---|
| 462 | basis_compressor(); | 
|---|
| 463 | ~basis_compressor(); | 
|---|
| 464 |  | 
|---|
| 465 | // 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. | 
|---|
| 466 | bool init(const basis_compressor_params ¶ms); | 
|---|
| 467 |  | 
|---|
| 468 | enum error_code | 
|---|
| 469 | { | 
|---|
| 470 | cECSuccess = 0, | 
|---|
| 471 | cECFailedInitializing, | 
|---|
| 472 | cECFailedReadingSourceImages, | 
|---|
| 473 | cECFailedValidating, | 
|---|
| 474 | cECFailedEncodeUASTC, | 
|---|
| 475 | cECFailedFrontEnd, | 
|---|
| 476 | , | 
|---|
| 477 | cECFailedBackend, | 
|---|
| 478 | cECFailedCreateBasisFile, | 
|---|
| 479 | cECFailedWritingOutput, | 
|---|
| 480 | cECFailedUASTCRDOPostProcess, | 
|---|
| 481 | cECFailedCreateKTX2File | 
|---|
| 482 | }; | 
|---|
| 483 |  | 
|---|
| 484 | error_code process(); | 
|---|
| 485 |  | 
|---|
| 486 | // The output .basis file will always be valid of process() succeeded. | 
|---|
| 487 | const uint8_vec &get_output_basis_file() const { return m_output_basis_file; } | 
|---|
| 488 |  | 
|---|
| 489 | // The output .ktx2 file will only be valid if m_create_ktx2_file was true and process() succeeded. | 
|---|
| 490 | const uint8_vec& get_output_ktx2_file() const { return m_output_ktx2_file; } | 
|---|
| 491 |  | 
|---|
| 492 | const basisu::vector<image_stats> &get_stats() const { return m_stats; } | 
|---|
| 493 |  | 
|---|
| 494 | uint32_t get_basis_file_size() const { return m_basis_file_size; } | 
|---|
| 495 | double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; } | 
|---|
| 496 |  | 
|---|
| 497 | bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; } | 
|---|
| 498 |  | 
|---|
| 499 | bool get_opencl_failed() const { return m_opencl_failed; } | 
|---|
| 500 |  | 
|---|
| 501 | private: | 
|---|
| 502 | basis_compressor_params m_params; | 
|---|
| 503 |  | 
|---|
| 504 | opencl_context_ptr m_pOpenCL_context; | 
|---|
| 505 |  | 
|---|
| 506 | basisu::vector<image> m_slice_images; | 
|---|
| 507 |  | 
|---|
| 508 | basisu::vector<image_stats> m_stats; | 
|---|
| 509 |  | 
|---|
| 510 | uint32_t m_basis_file_size; | 
|---|
| 511 | double m_basis_bits_per_texel; | 
|---|
| 512 |  | 
|---|
| 513 | basisu_backend_slice_desc_vec m_slice_descs; | 
|---|
| 514 |  | 
|---|
| 515 | uint32_t m_total_blocks; | 
|---|
| 516 |  | 
|---|
| 517 | basisu_frontend m_frontend; | 
|---|
| 518 | pixel_block_vec m_source_blocks; | 
|---|
| 519 |  | 
|---|
| 520 | basisu::vector<gpu_image> m_frontend_output_textures; | 
|---|
| 521 |  | 
|---|
| 522 | basisu::vector<gpu_image> m_best_etc1s_images; | 
|---|
| 523 | basisu::vector<image> m_best_etc1s_images_unpacked; | 
|---|
| 524 |  | 
|---|
| 525 | basisu_backend m_backend; | 
|---|
| 526 |  | 
|---|
| 527 | basisu_file m_basis_file; | 
|---|
| 528 |  | 
|---|
| 529 | basisu::vector<gpu_image> m_decoded_output_textures; | 
|---|
| 530 | basisu::vector<image> m_decoded_output_textures_unpacked; | 
|---|
| 531 | basisu::vector<gpu_image> m_decoded_output_textures_bc7; | 
|---|
| 532 | basisu::vector<image> m_decoded_output_textures_unpacked_bc7; | 
|---|
| 533 |  | 
|---|
| 534 | uint8_vec m_output_basis_file; | 
|---|
| 535 | uint8_vec m_output_ktx2_file; | 
|---|
| 536 |  | 
|---|
| 537 | basisu::vector<gpu_image> m_uastc_slice_textures; | 
|---|
| 538 | basisu_backend_output m_uastc_backend_output; | 
|---|
| 539 |  | 
|---|
| 540 | bool m_any_source_image_has_alpha; | 
|---|
| 541 |  | 
|---|
| 542 | bool m_opencl_failed; | 
|---|
| 543 |  | 
|---|
| 544 | bool read_source_images(); | 
|---|
| 545 | bool (); | 
|---|
| 546 | bool process_frontend(); | 
|---|
| 547 | bool (); | 
|---|
| 548 | bool process_backend(); | 
|---|
| 549 | bool create_basis_file_and_transcode(); | 
|---|
| 550 | bool write_output_files_and_compute_stats(); | 
|---|
| 551 | error_code encode_slices_to_uastc(); | 
|---|
| 552 | bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha); | 
|---|
| 553 | bool validate_texture_type_constraints(); | 
|---|
| 554 | bool validate_ktx2_constraints(); | 
|---|
| 555 | void (uint8_vec& dfd, const basist::ktx2_header& hdr); | 
|---|
| 556 | bool create_ktx2_file(); | 
|---|
| 557 | }; | 
|---|
| 558 |  | 
|---|
| 559 | // Alternative simple C-style wrapper API around the basis_compressor class. | 
|---|
| 560 | // This doesn't expose every encoder feature, but it's enough to get going. | 
|---|
| 561 | // Important: basisu_encoder_init() MUST be called first before calling these functions. | 
|---|
| 562 | // | 
|---|
| 563 | // Input parameters: | 
|---|
| 564 | //   source_images: Array of "image" objects, one per mipmap level, largest mipmap level first. | 
|---|
| 565 | // OR | 
|---|
| 566 | //   pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory. | 
|---|
| 567 | //   width/height/pitch_in_pixels: dimensions of pImageRGBA | 
|---|
| 568 | // | 
|---|
| 569 | // 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". | 
|---|
| 570 | //	  In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files) | 
|---|
| 571 | //	  In UASTC mode, the lower 8-bits are the UASTC pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly. | 
|---|
| 572 | // | 
|---|
| 573 | // uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5) | 
|---|
| 574 | // | 
|---|
| 575 | // pSize: Returns the output data's compressed size in bytes | 
|---|
| 576 | // | 
|---|
| 577 | // Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it. | 
|---|
| 578 | enum | 
|---|
| 579 | { | 
|---|
| 580 | cFlagUseOpenCL = 1 << 8,		// use OpenCL if available | 
|---|
| 581 | cFlagThreaded = 1 << 9,			// use multiple threads for compression | 
|---|
| 582 | cFlagDebug = 1 << 10,			// enable debug output | 
|---|
| 583 |  | 
|---|
| 584 | cFlagKTX2 = 1 << 11,			// generate a KTX2 file | 
|---|
| 585 | cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files | 
|---|
| 586 |  | 
|---|
| 587 | 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 | 
|---|
| 588 | cFlagGenMipsClamp = 1 << 14,  // generate mipmaps with clamp addressing | 
|---|
| 589 | cFlagGenMipsWrap = 1 << 15,  // generate mipmaps with wrap addressing | 
|---|
| 590 |  | 
|---|
| 591 | cFlagYFlip = 1 << 16,		// flip source image on Y axis before compression | 
|---|
| 592 |  | 
|---|
| 593 | cFlagUASTC = 1 << 17,		// use UASTC compression vs. ETC1S | 
|---|
| 594 | cFlagUASTCRDO = 1 << 18,		// use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar) | 
|---|
| 595 |  | 
|---|
| 596 | cFlagPrintStats = 1 << 19,	// print image stats to stdout | 
|---|
| 597 | cFlagPrintStatus = 1 << 20	// print status to stdout | 
|---|
| 598 | }; | 
|---|
| 599 |  | 
|---|
| 600 | // This function accepts an array of source images. | 
|---|
| 601 | // If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled. | 
|---|
| 602 | // Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data. The returned block must be freed using basis_free_data(). | 
|---|
| 603 | // basisu_encoder_init() MUST be called first! | 
|---|
| 604 | void* basis_compress( | 
|---|
| 605 | const basisu::vector<image> &source_images, | 
|---|
| 606 | uint32_t flags_and_quality, float uastc_rdo_quality, | 
|---|
| 607 | size_t* pSize, | 
|---|
| 608 | image_stats* pStats = nullptr); | 
|---|
| 609 |  | 
|---|
| 610 | // This function only accepts a single source image. | 
|---|
| 611 | void* basis_compress( | 
|---|
| 612 | const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels, | 
|---|
| 613 | uint32_t flags_and_quality, float uastc_rdo_quality, | 
|---|
| 614 | size_t* pSize, | 
|---|
| 615 | image_stats* pStats = nullptr); | 
|---|
| 616 |  | 
|---|
| 617 | // Frees the dynamically allocated file data returned by basis_compress(). | 
|---|
| 618 | void basis_free_data(void* p); | 
|---|
| 619 |  | 
|---|
| 620 | // Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled. | 
|---|
| 621 | // Returns true if opencl is worth using on this system, otherwise false. | 
|---|
| 622 | // 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. | 
|---|
| 623 | // basisu_encoder_init() MUST be called first. If OpenCL support wasn't enabled this always returns false. | 
|---|
| 624 | bool basis_benchmark_etc1s_opencl(bool *pOpenCL_failed = nullptr); | 
|---|
| 625 |  | 
|---|
| 626 | // Parallel compression API | 
|---|
| 627 | struct parallel_results | 
|---|
| 628 | { | 
|---|
| 629 | double m_total_time; | 
|---|
| 630 | basis_compressor::error_code m_error_code; | 
|---|
| 631 | uint8_vec m_basis_file; | 
|---|
| 632 | uint8_vec m_ktx2_file; | 
|---|
| 633 | basisu::vector<image_stats> m_stats; | 
|---|
| 634 | double m_basis_bits_per_texel; | 
|---|
| 635 | bool m_any_source_image_has_alpha; | 
|---|
| 636 |  | 
|---|
| 637 | parallel_results() | 
|---|
| 638 | { | 
|---|
| 639 | clear(); | 
|---|
| 640 | } | 
|---|
| 641 |  | 
|---|
| 642 | void clear() | 
|---|
| 643 | { | 
|---|
| 644 | m_total_time = 0.0f; | 
|---|
| 645 | m_error_code = basis_compressor::cECFailedInitializing; | 
|---|
| 646 | m_basis_file.clear(); | 
|---|
| 647 | m_ktx2_file.clear(); | 
|---|
| 648 | m_stats.clear(); | 
|---|
| 649 | m_basis_bits_per_texel = 0.0f; | 
|---|
| 650 | m_any_source_image_has_alpha = false; | 
|---|
| 651 | } | 
|---|
| 652 | }; | 
|---|
| 653 |  | 
|---|
| 654 | // Compresses an array of input textures across total_threads threads using the basis_compressor class. | 
|---|
| 655 | // Compressing multiple textures at a time is substantially more efficient than just compressing one at a time. | 
|---|
| 656 | // total_threads must be >= 1. | 
|---|
| 657 | bool basis_parallel_compress( | 
|---|
| 658 | uint32_t total_threads, | 
|---|
| 659 | const basisu::vector<basis_compressor_params> ¶ms_vec, | 
|---|
| 660 | basisu::vector< parallel_results > &results_vec); | 
|---|
| 661 |  | 
|---|
| 662 | } // namespace basisu | 
|---|
| 663 |  | 
|---|
| 664 |  | 
|---|