blob: 271bbc6f1daf3f1a5b04db289528dc33576fe36d [file] [log] [blame]
// basisu_uastc_enc.cpp
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_uastc_enc.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "basisu_gpu_texture.h"
#include "basisu_bc7enc.h"
#ifdef _DEBUG
// When BASISU_VALIDATE_UASTC_ENC is 1, we pack and unpack to/from UASTC and ASTC, then validate that each codec returns the exact same results. This is slower.
#define BASISU_VALIDATE_UASTC_ENC 1
#endif
#define BASISU_SUPPORT_FORCE_MODE 0
using namespace basist;
namespace basisu
{
const uint32_t MAX_ENCODE_RESULTS = 512;
#if BASISU_VALIDATE_UASTC_ENC
static void validate_func(bool condition, int line)
{
if (!condition)
{
fprintf(stderr, "basisu_uastc_enc: Internal validation failed on line %u!\n", line);
}
}
#define VALIDATE(c) validate_func(c, __LINE__);
#else
#define VALIDATE(c)
#endif
enum dxt_constants
{
cDXT1SelectorBits = 2U, cDXT1SelectorValues = 1U << cDXT1SelectorBits, cDXT1SelectorMask = cDXT1SelectorValues - 1U,
cDXT5SelectorBits = 3U, cDXT5SelectorValues = 1U << cDXT5SelectorBits, cDXT5SelectorMask = cDXT5SelectorValues - 1U,
};
struct dxt1_block
{
enum { cTotalEndpointBytes = 2, cTotalSelectorBytes = 4 };
uint8_t m_low_color[cTotalEndpointBytes];
uint8_t m_high_color[cTotalEndpointBytes];
uint8_t m_selectors[cTotalSelectorBytes];
inline void clear() { basisu::clear_obj(*this); }
inline uint32_t get_high_color() const { return m_high_color[0] | (m_high_color[1] << 8U); }
inline uint32_t get_low_color() const { return m_low_color[0] | (m_low_color[1] << 8U); }
inline void set_low_color(uint16_t c) { m_low_color[0] = static_cast<uint8_t>(c & 0xFF); m_low_color[1] = static_cast<uint8_t>((c >> 8) & 0xFF); }
inline void set_high_color(uint16_t c) { m_high_color[0] = static_cast<uint8_t>(c & 0xFF); m_high_color[1] = static_cast<uint8_t>((c >> 8) & 0xFF); }
inline uint32_t get_selector(uint32_t x, uint32_t y) const { assert((x < 4U) && (y < 4U)); return (m_selectors[y] >> (x * cDXT1SelectorBits))& cDXT1SelectorMask; }
inline void set_selector(uint32_t x, uint32_t y, uint32_t val) { assert((x < 4U) && (y < 4U) && (val < 4U)); m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits))); m_selectors[y] |= (val << (x * cDXT1SelectorBits)); }
static uint16_t pack_color(const color_rgba& color, bool scaled, uint32_t bias = 127U)
{
uint32_t r = color.r, g = color.g, b = color.b;
if (scaled)
{
r = (r * 31U + bias) / 255U;
g = (g * 63U + bias) / 255U;
b = (b * 31U + bias) / 255U;
}
return static_cast<uint16_t>(basisu::minimum(b, 31U) | (basisu::minimum(g, 63U) << 5U) | (basisu::minimum(r, 31U) << 11U));
}
static uint16_t pack_unscaled_color(uint32_t r, uint32_t g, uint32_t b) { return static_cast<uint16_t>(b | (g << 5U) | (r << 11U)); }
};
#define UASTC_WRITE_MODE_DESCS 0
static inline void uastc_write_bits(uint8_t* pBuf, uint32_t& bit_offset, uint64_t code, uint32_t codesize, const char* pDesc)
{
(void)pDesc;
#if UASTC_WRITE_MODE_DESCS
if (pDesc)
printf("%s: %u %u\n", pDesc, bit_offset, codesize);
#endif
assert((codesize == 64) || (code < (1ULL << codesize)));
while (codesize)
{
uint32_t byte_bit_offset = bit_offset & 7;
uint32_t bits_to_write = basisu::minimum<int>(codesize, 8 - byte_bit_offset);
pBuf[bit_offset >> 3] |= (code << byte_bit_offset);
code >>= bits_to_write;
codesize -= bits_to_write;
bit_offset += bits_to_write;
}
}
void pack_uastc(basist::uastc_block& blk, const uastc_encode_results& result, const etc_block& etc1_blk, uint32_t etc1_bias, const eac_a8_block& etc_eac_a8_blk, bool bc1_hint0, bool bc1_hint1)
{
if ((g_uastc_mode_has_alpha[result.m_uastc_mode]) && (result.m_uastc_mode != UASTC_MODE_INDEX_SOLID_COLOR))
{
assert(etc_eac_a8_blk.m_multiplier >= 1);
}
uint8_t buf[32];
memset(buf, 0, sizeof(buf));
uint32_t block_bit_offset = 0;
#if UASTC_WRITE_MODE_DESCS
printf("**** Mode: %u\n", result.m_uastc_mode);
#endif
uastc_write_bits(buf, block_bit_offset, g_uastc_mode_huff_codes[result.m_uastc_mode][0], g_uastc_mode_huff_codes[result.m_uastc_mode][1], "mode");
if (result.m_uastc_mode == UASTC_MODE_INDEX_SOLID_COLOR)
{
uastc_write_bits(buf, block_bit_offset, result.m_solid_color.r, 8, "R");
uastc_write_bits(buf, block_bit_offset, result.m_solid_color.g, 8, "G");
uastc_write_bits(buf, block_bit_offset, result.m_solid_color.b, 8, "B");
uastc_write_bits(buf, block_bit_offset, result.m_solid_color.a, 8, "A");
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_diff_bit(), 1, "ETC1D");
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_inten_table(0), 3, "ETC1I");
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_selector(0, 0), 2, "ETC1S");
uint32_t r, g, b;
if (etc1_blk.get_diff_bit())
etc_block::unpack_color5(r, g, b, etc1_blk.get_base5_color(), false);
else
etc_block::unpack_color4(r, g, b, etc1_blk.get_base4_color(0), false);
uastc_write_bits(buf, block_bit_offset, r, 5, "ETC1R");
uastc_write_bits(buf, block_bit_offset, g, 5, "ETC1G");
uastc_write_bits(buf, block_bit_offset, b, 5, "ETC1B");
memcpy(&blk, buf, sizeof(blk));
return;
}
if (g_uastc_mode_has_bc1_hint0[result.m_uastc_mode])
uastc_write_bits(buf, block_bit_offset, bc1_hint0, 1, "BC1H0");
else
{
assert(bc1_hint0 == false);
}
if (g_uastc_mode_has_bc1_hint1[result.m_uastc_mode])
uastc_write_bits(buf, block_bit_offset, bc1_hint1, 1, "BC1H1");
else
{
assert(bc1_hint1 == false);
}
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_flip_bit(), 1, "ETC1F");
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_diff_bit(), 1, "ETC1D");
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_inten_table(0), 3, "ETC1I0");
uastc_write_bits(buf, block_bit_offset, etc1_blk.get_inten_table(1), 3, "ETC1I1");
if (g_uastc_mode_has_etc1_bias[result.m_uastc_mode])
uastc_write_bits(buf, block_bit_offset, etc1_bias, 5, "ETC1BIAS");
else
{
assert(etc1_bias == 0);
}
if (g_uastc_mode_has_alpha[result.m_uastc_mode])
{
const uint32_t etc2_hints = etc_eac_a8_blk.m_table | (etc_eac_a8_blk.m_multiplier << 4);
assert(etc2_hints > 0 && etc2_hints <= 0xFF);
uastc_write_bits(buf, block_bit_offset, etc2_hints, 8, "ETC2TM");
}
uint32_t subsets = 1;
switch (result.m_uastc_mode)
{
case 2:
case 4:
case 7:
case 9:
case 16:
uastc_write_bits(buf, block_bit_offset, result.m_common_pattern, 5, "PAT");
subsets = 2;
break;
case 3:
uastc_write_bits(buf, block_bit_offset, result.m_common_pattern, 4, "PAT");
subsets = 3;
break;
default:
break;
}
#ifdef _DEBUG
uint32_t part_seed = 0;
switch (result.m_uastc_mode)
{
case 2:
case 4:
case 9:
case 16:
part_seed = g_astc_bc7_common_partitions2[result.m_common_pattern].m_astc;
break;
case 3:
part_seed = g_astc_bc7_common_partitions3[result.m_common_pattern].m_astc;
break;
case 7:
part_seed = g_bc7_3_astc2_common_partitions[result.m_common_pattern].m_astc2;
break;
default:
break;
}
#endif
uint32_t total_planes = 1;
switch (result.m_uastc_mode)
{
case 6:
case 11:
case 13:
uastc_write_bits(buf, block_bit_offset, result.m_astc.m_ccs, 2, "COMPSEL");
total_planes = 2;
break;
case 17:
// CCS field is always 3 for dual plane LA.
assert(result.m_astc.m_ccs == 3);
total_planes = 2;
break;
default:
break;
}
uint8_t weights[32];
memcpy(weights, result.m_astc.m_weights, 16 * total_planes);
uint8_t endpoints[18];
memcpy(endpoints, result.m_astc.m_endpoints, sizeof(endpoints));
const uint32_t total_comps = g_uastc_mode_comps[result.m_uastc_mode];
// LLAA
// LLAA LLAA
// LLAA LLAA LLAA
// RRGGBB
// RRGGBB RRGGBB
// RRGGBB RRGGBB RRGGBB
// RRGGBBAA
// RRGGBBAA RRGGBBAA
const uint32_t weight_bits = g_uastc_mode_weight_bits[result.m_uastc_mode];
const uint8_t* pPartition_pattern;
const uint8_t* pSubset_anchor_indices = basist::get_anchor_indices(subsets, result.m_uastc_mode, result.m_common_pattern, pPartition_pattern);
for (uint32_t plane_index = 0; plane_index < total_planes; plane_index++)
{
for (uint32_t subset_index = 0; subset_index < subsets; subset_index++)
{
const uint32_t anchor_index = pSubset_anchor_indices[subset_index];
#ifdef _DEBUG
if (subsets >= 2)
{
for (uint32_t i = 0; i < 16; i++)
{
const uint32_t part_index = astc_compute_texel_partition(part_seed, i & 3, i >> 2, 0, subsets, true);
if (part_index == subset_index)
{
assert(anchor_index == i);
break;
}
}
}
else
{
assert(!anchor_index);
}
#endif
// Check anchor weight's MSB - if it's set then invert this subset's weights and swap the endpoints
if (weights[anchor_index * total_planes + plane_index] & (1 << (weight_bits - 1)))
{
for (uint32_t i = 0; i < 16; i++)
{
const uint32_t part_index = pPartition_pattern[i];
#ifdef _DEBUG
if (subsets >= 2)
{
assert(part_index == (uint32_t)astc_compute_texel_partition(part_seed, i & 3, i >> 2, 0, subsets, true));
}
else
{
assert(!part_index);
}
#endif
if (part_index == subset_index)
weights[i * total_planes + plane_index] = ((1 << weight_bits) - 1) - weights[i * total_planes + plane_index];
}
if (total_planes == 2)
{
for (int c = 0; c < (int)total_comps; c++)
{
const uint32_t comp_plane = (total_comps == 2) ? c : ((c == result.m_astc.m_ccs) ? 1 : 0);
if (comp_plane == plane_index)
std::swap(endpoints[c * 2 + 0], endpoints[c * 2 + 1]);
}
}
else
{
for (uint32_t c = 0; c < total_comps; c++)
std::swap(endpoints[subset_index * total_comps * 2 + c * 2 + 0], endpoints[subset_index * total_comps * 2 + c * 2 + 1]);
}
}
} // subset_index
} // plane_index
const uint32_t total_values = total_comps * 2 * subsets;
const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[result.m_uastc_mode];
uint32_t bit_values[18];
uint32_t tq_values[8];
uint32_t total_tq_values = 0;
uint32_t tq_accum = 0;
uint32_t tq_mul = 1;
const uint32_t ep_bits = g_astc_bise_range_table[endpoint_range][0];
const uint32_t ep_trits = g_astc_bise_range_table[endpoint_range][1];
const uint32_t ep_quints = g_astc_bise_range_table[endpoint_range][2];
for (uint32_t i = 0; i < total_values; i++)
{
uint32_t val = endpoints[i];
uint32_t bits = val & ((1 << ep_bits) - 1);
uint32_t tq = val >> ep_bits;
bit_values[i] = bits;
if (ep_trits)
{
assert(tq < 3);
tq_accum += tq * tq_mul;
tq_mul *= 3;
if (tq_mul == 243)
{
tq_values[total_tq_values++] = tq_accum;
tq_accum = 0;
tq_mul = 1;
}
}
else if (ep_quints)
{
assert(tq < 5);
tq_accum += tq * tq_mul;
tq_mul *= 5;
if (tq_mul == 125)
{
tq_values[total_tq_values++] = tq_accum;
tq_accum = 0;
tq_mul = 1;
}
}
}
uint32_t total_endpoint_bits = 0;
for (uint32_t i = 0; i < total_tq_values; i++)
{
const uint32_t num_bits = ep_trits ? 8 : 7;
uastc_write_bits(buf, block_bit_offset, tq_values[i], num_bits, "ETQ");
total_endpoint_bits += num_bits;
}
if (tq_mul > 1)
{
uint32_t num_bits;
if (ep_trits)
{
if (tq_mul == 3)
num_bits = 2;
else if (tq_mul == 9)
num_bits = 4;
else if (tq_mul == 27)
num_bits = 5;
else //if (tq_mul == 81)
num_bits = 7;
}
else
{
if (tq_mul == 5)
num_bits = 3;
else //if (tq_mul == 25)
num_bits = 5;
}
uastc_write_bits(buf, block_bit_offset, tq_accum, num_bits, "ETQ");
total_endpoint_bits += num_bits;
}
for (uint32_t i = 0; i < total_values; i++)
{
uastc_write_bits(buf, block_bit_offset, bit_values[i], ep_bits, "EBITS");
total_endpoint_bits += ep_bits;
}
#if UASTC_WRITE_MODE_DESCS
uint32_t weight_start = block_bit_offset;
#endif
uint32_t total_weight_bits = 0;
const uint32_t plane_shift = (total_planes == 2) ? 1 : 0;
for (uint32_t i = 0; i < 16 * total_planes; i++)
{
uint32_t numbits = weight_bits;
for (uint32_t s = 0; s < subsets; s++)
{
if (pSubset_anchor_indices[s] == (i >> plane_shift))
{
numbits--;
break;
}
}
uastc_write_bits(buf, block_bit_offset, weights[i], numbits, nullptr);
total_weight_bits += numbits;
}
#if UASTC_WRITE_MODE_DESCS
printf("WEIGHTS: %u %u\n", weight_start, total_weight_bits);
#endif
assert(block_bit_offset <= 128);
memcpy(&blk, buf, sizeof(blk));
#if UASTC_WRITE_MODE_DESCS
printf("Total bits: %u, endpoint bits: %u, weight bits: %u\n", block_bit_offset, total_endpoint_bits, total_weight_bits);
#endif
}
// MODE 0
// 0. DualPlane: 0, WeightRange: 8 (16), Subsets: 1, CEM: 8 (RGB Direct ), EndpointRange: 19 (192) MODE6 RGB
// 18. DualPlane: 0, WeightRange: 11 (32), Subsets: 1, CEM: 8 (RGB Direct ), EndpointRange: 11 (32) MODE6 RGB
static void astc_mode0_or_18(uint32_t mode, const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params, const uint8_t *pForce_selectors = nullptr)
{
const uint32_t endpoint_range = (mode == 18) ? 11 : 19;
const uint32_t weight_range = (mode == 18) ? 11 : 8;
color_cell_compressor_params ccell_params;
memset(&ccell_params, 0, sizeof(ccell_params));
ccell_params.m_num_pixels = 16;
ccell_params.m_pPixels = (color_quad_u8*)&block[0][0];
ccell_params.m_num_selector_weights = (mode == 18) ? 32 : 16;
ccell_params.m_pSelector_weights = (mode == 18) ? g_astc_weights5 : g_astc_weights4;
ccell_params.m_pSelector_weightsx = (mode == 18) ? (const bc7enc_vec4F*)g_astc_weights5x : (const bc7enc_vec4F*)g_astc_weights4x;
ccell_params.m_astc_endpoint_range = endpoint_range;
ccell_params.m_weights[0] = 1;
ccell_params.m_weights[1] = 1;
ccell_params.m_weights[2] = 1;
ccell_params.m_weights[3] = 1;
ccell_params.m_pForce_selectors = pForce_selectors;
color_cell_compressor_results ccell_results;
uint8_t ccell_result_selectors[16];
uint8_t ccell_result_selectors_temp[16];
memset(&ccell_results, 0, sizeof(ccell_results));
ccell_results.m_pSelectors = &ccell_result_selectors[0];
ccell_results.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err = color_cell_compression(255, &ccell_params, &ccell_results, &comp_params);
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = weight_range;// (mode == 18) ? 11 : 8;
astc_results.m_ccs = 0;
astc_results.m_subsets = 1;
astc_results.m_partition_seed = 0;
astc_results.m_cem = 8;
astc_results.m_endpoints[0] = ccell_results.m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results.m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results.m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results.m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results.m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results.m_astc_high_endpoint.m_c[2];
bool invert = false;
if (pForce_selectors == nullptr)
{
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
invert = true;
}
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
astc_results.m_weights[x + y * 4] = ccell_result_selectors[x + y * 4];
if (invert)
astc_results.m_weights[x + y * 4] = ((mode == 18) ? 31 : 15) - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = mode;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = part_err;
total_results++;
}
}
// MODE 1
// 1-subset, 2-bit indices, 8-bit endpoints, BC7 mode 3
// DualPlane: 0, WeightRange: 2 (4), Subsets: 1, CEM: 8 (RGB Direct ), EndpointRange: 20 (256) MODE3 or MODE5 RGB
static void astc_mode1(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
color_cell_compressor_params ccell_params;
memset(&ccell_params, 0, sizeof(ccell_params));
ccell_params.m_num_pixels = 16;
ccell_params.m_pPixels = (color_quad_u8*)&block[0][0];
ccell_params.m_num_selector_weights = 4;
ccell_params.m_pSelector_weights = g_bc7_weights2;
ccell_params.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params.m_astc_endpoint_range = 20;
ccell_params.m_weights[0] = 1;
ccell_params.m_weights[1] = 1;
ccell_params.m_weights[2] = 1;
ccell_params.m_weights[3] = 1;
color_cell_compressor_results ccell_results;
uint8_t ccell_result_selectors[16];
uint8_t ccell_result_selectors_temp[16];
memset(&ccell_results, 0, sizeof(ccell_results));
ccell_results.m_pSelectors = &ccell_result_selectors[0];
ccell_results.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err = color_cell_compression(255, &ccell_params, &ccell_results, &comp_params);
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = 2;
astc_results.m_ccs = 0;
astc_results.m_subsets = 1;
astc_results.m_partition_seed = 0;
astc_results.m_cem = 8;
astc_results.m_endpoints[0] = ccell_results.m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results.m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results.m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results.m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results.m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results.m_astc_high_endpoint.m_c[2];
const uint32_t range = 20;
bool invert = false;
int s0 = g_astc_unquant[range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
astc_results.m_weights[x + y * 4] = ccell_result_selectors[x + y * 4];
if (invert)
astc_results.m_weights[x + y * 4] = 3 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 1;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = part_err;
total_results++;
}
}
static uint32_t estimate_partition2(uint32_t num_weights, uint32_t num_comps, const uint32_t* pWeights, const color_rgba block[4][4], const uint32_t weights[4])
{
assert(pWeights[0] == 0 && pWeights[num_weights - 1] == 64);
uint64_t best_err = UINT64_MAX;
uint32_t best_common_pattern = 0;
for (uint32_t common_pattern = 0; common_pattern < TOTAL_ASTC_BC7_COMMON_PARTITIONS2; common_pattern++)
{
const uint32_t bc7_pattern = g_astc_bc7_common_partitions2[common_pattern].m_bc7;
const uint8_t* pPartition = &g_bc7_partition2[bc7_pattern * 16];
color_quad_u8 subset_colors[2][16];
uint32_t subset_total_colors[2] = { 0, 0 };
for (uint32_t index = 0; index < 16; index++)
subset_colors[pPartition[index]][subset_total_colors[pPartition[index]]++] = ((const color_quad_u8*)block)[index];
uint64_t total_subset_err = 0;
for (uint32_t subset = 0; (subset < 2) && (total_subset_err < best_err); subset++)
total_subset_err += color_cell_compression_est_astc(num_weights, num_comps, pWeights, subset_total_colors[subset], &subset_colors[subset][0], best_err, weights);
if (total_subset_err < best_err)
{
best_err = total_subset_err;
best_common_pattern = common_pattern;
}
}
return best_common_pattern;
}
// MODE 2
// 2-subset, 3-bit indices, 4-bit endpoints, BC7 mode 1
// DualPlane: 0, WeightRange: 5 (8), Subsets: 2, CEM: 8 (RGB Direct ), EndpointRange: 8 (16) MODE1
static void astc_mode2(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params, bool estimate_partition)
{
uint32_t first_common_pattern = 0;
uint32_t last_common_pattern = TOTAL_ASTC_BC7_COMMON_PARTITIONS2;
if (estimate_partition)
{
const uint32_t weights[4] = { 1, 1, 1, 1 };
first_common_pattern = estimate_partition2(8, 3, g_bc7_weights3, block, weights);
last_common_pattern = first_common_pattern + 1;
}
for (uint32_t common_pattern = first_common_pattern; common_pattern < last_common_pattern; common_pattern++)
{
const uint32_t bc7_pattern = g_astc_bc7_common_partitions2[common_pattern].m_bc7;
color_rgba part_pixels[2][16];
uint32_t part_pixel_index[4][4];
uint32_t num_part_pixels[2] = { 0, 0 };
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t part = g_bc7_partition2[16 * bc7_pattern + x + y * 4];
part_pixel_index[y][x] = num_part_pixels[part];
part_pixels[part][num_part_pixels[part]++] = block[y][x];
}
}
color_cell_compressor_params ccell_params[2];
color_cell_compressor_results ccell_results[2];
uint8_t ccell_result_selectors[2][16];
uint8_t ccell_result_selectors_temp[2][16];
uint64_t total_part_err = 0;
for (uint32_t part = 0; part < 2; part++)
{
memset(&ccell_params[part], 0, sizeof(ccell_params[part]));
ccell_params[part].m_num_pixels = num_part_pixels[part];
ccell_params[part].m_pPixels = (color_quad_u8*)&part_pixels[part][0];
ccell_params[part].m_num_selector_weights = 8;
ccell_params[part].m_pSelector_weights = g_bc7_weights3;
ccell_params[part].m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights3x;
ccell_params[part].m_astc_endpoint_range = 8;
ccell_params[part].m_weights[0] = 1;
ccell_params[part].m_weights[1] = 1;
ccell_params[part].m_weights[2] = 1;
ccell_params[part].m_weights[3] = 1;
memset(&ccell_results[part], 0, sizeof(ccell_results[part]));
ccell_results[part].m_pSelectors = &ccell_result_selectors[part][0];
ccell_results[part].m_pSelectors_temp = &ccell_result_selectors_temp[part][0];
uint64_t part_err = color_cell_compression(255, &ccell_params[part], &ccell_results[part], &comp_params);
total_part_err += part_err;
} // part
{
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = 5;
astc_results.m_ccs = 0;
astc_results.m_subsets = 2;
astc_results.m_partition_seed = g_astc_bc7_common_partitions2[common_pattern].m_astc;
astc_results.m_cem = 8;
uint32_t p0 = 0;
uint32_t p1 = 1;
if (g_astc_bc7_common_partitions2[common_pattern].m_invert)
std::swap(p0, p1);
astc_results.m_endpoints[0] = ccell_results[p0].m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results[p0].m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results[p0].m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results[p0].m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results[p0].m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results[p0].m_astc_high_endpoint.m_c[2];
const uint32_t range = 8;
bool invert[2] = { false, false };
int s0 = g_astc_unquant[range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
invert[0] = true;
}
astc_results.m_endpoints[6] = ccell_results[p1].m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[7] = ccell_results[p1].m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[8] = ccell_results[p1].m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[9] = ccell_results[p1].m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[10] = ccell_results[p1].m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[11] = ccell_results[p1].m_astc_high_endpoint.m_c[2];
s0 = g_astc_unquant[range][astc_results.m_endpoints[0 + 6]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[2 + 6]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[4 + 6]].m_unquant;
s1 = g_astc_unquant[range][astc_results.m_endpoints[1 + 6]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[3 + 6]].m_unquant + g_astc_unquant[range][astc_results.m_endpoints[5 + 6]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0 + 6], astc_results.m_endpoints[1 + 6]);
std::swap(astc_results.m_endpoints[2 + 6], astc_results.m_endpoints[3 + 6]);
std::swap(astc_results.m_endpoints[4 + 6], astc_results.m_endpoints[5 + 6]);
invert[1] = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t bc7_part = g_bc7_partition2[16 * bc7_pattern + x + y * 4];
astc_results.m_weights[x + y * 4] = ccell_result_selectors[bc7_part][part_pixel_index[y][x]];
uint32_t astc_part = bc7_part;
if (g_astc_bc7_common_partitions2[common_pattern].m_invert)
astc_part = 1 - astc_part;
if (invert[astc_part])
astc_results.m_weights[x + y * 4] = 7 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 2;
pResults[total_results].m_common_pattern = common_pattern;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = total_part_err;
total_results++;
}
}
} // common_pattern
}
// MODE 3
// 3-subsets, 2-bit indices, [0,11] endpoints, BC7 mode 2
// DualPlane: 0, WeightRange: 2 (4), Subsets: 3, CEM: 8 (RGB Direct ), EndpointRange: 7 (12) MODE2
static void astc_mode3(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params, bool estimate_partition)
{
uint32_t first_common_pattern = 0;
uint32_t last_common_pattern = TOTAL_ASTC_BC7_COMMON_PARTITIONS3;
if (estimate_partition)
{
uint64_t best_err = UINT64_MAX;
uint32_t best_common_pattern = 0;
const uint32_t weights[4] = { 1, 1, 1, 1 };
for (uint32_t common_pattern = 0; common_pattern < TOTAL_ASTC_BC7_COMMON_PARTITIONS3; common_pattern++)
{
const uint32_t bc7_pattern = g_astc_bc7_common_partitions3[common_pattern].m_bc7;
const uint8_t* pPartition = &g_bc7_partition3[bc7_pattern * 16];
color_quad_u8 subset_colors[3][16];
uint32_t subset_total_colors[3] = { 0, 0 };
for (uint32_t index = 0; index < 16; index++)
subset_colors[pPartition[index]][subset_total_colors[pPartition[index]]++] = ((const color_quad_u8*)block)[index];
uint64_t total_subset_err = 0;
for (uint32_t subset = 0; (subset < 3) && (total_subset_err < best_err); subset++)
total_subset_err += color_cell_compression_est_astc(4, 3, g_bc7_weights2, subset_total_colors[subset], &subset_colors[subset][0], best_err, weights);
if (total_subset_err < best_err)
{
best_err = total_subset_err;
best_common_pattern = common_pattern;
}
}
first_common_pattern = best_common_pattern;
last_common_pattern = best_common_pattern + 1;
}
for (uint32_t common_pattern = first_common_pattern; common_pattern < last_common_pattern; common_pattern++)
{
const uint32_t endpoint_range = 7;
const uint32_t bc7_pattern = g_astc_bc7_common_partitions3[common_pattern].m_bc7;
color_rgba part_pixels[3][16];
uint32_t part_pixel_index[4][4];
uint32_t num_part_pixels[3] = { 0, 0, 0 };
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t bc7_part = g_bc7_partition3[16 * bc7_pattern + x + y * 4];
part_pixel_index[y][x] = num_part_pixels[bc7_part];
part_pixels[bc7_part][num_part_pixels[bc7_part]++] = block[y][x];
}
}
color_cell_compressor_params ccell_params[3];
color_cell_compressor_results ccell_results[3];
uint8_t ccell_result_selectors[3][16];
uint8_t ccell_result_selectors_temp[3][16];
uint64_t total_part_err = 0;
for (uint32_t bc7_part = 0; bc7_part < 3; bc7_part++)
{
memset(&ccell_params[bc7_part], 0, sizeof(ccell_params[bc7_part]));
ccell_params[bc7_part].m_num_pixels = num_part_pixels[bc7_part];
ccell_params[bc7_part].m_pPixels = (color_quad_u8*)&part_pixels[bc7_part][0];
ccell_params[bc7_part].m_num_selector_weights = 4;
ccell_params[bc7_part].m_pSelector_weights = g_bc7_weights2;
ccell_params[bc7_part].m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params[bc7_part].m_astc_endpoint_range = endpoint_range;
ccell_params[bc7_part].m_weights[0] = 1;
ccell_params[bc7_part].m_weights[1] = 1;
ccell_params[bc7_part].m_weights[2] = 1;
ccell_params[bc7_part].m_weights[3] = 1;
memset(&ccell_results[bc7_part], 0, sizeof(ccell_results[bc7_part]));
ccell_results[bc7_part].m_pSelectors = &ccell_result_selectors[bc7_part][0];
ccell_results[bc7_part].m_pSelectors_temp = &ccell_result_selectors_temp[bc7_part][0];
uint64_t part_err = color_cell_compression(255, &ccell_params[bc7_part], &ccell_results[bc7_part], &comp_params);
total_part_err += part_err;
} // part
{
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = 2;
astc_results.m_ccs = 0;
astc_results.m_subsets = 3;
astc_results.m_partition_seed = g_astc_bc7_common_partitions3[common_pattern].m_astc;
astc_results.m_cem = 8;
uint32_t astc_to_bc7_part[3]; // converts ASTC to BC7 partition index
const uint32_t perm = g_astc_bc7_common_partitions3[common_pattern].m_astc_to_bc7_perm;
astc_to_bc7_part[0] = g_astc_to_bc7_partition_index_perm_tables[perm][0];
astc_to_bc7_part[1] = g_astc_to_bc7_partition_index_perm_tables[perm][1];
astc_to_bc7_part[2] = g_astc_to_bc7_partition_index_perm_tables[perm][2];
bool invert_astc_part[3] = { false, false, false };
for (uint32_t astc_part = 0; astc_part < 3; astc_part++)
{
uint8_t* pEndpoints = &astc_results.m_endpoints[6 * astc_part];
pEndpoints[0] = ccell_results[astc_to_bc7_part[astc_part]].m_astc_low_endpoint.m_c[0];
pEndpoints[1] = ccell_results[astc_to_bc7_part[astc_part]].m_astc_high_endpoint.m_c[0];
pEndpoints[2] = ccell_results[astc_to_bc7_part[astc_part]].m_astc_low_endpoint.m_c[1];
pEndpoints[3] = ccell_results[astc_to_bc7_part[astc_part]].m_astc_high_endpoint.m_c[1];
pEndpoints[4] = ccell_results[astc_to_bc7_part[astc_part]].m_astc_low_endpoint.m_c[2];
pEndpoints[5] = ccell_results[astc_to_bc7_part[astc_part]].m_astc_high_endpoint.m_c[2];
int s0 = g_astc_unquant[endpoint_range][pEndpoints[0]].m_unquant + g_astc_unquant[endpoint_range][pEndpoints[2]].m_unquant + g_astc_unquant[endpoint_range][pEndpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][pEndpoints[1]].m_unquant + g_astc_unquant[endpoint_range][pEndpoints[3]].m_unquant + g_astc_unquant[endpoint_range][pEndpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(pEndpoints[0], pEndpoints[1]);
std::swap(pEndpoints[2], pEndpoints[3]);
std::swap(pEndpoints[4], pEndpoints[5]);
invert_astc_part[astc_part] = true;
}
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t bc7_part = g_bc7_partition3[16 * bc7_pattern + x + y * 4];
astc_results.m_weights[x + y * 4] = ccell_result_selectors[bc7_part][part_pixel_index[y][x]];
uint32_t astc_part = 0;
for (uint32_t i = 0; i < 3; i++)
{
if (astc_to_bc7_part[i] == bc7_part)
{
astc_part = i;
break;
}
}
if (invert_astc_part[astc_part])
astc_results.m_weights[x + y * 4] = 3 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 3;
pResults[total_results].m_common_pattern = common_pattern;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = total_part_err;
total_results++;
}
}
} // common_pattern
}
// MODE 4
// DualPlane: 0, WeightRange: 2 (4), Subsets: 2, CEM: 8 (RGB Direct ), EndpointRange: 12 (40) MODE3
static void astc_mode4(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params, bool estimate_partition)
{
//const uint32_t weight_range = 2;
const uint32_t endpoint_range = 12;
uint32_t first_common_pattern = 0;
uint32_t last_common_pattern = TOTAL_ASTC_BC7_COMMON_PARTITIONS2;
if (estimate_partition)
{
const uint32_t weights[4] = { 1, 1, 1, 1 };
first_common_pattern = estimate_partition2(4, 3, g_bc7_weights2, block, weights);
last_common_pattern = first_common_pattern + 1;
}
for (uint32_t common_pattern = first_common_pattern; common_pattern < last_common_pattern; common_pattern++)
{
const uint32_t bc7_pattern = g_astc_bc7_common_partitions2[common_pattern].m_bc7;
color_rgba part_pixels[2][16];
uint32_t part_pixel_index[4][4];
uint32_t num_part_pixels[2] = { 0, 0 };
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t part = g_bc7_partition2[16 * bc7_pattern + x + y * 4];
part_pixel_index[y][x] = num_part_pixels[part];
part_pixels[part][num_part_pixels[part]++] = block[y][x];
}
}
color_cell_compressor_params ccell_params[2];
color_cell_compressor_results ccell_results[2];
uint8_t ccell_result_selectors[2][16];
uint8_t ccell_result_selectors_temp[2][16];
uint64_t total_part_err = 0;
for (uint32_t part = 0; part < 2; part++)
{
memset(&ccell_params[part], 0, sizeof(ccell_params[part]));
ccell_params[part].m_num_pixels = num_part_pixels[part];
ccell_params[part].m_pPixels = (color_quad_u8*)&part_pixels[part][0];
ccell_params[part].m_num_selector_weights = 4;
ccell_params[part].m_pSelector_weights = g_bc7_weights2;
ccell_params[part].m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params[part].m_astc_endpoint_range = endpoint_range;
ccell_params[part].m_weights[0] = 1;
ccell_params[part].m_weights[1] = 1;
ccell_params[part].m_weights[2] = 1;
ccell_params[part].m_weights[3] = 1;
memset(&ccell_results[part], 0, sizeof(ccell_results[part]));
ccell_results[part].m_pSelectors = &ccell_result_selectors[part][0];
ccell_results[part].m_pSelectors_temp = &ccell_result_selectors_temp[part][0];
uint64_t part_err = color_cell_compression(255, &ccell_params[part], &ccell_results[part], &comp_params);
total_part_err += part_err;
} // part
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = 2;
astc_results.m_ccs = 0;
astc_results.m_subsets = 2;
astc_results.m_partition_seed = g_astc_bc7_common_partitions2[common_pattern].m_astc;
astc_results.m_cem = 8;
uint32_t p0 = 0;
uint32_t p1 = 1;
if (g_astc_bc7_common_partitions2[common_pattern].m_invert)
std::swap(p0, p1);
astc_results.m_endpoints[0] = ccell_results[p0].m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results[p0].m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results[p0].m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results[p0].m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results[p0].m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results[p0].m_astc_high_endpoint.m_c[2];
bool invert[2] = { false, false };
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
invert[0] = true;
}
astc_results.m_endpoints[6] = ccell_results[p1].m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[7] = ccell_results[p1].m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[8] = ccell_results[p1].m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[9] = ccell_results[p1].m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[10] = ccell_results[p1].m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[11] = ccell_results[p1].m_astc_high_endpoint.m_c[2];
s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0 + 6]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2 + 6]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4 + 6]].m_unquant;
s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1 + 6]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3 + 6]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5 + 6]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0 + 6], astc_results.m_endpoints[1 + 6]);
std::swap(astc_results.m_endpoints[2 + 6], astc_results.m_endpoints[3 + 6]);
std::swap(astc_results.m_endpoints[4 + 6], astc_results.m_endpoints[5 + 6]);
invert[1] = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t bc7_part = g_bc7_partition2[16 * bc7_pattern + x + y * 4];
astc_results.m_weights[x + y * 4] = ccell_result_selectors[bc7_part][part_pixel_index[y][x]];
uint32_t astc_part = bc7_part;
if (g_astc_bc7_common_partitions2[common_pattern].m_invert)
astc_part = 1 - astc_part;
if (invert[astc_part])
astc_results.m_weights[x + y * 4] = 3 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 4;
pResults[total_results].m_common_pattern = common_pattern;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = total_part_err;
total_results++;
}
} // common_pattern
}
// MODE 5
// DualPlane: 0, WeightRange: 5 (8), Subsets: 1, CEM: 8 (RGB Direct ), EndpointRange: 20 (256) BC7 MODE 6 (or MODE 1 1-subset)
static void astc_mode5(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
const uint32_t weight_range = 5;
const uint32_t endpoint_range = 20;
color_cell_compressor_params ccell_params;
memset(&ccell_params, 0, sizeof(ccell_params));
ccell_params.m_num_pixels = 16;
ccell_params.m_pPixels = (color_quad_u8*)&block[0][0];
ccell_params.m_num_selector_weights = 8;
ccell_params.m_pSelector_weights = g_bc7_weights3;
ccell_params.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights3x;
ccell_params.m_astc_endpoint_range = endpoint_range;
ccell_params.m_weights[0] = 1;
ccell_params.m_weights[1] = 1;
ccell_params.m_weights[2] = 1;
ccell_params.m_weights[3] = 1;
color_cell_compressor_results ccell_results;
uint8_t ccell_result_selectors[16];
uint8_t ccell_result_selectors_temp[16];
memset(&ccell_results, 0, sizeof(ccell_results));
ccell_results.m_pSelectors = &ccell_result_selectors[0];
ccell_results.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err = color_cell_compression(255, &ccell_params, &ccell_results, &comp_params);
// ASTC
astc_block_desc blk;
memset(&blk, 0, sizeof(blk));
blk.m_dual_plane = false;
blk.m_weight_range = weight_range;
blk.m_ccs = 0;
blk.m_subsets = 1;
blk.m_partition_seed = 0;
blk.m_cem = 8;
blk.m_endpoints[0] = ccell_results.m_astc_low_endpoint.m_c[0];
blk.m_endpoints[1] = ccell_results.m_astc_high_endpoint.m_c[0];
blk.m_endpoints[2] = ccell_results.m_astc_low_endpoint.m_c[1];
blk.m_endpoints[3] = ccell_results.m_astc_high_endpoint.m_c[1];
blk.m_endpoints[4] = ccell_results.m_astc_low_endpoint.m_c[2];
blk.m_endpoints[5] = ccell_results.m_astc_high_endpoint.m_c[2];
bool invert = false;
int s0 = g_astc_unquant[endpoint_range][blk.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][blk.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);
std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);
std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
blk.m_weights[x + y * 4] = ccell_result_selectors[x + y * 4];
if (invert)
blk.m_weights[x + y * 4] = 7 - blk.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 5;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = blk;
pResults[total_results].m_astc_err = part_err;
total_results++;
}
}
// MODE 6
// DualPlane: 1, WeightRange: 2 (4), Subsets: 1, CEM: 8 (RGB Direct ), EndpointRange: 18 (160) BC7 MODE5
static void astc_mode6(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
for (uint32_t rot_comp = 0; rot_comp < 3; rot_comp++)
{
const uint32_t weight_range = 2;
const uint32_t endpoint_range = 18;
color_quad_u8 block_rgb[16];
color_quad_u8 block_a[16];
for (uint32_t i = 0; i < 16; i++)
{
block_rgb[i] = ((color_quad_u8*)&block[0][0])[i];
block_a[i] = block_rgb[i];
uint8_t c = block_a[i].m_c[rot_comp];
block_a[i].m_c[0] = c;
block_a[i].m_c[1] = c;
block_a[i].m_c[2] = c;
block_a[i].m_c[3] = 255;
block_rgb[i].m_c[rot_comp] = 255;
}
uint8_t ccell_result_selectors_temp[16];
color_cell_compressor_params ccell_params_rgb;
memset(&ccell_params_rgb, 0, sizeof(ccell_params_rgb));
ccell_params_rgb.m_num_pixels = 16;
ccell_params_rgb.m_pPixels = block_rgb;
ccell_params_rgb.m_num_selector_weights = 4;
ccell_params_rgb.m_pSelector_weights = g_bc7_weights2;
ccell_params_rgb.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params_rgb.m_astc_endpoint_range = endpoint_range;
ccell_params_rgb.m_weights[0] = 1;
ccell_params_rgb.m_weights[1] = 1;
ccell_params_rgb.m_weights[2] = 1;
ccell_params_rgb.m_weights[3] = 1;
color_cell_compressor_results ccell_results_rgb;
uint8_t ccell_result_selectors_rgb[16];
memset(&ccell_results_rgb, 0, sizeof(ccell_results_rgb));
ccell_results_rgb.m_pSelectors = &ccell_result_selectors_rgb[0];
ccell_results_rgb.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err_rgb = color_cell_compression(255, &ccell_params_rgb, &ccell_results_rgb, &comp_params);
color_cell_compressor_params ccell_params_a;
memset(&ccell_params_a, 0, sizeof(ccell_params_a));
ccell_params_a.m_num_pixels = 16;
ccell_params_a.m_pPixels = block_a;
ccell_params_a.m_num_selector_weights = 4;
ccell_params_a.m_pSelector_weights = g_bc7_weights2;
ccell_params_a.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params_a.m_astc_endpoint_range = endpoint_range;
ccell_params_a.m_weights[0] = 1;
ccell_params_a.m_weights[1] = 1;
ccell_params_a.m_weights[2] = 1;
ccell_params_a.m_weights[3] = 1;
color_cell_compressor_results ccell_results_a;
uint8_t ccell_result_selectors_a[16];
memset(&ccell_results_a, 0, sizeof(ccell_results_a));
ccell_results_a.m_pSelectors = &ccell_result_selectors_a[0];
ccell_results_a.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err_a = color_cell_compression(255, &ccell_params_a, &ccell_results_a, &comp_params) / 3;
uint64_t total_err = part_err_rgb + part_err_a;
// ASTC
astc_block_desc blk;
memset(&blk, 0, sizeof(blk));
blk.m_dual_plane = true;
blk.m_weight_range = weight_range;
blk.m_ccs = rot_comp;
blk.m_subsets = 1;
blk.m_partition_seed = 0;
blk.m_cem = 8;
blk.m_endpoints[0] = (rot_comp == 0 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[0];
blk.m_endpoints[1] = (rot_comp == 0 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[0];
blk.m_endpoints[2] = (rot_comp == 1 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[1];
blk.m_endpoints[3] = (rot_comp == 1 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[1];
blk.m_endpoints[4] = (rot_comp == 2 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[2];
blk.m_endpoints[5] = (rot_comp == 2 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[2];
bool invert = false;
int s0 = g_astc_unquant[endpoint_range][blk.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][blk.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);
std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);
std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
uint32_t rgb_index = ccell_result_selectors_rgb[x + y * 4];
uint32_t a_index = ccell_result_selectors_a[x + y * 4];
if (invert)
{
rgb_index = 3 - rgb_index;
a_index = 3 - a_index;
}
blk.m_weights[(x + y * 4) * 2 + 0] = (uint8_t)rgb_index;
blk.m_weights[(x + y * 4) * 2 + 1] = (uint8_t)a_index;
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 6;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = blk;
pResults[total_results].m_astc_err = total_err;
total_results++;
}
} // rot_comp
}
// MODE 7 - 2 subset ASTC, 3 subset BC7
// DualPlane: 0, WeightRange: 2 (4), Subsets: 2, CEM: 8 (RGB Direct ), EndpointRange: 12 (40) MODE2
static void astc_mode7(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params, bool estimate_partition)
{
uint32_t first_common_pattern = 0;
uint32_t last_common_pattern = TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS;
if (estimate_partition)
{
uint64_t best_err = UINT64_MAX;
uint32_t best_common_pattern = 0;
const uint32_t weights[4] = { 1, 1, 1, 1 };
for (uint32_t common_pattern = 0; common_pattern < TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS; common_pattern++)
{
const uint8_t* pPartition = &g_bc7_3_astc2_patterns2[common_pattern][0];
#ifdef _DEBUG
const uint32_t astc_pattern = g_bc7_3_astc2_common_partitions[common_pattern].m_astc2;
const uint32_t bc7_pattern = g_bc7_3_astc2_common_partitions[common_pattern].m_bc73;
const uint32_t common_pattern_k = g_bc7_3_astc2_common_partitions[common_pattern].k;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t astc_part = bc7_convert_partition_index_3_to_2(g_bc7_partition3[16 * bc7_pattern + x + y * 4], common_pattern_k);
assert((int)astc_part == astc_compute_texel_partition(astc_pattern, x, y, 0, 2, true));
assert(astc_part == pPartition[x + y * 4]);
}
}
#endif
color_quad_u8 subset_colors[2][16];
uint32_t subset_total_colors[2] = { 0, 0 };
for (uint32_t index = 0; index < 16; index++)
subset_colors[pPartition[index]][subset_total_colors[pPartition[index]]++] = ((const color_quad_u8*)block)[index];
uint64_t total_subset_err = 0;
for (uint32_t subset = 0; (subset < 2) && (total_subset_err < best_err); subset++)
total_subset_err += color_cell_compression_est_astc(4, 3, g_bc7_weights2, subset_total_colors[subset], &subset_colors[subset][0], best_err, weights);
if (total_subset_err < best_err)
{
best_err = total_subset_err;
best_common_pattern = common_pattern;
}
}
first_common_pattern = best_common_pattern;
last_common_pattern = best_common_pattern + 1;
}
//const uint32_t weight_range = 2;
const uint32_t endpoint_range = 12;
for (uint32_t common_pattern = first_common_pattern; common_pattern < last_common_pattern; common_pattern++)
{
const uint32_t astc_pattern = g_bc7_3_astc2_common_partitions[common_pattern].m_astc2;
const uint32_t bc7_pattern = g_bc7_3_astc2_common_partitions[common_pattern].m_bc73;
const uint32_t common_pattern_k = g_bc7_3_astc2_common_partitions[common_pattern].k;
color_rgba part_pixels[2][16];
uint32_t part_pixel_index[4][4];
uint32_t num_part_pixels[2] = { 0, 0 };
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t astc_part = bc7_convert_partition_index_3_to_2(g_bc7_partition3[16 * bc7_pattern + x + y * 4], common_pattern_k);
#ifdef _DEBUG
assert((int)astc_part == astc_compute_texel_partition(astc_pattern, x, y, 0, 2, true));
#endif
part_pixel_index[y][x] = num_part_pixels[astc_part];
part_pixels[astc_part][num_part_pixels[astc_part]++] = block[y][x];
}
}
color_cell_compressor_params ccell_params[2];
color_cell_compressor_results ccell_results[2];
uint8_t ccell_result_selectors[2][16];
uint8_t ccell_result_selectors_temp[2][16];
uint64_t total_part_err = 0;
for (uint32_t part = 0; part < 2; part++)
{
memset(&ccell_params[part], 0, sizeof(ccell_params[part]));
ccell_params[part].m_num_pixels = num_part_pixels[part];
ccell_params[part].m_pPixels = (color_quad_u8*)&part_pixels[part][0];
ccell_params[part].m_num_selector_weights = 4;
ccell_params[part].m_pSelector_weights = g_bc7_weights2;
ccell_params[part].m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params[part].m_astc_endpoint_range = endpoint_range;
ccell_params[part].m_weights[0] = 1;
ccell_params[part].m_weights[1] = 1;
ccell_params[part].m_weights[2] = 1;
ccell_params[part].m_weights[3] = 1;
memset(&ccell_results[part], 0, sizeof(ccell_results[part]));
ccell_results[part].m_pSelectors = &ccell_result_selectors[part][0];
ccell_results[part].m_pSelectors_temp = &ccell_result_selectors_temp[part][0];
uint64_t part_err = color_cell_compression(255, &ccell_params[part], &ccell_results[part], &comp_params);
total_part_err += part_err;
} // part
// ASTC
astc_block_desc blk;
memset(&blk, 0, sizeof(blk));
blk.m_dual_plane = false;
blk.m_weight_range = 2;
blk.m_ccs = 0;
blk.m_subsets = 2;
blk.m_partition_seed = astc_pattern;
blk.m_cem = 8;
const uint32_t p0 = 0;
const uint32_t p1 = 1;
blk.m_endpoints[0] = ccell_results[p0].m_astc_low_endpoint.m_c[0];
blk.m_endpoints[1] = ccell_results[p0].m_astc_high_endpoint.m_c[0];
blk.m_endpoints[2] = ccell_results[p0].m_astc_low_endpoint.m_c[1];
blk.m_endpoints[3] = ccell_results[p0].m_astc_high_endpoint.m_c[1];
blk.m_endpoints[4] = ccell_results[p0].m_astc_low_endpoint.m_c[2];
blk.m_endpoints[5] = ccell_results[p0].m_astc_high_endpoint.m_c[2];
bool invert[2] = { false, false };
int s0 = g_astc_unquant[endpoint_range][blk.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][blk.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);
std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);
std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);
invert[0] = true;
}
blk.m_endpoints[6] = ccell_results[p1].m_astc_low_endpoint.m_c[0];
blk.m_endpoints[7] = ccell_results[p1].m_astc_high_endpoint.m_c[0];
blk.m_endpoints[8] = ccell_results[p1].m_astc_low_endpoint.m_c[1];
blk.m_endpoints[9] = ccell_results[p1].m_astc_high_endpoint.m_c[1];
blk.m_endpoints[10] = ccell_results[p1].m_astc_low_endpoint.m_c[2];
blk.m_endpoints[11] = ccell_results[p1].m_astc_high_endpoint.m_c[2];
s0 = g_astc_unquant[endpoint_range][blk.m_endpoints[0 + 6]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[2 + 6]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[4 + 6]].m_unquant;
s1 = g_astc_unquant[endpoint_range][blk.m_endpoints[1 + 6]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[3 + 6]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[5 + 6]].m_unquant;
if (s1 < s0)
{
std::swap(blk.m_endpoints[0 + 6], blk.m_endpoints[1 + 6]);
std::swap(blk.m_endpoints[2 + 6], blk.m_endpoints[3 + 6]);
std::swap(blk.m_endpoints[4 + 6], blk.m_endpoints[5 + 6]);
invert[1] = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t astc_part = bc7_convert_partition_index_3_to_2(g_bc7_partition3[16 * bc7_pattern + x + y * 4], common_pattern_k);
blk.m_weights[x + y * 4] = ccell_result_selectors[astc_part][part_pixel_index[y][x]];
if (invert[astc_part])
blk.m_weights[x + y * 4] = 3 - blk.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 7;
pResults[total_results].m_common_pattern = common_pattern;
pResults[total_results].m_astc = blk;
pResults[total_results].m_astc_err = total_part_err;
total_results++;
}
} // common_pattern
}
static void estimate_partition2_list(uint32_t num_weights, uint32_t num_comps, const uint32_t* pWeights, const color_rgba block[4][4], uint32_t* pParts, uint32_t max_parts, const uint32_t weights[4])
{
assert(pWeights[0] == 0 && pWeights[num_weights - 1] == 64);
const uint32_t MAX_PARTS = 8;
assert(max_parts <= MAX_PARTS);
uint64_t part_error[MAX_PARTS];
memset(part_error, 0xFF, sizeof(part_error));
memset(pParts, 0, sizeof(pParts[0]) * max_parts);
for (uint32_t common_pattern = 0; common_pattern < TOTAL_ASTC_BC7_COMMON_PARTITIONS2; common_pattern++)
{
const uint32_t bc7_pattern = g_astc_bc7_common_partitions2[common_pattern].m_bc7;
const uint8_t* pPartition = &g_bc7_partition2[bc7_pattern * 16];
color_quad_u8 subset_colors[2][16];
uint32_t subset_total_colors[2] = { 0, 0 };
for (uint32_t index = 0; index < 16; index++)
subset_colors[pPartition[index]][subset_total_colors[pPartition[index]]++] = ((const color_quad_u8*)block)[index];
uint64_t total_subset_err = 0;
for (uint32_t subset = 0; subset < 2; subset++)
total_subset_err += color_cell_compression_est_astc(num_weights, num_comps, pWeights, subset_total_colors[subset], &subset_colors[subset][0], UINT64_MAX, weights);
for (int i = 0; i < (int)max_parts; i++)
{
if (total_subset_err < part_error[i])
{
for (int j = max_parts - 1; j > i; --j)
{
pParts[j] = pParts[j - 1];
part_error[j] = part_error[j - 1];
}
pParts[i] = common_pattern;
part_error[i] = total_subset_err;
break;
}
}
}
#ifdef _DEBUG
for (uint32_t i = 0; i < max_parts - 1; i++)
{
assert(part_error[i] <= part_error[i + 1]);
}
#endif
}
// 9. DualPlane: 0, WeightRange: 2 (4), Subsets: 2, CEM: 12 (RGBA Direct), EndpointRange: 8 (16) - BC7 MODE 7
// 16. DualPlane: 0, WeightRange : 2 (4), Subsets : 2, CEM: 4 (LA Direct), EndpointRange : 20 (256) - BC7 MODE 7
static void astc_mode9_or_16(uint32_t mode, const color_rgba source_block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params, uint32_t estimate_partition_list_size)
{
assert(mode == 9 || mode == 16);
const color_rgba* pBlock = &source_block[0][0];
color_rgba temp_block[16];
if (mode == 16)
{
for (uint32_t i = 0; i < 16; i++)
{
if (mode == 16)
{
assert(pBlock[i].r == pBlock[i].g);
assert(pBlock[i].r == pBlock[i].b);
}
const uint32_t l = pBlock[i].r;
const uint32_t a = pBlock[i].a;
// Use (l,0,0,a) not (l,l,l,a) so both components are treated equally.
temp_block[i].set_noclamp_rgba(l, 0, 0, a);
}
pBlock = temp_block;
}
const uint32_t weights[4] = { 1, 1, 1, 1 };
//const uint32_t weight_range = 2;
const uint32_t endpoint_range = (mode == 16) ? 20 : 8;
uint32_t first_common_pattern = 0;
uint32_t last_common_pattern = TOTAL_ASTC_BC7_COMMON_PARTITIONS2;
bool use_part_list = false;
const uint32_t MAX_PARTS = 8;
uint32_t parts[MAX_PARTS];
if (estimate_partition_list_size == 1)
{
first_common_pattern = estimate_partition2(4, 4, g_bc7_weights2, (const color_rgba(*)[4])pBlock, weights);
last_common_pattern = first_common_pattern + 1;
}
else if (estimate_partition_list_size > 0)
{
assert(estimate_partition_list_size <= MAX_PARTS);
estimate_partition_list_size = basisu::minimum(estimate_partition_list_size, MAX_PARTS);
estimate_partition2_list(4, 4, g_bc7_weights2, (const color_rgba(*)[4])pBlock, parts, estimate_partition_list_size, weights);
first_common_pattern = 0;
last_common_pattern = estimate_partition_list_size;
use_part_list = true;
#ifdef _DEBUG
assert(parts[0] == estimate_partition2(4, 4, g_bc7_weights2, (const color_rgba(*)[4])pBlock, weights));
#endif
}
for (uint32_t common_pattern_iter = first_common_pattern; common_pattern_iter < last_common_pattern; common_pattern_iter++)
{
const uint32_t common_pattern = use_part_list ? parts[common_pattern_iter] : common_pattern_iter;
const uint32_t bc7_pattern = g_astc_bc7_common_partitions2[common_pattern].m_bc7;
color_rgba part_pixels[2][16];
uint32_t part_pixel_index[4][4];
uint32_t num_part_pixels[2] = { 0, 0 };
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t part = g_bc7_partition2[16 * bc7_pattern + x + y * 4];
part_pixel_index[y][x] = num_part_pixels[part];
part_pixels[part][num_part_pixels[part]++] = pBlock[y * 4 + x];
}
}
color_cell_compressor_params ccell_params[2];
color_cell_compressor_results ccell_results[2];
uint8_t ccell_result_selectors[2][16];
uint8_t ccell_result_selectors_temp[2][16];
uint64_t total_err = 0;
for (uint32_t subset = 0; subset < 2; subset++)
{
memset(&ccell_params[subset], 0, sizeof(ccell_params[subset]));
ccell_params[subset].m_num_pixels = num_part_pixels[subset];
ccell_params[subset].m_pPixels = (color_quad_u8*)&part_pixels[subset][0];
ccell_params[subset].m_num_selector_weights = 4;
ccell_params[subset].m_pSelector_weights = g_bc7_weights2;
ccell_params[subset].m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params[subset].m_astc_endpoint_range = endpoint_range;
ccell_params[subset].m_weights[0] = weights[0];
ccell_params[subset].m_weights[1] = weights[1];
ccell_params[subset].m_weights[2] = weights[2];
ccell_params[subset].m_weights[3] = weights[3];
ccell_params[subset].m_has_alpha = true;
memset(&ccell_results[subset], 0, sizeof(ccell_results[subset]));
ccell_results[subset].m_pSelectors = &ccell_result_selectors[subset][0];
ccell_results[subset].m_pSelectors_temp = &ccell_result_selectors_temp[subset][0];
uint64_t subset_err = color_cell_compression(255, &ccell_params[subset], &ccell_results[subset], &comp_params);
if (mode == 16)
{
color_rgba colors[4];
for (uint32_t c = 0; c < 4; c++)
{
colors[0].m_comps[c] = g_astc_unquant[endpoint_range][ccell_results[subset].m_astc_low_endpoint.m_c[(c < 3) ? 0 : 3]].m_unquant;
colors[3].m_comps[c] = g_astc_unquant[endpoint_range][ccell_results[subset].m_astc_high_endpoint.m_c[(c < 3) ? 0 : 3]].m_unquant;
}
for (uint32_t i = 1; i < 4 - 1; i++)
for (uint32_t c = 0; c < 4; c++)
colors[i].m_comps[c] = (uint8_t)astc_interpolate(colors[0].m_comps[c], colors[3].m_comps[c], g_bc7_weights2[i], false);
for (uint32_t p = 0; p < ccell_params[subset].m_num_pixels; p++)
{
color_rgba orig_pix(part_pixels[subset][p]);
orig_pix.g = orig_pix.r;
orig_pix.b = orig_pix.r;
total_err += color_distance_la(orig_pix, colors[ccell_result_selectors[subset][p]]);
}
}
else
{
total_err += subset_err;
}
} // subset
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = 2;
astc_results.m_ccs = 0;
astc_results.m_subsets = 2;
astc_results.m_partition_seed = g_astc_bc7_common_partitions2[common_pattern].m_astc;
astc_results.m_cem = (mode == 16) ? 4 : 12;
uint32_t part[2] = { 0, 1 };
if (g_astc_bc7_common_partitions2[common_pattern].m_invert)
std::swap(part[0], part[1]);
bool invert[2] = { false, false };
for (uint32_t p = 0; p < 2; p++)
{
if (mode == 16)
{
astc_results.m_endpoints[p * 4 + 0] = ccell_results[part[p]].m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[p * 4 + 1] = ccell_results[part[p]].m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[p * 4 + 2] = ccell_results[part[p]].m_astc_low_endpoint.m_c[3];
astc_results.m_endpoints[p * 4 + 3] = ccell_results[part[p]].m_astc_high_endpoint.m_c[3];
}
else
{
for (uint32_t c = 0; c < 4; c++)
{
astc_results.m_endpoints[p * 8 + c * 2] = ccell_results[part[p]].m_astc_low_endpoint.m_c[c];
astc_results.m_endpoints[p * 8 + c * 2 + 1] = ccell_results[part[p]].m_astc_high_endpoint.m_c[c];
}
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[p * 8 + 0]].m_unquant +
g_astc_unquant[endpoint_range][astc_results.m_endpoints[p * 8 + 2]].m_unquant +
g_astc_unquant[endpoint_range][astc_results.m_endpoints[p * 8 + 4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[p * 8 + 1]].m_unquant +
g_astc_unquant[endpoint_range][astc_results.m_endpoints[p * 8 + 3]].m_unquant +
g_astc_unquant[endpoint_range][astc_results.m_endpoints[p * 8 + 5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[p * 8 + 0], astc_results.m_endpoints[p * 8 + 1]);
std::swap(astc_results.m_endpoints[p * 8 + 2], astc_results.m_endpoints[p * 8 + 3]);
std::swap(astc_results.m_endpoints[p * 8 + 4], astc_results.m_endpoints[p * 8 + 5]);
std::swap(astc_results.m_endpoints[p * 8 + 6], astc_results.m_endpoints[p * 8 + 7]);
invert[p] = true;
}
}
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
const uint32_t bc7_part = g_bc7_partition2[16 * bc7_pattern + x + y * 4];
astc_results.m_weights[x + y * 4] = ccell_result_selectors[bc7_part][part_pixel_index[y][x]];
uint32_t astc_part = bc7_part;
if (g_astc_bc7_common_partitions2[common_pattern].m_invert)
astc_part = 1 - astc_part;
if (invert[astc_part])
astc_results.m_weights[x + y * 4] = 3 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = mode;
pResults[total_results].m_common_pattern = common_pattern;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = total_err;
total_results++;
}
} // common_pattern
}
// MODE 10
// DualPlane: 0, WeightRange: 8 (16), Subsets: 1, CEM: 12 (RGBA Direct ), EndpointRange: 13 (48) MODE6
static void astc_mode10(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
const uint32_t weight_range = 8;
const uint32_t endpoint_range = 13;
color_cell_compressor_params ccell_params;
memset(&ccell_params, 0, sizeof(ccell_params));
ccell_params.m_num_pixels = 16;
ccell_params.m_pPixels = (color_quad_u8*)&block[0][0];
ccell_params.m_num_selector_weights = 16;
ccell_params.m_pSelector_weights = g_astc_weights4;
ccell_params.m_pSelector_weightsx = (const bc7enc_vec4F*)g_astc_weights4x;
ccell_params.m_astc_endpoint_range = endpoint_range;
ccell_params.m_weights[0] = 1;
ccell_params.m_weights[1] = 1;
ccell_params.m_weights[2] = 1;
ccell_params.m_weights[3] = 1;
ccell_params.m_has_alpha = true;
color_cell_compressor_results ccell_results;
uint8_t ccell_result_selectors[16];
uint8_t ccell_result_selectors_temp[16];
memset(&ccell_results, 0, sizeof(ccell_results));
ccell_results.m_pSelectors = &ccell_result_selectors[0];
ccell_results.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err = color_cell_compression(255, &ccell_params, &ccell_results, &comp_params);
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = weight_range;
astc_results.m_ccs = 0;
astc_results.m_subsets = 1;
astc_results.m_partition_seed = 0;
astc_results.m_cem = 12;
astc_results.m_endpoints[0] = ccell_results.m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results.m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results.m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results.m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results.m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results.m_astc_high_endpoint.m_c[2];
astc_results.m_endpoints[6] = ccell_results.m_astc_low_endpoint.m_c[3];
astc_results.m_endpoints[7] = ccell_results.m_astc_high_endpoint.m_c[3];
bool invert = false;
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
std::swap(astc_results.m_endpoints[6], astc_results.m_endpoints[7]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
astc_results.m_weights[x + y * 4] = ccell_result_selectors[x + y * 4];
if (invert)
astc_results.m_weights[x + y * 4] = 15 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 10;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = part_err;
total_results++;
}
}
// 11. DualPlane: 1, WeightRange: 2 (4), Subsets: 1, CEM: 12 (RGBA Direct), EndpointRange: 13 (48) MODE5
// 17. DualPlane: 1, WeightRange : 2 (4), Subsets : 1, CEM : 4 (LA Direct), EndpointRange : 20 (256) BC7 MODE5
static void astc_mode11_or_17(uint32_t mode, const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
assert((mode == 11) || (mode == 17));
const uint32_t weight_range = 2;
const uint32_t endpoint_range = (mode == 17) ? 20 : 13;
bc7enc_compress_block_params local_comp_params(comp_params);
local_comp_params.m_perceptual = false;
local_comp_params.m_weights[0] = 1;
local_comp_params.m_weights[1] = 1;
local_comp_params.m_weights[2] = 1;
local_comp_params.m_weights[3] = 1;
const uint32_t last_rot_comp = (mode == 17) ? 1 : 4;
for (uint32_t rot_comp = 0; rot_comp < last_rot_comp; rot_comp++)
{
color_quad_u8 block_rgb[16];
color_quad_u8 block_a[16];
for (uint32_t i = 0; i < 16; i++)
{
block_rgb[i] = ((color_quad_u8*)&block[0][0])[i];
block_a[i] = block_rgb[i];
if (mode == 17)
{
assert(block_rgb[i].m_c[0] == block_rgb[i].m_c[1]);
assert(block_rgb[i].m_c[0] == block_rgb[i].m_c[2]);
block_a[i].m_c[0] = block_rgb[i].m_c[3];
block_a[i].m_c[1] = block_rgb[i].m_c[3];
block_a[i].m_c[2] = block_rgb[i].m_c[3];
block_a[i].m_c[3] = 255;
block_rgb[i].m_c[1] = block_rgb[i].m_c[0];
block_rgb[i].m_c[2] = block_rgb[i].m_c[0];
block_rgb[i].m_c[3] = 255;
}
else
{
uint8_t c = block_a[i].m_c[rot_comp];
block_a[i].m_c[0] = c;
block_a[i].m_c[1] = c;
block_a[i].m_c[2] = c;
block_a[i].m_c[3] = 255;
block_rgb[i].m_c[rot_comp] = block_rgb[i].m_c[3];
block_rgb[i].m_c[3] = 255;
}
}
uint8_t ccell_result_selectors_temp[16];
color_cell_compressor_params ccell_params_rgb;
memset(&ccell_params_rgb, 0, sizeof(ccell_params_rgb));
ccell_params_rgb.m_num_pixels = 16;
ccell_params_rgb.m_pPixels = block_rgb;
ccell_params_rgb.m_num_selector_weights = 4;
ccell_params_rgb.m_pSelector_weights = g_bc7_weights2;
ccell_params_rgb.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params_rgb.m_astc_endpoint_range = endpoint_range;
ccell_params_rgb.m_weights[0] = 1;
ccell_params_rgb.m_weights[1] = 1;
ccell_params_rgb.m_weights[2] = 1;
ccell_params_rgb.m_weights[3] = 1;
color_cell_compressor_results ccell_results_rgb;
uint8_t ccell_result_selectors_rgb[16];
memset(&ccell_results_rgb, 0, sizeof(ccell_results_rgb));
ccell_results_rgb.m_pSelectors = &ccell_result_selectors_rgb[0];
ccell_results_rgb.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err_rgb = color_cell_compression(255, &ccell_params_rgb, &ccell_results_rgb, &local_comp_params);
color_cell_compressor_params ccell_params_a;
memset(&ccell_params_a, 0, sizeof(ccell_params_a));
ccell_params_a.m_num_pixels = 16;
ccell_params_a.m_pPixels = block_a;
ccell_params_a.m_num_selector_weights = 4;
ccell_params_a.m_pSelector_weights = g_bc7_weights2;
ccell_params_a.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params_a.m_astc_endpoint_range = endpoint_range;
ccell_params_a.m_weights[0] = 1;
ccell_params_a.m_weights[1] = 1;
ccell_params_a.m_weights[2] = 1;
ccell_params_a.m_weights[3] = 1;
color_cell_compressor_results ccell_results_a;
uint8_t ccell_result_selectors_a[16];
memset(&ccell_results_a, 0, sizeof(ccell_results_a));
ccell_results_a.m_pSelectors = &ccell_result_selectors_a[0];
ccell_results_a.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err_a = color_cell_compression(255, &ccell_params_a, &ccell_results_a, &local_comp_params) / 3;
uint64_t total_err = (mode == 17) ? ((part_err_rgb / 3) + part_err_a) : (part_err_rgb + part_err_a);
// ASTC
astc_block_desc blk;
memset(&blk, 0, sizeof(blk));
blk.m_dual_plane = true;
blk.m_weight_range = weight_range;
blk.m_ccs = (mode == 17) ? 3 : rot_comp;
blk.m_subsets = 1;
blk.m_partition_seed = 0;
blk.m_cem = (mode == 17) ? 4 : 12;
bool invert = false;
if (mode == 17)
{
assert(ccell_results_rgb.m_astc_low_endpoint.m_c[0] == ccell_results_rgb.m_astc_low_endpoint.m_c[1]);
assert(ccell_results_rgb.m_astc_low_endpoint.m_c[0] == ccell_results_rgb.m_astc_low_endpoint.m_c[2]);
assert(ccell_results_rgb.m_astc_high_endpoint.m_c[0] == ccell_results_rgb.m_astc_high_endpoint.m_c[1]);
assert(ccell_results_rgb.m_astc_high_endpoint.m_c[0] == ccell_results_rgb.m_astc_high_endpoint.m_c[2]);
blk.m_endpoints[0] = ccell_results_rgb.m_astc_low_endpoint.m_c[0];
blk.m_endpoints[1] = ccell_results_rgb.m_astc_high_endpoint.m_c[0];
blk.m_endpoints[2] = ccell_results_a.m_astc_low_endpoint.m_c[0];
blk.m_endpoints[3] = ccell_results_a.m_astc_high_endpoint.m_c[0];
}
else
{
blk.m_endpoints[0] = (rot_comp == 0 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[0];
blk.m_endpoints[1] = (rot_comp == 0 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[0];
blk.m_endpoints[2] = (rot_comp == 1 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[1];
blk.m_endpoints[3] = (rot_comp == 1 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[1];
blk.m_endpoints[4] = (rot_comp == 2 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[2];
blk.m_endpoints[5] = (rot_comp == 2 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[2];
if (rot_comp == 3)
{
blk.m_endpoints[6] = ccell_results_a.m_astc_low_endpoint.m_c[0];
blk.m_endpoints[7] = ccell_results_a.m_astc_high_endpoint.m_c[0];
}
else
{
blk.m_endpoints[6] = ccell_results_rgb.m_astc_low_endpoint.m_c[rot_comp];
blk.m_endpoints[7] = ccell_results_rgb.m_astc_high_endpoint.m_c[rot_comp];
}
int s0 = g_astc_unquant[endpoint_range][blk.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][blk.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);
std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);
std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);
std::swap(blk.m_endpoints[6], blk.m_endpoints[7]);
invert = true;
}
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
uint32_t rgb_index = ccell_result_selectors_rgb[x + y * 4];
uint32_t a_index = ccell_result_selectors_a[x + y * 4];
if (invert)
{
rgb_index = 3 - rgb_index;
a_index = 3 - a_index;
}
blk.m_weights[(x + y * 4) * 2 + 0] = (uint8_t)rgb_index;
blk.m_weights[(x + y * 4) * 2 + 1] = (uint8_t)a_index;
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = mode;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = blk;
pResults[total_results].m_astc_err = total_err;
total_results++;
}
} // rot_comp
}
// MODE 12
// DualPlane: 0, WeightRange: 5 (8), Subsets: 1, CEM: 12 (RGBA Direct ), EndpointRange: 19 (192) MODE6
static void astc_mode12(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
const uint32_t weight_range = 5;
const uint32_t endpoint_range = 19;
color_cell_compressor_params ccell_params;
memset(&ccell_params, 0, sizeof(ccell_params));
ccell_params.m_num_pixels = 16;
ccell_params.m_pPixels = (color_quad_u8*)&block[0][0];
ccell_params.m_num_selector_weights = 8;
ccell_params.m_pSelector_weights = g_bc7_weights3;
ccell_params.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights3x;
ccell_params.m_astc_endpoint_range = endpoint_range;
ccell_params.m_weights[0] = 1;
ccell_params.m_weights[1] = 1;
ccell_params.m_weights[2] = 1;
ccell_params.m_weights[3] = 1;
ccell_params.m_has_alpha = true;
color_cell_compressor_results ccell_results;
uint8_t ccell_result_selectors[16];
uint8_t ccell_result_selectors_temp[16];
memset(&ccell_results, 0, sizeof(ccell_results));
ccell_results.m_pSelectors = &ccell_result_selectors[0];
ccell_results.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err = color_cell_compression(255, &ccell_params, &ccell_results, &comp_params);
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = weight_range;
astc_results.m_ccs = 0;
astc_results.m_subsets = 1;
astc_results.m_partition_seed = 0;
astc_results.m_cem = 12;
astc_results.m_endpoints[0] = ccell_results.m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results.m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results.m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results.m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results.m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results.m_astc_high_endpoint.m_c[2];
astc_results.m_endpoints[6] = ccell_results.m_astc_low_endpoint.m_c[3];
astc_results.m_endpoints[7] = ccell_results.m_astc_high_endpoint.m_c[3];
bool invert = false;
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
std::swap(astc_results.m_endpoints[6], astc_results.m_endpoints[7]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
astc_results.m_weights[x + y * 4] = ccell_result_selectors[x + y * 4];
if (invert)
astc_results.m_weights[x + y * 4] = 7 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 12;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = astc_results;
pResults[total_results].m_astc_err = part_err;
total_results++;
}
}
// 13. DualPlane: 1, WeightRange: 0 (2), Subsets: 1, CEM: 12 (RGBA Direct ), EndpointRange: 20 (256) MODE5
static void astc_mode13(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
bc7enc_compress_block_params local_comp_params(comp_params);
local_comp_params.m_perceptual = false;
local_comp_params.m_weights[0] = 1;
local_comp_params.m_weights[1] = 1;
local_comp_params.m_weights[2] = 1;
local_comp_params.m_weights[3] = 1;
for (uint32_t rot_comp = 0; rot_comp < 4; rot_comp++)
{
const uint32_t weight_range = 0;
const uint32_t endpoint_range = 20;
color_quad_u8 block_rgb[16];
color_quad_u8 block_a[16];
for (uint32_t i = 0; i < 16; i++)
{
block_rgb[i] = ((color_quad_u8*)&block[0][0])[i];
block_a[i] = block_rgb[i];
uint8_t c = block_a[i].m_c[rot_comp];
block_a[i].m_c[0] = c;
block_a[i].m_c[1] = c;
block_a[i].m_c[2] = c;
block_a[i].m_c[3] = 255;
block_rgb[i].m_c[rot_comp] = block_rgb[i].m_c[3];
block_rgb[i].m_c[3] = 255;
}
uint8_t ccell_result_selectors_temp[16];
color_cell_compressor_params ccell_params_rgb;
memset(&ccell_params_rgb, 0, sizeof(ccell_params_rgb));
ccell_params_rgb.m_num_pixels = 16;
ccell_params_rgb.m_pPixels = block_rgb;
ccell_params_rgb.m_num_selector_weights = 2;
ccell_params_rgb.m_pSelector_weights = g_bc7_weights1;
ccell_params_rgb.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights1x;
ccell_params_rgb.m_astc_endpoint_range = endpoint_range;
ccell_params_rgb.m_weights[0] = 1;
ccell_params_rgb.m_weights[1] = 1;
ccell_params_rgb.m_weights[2] = 1;
ccell_params_rgb.m_weights[3] = 1;
color_cell_compressor_results ccell_results_rgb;
uint8_t ccell_result_selectors_rgb[16];
memset(&ccell_results_rgb, 0, sizeof(ccell_results_rgb));
ccell_results_rgb.m_pSelectors = &ccell_result_selectors_rgb[0];
ccell_results_rgb.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err_rgb = color_cell_compression(255, &ccell_params_rgb, &ccell_results_rgb, &local_comp_params);
color_cell_compressor_params ccell_params_a;
memset(&ccell_params_a, 0, sizeof(ccell_params_a));
ccell_params_a.m_num_pixels = 16;
ccell_params_a.m_pPixels = block_a;
ccell_params_a.m_num_selector_weights = 2;
ccell_params_a.m_pSelector_weights = g_bc7_weights1;
ccell_params_a.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights1x;
ccell_params_a.m_astc_endpoint_range = endpoint_range;
ccell_params_a.m_weights[0] = 1;
ccell_params_a.m_weights[1] = 1;
ccell_params_a.m_weights[2] = 1;
ccell_params_a.m_weights[3] = 1;
color_cell_compressor_results ccell_results_a;
uint8_t ccell_result_selectors_a[16];
memset(&ccell_results_a, 0, sizeof(ccell_results_a));
ccell_results_a.m_pSelectors = &ccell_result_selectors_a[0];
ccell_results_a.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err_a = color_cell_compression(255, &ccell_params_a, &ccell_results_a, &local_comp_params) / 3;
uint64_t total_err = part_err_rgb + part_err_a;
// ASTC
astc_block_desc blk;
memset(&blk, 0, sizeof(blk));
blk.m_dual_plane = true;
blk.m_weight_range = weight_range;
blk.m_ccs = rot_comp;
blk.m_subsets = 1;
blk.m_partition_seed = 0;
blk.m_cem = 12;
blk.m_endpoints[0] = (rot_comp == 0 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[0];
blk.m_endpoints[1] = (rot_comp == 0 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[0];
blk.m_endpoints[2] = (rot_comp == 1 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[1];
blk.m_endpoints[3] = (rot_comp == 1 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[1];
blk.m_endpoints[4] = (rot_comp == 2 ? ccell_results_a : ccell_results_rgb).m_astc_low_endpoint.m_c[2];
blk.m_endpoints[5] = (rot_comp == 2 ? ccell_results_a : ccell_results_rgb).m_astc_high_endpoint.m_c[2];
if (rot_comp == 3)
{
blk.m_endpoints[6] = ccell_results_a.m_astc_low_endpoint.m_c[0];
blk.m_endpoints[7] = ccell_results_a.m_astc_high_endpoint.m_c[0];
}
else
{
blk.m_endpoints[6] = ccell_results_rgb.m_astc_low_endpoint.m_c[rot_comp];
blk.m_endpoints[7] = ccell_results_rgb.m_astc_high_endpoint.m_c[rot_comp];
}
bool invert = false;
int s0 = g_astc_unquant[endpoint_range][blk.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][blk.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][blk.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(blk.m_endpoints[0], blk.m_endpoints[1]);
std::swap(blk.m_endpoints[2], blk.m_endpoints[3]);
std::swap(blk.m_endpoints[4], blk.m_endpoints[5]);
std::swap(blk.m_endpoints[6], blk.m_endpoints[7]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
uint32_t rgb_index = ccell_result_selectors_rgb[x + y * 4];
uint32_t a_index = ccell_result_selectors_a[x + y * 4];
if (invert)
{
rgb_index = 1 - rgb_index;
a_index = 1 - a_index;
}
blk.m_weights[(x + y * 4) * 2 + 0] = (uint8_t)rgb_index;
blk.m_weights[(x + y * 4) * 2 + 1] = (uint8_t)a_index;
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 13;
pResults[total_results].m_common_pattern = 0;
pResults[total_results].m_astc = blk;
pResults[total_results].m_astc_err = total_err;
total_results++;
}
} // rot_comp
}
// MODE14
// DualPlane: 0, WeightRange: 2 (4), Subsets: 1, CEM: 12 (RGBA Direct ), EndpointRange: 20 (256) MODE6
static void astc_mode14(const color_rgba block[4][4], uastc_encode_results* pResults, uint32_t& total_results, bc7enc_compress_block_params& comp_params)
{
const uint32_t weight_range = 2;
const uint32_t endpoint_range = 20;
color_cell_compressor_params ccell_params;
memset(&ccell_params, 0, sizeof(ccell_params));
ccell_params.m_num_pixels = 16;
ccell_params.m_pPixels = (color_quad_u8*)&block[0][0];
ccell_params.m_num_selector_weights = 4;
ccell_params.m_pSelector_weights = g_bc7_weights2;
ccell_params.m_pSelector_weightsx = (const bc7enc_vec4F*)g_bc7_weights2x;
ccell_params.m_astc_endpoint_range = endpoint_range;
ccell_params.m_weights[0] = 1;
ccell_params.m_weights[1] = 1;
ccell_params.m_weights[2] = 1;
ccell_params.m_weights[3] = 1;
ccell_params.m_has_alpha = true;
color_cell_compressor_results ccell_results;
uint8_t ccell_result_selectors[16];
uint8_t ccell_result_selectors_temp[16];
memset(&ccell_results, 0, sizeof(ccell_results));
ccell_results.m_pSelectors = &ccell_result_selectors[0];
ccell_results.m_pSelectors_temp = &ccell_result_selectors_temp[0];
uint64_t part_err = color_cell_compression(255, &ccell_params, &ccell_results, &comp_params);
// ASTC
astc_block_desc astc_results;
memset(&astc_results, 0, sizeof(astc_results));
astc_results.m_dual_plane = false;
astc_results.m_weight_range = weight_range;
astc_results.m_ccs = 0;
astc_results.m_subsets = 1;
astc_results.m_partition_seed = 0;
astc_results.m_cem = 12;
astc_results.m_endpoints[0] = ccell_results.m_astc_low_endpoint.m_c[0];
astc_results.m_endpoints[1] = ccell_results.m_astc_high_endpoint.m_c[0];
astc_results.m_endpoints[2] = ccell_results.m_astc_low_endpoint.m_c[1];
astc_results.m_endpoints[3] = ccell_results.m_astc_high_endpoint.m_c[1];
astc_results.m_endpoints[4] = ccell_results.m_astc_low_endpoint.m_c[2];
astc_results.m_endpoints[5] = ccell_results.m_astc_high_endpoint.m_c[2];
astc_results.m_endpoints[6] = ccell_results.m_astc_low_endpoint.m_c[3];
astc_results.m_endpoints[7] = ccell_results.m_astc_high_endpoint.m_c[3];
bool invert = false;
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
std::swap(astc_results.m_endpoints[6], astc_results.m_endpoints[7]);
invert = true;
}
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
astc_results.m_weights[x + y * 4] = ccell_result_selectors[x + y * 4];
if (invert)
astc_results.m_weights[x + y * 4] = 3 - astc_results.m_weights[x + y * 4];
}
}
assert(total_results < MAX_ENCODE_RESULTS);
if (total_results < MAX_ENCODE_RESULTS)
{
pResults[total_results].m_uastc_mode = 14;
pResults[total_results].m_common_pattern = 0;
pResults[total_results