summaryrefslogtreecommitdiffstats
path: root/thirdparty/basis_universal/encoder/basisu_astc_hdr_enc.h
blob: ee122ff7cee9205fbfac44d274b7bca7b56eaed1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// basisu_astc_hdr_enc.h
#pragma once
#include "basisu_enc.h"
#include "basisu_gpu_texture.h"
#include "../transcoder/basisu_astc_helpers.h"
#include "../transcoder/basisu_astc_hdr_core.h"

namespace basisu
{
	// This MUST be called before encoding any blocks.
	void astc_hdr_enc_init();

	const uint32_t MODE11_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE11_LAST_ISE_RANGE = astc_helpers::BISE_16_LEVELS;
	const uint32_t MODE7_PART1_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE7_PART1_LAST_ISE_RANGE = astc_helpers::BISE_16_LEVELS;
	const uint32_t MODE7_PART2_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE7_PART2_LAST_ISE_RANGE = astc_helpers::BISE_8_LEVELS;
	const uint32_t MODE11_PART2_FIRST_ISE_RANGE = astc_helpers::BISE_3_LEVELS, MODE11_PART2_LAST_ISE_RANGE = astc_helpers::BISE_4_LEVELS;
	const uint32_t MODE11_TOTAL_SUBMODES = 8; // plus an extra hidden submode, directly encoded, for direct, so really 9 (see tables 99/100 of the ASTC spec)
	const uint32_t MODE7_TOTAL_SUBMODES = 6;
		
	struct astc_hdr_codec_options
	{
		float m_bc6h_err_weight;

		bool m_use_solid;

		bool m_use_mode11;
		bool m_mode11_uber_mode;
		uint32_t m_first_mode11_weight_ise_range;
		uint32_t m_last_mode11_weight_ise_range;
		bool m_mode11_direct_only;
		int32_t m_first_mode11_submode;
		int32_t m_last_mode11_submode;

		bool m_use_mode7_part1;
		uint32_t m_first_mode7_part1_weight_ise_range;
		uint32_t m_last_mode7_part1_weight_ise_range;

		bool m_use_mode7_part2;
		uint32_t m_mode7_part2_part_masks;
		uint32_t m_first_mode7_part2_weight_ise_range;
		uint32_t m_last_mode7_part2_weight_ise_range;

		bool m_use_mode11_part2;
		uint32_t m_mode11_part2_part_masks;
		uint32_t m_first_mode11_part2_weight_ise_range;
		uint32_t m_last_mode11_part2_weight_ise_range;

		float m_r_err_scale, m_g_err_scale;

		bool m_refine_weights;

		uint32_t m_level;

		bool m_use_estimated_partitions;
		uint32_t m_max_estimated_partitions;

		// If true, the ASTC HDR compressor is allowed to more aggressively vary weight indices for slightly higher compression in non-fastest mode. This will hurt BC6H quality, however.
		bool m_allow_uber_mode;

		astc_hdr_codec_options();

		void init();
				
		// TODO: set_quality_level() is preferred to configure the codec for transcoding purposes.
		static const int cMinLevel = 0;
		static const int cMaxLevel = 4;
		static const int cDefaultLevel = 1;
		void set_quality_level(int level);

	private:
		void set_quality_best();
		void set_quality_normal();
		void set_quality_fastest();
	};

	struct astc_hdr_pack_results
	{
		double m_best_block_error;
		double m_bc6h_block_error; // note this is not used/set by the encoder, here for convienance 

		// Encoder results (logical ASTC block)
		astc_helpers::log_astc_block m_best_blk;
		
		// For statistical use
		uint32_t m_best_submodes[2];
		uint32_t m_best_pat_index;
		bool m_constrained_weights;

		bool m_improved_via_refinement_flag;
				
		// Only valid if the block is solid
		basist::astc_blk m_solid_blk;
		
		// The BC6H transcoded block
		basist::bc6h_block m_bc6h_block;

		// Solid color/void extent flag
		bool m_is_solid;

		void clear()
		{
			m_best_block_error = 1e+30f;
			m_bc6h_block_error = 1e+30f;

			m_best_blk.clear();
			m_best_blk.m_grid_width = 4;
			m_best_blk.m_grid_height = 4;
			m_best_blk.m_endpoint_ise_range = 20; // 0-255

			clear_obj(m_best_submodes);

			m_best_pat_index = 0;
			m_constrained_weights = false;
									
			clear_obj(m_bc6h_block);
			
			m_is_solid = false;
			m_improved_via_refinement_flag = false;
		}
	};
			
	void interpolate_qlog12_colors(
		const int e[2][3],
		basist::half_float* pDecoded_half,
		vec3F* pDecoded_float,
		uint32_t n, uint32_t ise_weight_range);
		
	bool get_astc_hdr_mode_11_block_colors(
		const uint8_t* pEndpoints,
		basist::half_float* pDecoded_half,
		vec3F* pDecoded_float,
		uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);
		
	bool get_astc_hdr_mode_7_block_colors(
		const uint8_t* pEndpoints,
		basist::half_float* pDecoded_half,
		vec3F* pDecoded_float,
		uint32_t n, uint32_t ise_weight_range, uint32_t ise_endpoint_range);

	double eval_selectors(
		uint32_t num_pixels,
		uint8_t* pWeights,
		const basist::half_float* pBlock_pixels_half,
		uint32_t num_weight_levels,
		const basist::half_float* pDecoded_half,
		const astc_hdr_codec_options& coptions,
		uint32_t usable_selector_bitmask = UINT32_MAX);

	double compute_block_error(const basist::half_float* pOrig_block, const basist::half_float* pPacked_block, const astc_hdr_codec_options& coptions);

	// Encodes a 4x4 ASTC HDR block given a 4x4 array of source block pixels/texels.
	// Supports solid color blocks, mode 11 (all submodes), mode 7/1 partition (all submodes), 
	// and mode 7/2 partitions (all submodes) - 30 patterns, only the ones also in common with the BC6H format.
	// The packed ASTC weight grid dimensions are currently always 4x4 texels, but may be also 3x3 in the future.
	// This function is thread safe, i.e. it may be called from multiple encoding threads simultanously with different blocks.
	// 
	// Parameters:
	// pRGBPixels - An array of 48 (16 RGB) floats: the 4x4 block to pack
	// pPacked_block - A pointer to the packed ASTC HDR block
	// coptions - Codec options
	// pInternal_results - An optional pointer to details about how the block was packed, for statistics/debugging purposes. May be nullptr.
	// 
	// Requirements: 
	// astc_hdr_enc_init() MUST have been called first to initialized the codec.
	// Input pixels are checked and cannot be NaN's, Inf's, signed, or too large (greater than MAX_HALF_FLOAT, or 65504). 
	// Normal values and denormals are okay.
	bool astc_hdr_enc_block(
		const float* pRGBPixels,
		const astc_hdr_codec_options& coptions,
		basisu::vector<astc_hdr_pack_results> &all_results);

	bool astc_hdr_pack_results_to_block(basist::astc_blk& dst_blk, const astc_hdr_pack_results& results);
		
	bool astc_hdr_refine_weights(const basist::half_float* pSource_block, astc_hdr_pack_results& cur_results, const astc_hdr_codec_options& coptions, float bc6h_weight, bool* pImproved_flag);

	struct astc_hdr_block_stats
	{
		std::mutex m_mutex;

		uint32_t m_total_blocks;
		uint32_t m_total_2part, m_total_solid;
		uint32_t m_total_mode7_1part, m_total_mode7_2part;
		uint32_t m_total_mode11_1part, m_total_mode11_2part;
		uint32_t m_total_mode11_1part_constrained_weights;

		uint32_t m_weight_range_hist_7[11];
		uint32_t m_weight_range_hist_7_2part[11];
		uint32_t m_mode7_submode_hist[6];

		uint32_t m_weight_range_hist_11[11];
		uint32_t m_weight_range_hist_11_2part[11];
		uint32_t m_mode11_submode_hist[9];
								
		uint32_t m_part_hist[32];

		uint32_t m_total_refined;
								
		astc_hdr_block_stats() { clear(); }

		void clear()
		{
			std::lock_guard<std::mutex> lck(m_mutex);

			m_total_blocks = 0;
			m_total_mode7_1part = 0, m_total_mode7_2part = 0, m_total_mode11_1part = 0, m_total_2part = 0, m_total_solid = 0, m_total_mode11_2part = 0;
			m_total_mode11_1part_constrained_weights = 0;
			m_total_refined = 0;

			clear_obj(m_weight_range_hist_11);
			clear_obj(m_weight_range_hist_11_2part);
			clear_obj(m_weight_range_hist_7);
			clear_obj(m_weight_range_hist_7_2part);
			clear_obj(m_mode7_submode_hist);
			clear_obj(m_mode11_submode_hist);
			clear_obj(m_part_hist);
		}

		void update(const astc_hdr_pack_results& log_blk);
		
		void print();
	};
		
} // namespace basisu