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 | |