Coverage Report

Created: 2026-09-01 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/astc-encoder/Source/astcenc_ideal_endpoints_and_weights.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// ----------------------------------------------------------------------------
3
// Copyright 2011-2024 Arm Limited
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
// use this file except in compliance with the License. You may obtain a copy
7
// of the License at:
8
//
9
//     http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
// License for the specific language governing permissions and limitations
15
// under the License.
16
// ----------------------------------------------------------------------------
17
18
#if !defined(ASTCENC_DECOMPRESS_ONLY)
19
20
/**
21
 * @brief Functions for computing color endpoints and texel weights.
22
 */
23
24
#include <cassert>
25
26
#include "astcenc_internal.h"
27
#include "astcenc_vecmathlib.h"
28
29
/**
30
 * @brief Compute the infilled weight for N texel indices in a decimated grid.
31
 *
32
 * @param di        The weight grid decimation to use.
33
 * @param weights   The decimated weight values to use.
34
 * @param index     The first texel index to interpolate.
35
 *
36
 * @return The interpolated weight for the given set of SIMD_WIDTH texels.
37
 */
38
static vfloat bilinear_infill_vla(
39
  const decimation_info& di,
40
  const float* weights,
41
  unsigned int index
42
2.51M
) {
43
  // Load the bilinear filter texel weight indexes in the decimated grid
44
2.51M
  const uint8_t* weight_idx0 = di.texel_weights_tr[0] + index;
45
2.51M
  const uint8_t* weight_idx1 = di.texel_weights_tr[1] + index;
46
2.51M
  const uint8_t* weight_idx2 = di.texel_weights_tr[2] + index;
47
2.51M
  const uint8_t* weight_idx3 = di.texel_weights_tr[3] + index;
48
49
  // Load the bilinear filter weights from the decimated grid
50
2.51M
  vfloat weight_val0 = gatherf_byte_inds<vfloat>(weights, weight_idx0);
51
2.51M
  vfloat weight_val1 = gatherf_byte_inds<vfloat>(weights, weight_idx1);
52
2.51M
  vfloat weight_val2 = gatherf_byte_inds<vfloat>(weights, weight_idx2);
53
2.51M
  vfloat weight_val3 = gatherf_byte_inds<vfloat>(weights, weight_idx3);
54
55
  // Load the weight contribution factors for each decimated weight
56
2.51M
  vfloat tex_weight_float0 = loada(di.texel_weight_contribs_float_tr[0] + index);
57
2.51M
  vfloat tex_weight_float1 = loada(di.texel_weight_contribs_float_tr[1] + index);
58
2.51M
  vfloat tex_weight_float2 = loada(di.texel_weight_contribs_float_tr[2] + index);
59
2.51M
  vfloat tex_weight_float3 = loada(di.texel_weight_contribs_float_tr[3] + index);
60
61
  // Compute the bilinear interpolation to generate the per-texel weight
62
2.51M
  return (weight_val0 * tex_weight_float0 + weight_val1 * tex_weight_float1) +
63
2.51M
         (weight_val2 * tex_weight_float2 + weight_val3 * tex_weight_float3);
64
2.51M
}
65
66
/**
67
 * @brief Compute the infilled weight for N texel indices in a decimated grid.
68
 *
69
 * This is specialized version which computes only two weights per texel for
70
 * encodings that are only decimated in a single axis.
71
 *
72
 * @param di        The weight grid decimation to use.
73
 * @param weights   The decimated weight values to use.
74
 * @param index     The first texel index to interpolate.
75
 *
76
 * @return The interpolated weight for the given set of SIMD_WIDTH texels.
77
 */
78
static vfloat bilinear_infill_vla_2(
79
  const decimation_info& di,
80
  const float* weights,
81
  unsigned int index
82
1.33M
) {
83
  // Load the bilinear filter texel weight indexes in the decimated grid
84
1.33M
  const uint8_t* weight_idx0 = di.texel_weights_tr[0] + index;
85
1.33M
  const uint8_t* weight_idx1 = di.texel_weights_tr[1] + index;
86
87
  // Load the bilinear filter weights from the decimated grid
88
1.33M
  vfloat weight_val0 = gatherf_byte_inds<vfloat>(weights, weight_idx0);
89
1.33M
  vfloat weight_val1 = gatherf_byte_inds<vfloat>(weights, weight_idx1);
90
91
  // Load the weight contribution factors for each decimated weight
92
1.33M
  vfloat tex_weight_float0 = loada(di.texel_weight_contribs_float_tr[0] + index);
93
1.33M
  vfloat tex_weight_float1 = loada(di.texel_weight_contribs_float_tr[1] + index);
94
95
  // Compute the bilinear interpolation to generate the per-texel weight
96
1.33M
  return (weight_val0 * tex_weight_float0 + weight_val1 * tex_weight_float1);
97
1.33M
}
98
99
/**
100
 * @brief Compute the ideal endpoints and weights for 1 color component.
101
 *
102
 * @param      blk         The image block color data to compress.
103
 * @param      pi          The partition info for the current trial.
104
 * @param[out] ei          The computed ideal endpoints and weights.
105
 * @param      component   The color component to compute.
106
 */
107
static void compute_ideal_colors_and_weights_1_comp(
108
  const image_block& blk,
109
  const partition_info& pi,
110
  endpoints_and_weights& ei,
111
  unsigned int component
112
5.65k
) {
113
5.65k
  unsigned int partition_count = pi.partition_count;
114
5.65k
  ei.ep.partition_count = partition_count;
115
5.65k
  promise(partition_count > 0);
116
117
5.65k
  unsigned int texel_count = blk.texel_count;
118
5.65k
  promise(texel_count > 0);
119
120
5.65k
  float error_weight;
121
5.65k
  const float* data_vr = nullptr;
122
123
5.65k
  assert(component < BLOCK_MAX_COMPONENTS);
124
5.65k
  switch (component)
125
5.65k
  {
126
1.34k
  case 0:
127
1.34k
    error_weight = blk.channel_weight.lane<0>();
128
1.34k
    data_vr = blk.data_r;
129
1.34k
    break;
130
1.36k
  case 1:
131
1.36k
    error_weight = blk.channel_weight.lane<1>();
132
1.36k
    data_vr = blk.data_g;
133
1.36k
    break;
134
1.48k
  case 2:
135
1.48k
    error_weight = blk.channel_weight.lane<2>();
136
1.48k
    data_vr = blk.data_b;
137
1.48k
    break;
138
1.46k
  default:
139
1.46k
    assert(component == 3);
140
1.46k
    error_weight = blk.channel_weight.lane<3>();
141
1.46k
    data_vr = blk.data_a;
142
1.46k
    break;
143
5.65k
  }
144
145
5.65k
  vmask4 sep_mask = vint4::lane_id() == vint4(component);
146
5.65k
  bool is_constant_wes { true };
147
5.65k
  float partition0_len_sq { 0.0f };
148
149
11.3k
  for (unsigned int i = 0; i < partition_count; i++)
150
5.65k
  {
151
5.65k
    float lowvalue { 1e10f };
152
5.65k
    float highvalue { -1e10f };
153
154
5.65k
    unsigned int partition_texel_count = pi.partition_texel_count[i];
155
149k
    for (unsigned int j = 0; j < partition_texel_count; j++)
156
143k
    {
157
143k
      unsigned int tix = pi.texels_of_partition[i][j];
158
143k
      float value = data_vr[tix];
159
143k
      lowvalue = astc::min(value, lowvalue);
160
143k
      highvalue = astc::max(value, highvalue);
161
143k
    }
162
163
5.65k
    if (highvalue <= lowvalue)
164
0
    {
165
0
      lowvalue = 0.0f;
166
0
      highvalue = 1e-7f;
167
0
    }
168
169
5.65k
    float length = highvalue - lowvalue;
170
5.65k
    float length_squared = length * length;
171
5.65k
    float scale = 1.0f / length;
172
173
5.65k
    if (i == 0)
174
5.65k
    {
175
5.65k
      partition0_len_sq = length_squared;
176
5.65k
    }
177
0
    else
178
0
    {
179
0
      is_constant_wes = is_constant_wes && length_squared == partition0_len_sq;
180
0
    }
181
182
149k
    for (unsigned int j = 0; j < partition_texel_count; j++)
183
143k
    {
184
143k
      unsigned int tix = pi.texels_of_partition[i][j];
185
143k
      float value = (data_vr[tix] - lowvalue) * scale;
186
143k
      value = astc::clamp1f(value);
187
188
143k
      ei.weights[tix] = value;
189
143k
      ei.weight_error_scale[tix] = length_squared * error_weight;
190
143k
      assert(!astc::isnan(ei.weight_error_scale[tix]));
191
143k
    }
192
193
5.65k
    ei.ep.endpt0[i] = select(blk.data_min, vfloat4(lowvalue), sep_mask);
194
5.65k
    ei.ep.endpt1[i] = select(blk.data_max, vfloat4(highvalue), sep_mask);
195
5.65k
  }
196
197
  // Zero initialize any SIMD over-fetch
198
5.65k
  size_t texel_count_simd = round_up_to_simd_multiple_vla(texel_count);
199
7.40k
  for (size_t i = texel_count; i < texel_count_simd; i++)
200
1.75k
  {
201
1.75k
    ei.weights[i] = 0.0f;
202
1.75k
    ei.weight_error_scale[i] = 0.0f;
203
1.75k
  }
204
205
5.65k
  ei.is_constant_weight_error_scale = is_constant_wes;
206
5.65k
}
207
208
/**
209
 * @brief Compute the ideal endpoints and weights for 2 color components.
210
 *
211
 * @param      blk          The image block color data to compress.
212
 * @param      pi           The partition info for the current trial.
213
 * @param[out] ei           The computed ideal endpoints and weights.
214
 * @param      component1   The first color component to compute.
215
 * @param      component2   The second color component to compute.
216
 */
217
static void compute_ideal_colors_and_weights_2_comp(
218
  const image_block& blk,
219
  const partition_info& pi,
220
  endpoints_and_weights& ei,
221
  int component1,
222
  int component2
223
689
) {
224
689
  unsigned int partition_count = pi.partition_count;
225
689
  ei.ep.partition_count = partition_count;
226
689
  promise(partition_count > 0);
227
228
689
  unsigned int texel_count = blk.texel_count;
229
689
  promise(texel_count > 0);
230
231
689
  partition_metrics pms[BLOCK_MAX_PARTITIONS];
232
233
689
  float error_weight;
234
689
  const float* data_vr = nullptr;
235
689
  const float* data_vg = nullptr;
236
237
689
  if (component1 == 0 && component2 == 1)
238
263
  {
239
263
    error_weight = hadd_s(blk.channel_weight.swz<0, 1>()) / 2.0f;
240
241
263
    data_vr = blk.data_r;
242
263
    data_vg = blk.data_g;
243
263
  }
244
426
  else if (component1 == 0 && component2 == 2)
245
211
  {
246
211
    error_weight = hadd_s(blk.channel_weight.swz<0, 2>()) / 2.0f;
247
248
211
    data_vr = blk.data_r;
249
211
    data_vg = blk.data_b;
250
211
  }
251
215
  else // (component1 == 1 && component2 == 2)
252
215
  {
253
215
    assert(component1 == 1 && component2 == 2);
254
255
215
    error_weight = hadd_s(blk.channel_weight.swz<1, 2>()) / 2.0f;
256
257
215
    data_vr = blk.data_g;
258
215
    data_vg = blk.data_b;
259
215
  }
260
261
689
  compute_avgs_and_dirs_2_comp(pi, blk, component1, component2, pms);
262
263
689
  bool is_constant_wes { true };
264
689
  float partition0_len_sq { 0.0f };
265
266
689
  vmask4 comp1_mask = vint4::lane_id() == vint4(component1);
267
689
  vmask4 comp2_mask = vint4::lane_id() == vint4(component2);
268
269
1.37k
  for (unsigned int i = 0; i < partition_count; i++)
270
689
  {
271
689
    vfloat4 dir = pms[i].dir;
272
689
    if (hadd_s(dir) < 0.0f)
273
10
    {
274
10
      dir = vfloat4::zero() - dir;
275
10
    }
276
277
689
    line2 line { pms[i].avg, normalize_safe(dir, unit2()) };
278
689
    float lowparam { 1e10f };
279
689
    float highparam { -1e10f };
280
281
689
    unsigned int partition_texel_count = pi.partition_texel_count[i];
282
14.7k
    for (unsigned int j = 0; j < partition_texel_count; j++)
283
14.0k
    {
284
14.0k
      unsigned int tix = pi.texels_of_partition[i][j];
285
14.0k
      vfloat4 point = vfloat2(data_vr[tix], data_vg[tix]);
286
14.0k
      float param = dot_s(point - line.a, line.b);
287
14.0k
      ei.weights[tix] = param;
288
289
14.0k
      lowparam = astc::min(param, lowparam);
290
14.0k
      highparam = astc::max(param, highparam);
291
14.0k
    }
292
293
    // It is possible for a uniform-color partition to produce length=0;
294
    // this causes NaN issues so set to small value to avoid this problem
295
689
    if (highparam <= lowparam)
296
14
    {
297
14
      lowparam = 0.0f;
298
14
      highparam = 1e-7f;
299
14
    }
300
301
689
    float length = highparam - lowparam;
302
689
    float length_squared = length * length;
303
689
    float scale = 1.0f / length;
304
305
689
    if (i == 0)
306
689
    {
307
689
      partition0_len_sq = length_squared;
308
689
    }
309
0
    else
310
0
    {
311
0
      is_constant_wes = is_constant_wes && length_squared == partition0_len_sq;
312
0
    }
313
314
14.7k
    for (unsigned int j = 0; j < partition_texel_count; j++)
315
14.0k
    {
316
14.0k
      unsigned int tix = pi.texels_of_partition[i][j];
317
14.0k
      float idx = (ei.weights[tix] - lowparam) * scale;
318
14.0k
      idx = astc::clamp1f(idx);
319
320
14.0k
      ei.weights[tix] = idx;
321
14.0k
      ei.weight_error_scale[tix] = length_squared * error_weight;
322
14.0k
      assert(!astc::isnan(ei.weight_error_scale[tix]));
323
14.0k
    }
324
325
689
    vfloat4 lowvalue = line.a + line.b * lowparam;
326
689
    vfloat4 highvalue = line.a + line.b * highparam;
327
328
689
    vfloat4 ep0 = select(blk.data_min, vfloat4(lowvalue.lane<0>()), comp1_mask);
329
689
    vfloat4 ep1 = select(blk.data_max, vfloat4(highvalue.lane<0>()), comp1_mask);
330
331
689
    ei.ep.endpt0[i] = select(ep0, vfloat4(lowvalue.lane<1>()), comp2_mask);
332
689
    ei.ep.endpt1[i] = select(ep1, vfloat4(highvalue.lane<1>()), comp2_mask);
333
689
  }
334
335
  // Zero initialize any SIMD over-fetch
336
689
  size_t texel_count_simd = round_up_to_simd_multiple_vla(texel_count);
337
816
  for (size_t i = texel_count; i < texel_count_simd; i++)
338
127
  {
339
127
    ei.weights[i] = 0.0f;
340
127
    ei.weight_error_scale[i] = 0.0f;
341
127
  }
342
343
689
  ei.is_constant_weight_error_scale = is_constant_wes;
344
689
}
345
346
/**
347
 * @brief Compute the ideal endpoints and weights for 3 color components.
348
 *
349
 * @param      blk                 The image block color data to compress.
350
 * @param      pi                  The partition info for the current trial.
351
 * @param[out] ei                  The computed ideal endpoints and weights.
352
 * @param      omitted_component   The color component excluded from the calculation.
353
 */
354
static void compute_ideal_colors_and_weights_3_comp(
355
  const image_block& blk,
356
  const partition_info& pi,
357
  endpoints_and_weights& ei,
358
  unsigned int omitted_component
359
6.37k
) {
360
6.37k
  unsigned int partition_count = pi.partition_count;
361
6.37k
  ei.ep.partition_count = partition_count;
362
6.37k
  promise(partition_count > 0);
363
364
6.37k
  unsigned int texel_count = blk.texel_count;
365
6.37k
  promise(texel_count > 0);
366
367
6.37k
  partition_metrics pms[BLOCK_MAX_PARTITIONS];
368
369
6.37k
  float error_weight;
370
6.37k
  const float* data_vr = nullptr;
371
6.37k
  const float* data_vg = nullptr;
372
6.37k
  const float* data_vb = nullptr;
373
6.37k
  if (omitted_component == 0)
374
1.12k
  {
375
1.12k
    error_weight = hadd_s(blk.channel_weight.swz<0, 1, 2>());
376
1.12k
    data_vr = blk.data_g;
377
1.12k
    data_vg = blk.data_b;
378
1.12k
    data_vb = blk.data_a;
379
1.12k
  }
380
5.24k
  else if (omitted_component == 1)
381
1.15k
  {
382
1.15k
    error_weight = hadd_s(blk.channel_weight.swz<0, 2, 3>());
383
1.15k
    data_vr = blk.data_r;
384
1.15k
    data_vg = blk.data_b;
385
1.15k
    data_vb = blk.data_a;
386
1.15k
  }
387
4.09k
  else if (omitted_component == 2)
388
1.21k
  {
389
1.21k
    error_weight = hadd_s(blk.channel_weight.swz<0, 1, 3>());
390
1.21k
    data_vr = blk.data_r;
391
1.21k
    data_vg = blk.data_g;
392
1.21k
    data_vb = blk.data_a;
393
1.21k
  }
394
2.87k
  else
395
2.87k
  {
396
2.87k
    assert(omitted_component == 3);
397
398
2.87k
    error_weight = hadd_s(blk.channel_weight.swz<0, 1, 2>());
399
2.87k
    data_vr = blk.data_r;
400
2.87k
    data_vg = blk.data_g;
401
2.87k
    data_vb = blk.data_b;
402
2.87k
  }
403
404
6.37k
  error_weight = error_weight * (1.0f / 3.0f);
405
406
6.37k
  if (omitted_component == 3)
407
2.87k
  {
408
2.87k
    compute_avgs_and_dirs_3_comp_rgb(pi, blk, pms);
409
2.87k
  }
410
3.49k
  else
411
3.49k
  {
412
3.49k
    compute_avgs_and_dirs_3_comp(pi, blk, omitted_component, pms);
413
3.49k
  }
414
415
6.37k
  bool is_constant_wes { true };
416
6.37k
  float partition0_len_sq { 0.0f };
417
418
14.5k
  for (unsigned int i = 0; i < partition_count; i++)
419
8.19k
  {
420
8.19k
    vfloat4 dir = pms[i].dir;
421
8.19k
    if (hadd_rgb_s(dir) < 0.0f)
422
237
    {
423
237
      dir = vfloat4::zero() - dir;
424
237
    }
425
426
8.19k
    line3 line { pms[i].avg, normalize_safe(dir, unit3()) };
427
8.19k
    float lowparam { 1e10f };
428
8.19k
    float highparam { -1e10f };
429
430
8.19k
    unsigned int partition_texel_count = pi.partition_texel_count[i];
431
168k
    for (unsigned int j = 0; j < partition_texel_count; j++)
432
160k
    {
433
160k
      unsigned int tix = pi.texels_of_partition[i][j];
434
160k
      vfloat4 point = vfloat3(data_vr[tix], data_vg[tix], data_vb[tix]);
435
160k
      float param = dot3_s(point - line.a, line.b);
436
160k
      ei.weights[tix] = param;
437
438
160k
      lowparam = astc::min(param, lowparam);
439
160k
      highparam = astc::max(param, highparam);
440
160k
    }
441
442
    // It is possible for a uniform-color partition to produce length=0;
443
    // this causes NaN issues so set to small value to avoid this problem
444
8.19k
    if (highparam <= lowparam)
445
465
    {
446
465
      lowparam = 0.0f;
447
465
      highparam = 1e-7f;
448
465
    }
449
450
8.19k
    float length = highparam - lowparam;
451
8.19k
    float length_squared = length * length;
452
8.19k
    float scale = 1.0f / length;
453
454
8.19k
    if (i == 0)
455
6.37k
    {
456
6.37k
      partition0_len_sq = length_squared;
457
6.37k
    }
458
1.82k
    else
459
1.82k
    {
460
1.82k
      is_constant_wes = is_constant_wes && length_squared == partition0_len_sq;
461
1.82k
    }
462
463
168k
    for (unsigned int j = 0; j < partition_texel_count; j++)
464
160k
    {
465
160k
      unsigned int tix = pi.texels_of_partition[i][j];
466
160k
      float idx = (ei.weights[tix] - lowparam) * scale;
467
160k
      idx = astc::clamp1f(idx);
468
469
160k
      ei.weights[tix] = idx;
470
160k
      ei.weight_error_scale[tix] = length_squared * error_weight;
471
160k
      assert(!astc::isnan(ei.weight_error_scale[tix]));
472
160k
    }
473
474
8.19k
    vfloat4 ep0 = line.a + line.b * lowparam;
475
8.19k
    vfloat4 ep1 = line.a + line.b * highparam;
476
477
8.19k
    vfloat4 bmin = blk.data_min;
478
8.19k
    vfloat4 bmax = blk.data_max;
479
480
8.19k
    assert(omitted_component < BLOCK_MAX_COMPONENTS);
481
8.19k
    switch (omitted_component)
482
8.19k
    {
483
1.12k
      case 0:
484
1.12k
        ei.ep.endpt0[i] = vfloat4(bmin.lane<0>(), ep0.lane<0>(), ep0.lane<1>(), ep0.lane<2>());
485
1.12k
        ei.ep.endpt1[i] = vfloat4(bmax.lane<0>(), ep1.lane<0>(), ep1.lane<1>(), ep1.lane<2>());
486
1.12k
        break;
487
1.15k
      case 1:
488
1.15k
        ei.ep.endpt0[i] = vfloat4(ep0.lane<0>(), bmin.lane<1>(), ep0.lane<1>(), ep0.lane<2>());
489
1.15k
        ei.ep.endpt1[i] = vfloat4(ep1.lane<0>(), bmax.lane<1>(), ep1.lane<1>(), ep1.lane<2>());
490
1.15k
        break;
491
1.21k
      case 2:
492
1.21k
        ei.ep.endpt0[i] = vfloat4(ep0.lane<0>(), ep0.lane<1>(), bmin.lane<2>(), ep0.lane<2>());
493
1.21k
        ei.ep.endpt1[i] = vfloat4(ep1.lane<0>(), ep1.lane<1>(), bmax.lane<2>(), ep1.lane<2>());
494
1.21k
        break;
495
4.70k
      default:
496
4.70k
        ei.ep.endpt0[i] = vfloat4(ep0.lane<0>(), ep0.lane<1>(), ep0.lane<2>(), bmin.lane<3>());
497
4.70k
        ei.ep.endpt1[i] = vfloat4(ep1.lane<0>(), ep1.lane<1>(), ep1.lane<2>(), bmax.lane<3>());
498
4.70k
        break;
499
8.19k
    }
500
8.19k
  }
501
502
  // Zero initialize any SIMD over-fetch
503
6.37k
  size_t texel_count_simd = round_up_to_simd_multiple_vla(texel_count);
504
8.33k
  for (size_t i = texel_count; i < texel_count_simd; i++)
505
1.96k
  {
506
1.96k
    ei.weights[i] = 0.0f;
507
1.96k
    ei.weight_error_scale[i] = 0.0f;
508
1.96k
  }
509
510
6.37k
  ei.is_constant_weight_error_scale = is_constant_wes;
511
6.37k
}
512
513
/**
514
 * @brief Compute the ideal endpoints and weights for 4 color components.
515
 *
516
 * @param      blk   The image block color data to compress.
517
 * @param      pi    The partition info for the current trial.
518
 * @param[out] ei    The computed ideal endpoints and weights.
519
 */
520
static void compute_ideal_colors_and_weights_4_comp(
521
  const image_block& blk,
522
  const partition_info& pi,
523
  endpoints_and_weights& ei
524
7.77k
) {
525
7.77k
  const float error_weight = hadd_s(blk.channel_weight) / 4.0f;
526
527
7.77k
  unsigned int partition_count = pi.partition_count;
528
529
7.77k
  unsigned int texel_count = blk.texel_count;
530
7.77k
  promise(texel_count > 0);
531
7.77k
  promise(partition_count > 0);
532
533
7.77k
  partition_metrics pms[BLOCK_MAX_PARTITIONS];
534
535
7.77k
  compute_avgs_and_dirs_4_comp(pi, blk, pms);
536
537
7.77k
  bool is_constant_wes { true };
538
7.77k
  float partition0_len_sq { 0.0f };
539
540
25.3k
  for (unsigned int i = 0; i < partition_count; i++)
541
17.6k
  {
542
17.6k
    vfloat4 dir = pms[i].dir;
543
17.6k
    if (hadd_rgb_s(dir) < 0.0f)
544
2.00k
    {
545
2.00k
      dir = vfloat4::zero() - dir;
546
2.00k
    }
547
548
17.6k
    line4 line { pms[i].avg, normalize_safe(dir, unit4()) };
549
17.6k
    float lowparam { 1e10f };
550
17.6k
    float highparam { -1e10f };
551
552
17.6k
    unsigned int partition_texel_count = pi.partition_texel_count[i];
553
237k
    for (unsigned int j = 0; j < partition_texel_count; j++)
554
219k
    {
555
219k
      unsigned int tix = pi.texels_of_partition[i][j];
556
219k
      vfloat4 point = blk.texel(tix);
557
219k
      float param = dot_s(point - line.a, line.b);
558
219k
      ei.weights[tix] = param;
559
560
219k
      lowparam = astc::min(param, lowparam);
561
219k
      highparam = astc::max(param, highparam);
562
219k
    }
563
564
    // It is possible for a uniform-color partition to produce length=0;
565
    // this causes NaN issues so set to small value to avoid this problem
566
17.6k
    if (highparam <= lowparam)
567
1.59k
    {
568
1.59k
      lowparam = 0.0f;
569
1.59k
      highparam = 1e-7f;
570
1.59k
    }
571
572
17.6k
    float length = highparam - lowparam;
573
17.6k
    float length_squared = length * length;
574
17.6k
    float scale = 1.0f / length;
575
576
17.6k
    if (i == 0)
577
7.77k
    {
578
7.77k
      partition0_len_sq = length_squared;
579
7.77k
    }
580
9.85k
    else
581
9.85k
    {
582
9.85k
      is_constant_wes = is_constant_wes && length_squared == partition0_len_sq;
583
9.85k
    }
584
585
17.6k
    ei.ep.endpt0[i] = line.a + line.b * lowparam;
586
17.6k
    ei.ep.endpt1[i] = line.a + line.b * highparam;
587
588
237k
    for (unsigned int j = 0; j < partition_texel_count; j++)
589
219k
    {
590
219k
      unsigned int tix = pi.texels_of_partition[i][j];
591
219k
      float idx = (ei.weights[tix] - lowparam) * scale;
592
219k
      idx = astc::clamp1f(idx);
593
594
219k
      ei.weights[tix] = idx;
595
219k
      ei.weight_error_scale[tix] = length_squared * error_weight;
596
219k
      assert(!astc::isnan(ei.weight_error_scale[tix]));
597
219k
    }
598
17.6k
  }
599
600
  // Zero initialize any SIMD over-fetch
601
7.77k
  size_t texel_count_simd = round_up_to_simd_multiple_vla(texel_count);
602
10.6k
  for (size_t i = texel_count; i < texel_count_simd; i++)
603
2.83k
  {
604
2.83k
    ei.weights[i] = 0.0f;
605
2.83k
    ei.weight_error_scale[i] = 0.0f;
606
2.83k
  }
607
608
7.77k
  ei.is_constant_weight_error_scale = is_constant_wes;
609
7.77k
}
610
611
/* See header for documentation. */
612
void compute_ideal_colors_and_weights_1plane(
613
  const image_block& blk,
614
  const partition_info& pi,
615
  endpoints_and_weights& ei
616
9.18k
) {
617
9.18k
  bool uses_alpha = !blk.is_constant_channel(3);
618
619
9.18k
  if (uses_alpha)
620
7.77k
  {
621
7.77k
    compute_ideal_colors_and_weights_4_comp(blk, pi, ei);
622
7.77k
  }
623
1.41k
  else
624
1.41k
  {
625
1.41k
    compute_ideal_colors_and_weights_3_comp(blk, pi, ei, 3);
626
1.41k
  }
627
9.18k
}
628
629
/* See header for documentation. */
630
void compute_ideal_colors_and_weights_2planes(
631
  const block_size_descriptor& bsd,
632
  const image_block& blk,
633
  unsigned int plane2_component,
634
  endpoints_and_weights& ei1,
635
  endpoints_and_weights& ei2
636
5.65k
) {
637
5.65k
  const auto& pi = bsd.get_partition_info(1, 0);
638
5.65k
  bool uses_alpha = !blk.is_constant_channel(3);
639
640
5.65k
  assert(plane2_component < BLOCK_MAX_COMPONENTS);
641
5.65k
  switch (plane2_component)
642
5.65k
  {
643
1.34k
  case 0: // Separate weights for red
644
1.34k
    if (uses_alpha)
645
1.12k
    {
646
1.12k
      compute_ideal_colors_and_weights_3_comp(blk, pi, ei1, 0);
647
1.12k
    }
648
215
    else
649
215
    {
650
215
      compute_ideal_colors_and_weights_2_comp(blk, pi, ei1, 1, 2);
651
215
    }
652
1.34k
    compute_ideal_colors_and_weights_1_comp(blk, pi, ei2, 0);
653
1.34k
    break;
654
655
1.36k
  case 1: // Separate weights for green
656
1.36k
    if (uses_alpha)
657
1.15k
    {
658
1.15k
      compute_ideal_colors_and_weights_3_comp(blk, pi, ei1, 1);
659
1.15k
    }
660
211
    else
661
211
    {
662
211
      compute_ideal_colors_and_weights_2_comp(blk, pi, ei1, 0, 2);
663
211
    }
664
1.36k
    compute_ideal_colors_and_weights_1_comp(blk, pi, ei2, 1);
665
1.36k
    break;
666
667
1.48k
  case 2: // Separate weights for blue
668
1.48k
    if (uses_alpha)
669
1.21k
    {
670
1.21k
      compute_ideal_colors_and_weights_3_comp(blk, pi, ei1, 2);
671
1.21k
    }
672
263
    else
673
263
    {
674
263
      compute_ideal_colors_and_weights_2_comp(blk, pi, ei1, 0, 1);
675
263
    }
676
1.48k
    compute_ideal_colors_and_weights_1_comp(blk, pi, ei2, 2);
677
1.48k
    break;
678
679
1.46k
  default: // Separate weights for alpha
680
1.46k
    assert(uses_alpha);
681
1.46k
    compute_ideal_colors_and_weights_3_comp(blk, pi, ei1, 3);
682
1.46k
    compute_ideal_colors_and_weights_1_comp(blk, pi, ei2, 3);
683
1.46k
    break;
684
5.65k
  }
685
5.65k
}
686
687
/* See header for documentation. */
688
float compute_error_of_weight_set_1plane(
689
  const endpoints_and_weights& eai,
690
  const decimation_info& di,
691
  const float* dec_weight_quant_uvalue
692
157k
) {
693
157k
  vfloatacc error_summav = vfloatacc::zero();
694
157k
  unsigned int texel_count = di.texel_count;
695
157k
  promise(texel_count > 0);
696
697
  // Process SIMD-width chunks, safe to over-fetch - the extra space is zero initialized
698
157k
  if (di.max_texel_weight_count > 2)
699
39.6k
  {
700
887k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
701
847k
    {
702
      // Compute the bilinear interpolation of the decimated weight grid
703
847k
      vfloat current_values = bilinear_infill_vla(di, dec_weight_quant_uvalue, i);
704
705
      // Compute the error between the computed value and the ideal weight
706
847k
      vfloat actual_values = loada(eai.weights + i);
707
847k
      vfloat diff = current_values - actual_values;
708
847k
      vfloat significance = loada(eai.weight_error_scale + i);
709
847k
      vfloat error = diff * diff * significance;
710
711
847k
      haccumulate(error_summav, error);
712
847k
    }
713
39.6k
  }
714
118k
  else if (di.max_texel_weight_count > 1)
715
53.7k
  {
716
663k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
717
609k
    {
718
      // Compute the bilinear interpolation of the decimated weight grid
719
609k
      vfloat current_values = bilinear_infill_vla_2(di, dec_weight_quant_uvalue, i);
720
721
      // Compute the error between the computed value and the ideal weight
722
609k
      vfloat actual_values = loada(eai.weights + i);
723
609k
      vfloat diff = current_values - actual_values;
724
609k
      vfloat significance = loada(eai.weight_error_scale + i);
725
609k
      vfloat error = diff * diff * significance;
726
727
609k
      haccumulate(error_summav, error);
728
609k
    }
729
53.7k
  }
730
64.3k
  else
731
64.3k
  {
732
366k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
733
302k
    {
734
      // Load the weight set directly, without interpolation
735
302k
      vfloat current_values = loada(dec_weight_quant_uvalue + i);
736
737
      // Compute the error between the computed value and the ideal weight
738
302k
      vfloat actual_values = loada(eai.weights + i);
739
302k
      vfloat diff = current_values - actual_values;
740
302k
      vfloat significance = loada(eai.weight_error_scale + i);
741
302k
      vfloat error = diff * diff * significance;
742
743
302k
      haccumulate(error_summav, error);
744
302k
    }
745
64.3k
  }
746
747
  // Resolve the final scalar accumulator sum
748
157k
  return hadd_s(error_summav);
749
157k
}
750
751
/* See header for documentation. */
752
float compute_error_of_weight_set_2planes(
753
  const endpoints_and_weights& eai1,
754
  const endpoints_and_weights& eai2,
755
  const decimation_info& di,
756
  const float* dec_weight_quant_uvalue_plane1,
757
  const float* dec_weight_quant_uvalue_plane2
758
42.6k
) {
759
42.6k
  vfloatacc error_summav = vfloatacc::zero();
760
42.6k
  unsigned int texel_count = di.texel_count;
761
42.6k
  promise(texel_count > 0);
762
763
  // Process SIMD-width chunks, safe to over-fetch - the extra space is zero initialized
764
42.6k
  if (di.max_texel_weight_count > 2)
765
19.1k
  {
766
260k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
767
240k
    {
768
      // Plane 1
769
      // Compute the bilinear interpolation of the decimated weight grid
770
240k
      vfloat current_values1 = bilinear_infill_vla(di, dec_weight_quant_uvalue_plane1, i);
771
772
      // Compute the error between the computed value and the ideal weight
773
240k
      vfloat actual_values1 = loada(eai1.weights + i);
774
240k
      vfloat diff = current_values1 - actual_values1;
775
240k
      vfloat error1 = diff * diff * loada(eai1.weight_error_scale + i);
776
777
      // Plane 2
778
      // Compute the bilinear interpolation of the decimated weight grid
779
240k
      vfloat current_values2 = bilinear_infill_vla(di, dec_weight_quant_uvalue_plane2, i);
780
781
      // Compute the error between the computed value and the ideal weight
782
240k
      vfloat actual_values2 = loada(eai2.weights + i);
783
240k
      diff = current_values2 - actual_values2;
784
240k
      vfloat error2 = diff * diff * loada(eai2.weight_error_scale + i);
785
786
240k
      haccumulate(error_summav, error1 + error2);
787
240k
    }
788
19.1k
  }
789
23.5k
  else if (di.max_texel_weight_count > 1)
790
15.2k
  {
791
91.0k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
792
75.8k
    {
793
      // Plane 1
794
      // Compute the bilinear interpolation of the decimated weight grid
795
75.8k
      vfloat current_values1 = bilinear_infill_vla_2(di, dec_weight_quant_uvalue_plane1, i);
796
797
      // Compute the error between the computed value and the ideal weight
798
75.8k
      vfloat actual_values1 = loada(eai1.weights + i);
799
75.8k
      vfloat diff = current_values1 - actual_values1;
800
75.8k
      vfloat error1 = diff * diff * loada(eai1.weight_error_scale + i);
801
802
      // Plane 2
803
      // Compute the bilinear interpolation of the decimated weight grid
804
75.8k
      vfloat current_values2 = bilinear_infill_vla_2(di, dec_weight_quant_uvalue_plane2, i);
805
806
      // Compute the error between the computed value and the ideal weight
807
75.8k
      vfloat actual_values2 = loada(eai2.weights + i);
808
75.8k
      diff = current_values2 - actual_values2;
809
75.8k
      vfloat error2 = diff * diff * loada(eai2.weight_error_scale + i);
810
811
75.8k
      haccumulate(error_summav, error1 + error2);
812
75.8k
    }
813
15.2k
  }
814
8.35k
  else
815
8.35k
  {
816
42.9k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
817
34.5k
    {
818
      // Plane 1
819
      // Load the weight set directly, without interpolation
820
34.5k
      vfloat current_values1 = loada(dec_weight_quant_uvalue_plane1 + i);
821
822
      // Compute the error between the computed value and the ideal weight
823
34.5k
      vfloat actual_values1 = loada(eai1.weights + i);
824
34.5k
      vfloat diff = current_values1 - actual_values1;
825
34.5k
      vfloat error1 = diff * diff * loada(eai1.weight_error_scale + i);
826
827
      // Plane 2
828
      // Load the weight set directly, without interpolation
829
34.5k
      vfloat current_values2 = loada(dec_weight_quant_uvalue_plane2 + i);
830
831
      // Compute the error between the computed value and the ideal weight
832
34.5k
      vfloat actual_values2 = loada(eai2.weights + i);
833
34.5k
      diff = current_values2 - actual_values2;
834
34.5k
      vfloat error2 = diff * diff * loada(eai2.weight_error_scale + i);
835
836
34.5k
      haccumulate(error_summav, error1 + error2);
837
34.5k
    }
838
8.35k
  }
839
840
  // Resolve the final scalar accumulator sum
841
42.6k
  return hadd_s(error_summav);
842
42.6k
}
843
844
/* See header for documentation. */
845
void compute_ideal_weights_for_decimation(
846
  const endpoints_and_weights& ei,
847
  const decimation_info& di,
848
  float* dec_weight_ideal_value
849
127k
) {
850
127k
  unsigned int texel_count = di.texel_count;
851
127k
  unsigned int weight_count = di.weight_count;
852
127k
  bool is_direct = texel_count == weight_count;
853
127k
  promise(texel_count > 0);
854
127k
  promise(weight_count > 0);
855
856
  // If we have a 1:1 mapping just shortcut the computation. Transfer enough to also copy the
857
  // zero-initialized SIMD over-fetch region
858
127k
  if (is_direct)
859
17.4k
  {
860
103k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
861
85.6k
    {
862
85.6k
      vfloat weight(ei.weights + i);
863
85.6k
      storea(weight, dec_weight_ideal_value + i);
864
85.6k
    }
865
866
17.4k
    return;
867
17.4k
  }
868
869
  // Otherwise compute an estimate and perform single refinement iteration
870
871
  // Compute an initial average for each decimated weight
872
110k
  bool constant_wes = ei.is_constant_weight_error_scale;
873
110k
  vfloat weight_error_scale(ei.weight_error_scale[0]);
874
875
  // This overshoots - this is OK as we initialize the array tails in the
876
  // decimation table structures to safe values ...
877
724k
  for (unsigned int i = 0; i < weight_count; i += ASTCENC_SIMD_WIDTH)
878
614k
  {
879
    // Start with a small value to avoid div-by-zero later
880
614k
    vfloat weight_weight(1e-10f);
881
614k
    vfloat initial_weight = vfloat::zero();
882
883
    // Accumulate error weighting of all the texels using this weight
884
614k
    vint weight_texel_count(di.weight_texel_count + i);
885
614k
    unsigned int max_texel_count = hmax_s(weight_texel_count);
886
614k
    promise(max_texel_count > 0);
887
888
5.07M
    for (unsigned int j = 0; j < max_texel_count; j++)
889
4.46M
    {
890
4.46M
      const uint8_t* texel = di.weight_texels_tr[j] + i;
891
4.46M
      vfloat weight = loada(di.weights_texel_contribs_tr[j] + i);
892
893
4.46M
      if (!constant_wes)
894
1.80M
      {
895
1.80M
        weight_error_scale = gatherf_byte_inds<vfloat>(ei.weight_error_scale, texel);
896
1.80M
      }
897
898
4.46M
      vfloat contrib_weight = weight * weight_error_scale;
899
900
4.46M
      weight_weight += contrib_weight;
901
4.46M
      initial_weight += gatherf_byte_inds<vfloat>(ei.weights, texel) * contrib_weight;
902
4.46M
    }
903
904
614k
    storea(initial_weight / weight_weight, dec_weight_ideal_value + i);
905
614k
  }
906
907
  // Populate the interpolated weight grid based on the initial average
908
  // Process SIMD-width texel coordinates at at time while we can. Safe to
909
  // over-process full SIMD vectors - the tail is zeroed.
910
110k
  ASTCENC_ALIGNAS float infilled_weights[BLOCK_MAX_TEXELS];
911
110k
  if (di.max_texel_weight_count <= 2)
912
46.6k
  {
913
469k
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
914
422k
    {
915
422k
      vfloat weight = bilinear_infill_vla_2(di, dec_weight_ideal_value, i);
916
422k
      storea(weight, infilled_weights + i);
917
422k
    }
918
46.6k
  }
919
63.7k
  else
920
63.7k
  {
921
1.09M
    for (unsigned int i = 0; i < texel_count; i += ASTCENC_SIMD_WIDTH)
922
1.02M
    {
923
1.02M
      vfloat weight = bilinear_infill_vla(di, dec_weight_ideal_value, i);
924
1.02M
      storea(weight, infilled_weights + i);
925
1.02M
    }
926
63.7k
  }
927
928
  // Perform a single iteration of refinement
929
  // Empirically determined step size; larger values don't help but smaller drops image quality
930
110k
  constexpr float stepsize = 0.25f;
931
110k
  constexpr float chd_scale = -WEIGHTS_TEXEL_SUM;
932
933
724k
  for (unsigned int i = 0; i < weight_count; i += ASTCENC_SIMD_WIDTH)
934
614k
  {
935
614k
    vfloat weight_val = loada(dec_weight_ideal_value + i);
936
937
    // Accumulate error weighting of all the texels using this weight
938
    // Start with a small value to avoid div-by-zero later
939
614k
    vfloat error_change0(1e-10f);
940
614k
    vfloat error_change1(0.0f);
941
942
    // Accumulate error weighting of all the texels using this weight
943
614k
    vint weight_texel_count(di.weight_texel_count + i);
944
614k
    unsigned int max_texel_count = hmax_s(weight_texel_count);
945
614k
    promise(max_texel_count > 0);
946
947
5.07M
    for (unsigned int j = 0; j < max_texel_count; j++)
948
4.46M
    {
949
4.46M
      const uint8_t* texel = di.weight_texels_tr[j] + i;
950
4.46M
      vfloat contrib_weight = loada(di.weights_texel_contribs_tr[j] + i);
951
952
4.46M
      if (!constant_wes)
953
1.80M
      {
954
1.80M
        weight_error_scale = gatherf_byte_inds<vfloat>(ei.weight_error_scale, texel);
955
1.80M
      }
956
957
4.46M
      vfloat scale = weight_error_scale * contrib_weight;
958
4.46M
      vfloat old_weight = gatherf_byte_inds<vfloat>(infilled_weights, texel);
959
4.46M
      vfloat ideal_weight = gatherf_byte_inds<vfloat>(ei.weights, texel);
960
961
4.46M
      error_change0 += contrib_weight * scale;
962
4.46M
      error_change1 += (old_weight - ideal_weight) * scale;
963
4.46M
    }
964
965
614k
    vfloat step = (error_change1 * chd_scale) / error_change0;
966
614k
    step = clamp(-stepsize, stepsize, step);
967
968
    // Update the weight; note this can store negative values
969
614k
    storea(weight_val + step, dec_weight_ideal_value + i);
970
614k
  }
971
110k
}
972
973
/* See header for documentation. */
974
void compute_quantized_weights_for_decimation(
975
  const decimation_info& di,
976
  float low_bound,
977
  float high_bound,
978
  const float* dec_weight_ideal_value,
979
  float* weight_set_out,
980
  uint8_t* quantized_weight_set,
981
  quant_method quant_level
982
243k
) {
983
243k
  int weight_count = di.weight_count;
984
243k
  promise(weight_count > 0);
985
243k
  const quant_and_transfer_table& qat = quant_and_xfer_tables[quant_level];
986
987
  // The available quant levels, stored with a minus 1 bias
988
243k
  static const float quant_levels_m1[12] {
989
243k
    1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 9.0f, 11.0f, 15.0f, 19.0f, 23.0f, 31.0f
990
243k
  };
991
992
243k
  vint steps_m1(get_quant_level(quant_level) - 1);
993
243k
  float quant_level_m1 = quant_levels_m1[quant_level];
994
995
  // Quantize the weight set using both the specified low/high bounds and standard 0..1 bounds
996
997
  // TODO: Oddity to investigate; triggered by test in issue #265.
998
243k
  if (high_bound <= low_bound)
999
1
  {
1000
1
    low_bound = 0.0f;
1001
1
    high_bound = 1.0f;
1002
1
  }
1003
1004
243k
  float rscale = high_bound - low_bound;
1005
243k
  float scale = 1.0f / rscale;
1006
1007
243k
  float scaled_low_bound = low_bound * scale;
1008
243k
  rscale *= 1.0f / 64.0f;
1009
1010
243k
  vfloat scalev(scale);
1011
243k
  vfloat scaled_low_boundv(scaled_low_bound);
1012
243k
  vfloat quant_level_m1v(quant_level_m1);
1013
243k
  vfloat rscalev(rscale);
1014
243k
  vfloat low_boundv(low_bound);
1015
1016
  // This runs to the rounded-up SIMD size, which is safe as the loop tail is filled with known
1017
  // safe data in compute_ideal_weights_for_decimation and arrays are always 64 elements
1018
243k
  if (get_quant_level(quant_level) <= 16)
1019
221k
  {
1020
221k
    vtable_16x8 table;
1021
221k
    vtable_prepare(table, qat.quant_to_unquant);
1022
1023
1.47M
    for (int i = 0; i < weight_count; i += ASTCENC_SIMD_WIDTH)
1024
1.25M
    {
1025
1.25M
      vfloat ix = loada(dec_weight_ideal_value + i) * scalev - scaled_low_boundv;
1026
1.25M
      ix = clampzo(ix);
1027
1028
      // Look up the two closest indexes and return the one that was closest
1029
1.25M
      vfloat ix1 = ix * quant_level_m1v;
1030
1031
1.25M
      vint weightl = float_to_int(ix1);
1032
1.25M
      vint weighth = min(weightl + vint(1), steps_m1);
1033
1034
1.25M
      vint ixli = vtable_lookup_32bit(table, weightl);
1035
1.25M
      vint ixhi = vtable_lookup_32bit(table, weighth);
1036
1037
1.25M
      vfloat ixl = int_to_float(ixli);
1038
1.25M
      vfloat ixh = int_to_float(ixhi);
1039
1040
1.25M
      vmask mask = (ixl + ixh) < (vfloat(128.0f) * ix);
1041
1.25M
      vint weight = select(ixli, ixhi, mask);
1042
1.25M
      ixl = select(ixl, ixh, mask);
1043
1044
      // Invert the weight-scaling that was done initially
1045
1.25M
      storea(ixl * rscalev + low_boundv, weight_set_out + i);
1046
1.25M
      pack_and_store_low_bytes(weight, quantized_weight_set + i);
1047
1.25M
    }
1048
221k
  }
1049
21.8k
  else
1050
21.8k
  {
1051
21.8k
    vtable_32x8 table;
1052
21.8k
    vtable_prepare(table, qat.quant_to_unquant);
1053
1054
102k
    for (int i = 0; i < weight_count; i += ASTCENC_SIMD_WIDTH)
1055
81.0k
    {
1056
81.0k
      vfloat ix = loada(dec_weight_ideal_value + i) * scalev - scaled_low_boundv;
1057
81.0k
      ix = clampzo(ix);
1058
1059
      // Look up the two closest indexes and return the one that was closest
1060
81.0k
      vfloat ix1 = ix * quant_level_m1v;
1061
1062
81.0k
      vint weightl = float_to_int(ix1);
1063
81.0k
      vint weighth = min(weightl + vint(1), steps_m1);
1064
1065
81.0k
      vint ixli = vtable_lookup_32bit(table, weightl);
1066
81.0k
      vint ixhi = vtable_lookup_32bit(table, weighth);
1067
1068
81.0k
      vfloat ixl = int_to_float(ixli);
1069
81.0k
      vfloat ixh = int_to_float(ixhi);
1070
1071
81.0k
      vmask mask = (ixl + ixh) < (vfloat(128.0f) * ix);
1072
81.0k
      vint weight = select(ixli, ixhi, mask);
1073
81.0k
      ixl = select(ixl, ixh, mask);
1074
1075
      // Invert the weight-scaling that was done initially
1076
81.0k
      storea(ixl * rscalev + low_boundv, weight_set_out + i);
1077
81.0k
      pack_and_store_low_bytes(weight, quantized_weight_set + i);
1078
81.0k
    }
1079
21.8k
  }
1080
243k
}
1081
1082
/**
1083
 * @brief Compute the RGB + offset for a HDR endpoint mode #7.
1084
 *
1085
 * Since the matrix needed has a regular structure we can simplify the inverse calculation. This
1086
 * gives us ~24 multiplications vs. 96 for a generic inverse.
1087
 *
1088
 *  mat[0] = vfloat4(rgba_ws.x,      0.0f,      0.0f, wght_ws.x);
1089
 *  mat[1] = vfloat4(     0.0f, rgba_ws.y,      0.0f, wght_ws.y);
1090
 *  mat[2] = vfloat4(     0.0f,      0.0f, rgba_ws.z, wght_ws.z);
1091
 *  mat[3] = vfloat4(wght_ws.x, wght_ws.y, wght_ws.z,      psum);
1092
 *  mat = invert(mat);
1093
 *
1094
 * @param rgba_weight_sum     Sum of partition component error weights.
1095
 * @param weight_weight_sum   Sum of partition component error weights * texel weight.
1096
 * @param rgbq_sum            Sum of partition component error weights * texel weight * color data.
1097
 * @param psum                Sum of RGB color weights * texel weight^2.
1098
 */
1099
static inline vfloat4 compute_rgbo_vector(
1100
  vfloat4 rgba_weight_sum,
1101
  vfloat4 weight_weight_sum,
1102
  vfloat4 rgbq_sum,
1103
  float psum
1104
53.3k
) {
1105
53.3k
  float X = rgba_weight_sum.lane<0>();
1106
53.3k
  float Y = rgba_weight_sum.lane<1>();
1107
53.3k
  float Z = rgba_weight_sum.lane<2>();
1108
53.3k
  float P = weight_weight_sum.lane<0>();
1109
53.3k
  float Q = weight_weight_sum.lane<1>();
1110
53.3k
  float R = weight_weight_sum.lane<2>();
1111
53.3k
  float S = psum;
1112
1113
53.3k
  float PP = P * P;
1114
53.3k
  float QQ = Q * Q;
1115
53.3k
  float RR = R * R;
1116
1117
53.3k
  float SZmRR = S * Z - RR;
1118
53.3k
  float DT = SZmRR * Y - Z * QQ;
1119
53.3k
  float YP = Y * P;
1120
53.3k
  float QX = Q * X;
1121
53.3k
  float YX = Y * X;
1122
53.3k
  float mZYP = -Z * YP;
1123
53.3k
  float mZQX = -Z * QX;
1124
53.3k
  float mRYX = -R * YX;
1125
53.3k
  float ZQP = Z * Q * P;
1126
53.3k
  float RYP = R * YP;
1127
53.3k
  float RQX = R * QX;
1128
1129
  // Compute the reciprocal of matrix determinant
1130
53.3k
  float rdet = 1.0f / (DT * X + mZYP * P);
1131
1132
  // Actually compute the adjugate, and then apply 1/det separately
1133
53.3k
  vfloat4 mat0(DT, ZQP, RYP, mZYP);
1134
53.3k
  vfloat4 mat1(ZQP, SZmRR * X - Z * PP, RQX, mZQX);
1135
53.3k
  vfloat4 mat2(RYP, RQX, (S * Y - QQ) * X - Y * PP, mRYX);
1136
53.3k
  vfloat4 mat3(mZYP, mZQX, mRYX, Z * YX);
1137
53.3k
  vfloat4 vect = rgbq_sum * rdet;
1138
1139
53.3k
  return vfloat4(dot_s(mat0, vect),
1140
53.3k
                 dot_s(mat1, vect),
1141
53.3k
                 dot_s(mat2, vect),
1142
53.3k
                 dot_s(mat3, vect));
1143
53.3k
}
1144
1145
/* See header for documentation. */
1146
void recompute_ideal_colors_1plane(
1147
  const image_block& blk,
1148
  const partition_info& pi,
1149
  const decimation_info& di,
1150
  const uint8_t* dec_weights_uquant,
1151
  endpoints& ep,
1152
  vfloat4 rgbs_vectors[BLOCK_MAX_PARTITIONS],
1153
  vfloat4 rgbo_vectors[BLOCK_MAX_PARTITIONS]
1154
40.2k
) {
1155
40.2k
  unsigned int weight_count = di.weight_count;
1156
40.2k
  unsigned int total_texel_count = blk.texel_count;
1157
40.2k
  unsigned int partition_count = pi.partition_count;
1158
1159
40.2k
  promise(weight_count > 0);
1160
40.2k
  promise(total_texel_count > 0);
1161
40.2k
  promise(partition_count > 0);
1162
1163
40.2k
  ASTCENC_ALIGNAS float dec_weight[BLOCK_MAX_WEIGHTS];
1164
258k
  for (unsigned int i = 0; i < weight_count; i += ASTCENC_SIMD_WIDTH)
1165
217k
  {
1166
217k
    vint unquant_value(dec_weights_uquant + i);
1167
217k
    vfloat unquant_valuef = int_to_float(unquant_value) * vfloat(1.0f / 64.0f);
1168
217k
    storea(unquant_valuef, dec_weight + i);
1169
217k
  }
1170
1171
40.2k
  ASTCENC_ALIGNAS float undec_weight[BLOCK_MAX_TEXELS];
1172
40.2k
  float* undec_weight_ref;
1173
40.2k
  if (di.max_texel_weight_count == 1)
1174
32.9k
  {
1175
32.9k
    undec_weight_ref = dec_weight;
1176
32.9k
  }
1177
7.25k
  else if (di.max_texel_weight_count <= 2)
1178
5.85k
  {
1179
82.4k
    for (unsigned int i = 0; i < total_texel_count; i += ASTCENC_SIMD_WIDTH)
1180
76.6k
    {
1181
76.6k
      vfloat weight = bilinear_infill_vla_2(di, dec_weight, i);
1182
76.6k
      storea(weight, undec_weight + i);
1183
76.6k
    }
1184
1185
5.85k
    undec_weight_ref = undec_weight;
1186
5.85k
  }
1187
1.40k
  else
1188
1.40k
  {
1189
34.4k
    for (unsigned int i = 0; i < total_texel_count; i += ASTCENC_SIMD_WIDTH)
1190
33.0k
    {
1191
33.0k
      vfloat weight = bilinear_infill_vla(di, dec_weight, i);
1192
33.0k
      storea(weight, undec_weight + i);
1193
33.0k
    }
1194
1195
1.40k
    undec_weight_ref = undec_weight;
1196
1.40k
  }
1197
1198
40.2k
  vfloat4 rgba_sum(blk.data_mean * static_cast<float>(blk.texel_count));
1199
1200
129k
  for (unsigned int i = 0; i < partition_count; i++)
1201
89.4k
  {
1202
89.4k
    unsigned int texel_count = pi.partition_texel_count[i];
1203
89.4k
    const uint8_t *texel_indexes = pi.texels_of_partition[i];
1204
1205
    // Only compute a partition mean if more than one partition
1206
89.4k
    if (partition_count > 1)
1207
78.6k
    {
1208
78.6k
      rgba_sum = vfloat4::zero();
1209
78.6k
      promise(texel_count > 0);
1210
822k
      for (unsigned int j = 0; j < texel_count; j++)
1211
743k
      {
1212
743k
        unsigned int tix = texel_indexes[j];
1213
743k
        rgba_sum += blk.texel(tix);
1214
743k
      }
1215
78.6k
    }
1216
1217
89.4k
    rgba_sum = rgba_sum * blk.channel_weight;
1218
89.4k
    vfloat4 rgba_weight_sum = max(blk.channel_weight * static_cast<float>(texel_count), 1e-17f);
1219
89.4k
    vfloat4 scale_dir = normalize((rgba_sum / rgba_weight_sum).swz<0, 1, 2>());
1220
1221
89.4k
    float scale_max = 0.0f;
1222
89.4k
    float scale_min = 1e10f;
1223
1224
89.4k
    float wmin1 = 1.0f;
1225
89.4k
    float wmax1 = 0.0f;
1226
1227
89.4k
    float left_sum_s = 0.0f;
1228
89.4k
    float middle_sum_s = 0.0f;
1229
89.4k
    float right_sum_s = 0.0f;
1230
1231
89.4k
    vfloat4 color_vec_x = vfloat4::zero();
1232
89.4k
    vfloat4 color_vec_y = vfloat4::zero();
1233
1234
89.4k
    vfloat4 scale_vec = vfloat4::zero();
1235
1236
89.4k
    float weight_weight_sum_s = 1e-17f;
1237
1238
89.4k
    vfloat4 color_weight = blk.channel_weight;
1239
89.4k
    float ls_weight = hadd_rgb_s(color_weight);
1240
1241
1.13M
    for (unsigned int j = 0; j < texel_count; j++)
1242
1.04M
    {
1243
1.04M
      unsigned int tix = texel_indexes[j];
1244
1.04M
      vfloat4 rgba = blk.texel(tix);
1245
1246
1.04M
      float idx0 = undec_weight_ref[tix];
1247
1248
1.04M
      float om_idx0 = 1.0f - idx0;
1249
1.04M
      wmin1 = astc::min(idx0, wmin1);
1250
1.04M
      wmax1 = astc::max(idx0, wmax1);
1251
1252
1.04M
      float scale = dot3_s(scale_dir, rgba);
1253
1.04M
      scale_min = astc::min(scale, scale_min);
1254
1.04M
      scale_max = astc::max(scale, scale_max);
1255
1256
1.04M
      left_sum_s   += om_idx0 * om_idx0;
1257
1.04M
      middle_sum_s += om_idx0 * idx0;
1258
1.04M
      right_sum_s  += idx0 * idx0;
1259
1.04M
      weight_weight_sum_s += idx0;
1260
1261
1.04M
      vfloat4 color_idx(idx0);
1262
1.04M
      vfloat4 cwprod = rgba;
1263
1.04M
      vfloat4 cwiprod = cwprod * color_idx;
1264
1265
1.04M
      color_vec_y += cwiprod;
1266
1.04M
      color_vec_x += cwprod - cwiprod;
1267
1268
1.04M
      scale_vec += vfloat2(om_idx0, idx0) * (scale * ls_weight);
1269
1.04M
    }
1270
1271
89.4k
    vfloat4 left_sum   = vfloat4(left_sum_s) * color_weight;
1272
89.4k
    vfloat4 middle_sum = vfloat4(middle_sum_s) * color_weight;
1273
89.4k
    vfloat4 right_sum  = vfloat4(right_sum_s) * color_weight;
1274
89.4k
    vfloat4 lmrs_sum   = vfloat3(left_sum_s, middle_sum_s, right_sum_s) * ls_weight;
1275
1276
89.4k
    color_vec_x = color_vec_x * color_weight;
1277
89.4k
    color_vec_y = color_vec_y * color_weight;
1278
1279
    // Initialize the luminance and scale vectors with a reasonable default
1280
89.4k
    float scalediv = scale_min / astc::max(scale_max, 1e-10f);
1281
89.4k
    scalediv = astc::clamp1f(scalediv);
1282
1283
89.4k
    vfloat4 sds = scale_dir * scale_max;
1284
1285
89.4k
    rgbs_vectors[i] = vfloat4(sds.lane<0>(), sds.lane<1>(), sds.lane<2>(), scalediv);
1286
1287
89.4k
    if (wmin1 >= wmax1 * 0.999f)
1288
9.18k
    {
1289
      // If all weights in the partition were equal, then just take average of all colors in
1290
      // the partition and use that as both endpoint colors
1291
9.18k
      vfloat4 avg = (color_vec_x + color_vec_y) / rgba_weight_sum;
1292
1293
9.18k
      vmask4 notnan_mask = avg == avg;
1294
9.18k
      ep.endpt0[i] = select(ep.endpt0[i], avg, notnan_mask);
1295
9.18k
      ep.endpt1[i] = select(ep.endpt1[i], avg, notnan_mask);
1296
1297
9.18k
      rgbs_vectors[i] = vfloat4(sds.lane<0>(), sds.lane<1>(), sds.lane<2>(), 1.0f);
1298
9.18k
    }
1299
80.3k
    else
1300
80.3k
    {
1301
      // Otherwise, complete the analytic calculation of ideal-endpoint-values for the given
1302
      // set of texel weights and pixel colors
1303
80.3k
      vfloat4 color_det1 = (left_sum * right_sum) - (middle_sum * middle_sum);
1304
80.3k
      vfloat4 color_rdet1 = 1.0f / color_det1;
1305
1306
80.3k
      float ls_det1  = (lmrs_sum.lane<0>() * lmrs_sum.lane<2>()) - (lmrs_sum.lane<1>() * lmrs_sum.lane<1>());
1307
80.3k
      float ls_rdet1 = 1.0f / ls_det1;
1308
1309
80.3k
      vfloat4 color_mss1 = (left_sum * left_sum)
1310
80.3k
                         + (2.0f * middle_sum * middle_sum)
1311
80.3k
                         + (right_sum * right_sum);
1312
1313
80.3k
      float ls_mss1 = (lmrs_sum.lane<0>() * lmrs_sum.lane<0>())
1314
80.3k
                    + (2.0f * lmrs_sum.lane<1>() * lmrs_sum.lane<1>())
1315
80.3k
                    + (lmrs_sum.lane<2>() * lmrs_sum.lane<2>());
1316
1317
80.3k
      vfloat4 ep0 = (right_sum * color_vec_x - middle_sum * color_vec_y) * color_rdet1;
1318
80.3k
      vfloat4 ep1 = (left_sum * color_vec_y - middle_sum * color_vec_x) * color_rdet1;
1319
1320
80.3k
      vmask4 det_mask = abs(color_det1) > (color_mss1 * 1e-4f);
1321
80.3k
      vmask4 notnan_mask = (ep0 == ep0) & (ep1 == ep1);
1322
80.3k
      vmask4 full_mask = det_mask & notnan_mask;
1323
1324
80.3k
      ep.endpt0[i] = select(ep.endpt0[i], ep0, full_mask);
1325
80.3k
      ep.endpt1[i] = select(ep.endpt1[i], ep1, full_mask);
1326
1327
80.3k
      float scale_ep0 = (lmrs_sum.lane<2>() * scale_vec.lane<0>() - lmrs_sum.lane<1>() * scale_vec.lane<1>()) * ls_rdet1;
1328
80.3k
      float scale_ep1 = (lmrs_sum.lane<0>() * scale_vec.lane<1>() - lmrs_sum.lane<1>() * scale_vec.lane<0>()) * ls_rdet1;
1329
1330
80.3k
      if (fabsf(ls_det1) > (ls_mss1 * 1e-4f) && scale_ep0 == scale_ep0 && scale_ep1 == scale_ep1 && scale_ep0 < scale_ep1)
1331
74.8k
      {
1332
74.8k
        float scalediv2 = scale_ep0 / scale_ep1;
1333
74.8k
        vfloat4 sdsm = scale_dir * scale_ep1;
1334
74.8k
        rgbs_vectors[i] = vfloat4(sdsm.lane<0>(), sdsm.lane<1>(), sdsm.lane<2>(), scalediv2);
1335
74.8k
      }
1336
80.3k
    }
1337
1338
    // Calculations specific to mode #7, the HDR RGB-scale mode - skip if known LDR
1339
89.4k
    if (blk.rgb_lns[0] || blk.alpha_lns[0])
1340
42.7k
    {
1341
42.7k
      vfloat4 weight_weight_sum = vfloat4(weight_weight_sum_s) * color_weight;
1342
42.7k
      float psum = right_sum_s * hadd_rgb_s(color_weight);
1343
1344
42.7k
      vfloat4 rgbq_sum = color_vec_x + color_vec_y;
1345
42.7k
      rgbq_sum.set_lane<3>(hadd_rgb_s(color_vec_y));
1346
1347
42.7k
      vfloat4 rgbovec = compute_rgbo_vector(rgba_weight_sum, weight_weight_sum, rgbq_sum, psum);
1348
42.7k
      rgbo_vectors[i] = rgbovec;
1349
1350
      // We can get a failure due to the use of a singular (non-invertible) matrix
1351
      // If it failed, compute rgbo_vectors[] with a different method ...
1352
42.7k
      if (astc::isnan(dot_s(rgbovec, rgbovec)))
1353
3.15k
      {
1354
3.15k
        vfloat4 v0 = ep.endpt0[i];
1355
3.15k
        vfloat4 v1 = ep.endpt1[i];
1356
1357
3.15k
        float avgdif = hadd_rgb_s(v1 - v0) * (1.0f / 3.0f);
1358
3.15k
        avgdif = astc::max(avgdif, 0.0f);
1359
1360
3.15k
        vfloat4 avg = (v0 + v1) * 0.5f;
1361
3.15k
        vfloat4 ep0 = avg - vfloat4(avgdif) * 0.5f;
1362
3.15k
        rgbo_vectors[i] = vfloat4(ep0.lane<0>(), ep0.lane<1>(), ep0.lane<2>(), avgdif);
1363
3.15k
      }
1364
42.7k
    }
1365
89.4k
  }
1366
40.2k
}
1367
1368
/* See header for documentation. */
1369
void recompute_ideal_colors_2planes(
1370
  const image_block& blk,
1371
  const block_size_descriptor& bsd,
1372
  const decimation_info& di,
1373
  const uint8_t* dec_weights_uquant_plane1,
1374
  const uint8_t* dec_weights_uquant_plane2,
1375
  endpoints& ep,
1376
  vfloat4& rgbs_vector,
1377
  vfloat4& rgbo_vector,
1378
  int plane2_component
1379
22.0k
) {
1380
22.0k
  unsigned int weight_count = di.weight_count;
1381
22.0k
  unsigned int total_texel_count = blk.texel_count;
1382
1383
22.0k
  promise(total_texel_count > 0);
1384
22.0k
  promise(weight_count > 0);
1385
1386
22.0k
  ASTCENC_ALIGNAS float dec_weight_plane1[BLOCK_MAX_WEIGHTS_2PLANE];
1387
22.0k
  ASTCENC_ALIGNAS float dec_weight_plane2[BLOCK_MAX_WEIGHTS_2PLANE];
1388
1389
22.0k
  assert(weight_count <= BLOCK_MAX_WEIGHTS_2PLANE);
1390
1391
112k
  for (unsigned int i = 0; i < weight_count; i += ASTCENC_SIMD_WIDTH)
1392
90.2k
  {
1393
90.2k
    vint unquant_value1(dec_weights_uquant_plane1 + i);
1394
90.2k
    vfloat unquant_value1f = int_to_float(unquant_value1) * vfloat(1.0f / 64.0f);
1395
90.2k
    storea(unquant_value1f, dec_weight_plane1 + i);
1396
1397
90.2k
    vint unquant_value2(dec_weights_uquant_plane2 + i);
1398
90.2k
    vfloat unquant_value2f = int_to_float(unquant_value2) * vfloat(1.0f / 64.0f);
1399
90.2k
    storea(unquant_value2f, dec_weight_plane2 + i);
1400
90.2k
  }
1401
1402
22.0k
  ASTCENC_ALIGNAS float undec_weight_plane1[BLOCK_MAX_TEXELS];
1403
22.0k
  ASTCENC_ALIGNAS float undec_weight_plane2[BLOCK_MAX_TEXELS];
1404
1405
22.0k
  float* undec_weight_plane1_ref;
1406
22.0k
  float* undec_weight_plane2_ref;
1407
1408
22.0k
  if (di.max_texel_weight_count == 1)
1409
9.92k
  {
1410
9.92k
    undec_weight_plane1_ref = dec_weight_plane1;
1411
9.92k
    undec_weight_plane2_ref = dec_weight_plane2;
1412
9.92k
  }
1413
12.1k
  else if (di.max_texel_weight_count <= 2)
1414
7.56k
  {
1415
45.2k
    for (unsigned int i = 0; i < total_texel_count; i += ASTCENC_SIMD_WIDTH)
1416
37.6k
    {
1417
37.6k
      vfloat weight = bilinear_infill_vla_2(di, dec_weight_plane1, i);
1418
37.6k
      storea(weight, undec_weight_plane1 + i);
1419
1420
37.6k
      weight = bilinear_infill_vla_2(di, dec_weight_plane2, i);
1421
37.6k
      storea(weight, undec_weight_plane2 + i);
1422
37.6k
    }
1423
1424
7.56k
    undec_weight_plane1_ref = undec_weight_plane1;
1425
7.56k
    undec_weight_plane2_ref = undec_weight_plane2;
1426
7.56k
  }
1427
4.59k
  else
1428
4.59k
  {
1429
66.5k
    for (unsigned int i = 0; i < total_texel_count; i += ASTCENC_SIMD_WIDTH)
1430
61.9k
    {
1431
61.9k
      vfloat weight = bilinear_infill_vla(di, dec_weight_plane1, i);
1432
61.9k
      storea(weight, undec_weight_plane1 + i);
1433
1434
61.9k
      weight = bilinear_infill_vla(di, dec_weight_plane2, i);
1435
61.9k
      storea(weight, undec_weight_plane2 + i);
1436
61.9k
    }
1437
1438
4.59k
    undec_weight_plane1_ref = undec_weight_plane1;
1439
4.59k
    undec_weight_plane2_ref = undec_weight_plane2;
1440
4.59k
  }
1441
1442
22.0k
  unsigned int texel_count = bsd.texel_count;
1443
22.0k
  vfloat4 rgba_weight_sum = max(blk.channel_weight * static_cast<float>(texel_count), 1e-17f);
1444
22.0k
  vfloat4 scale_dir = normalize(blk.data_mean.swz<0, 1, 2>());
1445
1446
22.0k
  float scale_max = 0.0f;
1447
22.0k
  float scale_min = 1e10f;
1448
1449
22.0k
  float wmin1 = 1.0f;
1450
22.0k
  float wmax1 = 0.0f;
1451
1452
22.0k
  float wmin2 = 1.0f;
1453
22.0k
  float wmax2 = 0.0f;
1454
1455
22.0k
  float left1_sum_s = 0.0f;
1456
22.0k
  float middle1_sum_s = 0.0f;
1457
22.0k
  float right1_sum_s = 0.0f;
1458
1459
22.0k
  float left2_sum_s = 0.0f;
1460
22.0k
  float middle2_sum_s = 0.0f;
1461
22.0k
  float right2_sum_s = 0.0f;
1462
1463
22.0k
  vfloat4 color_vec_x = vfloat4::zero();
1464
22.0k
  vfloat4 color_vec_y = vfloat4::zero();
1465
1466
22.0k
  vfloat4 scale_vec = vfloat4::zero();
1467
1468
22.0k
  vfloat4 weight_weight_sum = vfloat4(1e-17f);
1469
1470
22.0k
  vmask4 p2_mask = vint4::lane_id() == vint4(plane2_component);
1471
22.0k
  vfloat4 color_weight = blk.channel_weight;
1472
22.0k
  float ls_weight = hadd_rgb_s(color_weight);
1473
1474
576k
  for (unsigned int j = 0; j < texel_count; j++)
1475
554k
  {
1476
554k
    vfloat4 rgba = blk.texel(j);
1477
1478
554k
    float idx0 = undec_weight_plane1_ref[j];
1479
1480
554k
    float om_idx0 = 1.0f - idx0;
1481
554k
    wmin1 = astc::min(idx0, wmin1);
1482
554k
    wmax1 = astc::max(idx0, wmax1);
1483
1484
554k
    float scale = dot3_s(scale_dir, rgba);
1485
554k
    scale_min = astc::min(scale, scale_min);
1486
554k
    scale_max = astc::max(scale, scale_max);
1487
1488
554k
    left1_sum_s   += om_idx0 * om_idx0;
1489
554k
    middle1_sum_s += om_idx0 * idx0;
1490
554k
    right1_sum_s  += idx0 * idx0;
1491
1492
554k
    float idx1 = undec_weight_plane2_ref[j];
1493
1494
554k
    float om_idx1 = 1.0f - idx1;
1495
554k
    wmin2 = astc::min(idx1, wmin2);
1496
554k
    wmax2 = astc::max(idx1, wmax2);
1497
1498
554k
    left2_sum_s   += om_idx1 * om_idx1;
1499
554k
    middle2_sum_s += om_idx1 * idx1;
1500
554k
    right2_sum_s  += idx1 * idx1;
1501
1502
554k
    vfloat4 color_idx = select(vfloat4(idx0), vfloat4(idx1), p2_mask);
1503
1504
554k
    vfloat4 cwprod = rgba;
1505
554k
    vfloat4 cwiprod = cwprod * color_idx;
1506
1507
554k
    color_vec_y += cwiprod;
1508
554k
    color_vec_x += cwprod - cwiprod;
1509
1510
554k
    scale_vec += vfloat2(om_idx0, idx0) * (ls_weight * scale);
1511
554k
    weight_weight_sum += color_idx;
1512
554k
  }
1513
1514
22.0k
  vfloat4 left1_sum   = vfloat4(left1_sum_s) * color_weight;
1515
22.0k
  vfloat4 middle1_sum = vfloat4(middle1_sum_s) * color_weight;
1516
22.0k
  vfloat4 right1_sum  = vfloat4(right1_sum_s) * color_weight;
1517
22.0k
  vfloat4 lmrs_sum    = vfloat3(left1_sum_s, middle1_sum_s, right1_sum_s) * ls_weight;
1518
1519
22.0k
  vfloat4 left2_sum   = vfloat4(left2_sum_s) * color_weight;
1520
22.0k
  vfloat4 middle2_sum = vfloat4(middle2_sum_s) * color_weight;
1521
22.0k
  vfloat4 right2_sum  = vfloat4(right2_sum_s) * color_weight;
1522
1523
22.0k
  color_vec_x = color_vec_x * color_weight;
1524
22.0k
  color_vec_y = color_vec_y * color_weight;
1525
1526
  // Initialize the luminance and scale vectors with a reasonable default
1527
22.0k
  float scalediv = scale_min / astc::max(scale_max, 1e-10f);
1528
22.0k
  scalediv = astc::clamp1f(scalediv);
1529
1530
22.0k
  vfloat4 sds = scale_dir * scale_max;
1531
1532
22.0k
  rgbs_vector = vfloat4(sds.lane<0>(), sds.lane<1>(), sds.lane<2>(), scalediv);
1533
1534
22.0k
  if (wmin1 >= wmax1 * 0.999f)
1535
388
  {
1536
    // If all weights in the partition were equal, then just take average of all colors in
1537
    // the partition and use that as both endpoint colors
1538
388
    vfloat4 avg = (color_vec_x + color_vec_y) / rgba_weight_sum;
1539
1540
388
    vmask4 p1_mask = vint4::lane_id() != vint4(plane2_component);
1541
388
    vmask4 notnan_mask = avg == avg;
1542
388
    vmask4 full_mask = p1_mask & notnan_mask;
1543
1544
388
    ep.endpt0[0] = select(ep.endpt0[0], avg, full_mask);
1545
388
    ep.endpt1[0] = select(ep.endpt1[0], avg, full_mask);
1546
1547
388
    rgbs_vector = vfloat4(sds.lane<0>(), sds.lane<1>(), sds.lane<2>(), 1.0f);
1548
388
  }
1549
21.6k
  else
1550
21.6k
  {
1551
    // Otherwise, complete the analytic calculation of ideal-endpoint-values for the given
1552
    // set of texel weights and pixel colors
1553
21.6k
    vfloat4 color_det1 = (left1_sum * right1_sum) - (middle1_sum * middle1_sum);
1554
21.6k
    vfloat4 color_rdet1 = 1.0f / color_det1;
1555
1556
21.6k
    float ls_det1  = (lmrs_sum.lane<0>() * lmrs_sum.lane<2>()) - (lmrs_sum.lane<1>() * lmrs_sum.lane<1>());
1557
21.6k
    float ls_rdet1 = 1.0f / ls_det1;
1558
1559
21.6k
    vfloat4 color_mss1 = (left1_sum * left1_sum)
1560
21.6k
                       + (2.0f * middle1_sum * middle1_sum)
1561
21.6k
                       + (right1_sum * right1_sum);
1562
1563
21.6k
    float ls_mss1 = (lmrs_sum.lane<0>() * lmrs_sum.lane<0>())
1564
21.6k
                  + (2.0f * lmrs_sum.lane<1>() * lmrs_sum.lane<1>())
1565
21.6k
                  + (lmrs_sum.lane<2>() * lmrs_sum.lane<2>());
1566
1567
21.6k
    vfloat4 ep0 = (right1_sum * color_vec_x - middle1_sum * color_vec_y) * color_rdet1;
1568
21.6k
    vfloat4 ep1 = (left1_sum * color_vec_y - middle1_sum * color_vec_x) * color_rdet1;
1569
1570
21.6k
    float scale_ep0 = (lmrs_sum.lane<2>() * scale_vec.lane<0>() - lmrs_sum.lane<1>() * scale_vec.lane<1>()) * ls_rdet1;
1571
21.6k
    float scale_ep1 = (lmrs_sum.lane<0>() * scale_vec.lane<1>() - lmrs_sum.lane<1>() * scale_vec.lane<0>()) * ls_rdet1;
1572
1573
21.6k
    vmask4 p1_mask = vint4::lane_id() != vint4(plane2_component);
1574
21.6k
    vmask4 det_mask = abs(color_det1) > (color_mss1 * 1e-4f);
1575
21.6k
    vmask4 notnan_mask = (ep0 == ep0) & (ep1 == ep1);
1576
21.6k
    vmask4 full_mask = p1_mask & det_mask & notnan_mask;
1577
1578
21.6k
    ep.endpt0[0] = select(ep.endpt0[0], ep0, full_mask);
1579
21.6k
    ep.endpt1[0] = select(ep.endpt1[0], ep1, full_mask);
1580
1581
21.6k
    if (fabsf(ls_det1) > (ls_mss1 * 1e-4f) && scale_ep0 == scale_ep0 && scale_ep1 == scale_ep1 && scale_ep0 < scale_ep1)
1582
19.2k
    {
1583
19.2k
      float scalediv2 = scale_ep0 / scale_ep1;
1584
19.2k
      vfloat4 sdsm = scale_dir * scale_ep1;
1585
19.2k
      rgbs_vector = vfloat4(sdsm.lane<0>(), sdsm.lane<1>(), sdsm.lane<2>(), scalediv2);
1586
19.2k
    }
1587
21.6k
  }
1588
1589
22.0k
  if (wmin2 >= wmax2 * 0.999f)
1590
566
  {
1591
    // If all weights in the partition were equal, then just take average of all colors in
1592
    // the partition and use that as both endpoint colors
1593
566
    vfloat4 avg = (color_vec_x + color_vec_y) / rgba_weight_sum;
1594
1595
566
    vmask4 notnan_mask = avg == avg;
1596
566
    vmask4 full_mask = p2_mask & notnan_mask;
1597
1598
566
    ep.endpt0[0] = select(ep.endpt0[0], avg, full_mask);
1599
566
    ep.endpt1[0] = select(ep.endpt1[0], avg, full_mask);
1600
566
  }
1601
21.5k
  else
1602
21.5k
  {
1603
    // Otherwise, complete the analytic calculation of ideal-endpoint-values for the given
1604
    // set of texel weights and pixel colors
1605
21.5k
    vfloat4 color_det2 = (left2_sum * right2_sum) - (middle2_sum * middle2_sum);
1606
21.5k
    vfloat4 color_rdet2 = 1.0f / color_det2;
1607
1608
21.5k
    vfloat4 color_mss2 = (left2_sum * left2_sum)
1609
21.5k
                       + (2.0f * middle2_sum * middle2_sum)
1610
21.5k
                       + (right2_sum * right2_sum);
1611
1612
21.5k
    vfloat4 ep0 = (right2_sum * color_vec_x - middle2_sum * color_vec_y) * color_rdet2;
1613
21.5k
    vfloat4 ep1 = (left2_sum * color_vec_y - middle2_sum * color_vec_x) * color_rdet2;
1614
1615
21.5k
    vmask4 det_mask = abs(color_det2) > (color_mss2 * 1e-4f);
1616
21.5k
    vmask4 notnan_mask = (ep0 == ep0) & (ep1 == ep1);
1617
21.5k
    vmask4 full_mask = p2_mask & det_mask & notnan_mask;
1618
1619
21.5k
    ep.endpt0[0] = select(ep.endpt0[0], ep0, full_mask);
1620
21.5k
    ep.endpt1[0] = select(ep.endpt1[0], ep1, full_mask);
1621
21.5k
  }
1622
1623
  // Calculations specific to mode #7, the HDR RGB-scale mode - skip if known LDR
1624
22.0k
  if (blk.rgb_lns[0] || blk.alpha_lns[0])
1625
10.5k
  {
1626
10.5k
    weight_weight_sum = weight_weight_sum * color_weight;
1627
10.5k
    float psum = dot3_s(select(right1_sum, right2_sum, p2_mask), color_weight);
1628
1629
10.5k
    vfloat4 rgbq_sum = color_vec_x + color_vec_y;
1630
10.5k
    rgbq_sum.set_lane<3>(hadd_rgb_s(color_vec_y));
1631
1632
10.5k
    rgbo_vector = compute_rgbo_vector(rgba_weight_sum, weight_weight_sum, rgbq_sum, psum);
1633
1634
    // We can get a failure due to the use of a singular (non-invertible) matrix
1635
    // If it failed, compute rgbo_vectors[] with a different method ...
1636
10.5k
    if (astc::isnan(dot_s(rgbo_vector, rgbo_vector)))
1637
217
    {
1638
217
      vfloat4 v0 = ep.endpt0[0];
1639
217
      vfloat4 v1 = ep.endpt1[0];
1640
1641
217
      float avgdif = hadd_rgb_s(v1 - v0) * (1.0f / 3.0f);
1642
217
      avgdif = astc::max(avgdif, 0.0f);
1643
1644
217
      vfloat4 avg = (v0 + v1) * 0.5f;
1645
217
      vfloat4 ep0 = avg - vfloat4(avgdif) * 0.5f;
1646
1647
217
      rgbo_vector = vfloat4(ep0.lane<0>(), ep0.lane<1>(), ep0.lane<2>(), avgdif);
1648
217
    }
1649
10.5k
  }
1650
22.0k
}
1651
1652
#endif