basisu_comp.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // basisu_comp.h
  2. // Copyright (C) 2019 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_global_selector_palette.h"
  20. #include "transcoder/basisu_transcoder.h"
  21. namespace basisu
  22. {
  23. const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;
  24. // Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.
  25. const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f;
  26. // Allow block's color distance to increase by 1.25 while searching the selector history buffer for a close enough match.
  27. const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f;
  28. const int BASISU_DEFAULT_QUALITY = 128;
  29. const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;
  30. const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384;
  31. const uint32_t BASISU_QUALITY_MIN = 1;
  32. const uint32_t BASISU_QUALITY_MAX = 255;
  33. const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;
  34. const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;
  35. const uint32_t BASISU_MAX_SLICES = 0xFFFFFF;
  36. struct image_stats
  37. {
  38. image_stats()
  39. {
  40. clear();
  41. }
  42. void clear()
  43. {
  44. m_filename.clear();
  45. m_width = 0;
  46. m_height = 0;
  47. m_basis_etc1s_rgb_avg_psnr = 0.0f;
  48. m_basis_etc1s_luma_709_psnr = 0.0f;
  49. m_basis_etc1s_luma_601_psnr = 0.0f;
  50. m_basis_etc1s_luma_709_ssim = 0.0f;
  51. m_basis_bc1_rgb_avg_psnr = 0.0f;
  52. m_basis_bc1_luma_709_psnr = 0.0f;
  53. m_basis_bc1_luma_601_psnr = 0.0f;
  54. m_basis_bc1_luma_709_ssim = 0.0f;
  55. m_best_rgb_avg_psnr = 0.0f;
  56. m_best_luma_709_psnr = 0.0f;
  57. m_best_luma_601_psnr = 0.0f;
  58. m_best_luma_709_ssim = 0.0f;
  59. }
  60. std::string m_filename;
  61. uint32_t m_width;
  62. uint32_t m_height;
  63. // .basis compressed
  64. float m_basis_etc1s_rgb_avg_psnr;
  65. float m_basis_etc1s_luma_709_psnr;
  66. float m_basis_etc1s_luma_601_psnr;
  67. float m_basis_etc1s_luma_709_ssim;
  68. float m_basis_bc1_rgb_avg_psnr;
  69. float m_basis_bc1_luma_709_psnr;
  70. float m_basis_bc1_luma_601_psnr;
  71. float m_basis_bc1_luma_709_ssim;
  72. // Normal (highest quality) compressed ETC1S
  73. float m_best_rgb_avg_psnr;
  74. float m_best_luma_709_psnr;
  75. float m_best_luma_601_psnr;
  76. float m_best_luma_709_ssim;
  77. };
  78. template<bool def>
  79. struct bool_param
  80. {
  81. bool_param() :
  82. m_value(def),
  83. m_changed(false)
  84. {
  85. }
  86. void clear()
  87. {
  88. m_value = def;
  89. m_changed = false;
  90. }
  91. operator bool() const
  92. {
  93. return m_value;
  94. }
  95. bool operator= (bool v)
  96. {
  97. m_value = v;
  98. m_changed = true;
  99. return m_value;
  100. }
  101. bool was_changed() const { return m_changed; }
  102. void set_changed(bool flag) { m_changed = flag; }
  103. bool m_value;
  104. bool m_changed;
  105. };
  106. template<typename T>
  107. struct param
  108. {
  109. param(T def, T min_v, T max_v) :
  110. m_value(def),
  111. m_def(def),
  112. m_min(min_v),
  113. m_max(max_v),
  114. m_changed(false)
  115. {
  116. }
  117. void clear()
  118. {
  119. m_value = m_def;
  120. m_changed = false;
  121. }
  122. operator T() const
  123. {
  124. return m_value;
  125. }
  126. T operator= (T v)
  127. {
  128. m_value = clamp<T>(v, m_min, m_max);
  129. m_changed = true;
  130. return m_value;
  131. }
  132. T operator *= (T v)
  133. {
  134. m_value *= v;
  135. m_changed = true;
  136. return m_value;
  137. }
  138. bool was_changed() const { return m_changed; }
  139. void set_changed(bool flag) { m_changed = flag; }
  140. T m_value;
  141. T m_def;
  142. T m_min;
  143. T m_max;
  144. bool m_changed;
  145. };
  146. struct basis_compressor_params
  147. {
  148. basis_compressor_params() :
  149. m_hybrid_sel_cb_quality_thresh(BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH, 0.0f, 1e+10f),
  150. m_global_pal_bits(8, 0, ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS),
  151. m_global_mod_bits(8, 0, basist::etc1_global_palette_entry_modifier::cTotalBits),
  152. m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
  153. m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
  154. m_pSel_codebook(NULL),
  155. m_max_endpoint_clusters(512),
  156. m_max_selector_clusters(512),
  157. m_quality_level(-1),
  158. m_mip_scale(1.0f, .000125f, 4.0f),
  159. m_mip_smallest_dimension(1, 1, 16384),
  160. m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
  161. m_pJob_pool(nullptr)
  162. {
  163. clear();
  164. }
  165. void clear()
  166. {
  167. m_pSel_codebook = NULL;
  168. m_source_filenames.clear();
  169. m_source_alpha_filenames.clear();
  170. m_source_images.clear();
  171. m_out_filename.clear();
  172. m_y_flip.clear();
  173. m_debug.clear();
  174. m_debug_images.clear();
  175. m_global_sel_pal.clear();
  176. m_no_auto_global_sel_pal.clear();
  177. m_no_hybrid_sel_cb.clear();
  178. m_perceptual.clear();
  179. m_no_selector_rdo.clear();
  180. m_selector_rdo_thresh.clear();
  181. m_read_source_images.clear();
  182. m_write_output_basis_files.clear();
  183. m_compression_level.clear();
  184. m_compute_stats.clear();
  185. m_check_for_alpha.clear();
  186. m_force_alpha.clear();
  187. m_multithreading.clear();
  188. m_seperate_rg_to_color_alpha.clear();
  189. m_hybrid_sel_cb_quality_thresh.clear();
  190. m_global_pal_bits.clear();
  191. m_global_mod_bits.clear();
  192. m_disable_hierarchical_endpoint_codebooks.clear();
  193. m_no_endpoint_rdo.clear();
  194. m_endpoint_rdo_thresh.clear();
  195. m_mip_gen.clear();
  196. m_mip_scale.clear();
  197. m_mip_filter = "kaiser";
  198. m_mip_scale = 1.0f;
  199. m_mip_srgb.clear();
  200. m_mip_premultiplied.clear();
  201. m_mip_renormalize.clear();
  202. m_mip_wrapping.clear();
  203. m_mip_smallest_dimension.clear();
  204. m_max_endpoint_clusters = 0;
  205. m_max_selector_clusters = 0;
  206. m_quality_level = -1;
  207. m_tex_type = basist::cBASISTexType2D;
  208. m_userdata0 = 0;
  209. m_userdata1 = 0;
  210. m_us_per_frame = 0;
  211. m_pJob_pool = nullptr;
  212. }
  213. // Pointer to the global selector codebook, or nullptr to not use a global selector codebook
  214. const basist::etc1_global_selector_codebook *m_pSel_codebook;
  215. // If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read.
  216. // Otherwise, the compressor processes the images in m_source_images.
  217. std::vector<std::string> m_source_filenames;
  218. std::vector<std::string> m_source_alpha_filenames;
  219. std::vector<image> m_source_images;
  220. // TODO: Allow caller to supply their own mipmaps
  221. // Filename of the output basis file
  222. std::string m_out_filename;
  223. // The params are done this way so we can detect when the user has explictly changed them.
  224. // Flip images across Y axis
  225. bool_param<false> m_y_flip;
  226. // Output debug information during compression
  227. bool_param<false> m_debug;
  228. // m_debug_images is pretty slow
  229. bool_param<false> m_debug_images;
  230. // Compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower)
  231. param<int> m_compression_level;
  232. bool_param<false> m_global_sel_pal;
  233. bool_param<false> m_no_auto_global_sel_pal;
  234. // Frontend/backend codec parameters
  235. bool_param<false> m_no_hybrid_sel_cb;
  236. // Use perceptual sRGB colorspace metrics (for normal maps, etc.)
  237. bool_param<true> m_perceptual;
  238. // Disable selector RDO, for faster compression but larger files
  239. bool_param<false> m_no_selector_rdo;
  240. param<float> m_selector_rdo_thresh;
  241. bool_param<false> m_no_endpoint_rdo;
  242. param<float> m_endpoint_rdo_thresh;
  243. // Read source images from m_source_filenames/m_source_alpha_filenames
  244. bool_param<false> m_read_source_images;
  245. // Write the output basis file to disk using m_out_filename
  246. bool_param<false> m_write_output_basis_files;
  247. // Compute and display image metrics
  248. bool_param<false> m_compute_stats;
  249. // Check to see if any input image has an alpha channel, if so then the output basis file will have alpha channels
  250. bool_param<true> m_check_for_alpha;
  251. // Always put alpha slices in the output basis file, even when the input doesn't have alpha
  252. bool_param<false> m_force_alpha;
  253. bool_param<true> m_multithreading;
  254. // Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels
  255. bool_param<false> m_seperate_rg_to_color_alpha;
  256. bool_param<false> m_disable_hierarchical_endpoint_codebooks;
  257. // Global/hybrid selector codebook parameters
  258. param<float> m_hybrid_sel_cb_quality_thresh;
  259. param<int> m_global_pal_bits;
  260. param<int> m_global_mod_bits;
  261. // mipmap generation parameters
  262. bool_param<false> m_mip_gen;
  263. param<float> m_mip_scale;
  264. std::string m_mip_filter;
  265. bool_param<false> m_mip_srgb;
  266. bool_param<true> m_mip_premultiplied; // not currently supported
  267. bool_param<false> m_mip_renormalize;
  268. bool_param<true> m_mip_wrapping;
  269. param<int> m_mip_smallest_dimension;
  270. // Codebook size (quality) control.
  271. // If m_quality_level != -1, it controls the quality level. It ranges from [0,255].
  272. // Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
  273. uint32_t m_max_endpoint_clusters;
  274. uint32_t m_max_selector_clusters;
  275. int m_quality_level;
  276. // m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the Basis file header.
  277. basist::basis_texture_type m_tex_type;
  278. uint32_t m_userdata0;
  279. uint32_t m_userdata1;
  280. uint32_t m_us_per_frame;
  281. job_pool *m_pJob_pool;
  282. };
  283. class basis_compressor
  284. {
  285. BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);
  286. public:
  287. basis_compressor();
  288. bool init(const basis_compressor_params &params);
  289. enum error_code
  290. {
  291. cECSuccess = 0,
  292. cECFailedReadingSourceImages,
  293. cECFailedValidating,
  294. cECFailedFrontEnd,
  295. cECFailedFontendExtract,
  296. cECFailedBackend,
  297. cECFailedCreateBasisFile,
  298. cECFailedWritingOutput
  299. };
  300. error_code process();
  301. const uint8_vec &get_output_basis_file() const { return m_output_basis_file; }
  302. const etc_block_vec &get_output_blocks() const { return m_output_blocks; }
  303. const std::vector<image_stats> &get_stats() const { return m_stats; }
  304. uint32_t get_basis_file_size() const { return m_basis_file_size; }
  305. double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
  306. bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
  307. private:
  308. basis_compressor_params m_params;
  309. std::vector<image> m_slice_images;
  310. std::vector<image_stats> m_stats;
  311. uint32_t m_basis_file_size;
  312. double m_basis_bits_per_texel;
  313. basisu_backend_slice_desc_vec m_slice_descs;
  314. uint32_t m_total_blocks;
  315. bool m_auto_global_sel_pal;
  316. basisu_frontend m_frontend;
  317. pixel_block_vec m_source_blocks;
  318. std::vector<gpu_image> m_frontend_output_textures;
  319. std::vector<gpu_image> m_best_etc1s_images;
  320. std::vector<image> m_best_etc1s_images_unpacked;
  321. basisu_backend m_backend;
  322. basisu_file m_basis_file;
  323. std::vector<gpu_image> m_decoded_output_textures;
  324. std::vector<image> m_decoded_output_textures_unpacked;
  325. std::vector<gpu_image> m_decoded_output_textures_bc1;
  326. std::vector<image> m_decoded_output_textures_unpacked_bc1;
  327. uint8_vec m_output_basis_file;
  328. etc_block_vec m_output_blocks;
  329. bool m_any_source_image_has_alpha;
  330. bool read_source_images();
  331. bool process_frontend();
  332. bool extract_frontend_texture_data();
  333. bool process_backend();
  334. bool create_basis_file_and_transcode();
  335. bool write_output_files_and_compute_stats();
  336. bool generate_mipmaps(const image &img, std::vector<image> &mips, bool has_alpha);
  337. bool validate_texture_type_constraints();
  338. };
  339. } // namespace basisu