Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/basis_universal/encoder/basisu_comp.h
9902 views
1
// basisu_comp.h
2
// Copyright (C) 2019-2024 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
#include "basisu_uastc_hdr_4x4_enc.h"
22
#include "basisu_astc_hdr_6x6_enc.h"
23
24
#define BASISU_LIB_VERSION 160
25
#define BASISU_LIB_VERSION_STRING "1.60"
26
27
#ifndef BASISD_SUPPORT_KTX2
28
#error BASISD_SUPPORT_KTX2 is undefined
29
#endif
30
#ifndef BASISD_SUPPORT_KTX2_ZSTD
31
#error BASISD_SUPPORT_KTX2_ZSTD is undefined
32
#endif
33
34
#if !BASISD_SUPPORT_KTX2
35
#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
36
#endif
37
38
namespace basisu
39
{
40
struct opencl_context;
41
typedef opencl_context* opencl_context_ptr;
42
43
const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;
44
45
// Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.
46
const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f;
47
48
// Allow block's color distance to increase by 1.25 while searching the selector history buffer for a close enough match.
49
const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f;
50
51
const int BASISU_DEFAULT_QUALITY = 128;
52
const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;
53
54
const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384;
55
const uint32_t BASISU_QUALITY_MIN = 1;
56
const uint32_t BASISU_QUALITY_MAX = 255;
57
const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;
58
const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;
59
60
const uint32_t BASISU_MAX_SLICES = 0xFFFFFF;
61
62
const int BASISU_RDO_UASTC_DICT_SIZE_DEFAULT = 4096; // 32768;
63
const int BASISU_RDO_UASTC_DICT_SIZE_MIN = 64;
64
const int BASISU_RDO_UASTC_DICT_SIZE_MAX = 65536;
65
66
struct image_stats
67
{
68
image_stats()
69
{
70
clear();
71
}
72
73
void clear()
74
{
75
m_filename.clear();
76
m_width = 0;
77
m_height = 0;
78
79
m_basis_rgb_avg_psnr = 0.0f;
80
m_basis_rgb_avg_log2_psnr = 0.0f;
81
82
m_basis_rgba_avg_psnr = 0.0f;
83
m_basis_a_avg_psnr = 0.0f;
84
m_basis_luma_709_psnr = 0.0f;
85
m_basis_luma_601_psnr = 0.0f;
86
m_basis_luma_709_ssim = 0.0f;
87
88
m_basis_rgb_avg_bc6h_psnr = 0.0f;
89
m_basis_rgb_avg_bc6h_log2_psnr = 0.0f;
90
91
m_bc7_rgb_avg_psnr = 0.0f;
92
m_bc7_rgba_avg_psnr = 0.0f;
93
m_bc7_a_avg_psnr = 0.0f;
94
m_bc7_luma_709_psnr = 0.0f;
95
m_bc7_luma_601_psnr = 0.0f;
96
m_bc7_luma_709_ssim = 0.0f;
97
98
m_best_etc1s_rgb_avg_psnr = 0.0f;
99
m_best_etc1s_luma_709_psnr = 0.0f;
100
m_best_etc1s_luma_601_psnr = 0.0f;
101
m_best_etc1s_luma_709_ssim = 0.0f;
102
103
m_opencl_failed = false;
104
}
105
106
std::string m_filename;
107
uint32_t m_width;
108
uint32_t m_height;
109
110
// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)
111
float m_basis_rgb_avg_psnr;
112
float m_basis_rgb_avg_log2_psnr;
113
114
float m_basis_rgba_avg_psnr;
115
float m_basis_a_avg_psnr;
116
float m_basis_luma_709_psnr;
117
float m_basis_luma_601_psnr;
118
float m_basis_luma_709_ssim;
119
120
// UASTC HDR only.
121
float m_basis_rgb_avg_bc6h_psnr;
122
float m_basis_rgb_avg_bc6h_log2_psnr;
123
124
// LDR: BC7 statistics
125
float m_bc7_rgb_avg_psnr;
126
float m_bc7_rgba_avg_psnr;
127
float m_bc7_a_avg_psnr;
128
float m_bc7_luma_709_psnr;
129
float m_bc7_luma_601_psnr;
130
float m_bc7_luma_709_ssim;
131
132
// LDR: Highest achievable quality ETC1S statistics
133
float m_best_etc1s_rgb_avg_psnr;
134
float m_best_etc1s_luma_709_psnr;
135
float m_best_etc1s_luma_601_psnr;
136
float m_best_etc1s_luma_709_ssim;
137
138
bool m_opencl_failed;
139
};
140
141
enum class hdr_modes
142
{
143
// standard but constrained ASTC HDR 4x4 tex data that can be rapidly transcoded to BC6H
144
cUASTC_HDR_4X4,
145
// standard RDO optimized or non-RDO (highest quality) ASTC HDR 6x6 tex data that can be rapidly re-encoded to BC6H
146
cASTC_HDR_6X6,
147
// a custom intermediate format based off ASTC HDR that can be rapidly decoded straight to ASTC HDR or re-encoded to BC6H
148
cASTC_HDR_6X6_INTERMEDIATE,
149
cTotal
150
};
151
152
template<bool def>
153
struct bool_param
154
{
155
bool_param() :
156
m_value(def),
157
m_changed(false)
158
{
159
}
160
161
void clear()
162
{
163
m_value = def;
164
m_changed = false;
165
}
166
167
operator bool() const
168
{
169
return m_value;
170
}
171
172
bool operator= (bool v)
173
{
174
m_value = v;
175
m_changed = true;
176
return m_value;
177
}
178
179
bool was_changed() const { return m_changed; }
180
void set_changed(bool flag) { m_changed = flag; }
181
182
bool m_value;
183
bool m_changed;
184
};
185
186
template<typename T>
187
struct param
188
{
189
param(T def, T min_v, T max_v) :
190
m_value(def),
191
m_def(def),
192
m_min(min_v),
193
m_max(max_v),
194
m_changed(false)
195
{
196
}
197
198
void clear()
199
{
200
m_value = m_def;
201
m_changed = false;
202
}
203
204
operator T() const
205
{
206
return m_value;
207
}
208
209
T operator= (T v)
210
{
211
m_value = clamp<T>(v, m_min, m_max);
212
m_changed = true;
213
return m_value;
214
}
215
216
T operator *= (T v)
217
{
218
m_value *= v;
219
m_changed = true;
220
return m_value;
221
}
222
223
bool was_changed() const { return m_changed; }
224
void set_changed(bool flag) { m_changed = flag; }
225
226
T m_value;
227
T m_def;
228
T m_min;
229
T m_max;
230
bool m_changed;
231
};
232
233
struct basis_compressor_params
234
{
235
basis_compressor_params() :
236
m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
237
m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
238
m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
239
m_mip_scale(1.0f, .000125f, 4.0f),
240
m_mip_smallest_dimension(1, 1, 16384),
241
m_etc1s_max_endpoint_clusters(512),
242
m_etc1s_max_selector_clusters(512),
243
m_etc1s_quality_level(-1),
244
m_pack_uastc_ldr_4x4_flags(cPackUASTCLevelDefault),
245
m_rdo_uastc_ldr_4x4_quality_scalar(1.0f, 0.001f, 50.0f),
246
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),
247
m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
248
m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
249
m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
250
m_rdo_uastc_ldr_4x4_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
251
m_resample_width(0, 1, 16384),
252
m_resample_height(0, 1, 16384),
253
m_resample_factor(0.0f, .00125f, 100.0f),
254
m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE),
255
m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX),
256
m_ldr_hdr_upconversion_nit_multiplier(0.0f, 0.0f, basist::MAX_HALF_FLOAT),
257
m_ldr_hdr_upconversion_black_bias(0.0f, 0.0f, 1.0f),
258
m_pJob_pool(nullptr)
259
{
260
clear();
261
}
262
263
void clear()
264
{
265
m_uastc.clear();
266
m_hdr.clear();
267
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
268
269
m_use_opencl.clear();
270
m_status_output.clear();
271
272
m_source_filenames.clear();
273
m_source_alpha_filenames.clear();
274
275
m_source_images.clear();
276
m_source_mipmap_images.clear();
277
278
m_out_filename.clear();
279
280
m_y_flip.clear();
281
m_debug.clear();
282
m_validate_etc1s.clear();
283
m_debug_images.clear();
284
m_perceptual.clear();
285
m_no_selector_rdo.clear();
286
m_selector_rdo_thresh.clear();
287
m_read_source_images.clear();
288
m_write_output_basis_or_ktx2_files.clear();
289
m_compression_level.clear();
290
m_compute_stats.clear();
291
m_print_stats.clear();
292
m_check_for_alpha.clear();
293
m_force_alpha.clear();
294
m_multithreading.clear();
295
m_swizzle[0] = 0;
296
m_swizzle[1] = 1;
297
m_swizzle[2] = 2;
298
m_swizzle[3] = 3;
299
m_renormalize.clear();
300
m_disable_hierarchical_endpoint_codebooks.clear();
301
302
m_no_endpoint_rdo.clear();
303
m_endpoint_rdo_thresh.clear();
304
305
m_mip_gen.clear();
306
m_mip_scale.clear();
307
m_mip_filter = "kaiser";
308
m_mip_scale = 1.0f;
309
m_mip_srgb.clear();
310
m_mip_premultiplied.clear();
311
m_mip_renormalize.clear();
312
m_mip_wrapping.clear();
313
m_mip_fast.clear();
314
m_mip_smallest_dimension.clear();
315
316
m_etc1s_max_endpoint_clusters = 0;
317
m_etc1s_max_selector_clusters = 0;
318
m_etc1s_quality_level = -1;
319
320
m_tex_type = basist::cBASISTexType2D;
321
m_userdata0 = 0;
322
m_userdata1 = 0;
323
m_us_per_frame = 0;
324
325
m_pack_uastc_ldr_4x4_flags = cPackUASTCLevelDefault;
326
m_rdo_uastc_ldr_4x4.clear();
327
m_rdo_uastc_ldr_4x4_quality_scalar.clear();
328
m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale.clear();
329
m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev.clear();
330
m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio.clear();
331
m_rdo_uastc_ldr_4x4_skip_block_rms_thresh.clear();
332
m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode.clear();
333
m_rdo_uastc_ldr_4x4_multithreading.clear();
334
335
m_resample_width.clear();
336
m_resample_height.clear();
337
m_resample_factor.clear();
338
339
m_pGlobal_codebooks = nullptr;
340
341
m_create_ktx2_file.clear();
342
m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE;
343
m_ktx2_key_values.clear();
344
m_ktx2_zstd_supercompression_level.clear();
345
m_ktx2_srgb_transfer_func.clear();
346
347
m_validate_output_data.clear();
348
349
m_ldr_hdr_upconversion_srgb_to_linear.clear();
350
351
m_hdr_favor_astc.clear();
352
353
m_uastc_hdr_4x4_options.init();
354
m_astc_hdr_6x6_options.clear();
355
356
m_ldr_hdr_upconversion_nit_multiplier.clear();
357
m_ldr_hdr_upconversion_black_bias.clear();
358
359
m_pJob_pool = nullptr;
360
}
361
362
// Configures the compressor's mode by setting the proper parameters (which were preserved for backwards compatibility with old code).
363
void set_format_mode(basist::basis_tex_format m)
364
{
365
switch (m)
366
{
367
case basist::basis_tex_format::cETC1S:
368
{
369
m_hdr = false;
370
m_uastc = false;
371
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
372
break;
373
}
374
case basist::basis_tex_format::cUASTC4x4:
375
{
376
m_hdr = false;
377
m_uastc = true;
378
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
379
break;
380
}
381
case basist::basis_tex_format::cUASTC_HDR_4x4:
382
{
383
m_hdr = true;
384
m_uastc = true;
385
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
386
break;
387
}
388
case basist::basis_tex_format::cASTC_HDR_6x6:
389
{
390
m_hdr = true;
391
m_uastc = true;
392
m_hdr_mode = hdr_modes::cASTC_HDR_6X6;
393
break;
394
}
395
case basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:
396
{
397
m_hdr = true;
398
m_uastc = true;
399
m_hdr_mode = hdr_modes::cASTC_HDR_6X6_INTERMEDIATE;
400
break;
401
}
402
default:
403
assert(0);
404
break;
405
}
406
}
407
408
// By default we generate LDR ETC1S data.
409
// if m_uastc is true but m_hdr is not true, we generate UASTC 4x4 LDR data (8bpp with or without RDO).
410
// 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.
411
412
// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
413
// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, and ASTC 6x6 HDR intermediate).
414
bool_param<false> m_uastc;
415
416
// Set m_hdr to true to switch to UASTC HDR mode. m_hdr_mode then controls which format is output.
417
// m_hdr_mode then controls which format is output (4x4, 6x6, or 6x6 intermediate).
418
bool_param<false> m_hdr;
419
420
// 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.
421
hdr_modes m_hdr_mode;
422
423
bool_param<false> m_use_opencl;
424
425
// 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.
426
// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.
427
basisu::vector<std::string> m_source_filenames;
428
basisu::vector<std::string> m_source_alpha_filenames;
429
430
basisu::vector<image> m_source_images;
431
432
basisu::vector<imagef> m_source_images_hdr;
433
434
// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.
435
// 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.
436
// The compressor applies the user-provided swizzling (in m_swizzle) to these images.
437
basisu::vector< basisu::vector<image> > m_source_mipmap_images;
438
439
basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;
440
441
// Filename of the output basis/ktx2 file
442
std::string m_out_filename;
443
444
// The params are done this way so we can detect when the user has explictly changed them.
445
446
// Flip images across Y axis
447
bool_param<false> m_y_flip;
448
449
// If true, the compressor will print basis status to stdout during compression.
450
bool_param<true> m_status_output;
451
452
// Output debug information during compression
453
bool_param<false> m_debug;
454
bool_param<false> m_validate_etc1s;
455
456
// m_debug_images is pretty slow
457
bool_param<false> m_debug_images;
458
459
// ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower).
460
// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.
461
// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.
462
param<int> m_compression_level;
463
464
// Use perceptual sRGB colorspace metrics instead of linear
465
bool_param<true> m_perceptual;
466
467
// Disable selector RDO, for faster compression but larger files
468
bool_param<false> m_no_selector_rdo;
469
param<float> m_selector_rdo_thresh;
470
471
bool_param<false> m_no_endpoint_rdo;
472
param<float> m_endpoint_rdo_thresh;
473
474
// Read source images from m_source_filenames/m_source_alpha_filenames
475
bool_param<false> m_read_source_images;
476
477
// Write the output basis/ktx2 file to disk using m_out_filename
478
bool_param<false> m_write_output_basis_or_ktx2_files;
479
480
// Compute and display image metrics
481
bool_param<false> m_compute_stats;
482
483
// Print stats to stdout, if m_compute_stats is true.
484
bool_param<true> m_print_stats;
485
486
// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels
487
bool_param<true> m_check_for_alpha;
488
489
// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha
490
bool_param<false> m_force_alpha;
491
bool_param<true> m_multithreading;
492
493
// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels
494
uint8_t m_swizzle[4];
495
496
bool_param<false> m_renormalize;
497
498
// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.
499
// Note some m_compression_level's disable this automatically.
500
bool_param<false> m_disable_hierarchical_endpoint_codebooks;
501
502
// mipmap generation parameters
503
bool_param<false> m_mip_gen;
504
param<float> m_mip_scale;
505
std::string m_mip_filter;
506
bool_param<false> m_mip_srgb;
507
bool_param<true> m_mip_premultiplied; // not currently supported
508
bool_param<false> m_mip_renormalize;
509
bool_param<true> m_mip_wrapping;
510
bool_param<true> m_mip_fast;
511
param<int> m_mip_smallest_dimension;
512
513
// ETC1S codebook size (quality) control.
514
// If m_etc1s_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
515
// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
516
uint32_t m_etc1s_max_endpoint_clusters;
517
uint32_t m_etc1s_max_selector_clusters;
518
int m_etc1s_quality_level;
519
520
// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the .basis file header.
521
basist::basis_texture_type m_tex_type;
522
uint32_t m_userdata0;
523
uint32_t m_userdata1;
524
uint32_t m_us_per_frame;
525
526
// UASTC LDR 4x4 parameters
527
// cPackUASTCLevelDefault, etc.
528
uint32_t m_pack_uastc_ldr_4x4_flags;
529
bool_param<false> m_rdo_uastc_ldr_4x4;
530
param<float> m_rdo_uastc_ldr_4x4_quality_scalar;
531
param<int> m_rdo_uastc_ldr_4x4_dict_size;
532
param<float> m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale;
533
param<float> m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev;
534
param<float> m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio;
535
param<float> m_rdo_uastc_ldr_4x4_skip_block_rms_thresh;
536
bool_param<true> m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode;
537
bool_param<true> m_rdo_uastc_ldr_4x4_multithreading;
538
539
param<int> m_resample_width;
540
param<int> m_resample_height;
541
param<float> m_resample_factor;
542
543
const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
544
545
// KTX2 specific parameters.
546
// Internally, the compressor always creates a .basis file then it converts that lossless to KTX2.
547
bool_param<false> m_create_ktx2_file;
548
basist::ktx2_supercompression m_ktx2_uastc_supercompression;
549
basist::ktx2_transcoder::key_value_vec m_ktx2_key_values;
550
param<int> m_ktx2_zstd_supercompression_level;
551
bool_param<false> m_ktx2_srgb_transfer_func;
552
553
uastc_hdr_4x4_codec_options m_uastc_hdr_4x4_options;
554
astc_6x6_hdr::astc_hdr_6x6_global_config m_astc_hdr_6x6_options;
555
556
bool_param<false> m_validate_output_data;
557
558
// LDR->HDR upconversion parameters.
559
//
560
// 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.
561
// Otherwise, LDR images are assumed to already be in linear light (i.e. they don't use the sRGB transfer function).
562
bool_param<true> m_ldr_hdr_upconversion_srgb_to_linear;
563
564
// m_ldr_hdr_upconversion_nit_multiplier is only used when loading SDR/LDR images and compressing to an HDR output format.
565
// By default m_ldr_hdr_upconversion_nit_multiplier is 0. It's an override for the default.
566
// 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.
567
// With the 6x6 HDR encoder it does matter because it expects inputs in absolute nits, so the LDR upconversion luminance multiplier default will be 100 nits. (Most SDR monitors were/are 80-100 nits or so.)
568
param<float> m_ldr_hdr_upconversion_nit_multiplier;
569
570
// The optional sRGB space bias to use during LDR->HDR upconversion. Should be between [0,.49] or so. Only applied on black (0.0) color components.
571
// Defaults to no bias (0.0f).
572
param<float> m_ldr_hdr_upconversion_black_bias;
573
574
// If true, ASTC HDR quality is favored more than BC6H quality. Otherwise it's a rough balance.
575
bool_param<false> m_hdr_favor_astc;
576
577
job_pool *m_pJob_pool;
578
};
579
580
// Important: basisu_encoder_init() MUST be called first before using this class.
581
class basis_compressor
582
{
583
BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);
584
585
public:
586
basis_compressor();
587
~basis_compressor();
588
589
// 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.
590
bool init(const basis_compressor_params &params);
591
592
enum error_code
593
{
594
cECSuccess = 0,
595
cECFailedInitializing,
596
cECFailedReadingSourceImages,
597
cECFailedValidating,
598
cECFailedEncodeUASTC,
599
cECFailedFrontEnd,
600
cECFailedFontendExtract,
601
cECFailedBackend,
602
cECFailedCreateBasisFile,
603
cECFailedWritingOutput,
604
cECFailedUASTCRDOPostProcess,
605
cECFailedCreateKTX2File
606
};
607
608
error_code process();
609
610
// The output .basis file will always be valid of process() succeeded.
611
const uint8_vec &get_output_basis_file() const { return m_output_basis_file; }
612
613
// The output .ktx2 file will only be valid if m_create_ktx2_file was true and process() succeeded.
614
const uint8_vec& get_output_ktx2_file() const { return m_output_ktx2_file; }
615
616
const basisu::vector<image_stats> &get_stats() const { return m_stats; }
617
618
uint32_t get_basis_file_size() const { return m_basis_file_size; }
619
double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
620
621
bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
622
623
bool get_opencl_failed() const { return m_opencl_failed; }
624
625
private:
626
basis_compressor_params m_params;
627
628
opencl_context_ptr m_pOpenCL_context;
629
630
basist::basis_tex_format m_fmt_mode;
631
632
basisu::vector<image> m_slice_images;
633
basisu::vector<imagef> m_slice_images_hdr;
634
635
basisu::vector<image_stats> m_stats;
636
637
uint32_t m_basis_file_size;
638
double m_basis_bits_per_texel;
639
640
basisu_backend_slice_desc_vec m_slice_descs;
641
642
uint32_t m_total_blocks;
643
644
basisu_frontend m_frontend;
645
646
// These are 4x4 blocks.
647
pixel_block_vec m_source_blocks;
648
pixel_block_hdr_vec m_source_blocks_hdr;
649
650
basisu::vector<gpu_image> m_frontend_output_textures;
651
652
basisu::vector<gpu_image> m_best_etc1s_images;
653
basisu::vector<image> m_best_etc1s_images_unpacked;
654
655
basisu_backend m_backend;
656
657
basisu_file m_basis_file;
658
659
basisu::vector<gpu_image> m_decoded_output_textures; // BC6H in HDR mode
660
basisu::vector<image> m_decoded_output_textures_unpacked;
661
662
basisu::vector<gpu_image> m_decoded_output_textures_bc7;
663
basisu::vector<image> m_decoded_output_textures_unpacked_bc7;
664
665
basisu::vector<imagef> m_decoded_output_textures_bc6h_hdr_unpacked; // BC6H in HDR mode
666
667
basisu::vector<gpu_image> m_decoded_output_textures_astc_hdr;
668
basisu::vector<imagef> m_decoded_output_textures_astc_hdr_unpacked;
669
670
uint8_vec m_output_basis_file;
671
uint8_vec m_output_ktx2_file;
672
673
basisu::vector<gpu_image> m_uastc_slice_textures;
674
basisu_backend_output m_uastc_backend_output;
675
676
// The amount the HDR input has to be scaled up in case it had to be rescaled to fit into half floats.
677
float m_hdr_image_scale;
678
679
// The upconversion multiplier used to load LDR images in HDR mode.
680
float m_ldr_to_hdr_upconversion_nit_multiplier;
681
682
// True if any loaded source images were LDR and upconverted to HDR.
683
bool m_upconverted_any_ldr_images;
684
685
bool m_any_source_image_has_alpha;
686
687
bool m_opencl_failed;
688
689
void check_for_hdr_inputs();
690
bool sanity_check_input_params();
691
void clean_hdr_image(imagef& src_img);
692
bool read_dds_source_images();
693
bool read_source_images();
694
bool extract_source_blocks();
695
bool process_frontend();
696
bool extract_frontend_texture_data();
697
bool process_backend();
698
bool create_basis_file_and_transcode();
699
bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);
700
bool write_output_files_and_compute_stats();
701
error_code encode_slices_to_astc_6x6_hdr();
702
error_code encode_slices_to_uastc_4x4_hdr();
703
error_code encode_slices_to_uastc_4x4_ldr();
704
bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
705
bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
706
bool validate_texture_type_constraints();
707
bool validate_ktx2_constraints();
708
bool get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
709
bool create_ktx2_file();
710
void pick_format_mode();
711
712
uint32_t get_block_width() const
713
{
714
if (m_params.m_hdr)
715
{
716
switch (m_params.m_hdr_mode)
717
{
718
case hdr_modes::cASTC_HDR_6X6:
719
case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:
720
return 6;
721
default:
722
break;
723
}
724
}
725
return 4;
726
}
727
728
uint32_t get_block_height() const
729
{
730
if (m_params.m_hdr)
731
{
732
switch (m_params.m_hdr_mode)
733
{
734
case hdr_modes::cASTC_HDR_6X6:
735
case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:
736
return 6;
737
default:
738
break;
739
}
740
}
741
return 4;
742
}
743
};
744
745
// Alternative simple C-style wrapper API around the basis_compressor class.
746
// This doesn't expose every encoder feature, but it's enough to get going.
747
// Important: basisu_encoder_init() MUST be called first before calling these functions.
748
//
749
// Input parameters:
750
// source_images: Array of "image" objects, one per mipmap level, largest mipmap level first.
751
// OR
752
// pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.
753
// width/height/pitch_in_pixels: dimensions of pImageRGBA
754
//
755
// 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".
756
// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
757
// 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.
758
// In UASTC mode, be sure to set this, otherwise it defaults to 0 (fastest/lowest quality).
759
//
760
// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
761
//
762
// pSize: Returns the output data's compressed size in bytes
763
//
764
// Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it.
765
enum
766
{
767
cFlagUseOpenCL = 1 << 8, // use OpenCL if available
768
cFlagThreaded = 1 << 9, // use multiple threads for compression
769
cFlagDebug = 1 << 10, // enable debug output
770
771
cFlagKTX2 = 1 << 11, // generate a KTX2 file
772
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files
773
774
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
775
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
776
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
777
778
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
779
780
cFlagUASTCRDO = 1 << 17, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
781
782
cFlagPrintStats = 1 << 18, // print image stats to stdout
783
cFlagPrintStatus = 1 << 19, // print status to stdout
784
785
cFlagDebugImages = 1 << 20, // enable status output
786
787
cFlagREC2020 = 1 << 21 // ASTC 6x6 modes: treat input as REC 2020 vs. the default 709
788
};
789
790
// This function accepts an array of source images.
791
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
792
// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data.
793
// Important: The returned block MUST be manually freed using basis_free_data().
794
// basisu_encoder_init() MUST be called first!
795
// LDR version. To compress the LDR source image as HDR: Use the cFlagHDR flag.
796
void* basis_compress(
797
basist::basis_tex_format mode,
798
const basisu::vector<image> &source_images,
799
uint32_t flags_and_quality, float uastc_rdo_quality,
800
size_t* pSize,
801
image_stats* pStats = nullptr);
802
803
// HDR-only version.
804
// Important: The returned block MUST be manually freed using basis_free_data().
805
void* basis_compress(
806
basist::basis_tex_format mode,
807
const basisu::vector<imagef>& source_images_hdr,
808
uint32_t flags_and_quality, float lambda,
809
size_t* pSize,
810
image_stats* pStats = nullptr);
811
812
// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
813
// Important: The returned block MUST be manually freed using basis_free_data().
814
void* basis_compress(
815
basist::basis_tex_format mode,
816
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
817
uint32_t flags_and_quality, float uastc_rdo_quality,
818
size_t* pSize,
819
image_stats* pStats = nullptr);
820
821
// Frees the dynamically allocated file data returned by basis_compress().
822
// This MUST be called on the pointer returned by basis_compress() when you're done with it.
823
void basis_free_data(void* p);
824
825
// Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled.
826
// Returns true if opencl is worth using on this system, otherwise false.
827
// 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.
828
// basisu_encoder_init() MUST be called first. If OpenCL support wasn't enabled this always returns false.
829
bool basis_benchmark_etc1s_opencl(bool *pOpenCL_failed = nullptr);
830
831
// Parallel compression API
832
struct parallel_results
833
{
834
double m_total_time;
835
basis_compressor::error_code m_error_code;
836
uint8_vec m_basis_file;
837
uint8_vec m_ktx2_file;
838
basisu::vector<image_stats> m_stats;
839
double m_basis_bits_per_texel;
840
bool m_any_source_image_has_alpha;
841
842
parallel_results()
843
{
844
clear();
845
}
846
847
void clear()
848
{
849
m_total_time = 0.0f;
850
m_error_code = basis_compressor::cECFailedInitializing;
851
m_basis_file.clear();
852
m_ktx2_file.clear();
853
m_stats.clear();
854
m_basis_bits_per_texel = 0.0f;
855
m_any_source_image_has_alpha = false;
856
}
857
};
858
859
// Compresses an array of input textures across total_threads threads using the basis_compressor class.
860
// Compressing multiple textures at a time is substantially more efficient than just compressing one at a time.
861
// total_threads must be >= 1.
862
bool basis_parallel_compress(
863
uint32_t total_threads,
864
const basisu::vector<basis_compressor_params> &params_vec,
865
basisu::vector< parallel_results > &results_vec);
866
867
} // namespace basisu
868
869
870