mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-07-12 17:19:19 +00:00
5016 lines
159 KiB
C++
5016 lines
159 KiB
C++
// bc7e_scalar.cpp - Pure scalar C++17 port of bc7e.ispc (auto-derived, then
|
|
// hand-fixed). De-SIMD'd: the single SPMD foreach over blocks becomes a plain
|
|
// for loop; uniform/varying are stripped (one lane/block, no gang), and the
|
|
// ISPC stdlib surface is provided by ispc_compat.h. Encodes one 4x4 BC7 block
|
|
// per iteration. Logic/math and the public API are preserved verbatim.
|
|
#include "basisu_bc7e_scalar.h"
|
|
#include <cstdint>
|
|
#include <cstring> // memset, memcpy
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
|
|
// --- ISPC compatibility shim (formerly ispc_compat.h, inlined here so this .cpp
|
|
// --- plus basisu_bc7e_scalar.h are fully self-contained). Supplies the small ISPC
|
|
// --- stdlib surface the de-SIMD'd code uses, the base integer type aliases, and
|
|
// --- the coherent control-flow keyword macros. Kept in the .cpp (not the public
|
|
// --- header) so these global templates/macros don't leak into the rest of the project.
|
|
|
|
// ISPC base integer type aliases (the fixed-width *_t names come from <cstdint>).
|
|
typedef int8_t int8;
|
|
typedef int16_t int16;
|
|
typedef int32_t int32;
|
|
typedef int64_t int64;
|
|
|
|
// ISPC "coherent" control-flow keywords -> plain C++ control flow (no-op at gang width 1).
|
|
#define cif if
|
|
#define cfor for
|
|
#define cwhile while
|
|
|
|
// Mark an intentionally-unused variable/parameter (silences C4100/C4189).
|
|
#define NOTE_UNUSED(x) (void)(x)
|
|
|
|
// Range-checked cast for a float that is known to hold a small non-negative integral
|
|
// value (e.g. an 8-bit channel min/max). Asserts the value is in [0,255] in debug, then
|
|
// narrows to uint32_t. Used at the few float->uint sites so the truncation is explicit
|
|
// and guarded rather than an implicit (and portability-warning-triggering) conversion.
|
|
static inline uint32_t float_to_uint8(float v) { assert((v >= 0.0f) && (v <= 255.0f)); return (uint32_t)v; }
|
|
|
|
// ISPC stdlib scalar equivalents. min/max match minss/maxss semantics (the second
|
|
// operand is returned on an unordered/NaN comparison), and clamp() is defined as
|
|
// min(max(v,lo),hi) -- NOT a direct ternary -- so clamp(NaN,lo,hi) yields lo just as
|
|
// it does under ISPC (a direct ternary would yield NaN, which casts to INT_MIN and
|
|
// becomes an out-of-bounds index).
|
|
template <class T> static inline T min(T a, T b) { return a < b ? a : b; }
|
|
template <class T> static inline T max(T a, T b) { return a > b ? a : b; }
|
|
template <class T> static inline T clamp(T v, T lo, T hi) { return min(max(v, lo), hi); }
|
|
template <class T> static inline T select(bool c, T a, T b) { return c ? a : b; }
|
|
template <class T> static inline bool all(T v) { return (bool)v; }
|
|
template <class T> static inline bool any(T v) { return (bool)v; }
|
|
template <class T> static inline bool none(T v) { return !(bool)v; }
|
|
template <class T> static inline T abs(T v) { return v < 0 ? -v : v; }
|
|
// floor()/sqrt() on floats resolve to the global float overloads <cmath> provides under MSVC.
|
|
|
|
namespace bc7e_scalar {
|
|
|
|
// TEMP debug checkpoint (single-threaded runs only). Prints the source line so
|
|
// the last value before a crash pinpoints the failing statement. Remove once verified.
|
|
#define HERE() do { fprintf(stderr, "L%d\n", __LINE__); fflush(stderr); } while(0)
|
|
|
|
// Tiny bias added to data-dependent denominators that can legitimately be zero
|
|
// (flat blocks / flat channels -> zero endpoint deltas). The original SIMD code
|
|
// let the divide produce +/-Inf/NaN and relied on a later min/max clamp to
|
|
// sanitize it; in scalar code an unguarded 0/0 NaN can become an INT_MIN array
|
|
// index. Biasing keeps the divisor finite and nonzero with negligible effect on
|
|
// the result (denominators are otherwise >= 1 in these spots).
|
|
static const float BC7E_DENOM_BIAS = 0.0000125f;
|
|
|
|
// bc7e.ispc - Fast high quality SIMD BC7 encoder
|
|
// Copyright (C) 2018-2020 Binomial LLC, All rights reserved. Apache 2.0 license - see LICENSE.
|
|
// Typically compiled as: ispc -g -O2 "%(Filename).ispc" -o "$(TargetDir)%(Filename).obj" -h "$(ProjectDir)%(Filename)_ispc.h" --target=sse2,sse4,avx,avx2 --opt=fast-math --opt=disable-assertions
|
|
// --opt=fast-math is optional (doesn't make much if any measurable difference).
|
|
// Thanks to ArasP for the determinism fix.
|
|
|
|
#define BC7E_NON_DETERMINISTIC (0)
|
|
#define BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX (34)
|
|
#define BC7E_BLOCK_SIZE (16)
|
|
#define BC7E_MAX_PARTITIONS0 (16)
|
|
#define BC7E_MAX_PARTITIONS1 (64)
|
|
#define BC7E_MAX_PARTITIONS2 (64)
|
|
#define BC7E_MAX_PARTITIONS3 (64)
|
|
#define BC7E_MAX_PARTITIONS7 (64)
|
|
#define BC7E_MAX_UBER_LEVEL (4)
|
|
|
|
// endpoint_err::m_error is a real uint16_t (was a 64-bit alias inherited from the ISPC source).
|
|
// Verified safe to narrow: all stores are single-channel squared errors (k-c)^2 <= 65025,
|
|
// guarded by safe_cast_uint16(); reads only compare or sum 3-4 of them (integer-promoted to
|
|
// int, max ~260100), so 16 bits is sufficient and no 64-bit width is relied upon.
|
|
|
|
#ifndef UINT16_MAX
|
|
#define UINT16_MAX (0xFFFF)
|
|
#endif
|
|
|
|
#ifndef UINT_MAX
|
|
#define UINT_MAX (0xFFFFFFFFU)
|
|
#endif
|
|
|
|
#ifndef UINT64_MAX
|
|
#define UINT64_MAX (0xFFFFFFFFFFFFFFFFULL)
|
|
#endif
|
|
|
|
#ifndef INT64_MAX
|
|
#define INT64_MAX (0x7FFFFFFFFFFFFFFFULL)
|
|
#endif
|
|
|
|
|
|
static inline int32_t clampi( int32_t value, int32_t low, int32_t high) { return clamp(value, low, high); }
|
|
[[maybe_unused]] static inline uint32_t clampu( uint32_t value, uint32_t low, uint32_t high) { return clamp(value, low, high); }
|
|
static inline float clampf( float value, float low, float high) { return clamp(value, low, high); }
|
|
|
|
static inline float saturate( float value) { return clampf(value, 0, 1.0f); }
|
|
[[maybe_unused]] static inline float saturate255( float value) { return clampf(value, 0, 255.0f); }
|
|
|
|
[[maybe_unused]] static inline uint8_t minimumub( uint8_t a, uint8_t b) { return min(a, b); }
|
|
static inline int32_t minimumi( int32_t a, int32_t b) { return min(a, b); }
|
|
static inline uint32_t minimumu( uint32_t a, uint32_t b) { return min(a, b); }
|
|
[[maybe_unused]] static inline uint64_t minimumu64( uint64_t a, uint64_t b) { return min(a, b); }
|
|
static inline float minimumf( float a, float b) { return min(a, b); }
|
|
|
|
[[maybe_unused]] static inline uint8_t maximumub( uint8_t a, uint8_t b) { return max(a, b); }
|
|
static inline int32_t maximumi( int32_t a, int32_t b) { return max(a, b); }
|
|
static inline uint32_t maximumu( uint32_t a, uint32_t b) { return max(a, b); }
|
|
static inline float maximumf( float a, float b) { return max(a, b); }
|
|
|
|
static inline int32_t iabs32( int32_t v) { uint32_t msk = v >> 31; return (v ^ msk) - msk; }
|
|
|
|
[[maybe_unused]] static inline void swapub( uint8_t * a, uint8_t * b) { uint8_t t = *a; *a = *b; *b = t; }
|
|
static inline void swapu( uint32_t * a, uint32_t * b) { uint32_t t = *a; *a = *b; *b = t; }
|
|
static inline void swapi( int32_t * a, int32_t * b) { int32_t t = *a; *a = *b; *b = t; }
|
|
static inline void swapf( float * a, float * b) { float t = *a; *a = *b; *b = t; }
|
|
|
|
static inline float square(float s) { return s * s; }
|
|
[[maybe_unused]] static inline int square(int s) { return s * s; }
|
|
|
|
struct color_quad_u8
|
|
{
|
|
uint8_t m_c[4];
|
|
};
|
|
|
|
struct color_quad_i
|
|
{
|
|
int32_t m_c[4];
|
|
};
|
|
|
|
struct color_quad_f
|
|
{
|
|
float m_c[4];
|
|
};
|
|
|
|
[[maybe_unused]] static inline color_quad_i component_min_rgb(const color_quad_i * pA, const color_quad_i * pB)
|
|
{
|
|
color_quad_i res;
|
|
res.m_c[0] = minimumi(pA->m_c[0], pB->m_c[0]);
|
|
res.m_c[1] = minimumi(pA->m_c[1], pB->m_c[1]);
|
|
res.m_c[2] = minimumi(pA->m_c[2], pB->m_c[2]);
|
|
res.m_c[3] = 255;
|
|
return res;
|
|
}
|
|
|
|
[[maybe_unused]] static inline color_quad_i component_max_rgb(const color_quad_i * pA, const color_quad_i * pB)
|
|
{
|
|
color_quad_i res;
|
|
res.m_c[0] = maximumi(pA->m_c[0], pB->m_c[0]);
|
|
res.m_c[1] = maximumi(pA->m_c[1], pB->m_c[1]);
|
|
res.m_c[2] = maximumi(pA->m_c[2], pB->m_c[2]);
|
|
res.m_c[3] = 255;
|
|
return res;
|
|
}
|
|
|
|
static inline color_quad_i *color_quad_i_set_clamped( color_quad_i * pRes, int32_t r, int32_t g, int32_t b, int32_t a)
|
|
{
|
|
pRes->m_c[0] = clampi(r, 0, 255);
|
|
pRes->m_c[1] = clampi(g, 0, 255);
|
|
pRes->m_c[2] = clampi(b, 0, 255);
|
|
pRes->m_c[3] = clampi(a, 0, 255);
|
|
return pRes;
|
|
}
|
|
|
|
static inline color_quad_i *color_quad_i_set( color_quad_i * pRes, int32_t r, int32_t g, int32_t b, int32_t a)
|
|
{
|
|
pRes->m_c[0] = r;
|
|
pRes->m_c[1] = g;
|
|
pRes->m_c[2] = b;
|
|
pRes->m_c[3] = a;
|
|
return pRes;
|
|
}
|
|
|
|
static inline bool color_quad_i_equals(const color_quad_i * pLHS, const color_quad_i * pRHS)
|
|
{
|
|
return (pLHS->m_c[0] == pRHS->m_c[0]) && (pLHS->m_c[1] == pRHS->m_c[1]) && (pLHS->m_c[2] == pRHS->m_c[2]) && (pLHS->m_c[3] == pRHS->m_c[3]);
|
|
}
|
|
|
|
static inline bool color_quad_i_notequals(const color_quad_i * pLHS, const color_quad_i * pRHS)
|
|
{
|
|
return !color_quad_i_equals(pLHS, pRHS);
|
|
}
|
|
|
|
struct vec4F
|
|
{
|
|
float m_c[4];
|
|
};
|
|
|
|
static inline vec4F * vec4F_set_scalar( vec4F * pV, float x)
|
|
{
|
|
pV->m_c[0] = x;
|
|
pV->m_c[1] = x;
|
|
pV->m_c[2] = x;
|
|
pV->m_c[3] = x;
|
|
return pV;
|
|
}
|
|
|
|
static inline vec4F * vec4F_set( vec4F * pV, float x, float y, float z, float w)
|
|
{
|
|
pV->m_c[0] = x;
|
|
pV->m_c[1] = y;
|
|
pV->m_c[2] = z;
|
|
pV->m_c[3] = w;
|
|
return pV;
|
|
}
|
|
|
|
static inline vec4F * vec4F_saturate_in_place( vec4F * pV)
|
|
{
|
|
pV->m_c[0] = saturate(pV->m_c[0]);
|
|
pV->m_c[1] = saturate(pV->m_c[1]);
|
|
pV->m_c[2] = saturate(pV->m_c[2]);
|
|
pV->m_c[3] = saturate(pV->m_c[3]);
|
|
return pV;
|
|
}
|
|
|
|
static inline vec4F vec4F_saturate(const vec4F * pV)
|
|
{
|
|
vec4F res;
|
|
res.m_c[0] = saturate(pV->m_c[0]);
|
|
res.m_c[1] = saturate(pV->m_c[1]);
|
|
res.m_c[2] = saturate(pV->m_c[2]);
|
|
res.m_c[3] = saturate(pV->m_c[3]);
|
|
return res;
|
|
}
|
|
|
|
static inline vec4F vec4F_from_color(const color_quad_i * pC)
|
|
{
|
|
vec4F res;
|
|
vec4F_set(&res, (float)pC->m_c[0], (float)pC->m_c[1], (float)pC->m_c[2], (float)pC->m_c[3]);
|
|
return res;
|
|
}
|
|
|
|
static inline vec4F vec4F_add(const vec4F * pLHS, const vec4F * pRHS)
|
|
{
|
|
vec4F res;
|
|
vec4F_set(&res, pLHS->m_c[0] + pRHS->m_c[0], pLHS->m_c[1] + pRHS->m_c[1], pLHS->m_c[2] + pRHS->m_c[2], pLHS->m_c[3] + pRHS->m_c[3]);
|
|
return res;
|
|
}
|
|
|
|
static inline vec4F vec4F_sub(const vec4F * pLHS, const vec4F * pRHS)
|
|
{
|
|
vec4F res;
|
|
vec4F_set(&res, pLHS->m_c[0] - pRHS->m_c[0], pLHS->m_c[1] - pRHS->m_c[1], pLHS->m_c[2] - pRHS->m_c[2], pLHS->m_c[3] - pRHS->m_c[3]);
|
|
return res;
|
|
}
|
|
|
|
static inline float vec4F_dot(const vec4F * pLHS, const vec4F * pRHS)
|
|
{
|
|
return pLHS->m_c[0] * pRHS->m_c[0] + pLHS->m_c[1] * pRHS->m_c[1] + pLHS->m_c[2] * pRHS->m_c[2] + pLHS->m_c[3] * pRHS->m_c[3];
|
|
}
|
|
|
|
static inline vec4F vec4F_mul(const vec4F * pLHS, float s)
|
|
{
|
|
vec4F res;
|
|
vec4F_set(&res, pLHS->m_c[0] * s, pLHS->m_c[1] * s, pLHS->m_c[2] * s, pLHS->m_c[3] * s);
|
|
return res;
|
|
}
|
|
|
|
static inline vec4F *vec4F_normalize_in_place( vec4F * pV)
|
|
{
|
|
float s = pV->m_c[0] * pV->m_c[0] + pV->m_c[1] * pV->m_c[1] + pV->m_c[2] * pV->m_c[2] + pV->m_c[3] * pV->m_c[3];
|
|
|
|
if (s != 0.0f)
|
|
{
|
|
s = 1.0f / sqrt(s);
|
|
|
|
pV->m_c[0] *= s;
|
|
pV->m_c[1] *= s;
|
|
pV->m_c[2] *= s;
|
|
pV->m_c[3] *= s;
|
|
}
|
|
|
|
return pV;
|
|
}
|
|
|
|
static const uint32_t g_bc7_weights2[4] = { 0, 21, 43, 64 };
|
|
static const uint32_t g_bc7_weights3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 };
|
|
static const uint32_t g_bc7_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
|
|
|
|
// Precomputed weight constants used during least fit determination. For each entry in g_bc7_weights[]: w * w, (1.0f - w) * w, (1.0f - w) * (1.0f - w), w
|
|
static const float g_bc7_weights2x[4 * 4] = { 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.107666f, 0.220459f, 0.451416f, 0.328125f, 0.451416f, 0.220459f, 0.107666f, 0.671875f, 1.000000f, 0.000000f, 0.000000f, 1.000000f };
|
|
static const float g_bc7_weights3x[8 * 4] = { 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.019775f, 0.120850f, 0.738525f, 0.140625f, 0.079102f, 0.202148f, 0.516602f, 0.281250f, 0.177979f, 0.243896f, 0.334229f, 0.421875f, 0.334229f, 0.243896f, 0.177979f, 0.578125f, 0.516602f, 0.202148f,
|
|
0.079102f, 0.718750f, 0.738525f, 0.120850f, 0.019775f, 0.859375f, 1.000000f, 0.000000f, 0.000000f, 1.000000f };
|
|
static const float g_bc7_weights4x[16 * 4] = { 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.003906f, 0.058594f, 0.878906f, 0.062500f, 0.019775f, 0.120850f, 0.738525f, 0.140625f, 0.041260f, 0.161865f, 0.635010f, 0.203125f, 0.070557f, 0.195068f, 0.539307f, 0.265625f, 0.107666f, 0.220459f,
|
|
0.451416f, 0.328125f, 0.165039f, 0.241211f, 0.352539f, 0.406250f, 0.219727f, 0.249023f, 0.282227f, 0.468750f, 0.282227f, 0.249023f, 0.219727f, 0.531250f, 0.352539f, 0.241211f, 0.165039f, 0.593750f, 0.451416f, 0.220459f, 0.107666f, 0.671875f, 0.539307f, 0.195068f, 0.070557f, 0.734375f,
|
|
0.635010f, 0.161865f, 0.041260f, 0.796875f, 0.738525f, 0.120850f, 0.019775f, 0.859375f, 0.878906f, 0.058594f, 0.003906f, 0.937500f, 1.000000f, 0.000000f, 0.000000f, 1.000000f };
|
|
|
|
static const int g_bc7_partition1[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
|
|
|
static const int g_bc7_partition2[64 * 16] =
|
|
{
|
|
0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1, 0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1, 0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1, 0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
|
|
0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0, 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,
|
|
0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0, 0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, 0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,
|
|
0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1, 0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0, 0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0, 0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,
|
|
0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0, 0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1, 0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,
|
|
0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0, 0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,
|
|
0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1, 0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1, 0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0, 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1
|
|
};
|
|
|
|
static const int g_bc7_table_anchor_index_second_subset[64] =
|
|
{
|
|
15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15, 15, 2, 8, 2, 2, 8, 8,15, 2, 8, 2, 2, 8, 8, 2, 2, 15,15, 6, 8, 2, 8,15,15, 2, 8, 2, 2, 2,15,15, 6, 6, 2, 6, 8,15,15, 2, 2, 15,15,15,15,15, 2, 2,15
|
|
};
|
|
|
|
static const int g_bc7_partition3[64 * 16] =
|
|
{
|
|
0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1, 0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2, 0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1,
|
|
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2, 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2, 0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0,
|
|
0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0, 0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1, 0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1, 0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0,
|
|
0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2, 0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1, 0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2, 0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1,
|
|
0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1, 0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0, 0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, 0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1,
|
|
0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2, 0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1, 0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1,
|
|
0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1, 0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2, 0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2, 0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2,
|
|
0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2, 0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2, 0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2, 0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0,
|
|
};
|
|
|
|
static const int g_bc7_table_anchor_index_third_subset_1[64] =
|
|
{
|
|
3, 3,15,15, 8, 3,15,15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8,15, 3, 3, 6,10, 5, 8, 8, 6, 8, 5,15,15, 8,15, 3, 5, 6,10, 8,15, 15, 3,15, 5,15,15,15,15, 3,15, 5, 5, 5, 8, 5,10, 5,10, 8,13,15,12, 3, 3
|
|
};
|
|
|
|
static const int g_bc7_table_anchor_index_third_subset_2[64] =
|
|
{
|
|
15, 8, 8, 3,15,15, 3, 8, 15,15,15,15,15,15,15, 8, 15, 8,15, 3,15, 8,15, 8, 3,15, 6,10,15,15,10, 8, 15, 3,15,10,10, 8, 9,10, 6,15, 8,15, 3, 6, 6, 8, 15, 3,15,15,15,15,15,15, 15,15,15,15, 3,15,15, 8
|
|
};
|
|
|
|
static const int g_bc7_num_subsets[8] = { 3, 2, 3, 2, 1, 1, 1, 2 };
|
|
static const int g_bc7_partition_bits[8] = { 4, 6, 6, 6, 0, 0, 0, 6 };
|
|
[[maybe_unused]] static const int g_bc7_rotation_bits[8] = { 0, 0, 0, 0, 2, 2, 0, 0 };
|
|
static const int g_bc7_color_index_bitcount[8] = { 3, 3, 2, 2, 2, 2, 4, 2 };
|
|
static int get_bc7_color_index_size( int mode, int index_selection_bit) { return g_bc7_color_index_bitcount[mode] + index_selection_bit; }
|
|
static int g_bc7_alpha_index_bitcount[8] = { 0, 0, 0, 0, 3, 2, 4, 2 };
|
|
static int get_bc7_alpha_index_size( int mode, int index_selection_bit) { return g_bc7_alpha_index_bitcount[mode] - index_selection_bit; }
|
|
static const int g_bc7_mode_has_p_bits[8] = { 1, 1, 0, 1, 0, 0, 1, 1 };
|
|
static const int g_bc7_mode_has_shared_p_bits[8] = { 0, 1, 0, 0, 0, 0, 0, 0 };
|
|
static const int g_bc7_color_precision_table[8] = { 4, 6, 5, 7, 5, 7, 7, 5 };
|
|
[[maybe_unused]] static const int g_bc7_color_precision_plus_pbit_table[8] = { 5, 7, 5, 8, 5, 7, 8, 6 };
|
|
static const int g_bc7_alpha_precision_table[8] = { 0, 0, 0, 0, 6, 8, 7, 5 };
|
|
[[maybe_unused]] static const int g_bc7_alpha_precision_plus_pbit_table[8] = { 0, 0, 0, 0, 6, 8, 8, 6 };
|
|
static bool get_bc7_mode_has_seperate_alpha_selectors( int mode) { return (mode == 4) || (mode == 5); }
|
|
|
|
struct endpoint_err
|
|
{
|
|
uint16_t m_error;
|
|
uint8_t m_lo;
|
|
uint8_t m_hi;
|
|
};
|
|
|
|
// Guarded store into endpoint_err::m_error (a real uint16_t). The stored quantity
|
|
// is always a single-channel squared error (k-c)^2 with k,c in [0,255], so it is
|
|
// <= 255^2 = 65025 <= UINT16_MAX by construction. This assert verifies that
|
|
// invariant on every store; if it ever fires (e.g. a weighted error gets routed
|
|
// here), the value would silently truncate at 16 bits, so the assert must hold.
|
|
static inline uint16_t safe_cast_uint16(int v)
|
|
{
|
|
assert(v >= 0 && v <= (int)UINT16_MAX);
|
|
return (uint16_t)v;
|
|
}
|
|
|
|
static endpoint_err g_bc7_mode_1_optimal_endpoints[256][2]; // [c][pbit]
|
|
const uint32_t BC7E_MODE_1_OPTIMAL_INDEX = 2;
|
|
|
|
static endpoint_err g_bc7_mode_7_optimal_endpoints[256][2][2]; // [c][pbit][hp][lp]
|
|
const uint32_t BC7E_MODE_7_OPTIMAL_INDEX = 1;
|
|
|
|
static endpoint_err g_bc7_mode_6_optimal_endpoints[256][2][2]; // [c][hp][lp]
|
|
const uint32_t BC7E_MODE_6_OPTIMAL_INDEX = 5;
|
|
|
|
static uint32_t g_bc7_mode_4_optimal_endpoints3[256]; // [c]
|
|
static uint32_t g_bc7_mode_4_optimal_endpoints2[256]; // [c]
|
|
const uint32_t BC7E_MODE_4_OPTIMAL_INDEX3 = 2;
|
|
const uint32_t BC7E_MODE_4_OPTIMAL_INDEX2 = 1;
|
|
|
|
static uint32_t g_bc7_mode_5_optimal_endpoints[256]; // [c]
|
|
const uint32_t BC7E_MODE_5_OPTIMAL_INDEX = 1;
|
|
|
|
static endpoint_err g_bc7_mode_0_optimal_endpoints[256][2][2]; // [c][hp][lp]
|
|
const uint32_t BC7E_MODE_0_OPTIMAL_INDEX = 2;
|
|
|
|
static bool g_codec_initialized;
|
|
|
|
void bc7e_compress_block_init()
|
|
{
|
|
if (g_codec_initialized)
|
|
return;
|
|
|
|
// Mode 0: 444.1
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
for ( uint32_t hp = 0; hp < 2; hp++)
|
|
{
|
|
for ( uint32_t lp = 0; lp < 2; lp++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
|
|
for ( uint32_t l = 0; l < 16; l++)
|
|
{
|
|
uint32_t low = ((l << 1) | lp) << 3;
|
|
low |= (low >> 5);
|
|
|
|
for ( uint32_t h = 0; h < 16; h++)
|
|
{
|
|
uint32_t high = ((h << 1) | hp) << 3;
|
|
high |= (high >> 5);
|
|
|
|
const int k = (low * (64 - g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_0_optimal_endpoints[c][hp][lp] = best;
|
|
} // lp
|
|
} // hp
|
|
} // c
|
|
|
|
// Mode 1: 666.1
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
for ( uint32_t lp = 0; lp < 2; lp++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
|
|
for ( uint32_t l = 0; l < 64; l++)
|
|
{
|
|
uint32_t low = ((l << 1) | lp) << 1;
|
|
low |= (low >> 7);
|
|
|
|
for ( uint32_t h = 0; h < 64; h++)
|
|
{
|
|
uint32_t high = ((h << 1) | lp) << 1;
|
|
high |= (high >> 7);
|
|
|
|
const int k = (low * (64 - g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_1_optimal_endpoints[c][lp] = best;
|
|
} // lp
|
|
} // c
|
|
|
|
// Mode 6: 777.1 4-bit indices
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
for ( uint32_t hp = 0; hp < 2; hp++)
|
|
{
|
|
for ( uint32_t lp = 0; lp < 2; lp++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
|
|
for ( uint32_t l = 0; l < 128; l++)
|
|
{
|
|
uint32_t low = (l << 1) | lp;
|
|
|
|
for ( uint32_t h = 0; h < 128; h++)
|
|
{
|
|
uint32_t high = (h << 1) | hp;
|
|
|
|
const int k = (low * (64 - g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX]) + high * g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_6_optimal_endpoints[c][hp][lp] = best;
|
|
} // lp
|
|
} // hp
|
|
} // c
|
|
|
|
// Mode 5: 777 2-bit indices
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
best.m_lo = 0;
|
|
best.m_hi = 0;
|
|
|
|
for ( uint32_t l = 0; l < 128; l++)
|
|
{
|
|
uint32_t low = l << 1;
|
|
low |= (low >> 7);
|
|
|
|
for ( uint32_t h = 0; h < 128; h++)
|
|
{
|
|
uint32_t high = h << 1;
|
|
high |= (high >> 7);
|
|
|
|
const int k = (low * (64 - g_bc7_weights2[BC7E_MODE_5_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7E_MODE_5_OPTIMAL_INDEX] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_5_optimal_endpoints[c] = (uint32_t)best.m_lo | (((uint32_t)best.m_hi) << 8);
|
|
|
|
} // c
|
|
|
|
|
|
//Mode 4: 555 3-bit indices
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
best.m_lo = 0;
|
|
best.m_hi = 0;
|
|
|
|
for ( uint32_t l = 0; l < 32; l++)
|
|
{
|
|
uint32_t low = l << 3;
|
|
low |= (low >> 5);
|
|
|
|
for ( uint32_t h = 0; h < 32; h++)
|
|
{
|
|
uint32_t high = h << 3;
|
|
high |= (high >> 5);
|
|
|
|
const int k = (low * (64 - g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3]) + high * g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_4_optimal_endpoints3[c] = (uint32_t)best.m_lo | (((uint32_t)best.m_hi) << 8);
|
|
|
|
} // c
|
|
|
|
// Mode 4: 555 2-bit indices
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
best.m_lo = 0;
|
|
best.m_hi = 0;
|
|
|
|
for ( uint32_t l = 0; l < 32; l++)
|
|
{
|
|
uint32_t low = l << 3;
|
|
low |= (low >> 5);
|
|
|
|
for ( uint32_t h = 0; h < 32; h++)
|
|
{
|
|
uint32_t high = h << 3;
|
|
high |= (high >> 5);
|
|
|
|
const int k = (low * (64 - g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2]) + high * g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_4_optimal_endpoints2[c] = (uint32_t)best.m_lo | (((uint32_t)best.m_hi) << 8);
|
|
|
|
} // c
|
|
|
|
// Mode 7: 555.1 2-bit indices
|
|
for ( int c = 0; c < 256; c++)
|
|
{
|
|
endpoint_err best;
|
|
best.m_error = safe_cast_uint16(UINT16_MAX);
|
|
best.m_lo = 0;
|
|
best.m_hi = 0;
|
|
|
|
for ( uint32_t hp = 0; hp < 2; hp++)
|
|
{
|
|
for ( uint32_t lp = 0; lp < 2; lp++)
|
|
{
|
|
for ( uint32_t l = 0; l < 32; l++)
|
|
{
|
|
uint32_t low = ((l << 1) | lp) << 2;
|
|
low |= (low >> 6);
|
|
|
|
for ( uint32_t h = 0; h < 32; h++)
|
|
{
|
|
uint32_t high = ((h << 1) | hp) << 2;
|
|
high |= (high >> 6);
|
|
|
|
const int k = (low * (64 - g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX] + 32) >> 6;
|
|
|
|
const int err = (k - c) * (k - c);
|
|
if (err < best.m_error)
|
|
{
|
|
best.m_error = safe_cast_uint16(err);
|
|
best.m_lo = (uint8_t)l;
|
|
best.m_hi = (uint8_t)h;
|
|
}
|
|
} // h
|
|
} // l
|
|
|
|
g_bc7_mode_7_optimal_endpoints[c][hp][lp] = best;
|
|
|
|
} // hp
|
|
|
|
} // lp
|
|
|
|
} // c
|
|
|
|
g_codec_initialized = true;
|
|
}
|
|
|
|
static void compute_least_squares_endpoints_rgba(uint32_t N, const int * pSelectors, const vec4F * pSelector_weights, vec4F * pXl, vec4F * pXh, const color_quad_i * pColors)
|
|
{
|
|
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
|
|
// I did this in matrix form first, expanded out all the ops, then optimized it a bit.
|
|
float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;
|
|
float q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f;
|
|
float q00_g = 0.0f, q10_g = 0.0f, t_g = 0.0f;
|
|
float q00_b = 0.0f, q10_b = 0.0f, t_b = 0.0f;
|
|
float q00_a = 0.0f, q10_a = 0.0f, t_a = 0.0f;
|
|
for ( uint32_t i = 0; i < N; i++)
|
|
{
|
|
const uint32_t sel = pSelectors[i];
|
|
|
|
z00 += pSelector_weights[sel].m_c[0];
|
|
z10 += pSelector_weights[sel].m_c[1];
|
|
z11 += pSelector_weights[sel].m_c[2];
|
|
|
|
float w = pSelector_weights[sel].m_c[3];
|
|
|
|
q00_r += w * (int)pColors[i].m_c[0]; t_r += (int)pColors[i].m_c[0];
|
|
q00_g += w * (int)pColors[i].m_c[1]; t_g += (int)pColors[i].m_c[1];
|
|
q00_b += w * (int)pColors[i].m_c[2]; t_b += (int)pColors[i].m_c[2];
|
|
q00_a += w * (int)pColors[i].m_c[3]; t_a += (int)pColors[i].m_c[3];
|
|
}
|
|
|
|
q10_r = t_r - q00_r;
|
|
q10_g = t_g - q00_g;
|
|
q10_b = t_b - q00_b;
|
|
q10_a = t_a - q00_a;
|
|
|
|
z01 = z10;
|
|
|
|
float det = z00 * z11 - z01 * z10;
|
|
if (det != 0.0f)
|
|
det = 1.0f / det;
|
|
|
|
float iz00, iz01, iz10, iz11;
|
|
iz00 = z11 * det;
|
|
iz01 = -z01 * det;
|
|
iz10 = -z10 * det;
|
|
iz11 = z00 * det;
|
|
|
|
pXl->m_c[0] = (float)(iz00 * q00_r + iz01 * q10_r); pXh->m_c[0] = (float)(iz10 * q00_r + iz11 * q10_r);
|
|
pXl->m_c[1] = (float)(iz00 * q00_g + iz01 * q10_g); pXh->m_c[1] = (float)(iz10 * q00_g + iz11 * q10_g);
|
|
pXl->m_c[2] = (float)(iz00 * q00_b + iz01 * q10_b); pXh->m_c[2] = (float)(iz10 * q00_b + iz11 * q10_b);
|
|
pXl->m_c[3] = (float)(iz00 * q00_a + iz01 * q10_a); pXh->m_c[3] = (float)(iz10 * q00_a + iz11 * q10_a);
|
|
}
|
|
|
|
static void compute_least_squares_endpoints_rgb(uint32_t N, const int * pSelectors, const vec4F * pSelector_weights, vec4F * pXl, vec4F * pXh, const color_quad_i * pColors)
|
|
{
|
|
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
|
|
// I did this in matrix form first, expanded out all the ops, then optimized it a bit.
|
|
float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;
|
|
float q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f;
|
|
float q00_g = 0.0f, q10_g = 0.0f, t_g = 0.0f;
|
|
float q00_b = 0.0f, q10_b = 0.0f, t_b = 0.0f;
|
|
for ( uint32_t i = 0; i < N; i++)
|
|
{
|
|
const uint32_t sel = pSelectors[i];
|
|
|
|
z00 += pSelector_weights[sel].m_c[0];
|
|
z10 += pSelector_weights[sel].m_c[1];
|
|
z11 += pSelector_weights[sel].m_c[2];
|
|
float w = pSelector_weights[sel].m_c[3];
|
|
|
|
q00_r += w * (int)pColors[i].m_c[0]; t_r += (int)pColors[i].m_c[0];
|
|
q00_g += w * (int)pColors[i].m_c[1]; t_g += (int)pColors[i].m_c[1];
|
|
q00_b += w * (int)pColors[i].m_c[2]; t_b += (int)pColors[i].m_c[2];
|
|
}
|
|
|
|
q10_r = t_r - q00_r;
|
|
q10_g = t_g - q00_g;
|
|
q10_b = t_b - q00_b;
|
|
|
|
z01 = z10;
|
|
|
|
float det = z00 * z11 - z01 * z10;
|
|
if (det != 0.0f)
|
|
det = 1.0f / det;
|
|
|
|
float iz00, iz01, iz10, iz11;
|
|
iz00 = z11 * det;
|
|
iz01 = -z01 * det;
|
|
iz10 = -z10 * det;
|
|
iz11 = z00 * det;
|
|
|
|
pXl->m_c[0] = (float)(iz00 * q00_r + iz01 * q10_r); pXh->m_c[0] = (float)(iz10 * q00_r + iz11 * q10_r);
|
|
pXl->m_c[1] = (float)(iz00 * q00_g + iz01 * q10_g); pXh->m_c[1] = (float)(iz10 * q00_g + iz11 * q10_g);
|
|
pXl->m_c[2] = (float)(iz00 * q00_b + iz01 * q10_b); pXh->m_c[2] = (float)(iz10 * q00_b + iz11 * q10_b);
|
|
}
|
|
|
|
static void compute_least_squares_endpoints_a(uint32_t N, const int * pSelectors, const vec4F * pSelector_weights, float * pXl, float * pXh, const color_quad_i * pColors)
|
|
{
|
|
// Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf
|
|
// I did this in matrix form first, expanded out all the ops, then optimized it a bit.
|
|
float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f;
|
|
float q00_a = 0.0f, q10_a = 0.0f, t_a = 0.0f;
|
|
for ( uint32_t i = 0; i < N; i++)
|
|
{
|
|
const uint32_t sel = pSelectors[i];
|
|
|
|
z00 += pSelector_weights[sel].m_c[0];
|
|
z10 += pSelector_weights[sel].m_c[1];
|
|
z11 += pSelector_weights[sel].m_c[2];
|
|
float w = pSelector_weights[sel].m_c[3];
|
|
|
|
q00_a += w * (int)pColors[i].m_c[3]; t_a += (int)pColors[i].m_c[3];
|
|
}
|
|
|
|
q10_a = t_a - q00_a;
|
|
|
|
z01 = z10;
|
|
|
|
float det = z00 * z11 - z01 * z10;
|
|
if (det != 0.0f)
|
|
det = 1.0f / det;
|
|
|
|
float iz00, iz01, iz10, iz11;
|
|
iz00 = z11 * det;
|
|
iz01 = -z01 * det;
|
|
iz10 = -z10 * det;
|
|
iz11 = z00 * det;
|
|
|
|
*pXl = (float)(iz00 * q00_a + iz01 * q10_a); *pXh = (float)(iz10 * q00_a + iz11 * q10_a);
|
|
}
|
|
|
|
struct color_cell_compressor_params
|
|
{
|
|
uint32_t m_num_selector_weights;
|
|
const uint32_t * m_pSelector_weights;
|
|
const vec4F * m_pSelector_weightsx;
|
|
uint32_t m_comp_bits;
|
|
uint32_t m_weights[4];
|
|
bool m_has_alpha;
|
|
bool m_has_pbits;
|
|
bool m_endpoints_share_pbit;
|
|
bool m_perceptual;
|
|
};
|
|
|
|
static inline void color_cell_compressor_params_clear( color_cell_compressor_params * p)
|
|
{
|
|
p->m_num_selector_weights = 0;
|
|
p->m_pSelector_weights = NULL;
|
|
p->m_pSelector_weightsx = NULL;
|
|
p->m_comp_bits = 0;
|
|
p->m_perceptual = false;
|
|
p->m_weights[0] = 1;
|
|
p->m_weights[1] = 1;
|
|
p->m_weights[2] = 1;
|
|
p->m_weights[3] = 1;
|
|
p->m_has_alpha = false;
|
|
p->m_has_pbits = false;
|
|
p->m_endpoints_share_pbit = false;
|
|
}
|
|
|
|
struct color_cell_compressor_results
|
|
{
|
|
uint64_t m_best_overall_err;
|
|
color_quad_i m_low_endpoint;
|
|
color_quad_i m_high_endpoint;
|
|
uint32_t m_pbits[2];
|
|
int *m_pSelectors;
|
|
int *m_pSelectors_temp;
|
|
// True if this subset's result was produced by the precomputed "one color"
|
|
// optimal-endpoint lookup tables (the solid/allSame path or the average-color
|
|
// candidate). Those place endpoints at extreme positions that only land at one
|
|
// fixed weight -- a hint to callers that this subset is "weird" (fragile under
|
|
// lossy weight recoding).
|
|
bool m_used_lut;
|
|
};
|
|
|
|
static inline color_quad_i scale_color(const color_quad_i * pC, const color_cell_compressor_params * pParams)
|
|
{
|
|
color_quad_i results;
|
|
|
|
const uint32_t n = pParams->m_comp_bits + (pParams->m_has_pbits ? 1 : 0);
|
|
assert((n >= 4) && (n <= 8));
|
|
|
|
for ( uint32_t i = 0; i < 4; i++)
|
|
{
|
|
uint32_t v = pC->m_c[i] << (8 - n);
|
|
v |= (v >> n);
|
|
assert(v <= 255);
|
|
results.m_c[i] = v;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
static const float pr_weight = (.5f / (1.0f - .2126f)) * (.5f / (1.0f - .2126f));
|
|
static const float pb_weight = (.5f / (1.0f - .0722f)) * (.5f / (1.0f - .0722f));
|
|
|
|
static inline uint64_t compute_color_distance_rgb(const color_quad_i * pE1, const color_quad_i * pE2, bool perceptual, const uint32_t weights[4])
|
|
{
|
|
if (perceptual)
|
|
{
|
|
const float l1 = pE1->m_c[0] * .2126f + pE1->m_c[1] * .7152f + pE1->m_c[2] * .0722f;
|
|
const float cr1 = pE1->m_c[0] - l1;
|
|
const float cb1 = pE1->m_c[2] - l1;
|
|
|
|
const float l2 = pE2->m_c[0] * .2126f + pE2->m_c[1] * .7152f + pE2->m_c[2] * .0722f;
|
|
const float cr2 = pE2->m_c[0] - l2;
|
|
const float cb2 = pE2->m_c[2] - l2;
|
|
|
|
float dl = l1 - l2;
|
|
float dcr = cr1 - cr2;
|
|
float dcb = cb1 - cb2;
|
|
|
|
return (int64_t)(weights[0] * (dl * dl) + weights[1] * pr_weight * (dcr * dcr) + weights[2] * pb_weight * (dcb * dcb));
|
|
}
|
|
else
|
|
{
|
|
float dr = (float)pE1->m_c[0] - (float)pE2->m_c[0];
|
|
float dg = (float)pE1->m_c[1] - (float)pE2->m_c[1];
|
|
float db = (float)pE1->m_c[2] - (float)pE2->m_c[2];
|
|
|
|
return (int64_t)(weights[0] * dr * dr + weights[1] * dg * dg + weights[2] * db * db);
|
|
}
|
|
}
|
|
|
|
static inline uint64_t compute_color_distance_rgba(const color_quad_i * pE1, const color_quad_i * pE2, bool perceptual, const uint32_t weights[4])
|
|
{
|
|
float da = (float)pE1->m_c[3] - (float)pE2->m_c[3];
|
|
float a_err = weights[3] * (da * da);
|
|
|
|
if (perceptual)
|
|
{
|
|
const float l1 = pE1->m_c[0] * .2126f + pE1->m_c[1] * .7152f + pE1->m_c[2] * .0722f;
|
|
const float cr1 = pE1->m_c[0] - l1;
|
|
const float cb1 = pE1->m_c[2] - l1;
|
|
|
|
const float l2 = pE2->m_c[0] * .2126f + pE2->m_c[1] * .7152f + pE2->m_c[2] * .0722f;
|
|
const float cr2 = pE2->m_c[0] - l2;
|
|
const float cb2 = pE2->m_c[2] - l2;
|
|
|
|
float dl = l1 - l2;
|
|
float dcr = cr1 - cr2;
|
|
float dcb = cb1 - cb2;
|
|
|
|
return (int64_t)(weights[0] * (dl * dl) + weights[1] * pr_weight * (dcr * dcr) + weights[2] * pb_weight * (dcb * dcb) + a_err);
|
|
}
|
|
else
|
|
{
|
|
float dr = (float)pE1->m_c[0] - (float)pE2->m_c[0];
|
|
float dg = (float)pE1->m_c[1] - (float)pE2->m_c[1];
|
|
float db = (float)pE1->m_c[2] - (float)pE2->m_c[2];
|
|
|
|
return (int64_t)(weights[0] * dr * dr + weights[1] * dg * dg + weights[2] * db * db + a_err);
|
|
}
|
|
}
|
|
|
|
static uint64_t pack_mode1_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b,
|
|
int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
uint32_t best_err = UINT_MAX;
|
|
uint32_t best_p = 0;
|
|
|
|
for ( uint32_t p = 0; p < 2; p++)
|
|
{
|
|
uint32_t err = g_bc7_mode_1_optimal_endpoints[r][p].m_error + g_bc7_mode_1_optimal_endpoints[g][p].m_error + g_bc7_mode_1_optimal_endpoints[b][p].m_error;
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_p = p;
|
|
}
|
|
}
|
|
|
|
const endpoint_err *pEr = &g_bc7_mode_1_optimal_endpoints[r][best_p];
|
|
const endpoint_err *pEg = &g_bc7_mode_1_optimal_endpoints[g][best_p];
|
|
const endpoint_err *pEb = &g_bc7_mode_1_optimal_endpoints[b][best_p];
|
|
|
|
color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, 0);
|
|
color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, 0);
|
|
pResults->m_pbits[0] = best_p;
|
|
pResults->m_pbits[1] = 0;
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pSelectors[i] = BC7E_MODE_1_OPTIMAL_INDEX;
|
|
|
|
color_quad_i p;
|
|
|
|
for ( uint32_t i = 0; i < 3; i++)
|
|
{
|
|
uint32_t low = ((pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0]) << 1;
|
|
low |= (low >> 7);
|
|
|
|
uint32_t high = ((pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[0]) << 1;
|
|
high |= (high >> 7);
|
|
|
|
p.m_c[i] = (low * (64 - g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX] + 32) >> 6;
|
|
}
|
|
|
|
p.m_c[3] = 255;
|
|
|
|
uint64_t total_err = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
total_err += compute_color_distance_rgb(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights);
|
|
|
|
pResults->m_best_overall_err = total_err;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint64_t pack_mode24_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b,
|
|
int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
uint32_t er, eg, eb;
|
|
|
|
if (pParams->m_num_selector_weights == 8)
|
|
{
|
|
er = g_bc7_mode_4_optimal_endpoints3[r];
|
|
eg = g_bc7_mode_4_optimal_endpoints3[g];
|
|
eb = g_bc7_mode_4_optimal_endpoints3[b];
|
|
}
|
|
else
|
|
{
|
|
er = g_bc7_mode_4_optimal_endpoints2[r];
|
|
eg = g_bc7_mode_4_optimal_endpoints2[g];
|
|
eb = g_bc7_mode_4_optimal_endpoints2[b];
|
|
}
|
|
|
|
color_quad_i_set(&pResults->m_low_endpoint, er & 0xFF, eg & 0xFF, eb & 0xFF, 0);
|
|
color_quad_i_set(&pResults->m_high_endpoint, er >> 8, eg >> 8, eb >> 8, 0);
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pSelectors[i] = (pParams->m_num_selector_weights == 8) ? BC7E_MODE_4_OPTIMAL_INDEX3 : BC7E_MODE_4_OPTIMAL_INDEX2;
|
|
|
|
color_quad_i p;
|
|
|
|
for ( uint32_t i = 0; i < 3; i++)
|
|
{
|
|
uint32_t low = pResults->m_low_endpoint.m_c[i] << 3;
|
|
low |= (low >> 5);
|
|
|
|
uint32_t high = pResults->m_high_endpoint.m_c[i] << 3;
|
|
high |= (high >> 5);
|
|
|
|
if (pParams->m_num_selector_weights == 8)
|
|
p.m_c[i] = (low * (64 - g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3]) + high * g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3] + 32) >> 6;
|
|
else
|
|
p.m_c[i] = (low * (64 - g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2]) + high * g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2] + 32) >> 6;
|
|
}
|
|
|
|
p.m_c[3] = 255;
|
|
|
|
uint64_t total_err = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
total_err += compute_color_distance_rgb(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights);
|
|
|
|
pResults->m_best_overall_err = total_err;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint64_t pack_mode0_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b,
|
|
int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
uint32_t best_err = UINT_MAX;
|
|
uint32_t best_p = 0;
|
|
|
|
for ( uint32_t p = 0; p < 4; p++)
|
|
{
|
|
uint32_t err = g_bc7_mode_0_optimal_endpoints[r][p >> 1][p & 1].m_error + g_bc7_mode_0_optimal_endpoints[g][p >> 1][p & 1].m_error + g_bc7_mode_0_optimal_endpoints[b][p >> 1][p & 1].m_error;
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_p = p;
|
|
}
|
|
}
|
|
|
|
const endpoint_err *pEr = &g_bc7_mode_0_optimal_endpoints[r][best_p >> 1][best_p & 1];
|
|
const endpoint_err *pEg = &g_bc7_mode_0_optimal_endpoints[g][best_p >> 1][best_p & 1];
|
|
const endpoint_err *pEb = &g_bc7_mode_0_optimal_endpoints[b][best_p >> 1][best_p & 1];
|
|
|
|
color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, 0);
|
|
|
|
color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, 0);
|
|
|
|
pResults->m_pbits[0] = best_p & 1;
|
|
pResults->m_pbits[1] = best_p >> 1;
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pSelectors[i] = BC7E_MODE_0_OPTIMAL_INDEX;
|
|
|
|
color_quad_i p;
|
|
|
|
for ( uint32_t i = 0; i < 3; i++)
|
|
{
|
|
uint32_t low = ((pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0]) << 3;
|
|
low |= (low >> 5);
|
|
|
|
uint32_t high = ((pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[1]) << 3;
|
|
high |= (high >> 5);
|
|
|
|
p.m_c[i] = (low * (64 - g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX] + 32) >> 6;
|
|
}
|
|
|
|
p.m_c[3] = 255;
|
|
|
|
uint64_t total_err = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
total_err += compute_color_distance_rgb(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights);
|
|
|
|
pResults->m_best_overall_err = total_err;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint64_t pack_mode6_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, uint32_t a,
|
|
int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
uint32_t best_err = UINT_MAX;
|
|
uint32_t best_p = 0;
|
|
|
|
for ( uint32_t p = 0; p < 4; p++)
|
|
{
|
|
uint32_t hi_p = p >> 1;
|
|
uint32_t lo_p = p & 1;
|
|
uint32_t err = g_bc7_mode_6_optimal_endpoints[r][hi_p][lo_p].m_error + g_bc7_mode_6_optimal_endpoints[g][hi_p][lo_p].m_error + g_bc7_mode_6_optimal_endpoints[b][hi_p][lo_p].m_error + g_bc7_mode_6_optimal_endpoints[a][hi_p][lo_p].m_error;
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_p = p;
|
|
}
|
|
}
|
|
|
|
uint32_t best_hi_p = best_p >> 1;
|
|
uint32_t best_lo_p = best_p & 1;
|
|
|
|
const endpoint_err *pEr = &g_bc7_mode_6_optimal_endpoints[r][best_hi_p][best_lo_p];
|
|
const endpoint_err *pEg = &g_bc7_mode_6_optimal_endpoints[g][best_hi_p][best_lo_p];
|
|
const endpoint_err *pEb = &g_bc7_mode_6_optimal_endpoints[b][best_hi_p][best_lo_p];
|
|
const endpoint_err *pEa = &g_bc7_mode_6_optimal_endpoints[a][best_hi_p][best_lo_p];
|
|
|
|
color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, pEa->m_lo);
|
|
|
|
color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, pEa->m_hi);
|
|
|
|
pResults->m_pbits[0] = best_lo_p;
|
|
pResults->m_pbits[1] = best_hi_p;
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pSelectors[i] = BC7E_MODE_6_OPTIMAL_INDEX;
|
|
|
|
color_quad_i p;
|
|
|
|
for ( uint32_t i = 0; i < 4; i++)
|
|
{
|
|
uint32_t low = (pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0];
|
|
uint32_t high = (pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[1];
|
|
|
|
p.m_c[i] = (low * (64 - g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX]) + high * g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX] + 32) >> 6;
|
|
}
|
|
|
|
uint64_t total_err = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
total_err += compute_color_distance_rgba(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights);
|
|
|
|
pResults->m_best_overall_err = total_err;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint64_t pack_mode7_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, uint32_t a,
|
|
int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
uint32_t best_err = UINT_MAX;
|
|
uint32_t best_p = 0;
|
|
|
|
for ( uint32_t p = 0; p < 4; p++)
|
|
{
|
|
uint32_t hi_p = p >> 1;
|
|
uint32_t lo_p = p & 1;
|
|
uint32_t err = g_bc7_mode_7_optimal_endpoints[r][hi_p][lo_p].m_error + g_bc7_mode_7_optimal_endpoints[g][hi_p][lo_p].m_error + g_bc7_mode_7_optimal_endpoints[b][hi_p][lo_p].m_error + g_bc7_mode_7_optimal_endpoints[a][hi_p][lo_p].m_error;
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_p = p;
|
|
}
|
|
}
|
|
|
|
uint32_t best_hi_p = best_p >> 1;
|
|
uint32_t best_lo_p = best_p & 1;
|
|
|
|
const endpoint_err *pEr = &g_bc7_mode_7_optimal_endpoints[r][best_hi_p][best_lo_p];
|
|
const endpoint_err *pEg = &g_bc7_mode_7_optimal_endpoints[g][best_hi_p][best_lo_p];
|
|
const endpoint_err *pEb = &g_bc7_mode_7_optimal_endpoints[b][best_hi_p][best_lo_p];
|
|
const endpoint_err *pEa = &g_bc7_mode_7_optimal_endpoints[a][best_hi_p][best_lo_p];
|
|
|
|
color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, pEa->m_lo);
|
|
|
|
color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, pEa->m_hi);
|
|
|
|
pResults->m_pbits[0] = best_lo_p;
|
|
pResults->m_pbits[1] = best_hi_p;
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pSelectors[i] = BC7E_MODE_7_OPTIMAL_INDEX;
|
|
|
|
color_quad_i p;
|
|
|
|
for ( uint32_t i = 0; i < 4; i++)
|
|
{
|
|
uint32_t low = (pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0];
|
|
uint32_t high = (pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[1];
|
|
|
|
p.m_c[i] = (low * (64 - g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX] + 32) >> 6;
|
|
}
|
|
|
|
uint64_t total_err = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
total_err += compute_color_distance_rgba(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights);
|
|
|
|
pResults->m_best_overall_err = total_err;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint64_t evaluate_solution(const color_quad_i * pLow, const color_quad_i * pHigh, const uint32_t * pbits,
|
|
const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
color_quad_i quantMinColor = *pLow;
|
|
color_quad_i quantMaxColor = *pHigh;
|
|
|
|
if (pParams->m_has_pbits)
|
|
{
|
|
uint32_t minPBit, maxPBit;
|
|
|
|
if (pParams->m_endpoints_share_pbit)
|
|
maxPBit = minPBit = pbits[0];
|
|
else
|
|
{
|
|
minPBit = pbits[0];
|
|
maxPBit = pbits[1];
|
|
}
|
|
|
|
quantMinColor.m_c[0] = (pLow->m_c[0] << 1) | minPBit;
|
|
quantMinColor.m_c[1] = (pLow->m_c[1] << 1) | minPBit;
|
|
quantMinColor.m_c[2] = (pLow->m_c[2] << 1) | minPBit;
|
|
quantMinColor.m_c[3] = (pLow->m_c[3] << 1) | minPBit;
|
|
|
|
quantMaxColor.m_c[0] = (pHigh->m_c[0] << 1) | maxPBit;
|
|
quantMaxColor.m_c[1] = (pHigh->m_c[1] << 1) | maxPBit;
|
|
quantMaxColor.m_c[2] = (pHigh->m_c[2] << 1) | maxPBit;
|
|
quantMaxColor.m_c[3] = (pHigh->m_c[3] << 1) | maxPBit;
|
|
}
|
|
|
|
color_quad_i actualMinColor = scale_color(&quantMinColor, pParams);
|
|
color_quad_i actualMaxColor = scale_color(&quantMaxColor, pParams);
|
|
|
|
const uint32_t N = pParams->m_num_selector_weights;
|
|
const uint32_t nc = pParams->m_has_alpha ? 4 : 3;
|
|
|
|
float total_errf = 0;
|
|
|
|
float wr = (float)(pParams->m_weights[0]);
|
|
float wg = (float)(pParams->m_weights[1]);
|
|
float wb = (float)(pParams->m_weights[2]);
|
|
float wa = (float)(pParams->m_weights[3]);
|
|
|
|
color_quad_f weightedColors[16];
|
|
weightedColors[0].m_c[0] = (float)(actualMinColor.m_c[0]);
|
|
weightedColors[0].m_c[1] = (float)(actualMinColor.m_c[1]);
|
|
weightedColors[0].m_c[2] = (float)(actualMinColor.m_c[2]);
|
|
weightedColors[0].m_c[3] = (float)(actualMinColor.m_c[3]);
|
|
|
|
weightedColors[N - 1].m_c[0] = (float)(actualMaxColor.m_c[0]);
|
|
weightedColors[N - 1].m_c[1] = (float)(actualMaxColor.m_c[1]);
|
|
weightedColors[N - 1].m_c[2] = (float)(actualMaxColor.m_c[2]);
|
|
weightedColors[N - 1].m_c[3] = (float)(actualMaxColor.m_c[3]);
|
|
|
|
for ( uint32_t i = 1; i < (N - 1); i++)
|
|
for ( uint32_t j = 0; j < nc; j++)
|
|
weightedColors[i].m_c[j] = floor((weightedColors[0].m_c[j] * (64.0f - pParams->m_pSelector_weights[i]) + weightedColors[N - 1].m_c[j] * pParams->m_pSelector_weights[i] + 32) * (1.0f / 64.0f));
|
|
|
|
if (!pParams->m_perceptual)
|
|
{
|
|
if (!pParams->m_has_alpha)
|
|
{
|
|
if (N == 16)
|
|
{
|
|
float lr = (float)(actualMinColor.m_c[0]);
|
|
float lg = (float)(actualMinColor.m_c[1]);
|
|
float lb = (float)(actualMinColor.m_c[2]);
|
|
|
|
float dr = actualMaxColor.m_c[0] - lr;
|
|
float dg = actualMaxColor.m_c[1] - lg;
|
|
float db = actualMaxColor.m_c[2] - lb;
|
|
|
|
const float f = N / (dr * dr + dg * dg + db * db + BC7E_DENOM_BIAS);
|
|
|
|
lr *= -dr;
|
|
lg *= -dg;
|
|
lb *= -db;
|
|
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
float r = (float)(pC->m_c[0]);
|
|
float g = (float)(pC->m_c[1]);
|
|
float b = (float)(pC->m_c[2]);
|
|
|
|
float best_sel = floor(((r * dr + lr) + (g * dg + lg) + (b * db + lb)) * f + .5f);
|
|
best_sel = clamp(best_sel, (float)1, (float)(N - 1));
|
|
|
|
float best_sel0 = best_sel - 1;
|
|
|
|
float dr0 = weightedColors[(int)best_sel0].m_c[0] - r;
|
|
|
|
float dg0 = weightedColors[(int)best_sel0].m_c[1] - g;
|
|
|
|
float db0 = weightedColors[(int)best_sel0].m_c[2] - b;
|
|
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0;
|
|
|
|
float dr1 = weightedColors[(int)best_sel].m_c[0] - r;
|
|
|
|
float dg1 = weightedColors[(int)best_sel].m_c[1] - g;
|
|
|
|
float db1 = weightedColors[(int)best_sel].m_c[2] - b;
|
|
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1;
|
|
|
|
float min_err = min(err0, err1);
|
|
total_errf += min_err;
|
|
pResults->m_pSelectors_temp[i] = (int)select(min_err == err0, best_sel0, best_sel);
|
|
}
|
|
}
|
|
else if (N == 8)
|
|
{
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
float pr = (float)pPixels[i].m_c[0];
|
|
float pg = (float)pPixels[i].m_c[1];
|
|
float pb = (float)pPixels[i].m_c[2];
|
|
|
|
float best_err;
|
|
int best_sel;
|
|
|
|
{
|
|
float dr0 = weightedColors[0].m_c[0] - pr;
|
|
float dg0 = weightedColors[0].m_c[1] - pg;
|
|
float db0 = weightedColors[0].m_c[2] - pb;
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0;
|
|
|
|
float dr1 = weightedColors[1].m_c[0] - pr;
|
|
float dg1 = weightedColors[1].m_c[1] - pg;
|
|
float db1 = weightedColors[1].m_c[2] - pb;
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1;
|
|
|
|
float dr2 = weightedColors[2].m_c[0] - pr;
|
|
float dg2 = weightedColors[2].m_c[1] - pg;
|
|
float db2 = weightedColors[2].m_c[2] - pb;
|
|
float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2;
|
|
|
|
float dr3 = weightedColors[3].m_c[0] - pr;
|
|
float dg3 = weightedColors[3].m_c[1] - pg;
|
|
float db3 = weightedColors[3].m_c[2] - pb;
|
|
float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3;
|
|
|
|
best_err = min(min(min(err0, err1), err2), err3);
|
|
|
|
best_sel = select(best_err == err1, 1, 0);
|
|
best_sel = select(best_err == err2, 2, best_sel);
|
|
best_sel = select(best_err == err3, 3, best_sel);
|
|
}
|
|
|
|
{
|
|
float dr0 = weightedColors[4].m_c[0] - pr;
|
|
float dg0 = weightedColors[4].m_c[1] - pg;
|
|
float db0 = weightedColors[4].m_c[2] - pb;
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0;
|
|
|
|
float dr1 = weightedColors[5].m_c[0] - pr;
|
|
float dg1 = weightedColors[5].m_c[1] - pg;
|
|
float db1 = weightedColors[5].m_c[2] - pb;
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1;
|
|
|
|
float dr2 = weightedColors[6].m_c[0] - pr;
|
|
float dg2 = weightedColors[6].m_c[1] - pg;
|
|
float db2 = weightedColors[6].m_c[2] - pb;
|
|
float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2;
|
|
|
|
float dr3 = weightedColors[7].m_c[0] - pr;
|
|
float dg3 = weightedColors[7].m_c[1] - pg;
|
|
float db3 = weightedColors[7].m_c[2] - pb;
|
|
float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3;
|
|
|
|
best_err = min(best_err, min(min(min(err0, err1), err2), err3));
|
|
|
|
best_sel = select(best_err == err0, 4, best_sel);
|
|
best_sel = select(best_err == err1, 5, best_sel);
|
|
best_sel = select(best_err == err2, 6, best_sel);
|
|
best_sel = select(best_err == err3, 7, best_sel);
|
|
}
|
|
|
|
total_errf += best_err;
|
|
|
|
pResults->m_pSelectors_temp[i] = best_sel;
|
|
}
|
|
}
|
|
else // if (N == 4)
|
|
{
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
float pr = (float)pPixels[i].m_c[0];
|
|
float pg = (float)pPixels[i].m_c[1];
|
|
float pb = (float)pPixels[i].m_c[2];
|
|
|
|
float dr0 = weightedColors[0].m_c[0] - pr;
|
|
float dg0 = weightedColors[0].m_c[1] - pg;
|
|
float db0 = weightedColors[0].m_c[2] - pb;
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0;
|
|
|
|
float dr1 = weightedColors[1].m_c[0] - pr;
|
|
float dg1 = weightedColors[1].m_c[1] - pg;
|
|
float db1 = weightedColors[1].m_c[2] - pb;
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1;
|
|
|
|
float dr2 = weightedColors[2].m_c[0] - pr;
|
|
float dg2 = weightedColors[2].m_c[1] - pg;
|
|
float db2 = weightedColors[2].m_c[2] - pb;
|
|
float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2;
|
|
|
|
float dr3 = weightedColors[3].m_c[0] - pr;
|
|
float dg3 = weightedColors[3].m_c[1] - pg;
|
|
float db3 = weightedColors[3].m_c[2] - pb;
|
|
float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3;
|
|
|
|
float best_err = min(min(min(err0, err1), err2), err3);
|
|
|
|
int best_sel = select(best_err == err1, 1, 0);
|
|
best_sel = select(best_err == err2, 2, best_sel);
|
|
best_sel = select(best_err == err3, 3, best_sel);
|
|
|
|
total_errf += best_err;
|
|
|
|
pResults->m_pSelectors_temp[i] = best_sel;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// alpha
|
|
if (N == 16)
|
|
{
|
|
float lr = (float)(actualMinColor.m_c[0]);
|
|
float lg = (float)(actualMinColor.m_c[1]);
|
|
float lb = (float)(actualMinColor.m_c[2]);
|
|
float la = (float)(actualMinColor.m_c[3]);
|
|
|
|
float dr = actualMaxColor.m_c[0] - lr;
|
|
float dg = actualMaxColor.m_c[1] - lg;
|
|
float db = actualMaxColor.m_c[2] - lb;
|
|
float da = actualMaxColor.m_c[3] - la;
|
|
|
|
const float f = N / (dr * dr + dg * dg + db * db + da * da + BC7E_DENOM_BIAS);
|
|
|
|
lr *= -dr;
|
|
lg *= -dg;
|
|
lb *= -db;
|
|
la *= -da;
|
|
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
float r = (float)(pC->m_c[0]);
|
|
float g = (float)(pC->m_c[1]);
|
|
float b = (float)(pC->m_c[2]);
|
|
float a = (float)(pC->m_c[3]);
|
|
|
|
float best_sel = floor(((r * dr + lr) + (g * dg + lg) + (b * db + lb) + (a * da + la)) * f + .5f);
|
|
best_sel = clamp(best_sel, (float)1, (float)(N - 1));
|
|
|
|
float best_sel0 = best_sel - 1;
|
|
|
|
float dr0 = weightedColors[(int)best_sel0].m_c[0] - r;
|
|
float dg0 = weightedColors[(int)best_sel0].m_c[1] - g;
|
|
float db0 = weightedColors[(int)best_sel0].m_c[2] - b;
|
|
float da0 = weightedColors[(int)best_sel0].m_c[3] - a;
|
|
float err0 = (wr * dr0 * dr0) + (wg * dg0 * dg0) + (wb * db0 * db0) + (wa * da0 * da0);
|
|
|
|
float dr1 = weightedColors[(int)best_sel].m_c[0] - r;
|
|
float dg1 = weightedColors[(int)best_sel].m_c[1] - g;
|
|
float db1 = weightedColors[(int)best_sel].m_c[2] - b;
|
|
float da1 = weightedColors[(int)best_sel].m_c[3] - a;
|
|
|
|
float err1 = (wr * dr1 * dr1) + (wg * dg1 * dg1) + (wb * db1 * db1) + (wa * da1 * da1);
|
|
|
|
float min_err = min(err0, err1);
|
|
total_errf += min_err;
|
|
pResults->m_pSelectors_temp[i] = (int)select(min_err == err0, best_sel0, best_sel);
|
|
}
|
|
}
|
|
else if (N == 8)
|
|
{
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
float pr = (float)pPixels[i].m_c[0];
|
|
float pg = (float)pPixels[i].m_c[1];
|
|
float pb = (float)pPixels[i].m_c[2];
|
|
float pa = (float)pPixels[i].m_c[3];
|
|
|
|
float best_err;
|
|
int best_sel;
|
|
|
|
{
|
|
float dr0 = weightedColors[0].m_c[0] - pr;
|
|
float dg0 = weightedColors[0].m_c[1] - pg;
|
|
float db0 = weightedColors[0].m_c[2] - pb;
|
|
float da0 = weightedColors[0].m_c[3] - pa;
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0 + wa * da0 * da0;
|
|
|
|
float dr1 = weightedColors[1].m_c[0] - pr;
|
|
float dg1 = weightedColors[1].m_c[1] - pg;
|
|
float db1 = weightedColors[1].m_c[2] - pb;
|
|
float da1 = weightedColors[1].m_c[3] - pa;
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1 + wa * da1 * da1;
|
|
|
|
float dr2 = weightedColors[2].m_c[0] - pr;
|
|
float dg2 = weightedColors[2].m_c[1] - pg;
|
|
float db2 = weightedColors[2].m_c[2] - pb;
|
|
float da2 = weightedColors[2].m_c[3] - pa;
|
|
float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2 + wa * da2 * da2;
|
|
|
|
float dr3 = weightedColors[3].m_c[0] - pr;
|
|
float dg3 = weightedColors[3].m_c[1] - pg;
|
|
float db3 = weightedColors[3].m_c[2] - pb;
|
|
float da3 = weightedColors[3].m_c[3] - pa;
|
|
float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3 + wa * da3 * da3;
|
|
|
|
best_err = min(min(min(err0, err1), err2), err3);
|
|
|
|
best_sel = select(best_err == err1, 1, 0);
|
|
best_sel = select(best_err == err2, 2, best_sel);
|
|
best_sel = select(best_err == err3, 3, best_sel);
|
|
}
|
|
|
|
{
|
|
float dr0 = weightedColors[4].m_c[0] - pr;
|
|
float dg0 = weightedColors[4].m_c[1] - pg;
|
|
float db0 = weightedColors[4].m_c[2] - pb;
|
|
float da0 = weightedColors[4].m_c[3] - pa;
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0 + wa * da0 * da0;
|
|
|
|
float dr1 = weightedColors[5].m_c[0] - pr;
|
|
float dg1 = weightedColors[5].m_c[1] - pg;
|
|
float db1 = weightedColors[5].m_c[2] - pb;
|
|
float da1 = weightedColors[5].m_c[3] - pa;
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1 + wa * da1 * da1;
|
|
|
|
float dr2 = weightedColors[6].m_c[0] - pr;
|
|
float dg2 = weightedColors[6].m_c[1] - pg;
|
|
float db2 = weightedColors[6].m_c[2] - pb;
|
|
float da2 = weightedColors[6].m_c[3] - pa;
|
|
float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2 + wa * da2 * da2;
|
|
|
|
float dr3 = weightedColors[7].m_c[0] - pr;
|
|
float dg3 = weightedColors[7].m_c[1] - pg;
|
|
float db3 = weightedColors[7].m_c[2] - pb;
|
|
float da3 = weightedColors[7].m_c[3] - pa;
|
|
float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3 + wa * da3 * da3;
|
|
|
|
best_err = min(best_err, min(min(min(err0, err1), err2), err3));
|
|
|
|
best_sel = select(best_err == err0, 4, best_sel);
|
|
best_sel = select(best_err == err1, 5, best_sel);
|
|
best_sel = select(best_err == err2, 6, best_sel);
|
|
best_sel = select(best_err == err3, 7, best_sel);
|
|
}
|
|
|
|
total_errf += best_err;
|
|
|
|
pResults->m_pSelectors_temp[i] = best_sel;
|
|
}
|
|
}
|
|
else // if (N == 4)
|
|
{
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
float pr = (float)pPixels[i].m_c[0];
|
|
float pg = (float)pPixels[i].m_c[1];
|
|
float pb = (float)pPixels[i].m_c[2];
|
|
float pa = (float)pPixels[i].m_c[3];
|
|
|
|
float dr0 = weightedColors[0].m_c[0] - pr;
|
|
float dg0 = weightedColors[0].m_c[1] - pg;
|
|
float db0 = weightedColors[0].m_c[2] - pb;
|
|
float da0 = weightedColors[0].m_c[3] - pa;
|
|
float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0 + wa * da0 * da0;
|
|
|
|
float dr1 = weightedColors[1].m_c[0] - pr;
|
|
float dg1 = weightedColors[1].m_c[1] - pg;
|
|
float db1 = weightedColors[1].m_c[2] - pb;
|
|
float da1 = weightedColors[1].m_c[3] - pa;
|
|
float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1 + wa * da1 * da1;
|
|
|
|
float dr2 = weightedColors[2].m_c[0] - pr;
|
|
float dg2 = weightedColors[2].m_c[1] - pg;
|
|
float db2 = weightedColors[2].m_c[2] - pb;
|
|
float da2 = weightedColors[2].m_c[3] - pa;
|
|
float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2 + wa * da2 * da2;
|
|
|
|
float dr3 = weightedColors[3].m_c[0] - pr;
|
|
float dg3 = weightedColors[3].m_c[1] - pg;
|
|
float db3 = weightedColors[3].m_c[2] - pb;
|
|
float da3 = weightedColors[3].m_c[3] - pa;
|
|
float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3 + wa * da3 * da3;
|
|
|
|
float best_err = min(min(min(err0, err1), err2), err3);
|
|
|
|
int best_sel = select(best_err == err1, 1, 0);
|
|
best_sel = select(best_err == err2, 2, best_sel);
|
|
best_sel = select(best_err == err3, 3, best_sel);
|
|
|
|
total_errf += best_err;
|
|
|
|
pResults->m_pSelectors_temp[i] = best_sel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wg *= pr_weight;
|
|
wb *= pb_weight;
|
|
|
|
float weightedColorsY[16], weightedColorsCr[16], weightedColorsCb[16];
|
|
|
|
for ( uint32_t i = 0; i < N; i++)
|
|
{
|
|
float r = weightedColors[i].m_c[0];
|
|
float g = weightedColors[i].m_c[1];
|
|
float b = weightedColors[i].m_c[2];
|
|
|
|
float y = r * .2126f + g * .7152f + b * .0722f;
|
|
|
|
weightedColorsY[i] = y;
|
|
weightedColorsCr[i] = r - y;
|
|
weightedColorsCb[i] = b - y;
|
|
}
|
|
|
|
if (pParams->m_has_alpha)
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
float r = (float)(pPixels[i].m_c[0]);
|
|
float g = (float)(pPixels[i].m_c[1]);
|
|
float b = (float)(pPixels[i].m_c[2]);
|
|
float a = (float)(pPixels[i].m_c[3]);
|
|
|
|
float y = r * .2126f + g * .7152f + b * .0722f;
|
|
float cr = r - y;
|
|
float cb = b - y;
|
|
|
|
float best_err = 1e+10f;
|
|
int32_t best_sel = 0;
|
|
|
|
for ( uint32_t j = 0; j < N; j++)
|
|
{
|
|
float dl = y - weightedColorsY[j];
|
|
float dcr = cr - weightedColorsCr[j];
|
|
float dcb = cb - weightedColorsCb[j];
|
|
float da = a - weightedColors[j].m_c[3];
|
|
|
|
float err = (wr * dl * dl) + (wg * dcr * dcr) + (wb * dcb * dcb) + (wa * da * da);
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_sel = j;
|
|
}
|
|
}
|
|
|
|
total_errf += best_err;
|
|
|
|
pResults->m_pSelectors_temp[i] = best_sel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
float r = (float)(pPixels[i].m_c[0]);
|
|
float g = (float)(pPixels[i].m_c[1]);
|
|
float b = (float)(pPixels[i].m_c[2]);
|
|
|
|
float y = r * .2126f + g * .7152f + b * .0722f;
|
|
float cr = r - y;
|
|
float cb = b - y;
|
|
|
|
float best_err = 1e+10f;
|
|
int32_t best_sel = 0;
|
|
|
|
for ( uint32_t j = 0; j < N; j++)
|
|
{
|
|
float dl = y - weightedColorsY[j];
|
|
float dcr = cr - weightedColorsCr[j];
|
|
float dcb = cb - weightedColorsCb[j];
|
|
|
|
float err = (wr * dl * dl) + (wg * dcr * dcr) + (wb * dcb * dcb);
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_sel = j;
|
|
}
|
|
}
|
|
|
|
total_errf += best_err;
|
|
|
|
pResults->m_pSelectors_temp[i] = best_sel;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t total_err = (int64)total_errf;
|
|
|
|
if (total_err < pResults->m_best_overall_err)
|
|
{
|
|
pResults->m_best_overall_err = total_err;
|
|
|
|
pResults->m_low_endpoint = *pLow;
|
|
pResults->m_high_endpoint = *pHigh;
|
|
|
|
pResults->m_pbits[0] = pbits[0];
|
|
pResults->m_pbits[1] = pbits[1];
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pResults->m_pSelectors[i] = pResults->m_pSelectors_temp[i];
|
|
}
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static void fixDegenerateEndpoints( uint32_t mode, color_quad_i * pTrialMinColor, color_quad_i * pTrialMaxColor, const vec4F * pXl, const vec4F * pXh, uint32_t iscale)
|
|
{
|
|
if ((mode == 1) || (mode == 4)) // also mode 2
|
|
{
|
|
// fix degenerate case where the input collapses to a single colorspace voxel, and we loose all freedom (test with grayscale ramps)
|
|
for ( uint32_t i = 0; i < 3; i++)
|
|
{
|
|
if (pTrialMinColor->m_c[i] == pTrialMaxColor->m_c[i])
|
|
{
|
|
if (abs(pXl->m_c[i] - pXh->m_c[i]) > 0.0f)
|
|
{
|
|
if (pTrialMinColor->m_c[i] > (int)(iscale >> 1))
|
|
{
|
|
if (pTrialMinColor->m_c[i] > 0)
|
|
pTrialMinColor->m_c[i]--;
|
|
else
|
|
if (pTrialMaxColor->m_c[i] < (int)iscale)
|
|
pTrialMaxColor->m_c[i]++;
|
|
}
|
|
else
|
|
{
|
|
if (pTrialMaxColor->m_c[i] < (int)iscale)
|
|
pTrialMaxColor->m_c[i]++;
|
|
else if (pTrialMinColor->m_c[i] > 0)
|
|
pTrialMinColor->m_c[i]--;
|
|
}
|
|
|
|
if (mode == 4)
|
|
{
|
|
if (pTrialMinColor->m_c[i] > (int)(iscale >> 1))
|
|
{
|
|
if (pTrialMaxColor->m_c[i] < (int)iscale)
|
|
pTrialMaxColor->m_c[i]++;
|
|
else if (pTrialMinColor->m_c[i] > 0)
|
|
pTrialMinColor->m_c[i]--;
|
|
}
|
|
else
|
|
{
|
|
if (pTrialMinColor->m_c[i] > 0)
|
|
pTrialMinColor->m_c[i]--;
|
|
else if (pTrialMaxColor->m_c[i] < (int)iscale)
|
|
pTrialMaxColor->m_c[i]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint64_t find_optimal_solution( uint32_t mode, vec4F * pXl, vec4F * pXh, const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults,
|
|
bool pbit_search, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
vec4F xl = *pXl;
|
|
vec4F xh = *pXh;
|
|
|
|
vec4F_saturate_in_place(&xl);
|
|
vec4F_saturate_in_place(&xh);
|
|
|
|
if (pParams->m_has_pbits)
|
|
{
|
|
if (pbit_search)
|
|
{
|
|
// compensated rounding+pbit search
|
|
const int iscalep = (1 << (pParams->m_comp_bits + 1)) - 1;
|
|
const float scalep = (float)iscalep;
|
|
|
|
const int32_t totalComps = pParams->m_has_alpha ? 4 : 3;
|
|
NOTE_UNUSED(totalComps);
|
|
|
|
if (!pParams->m_endpoints_share_pbit)
|
|
{
|
|
color_quad_i lo[2], hi[2];
|
|
|
|
for ( int p = 0; p < 2; p++)
|
|
{
|
|
color_quad_i xMinColor, xMaxColor;
|
|
|
|
// Notes: The pbit controls which quantization intervals are selected.
|
|
// total_levels=2^(comp_bits+1), where comp_bits=4 for mode 0, etc.
|
|
// pbit 0: v=(b*2)/(total_levels-1), pbit 1: v=(b*2+1)/(total_levels-1) where b is the component bin from [0,total_levels/2-1] and v is the [0,1] component value
|
|
// rearranging you get for pbit 0: b=floor(v*(total_levels-1)/2+.5)
|
|
// rearranging you get for pbit 1: b=floor((v*(total_levels-1)-1)/2+.5)
|
|
for ( uint32_t c = 0; c < 4; c++)
|
|
{
|
|
xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p);
|
|
|
|
xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p);
|
|
}
|
|
|
|
lo[p] = xMinColor;
|
|
hi[p] = xMaxColor;
|
|
|
|
for ( int c = 0; c < 4; c++)
|
|
{
|
|
lo[p].m_c[c] >>= 1;
|
|
hi[p].m_c[c] >>= 1;
|
|
}
|
|
}
|
|
|
|
fixDegenerateEndpoints(mode, &lo[0], &hi[0], &xl, &xh, iscalep >> 1);
|
|
fixDegenerateEndpoints(mode, &lo[1], &hi[1], &xl, &xh, iscalep >> 1);
|
|
|
|
uint32_t pbits[2];
|
|
|
|
pbits[0] = 0; pbits[1] = 0;
|
|
evaluate_solution(&lo[0], &hi[0], pbits, pParams, pResults, num_pixels, pPixels);
|
|
|
|
pbits[0] = 0; pbits[1] = 1;
|
|
evaluate_solution(&lo[0], &hi[1], pbits, pParams, pResults, num_pixels, pPixels);
|
|
|
|
pbits[0] = 1; pbits[1] = 0;
|
|
evaluate_solution(&lo[1], &hi[0], pbits, pParams, pResults, num_pixels, pPixels);
|
|
|
|
pbits[0] = 1; pbits[1] = 1;
|
|
evaluate_solution(&lo[1], &hi[1], pbits, pParams, pResults, num_pixels, pPixels);
|
|
}
|
|
else
|
|
{
|
|
// Endpoints share pbits
|
|
color_quad_i lo[2], hi[2];
|
|
|
|
for ( int p = 0; p < 2; p++)
|
|
{
|
|
color_quad_i xMinColor, xMaxColor;
|
|
|
|
for ( uint32_t c = 0; c < 4; c++)
|
|
{
|
|
xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p);
|
|
|
|
xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p);
|
|
}
|
|
|
|
lo[p] = xMinColor;
|
|
hi[p] = xMaxColor;
|
|
|
|
for ( int c = 0; c < 4; c++)
|
|
{
|
|
lo[p].m_c[c] >>= 1;
|
|
hi[p].m_c[c] >>= 1;
|
|
}
|
|
}
|
|
|
|
fixDegenerateEndpoints(mode, &lo[0], &hi[0], &xl, &xh, iscalep >> 1);
|
|
fixDegenerateEndpoints(mode, &lo[1], &hi[1], &xl, &xh, iscalep >> 1);
|
|
|
|
uint32_t pbits[2];
|
|
|
|
pbits[0] = 0; pbits[1] = 0;
|
|
evaluate_solution(&lo[0], &hi[0], pbits, pParams, pResults, num_pixels, pPixels);
|
|
|
|
pbits[0] = 1; pbits[1] = 1;
|
|
evaluate_solution(&lo[1], &hi[1], pbits, pParams, pResults, num_pixels, pPixels);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// compensated rounding
|
|
const int iscalep = (1 << (pParams->m_comp_bits + 1)) - 1;
|
|
const float scalep = (float)iscalep;
|
|
|
|
const int32_t totalComps = pParams->m_has_alpha ? 4 : 3;
|
|
|
|
uint32_t best_pbits[2];
|
|
color_quad_i bestMinColor, bestMaxColor;
|
|
|
|
if (!pParams->m_endpoints_share_pbit)
|
|
{
|
|
float best_err0 = 1e+9;
|
|
float best_err1 = 1e+9;
|
|
|
|
for ( int p = 0; p < 2; p++)
|
|
{
|
|
color_quad_i xMinColor, xMaxColor;
|
|
|
|
// Notes: The pbit controls which quantization intervals are selected.
|
|
// total_levels=2^(comp_bits+1), where comp_bits=4 for mode 0, etc.
|
|
// pbit 0: v=(b*2)/(total_levels-1), pbit 1: v=(b*2+1)/(total_levels-1) where b is the component bin from [0,total_levels/2-1] and v is the [0,1] component value
|
|
// rearranging you get for pbit 0: b=floor(v*(total_levels-1)/2+.5)
|
|
// rearranging you get for pbit 1: b=floor((v*(total_levels-1)-1)/2+.5)
|
|
for ( uint32_t c = 0; c < 4; c++)
|
|
{
|
|
xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p);
|
|
|
|
xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p);
|
|
}
|
|
|
|
color_quad_i scaledLow = scale_color(&xMinColor, pParams);
|
|
color_quad_i scaledHigh = scale_color(&xMaxColor, pParams);
|
|
|
|
float err0 = 0;
|
|
float err1 = 0;
|
|
for ( int i = 0; i < totalComps; i++)
|
|
{
|
|
err0 += square(scaledLow.m_c[i] - xl.m_c[i]*255.0f);
|
|
err1 += square(scaledHigh.m_c[i] - xh.m_c[i]*255.0f);
|
|
}
|
|
|
|
if (err0 < best_err0)
|
|
{
|
|
best_err0 = err0;
|
|
best_pbits[0] = p;
|
|
|
|
bestMinColor.m_c[0] = xMinColor.m_c[0] >> 1;
|
|
bestMinColor.m_c[1] = xMinColor.m_c[1] >> 1;
|
|
bestMinColor.m_c[2] = xMinColor.m_c[2] >> 1;
|
|
bestMinColor.m_c[3] = xMinColor.m_c[3] >> 1;
|
|
}
|
|
|
|
if (err1 < best_err1)
|
|
{
|
|
best_err1 = err1;
|
|
best_pbits[1] = p;
|
|
|
|
bestMaxColor.m_c[0] = xMaxColor.m_c[0] >> 1;
|
|
bestMaxColor.m_c[1] = xMaxColor.m_c[1] >> 1;
|
|
bestMaxColor.m_c[2] = xMaxColor.m_c[2] >> 1;
|
|
bestMaxColor.m_c[3] = xMaxColor.m_c[3] >> 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Endpoints share pbits
|
|
float best_err = 1e+9;
|
|
|
|
for ( int p = 0; p < 2; p++)
|
|
{
|
|
color_quad_i xMinColor, xMaxColor;
|
|
|
|
for ( uint32_t c = 0; c < 4; c++)
|
|
{
|
|
xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p);
|
|
|
|
xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p;
|
|
xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p);
|
|
}
|
|
|
|
color_quad_i scaledLow = scale_color(&xMinColor, pParams);
|
|
color_quad_i scaledHigh = scale_color(&xMaxColor, pParams);
|
|
|
|
float err = 0;
|
|
for ( int i = 0; i < totalComps; i++)
|
|
err += square((scaledLow.m_c[i]/255.0f) - xl.m_c[i]) + square((scaledHigh.m_c[i]/255.0f) - xh.m_c[i]);
|
|
|
|
if (err < best_err)
|
|
{
|
|
best_err = err;
|
|
best_pbits[0] = p;
|
|
best_pbits[1] = p;
|
|
|
|
bestMinColor.m_c[0] = xMinColor.m_c[0] >> 1;
|
|
bestMinColor.m_c[1] = xMinColor.m_c[1] >> 1;
|
|
bestMinColor.m_c[2] = xMinColor.m_c[2] >> 1;
|
|
bestMinColor.m_c[3] = xMinColor.m_c[3] >> 1;
|
|
|
|
bestMaxColor.m_c[0] = xMaxColor.m_c[0] >> 1;
|
|
bestMaxColor.m_c[1] = xMaxColor.m_c[1] >> 1;
|
|
bestMaxColor.m_c[2] = xMaxColor.m_c[2] >> 1;
|
|
bestMaxColor.m_c[3] = xMaxColor.m_c[3] >> 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
fixDegenerateEndpoints(mode, &bestMinColor, &bestMaxColor, &xl, &xh, iscalep >> 1);
|
|
|
|
if ((pResults->m_best_overall_err == UINT64_MAX) || color_quad_i_notequals(&bestMinColor, &pResults->m_low_endpoint) || color_quad_i_notequals(&bestMaxColor, &pResults->m_high_endpoint) || (best_pbits[0] != pResults->m_pbits[0]) || (best_pbits[1] != pResults->m_pbits[1]))
|
|
{
|
|
evaluate_solution(&bestMinColor, &bestMaxColor, best_pbits, pParams, pResults, num_pixels, pPixels);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int iscale = (1 << pParams->m_comp_bits) - 1;
|
|
const float scale = (float)iscale;
|
|
|
|
color_quad_i trialMinColor, trialMaxColor;
|
|
color_quad_i_set_clamped(&trialMinColor, (int)(xl.m_c[0] * scale + .5f), (int)(xl.m_c[1] * scale + .5f), (int)(xl.m_c[2] * scale + .5f), (int)(xl.m_c[3] * scale + .5f));
|
|
color_quad_i_set_clamped(&trialMaxColor, (int)(xh.m_c[0] * scale + .5f), (int)(xh.m_c[1] * scale + .5f), (int)(xh.m_c[2] * scale + .5f), (int)(xh.m_c[3] * scale + .5f));
|
|
|
|
fixDegenerateEndpoints(mode, &trialMinColor, &trialMaxColor, &xl, &xh, iscale);
|
|
|
|
if ((pResults->m_best_overall_err == UINT64_MAX) || color_quad_i_notequals(&trialMinColor, &pResults->m_low_endpoint) || color_quad_i_notequals(&trialMaxColor, &pResults->m_high_endpoint))
|
|
{
|
|
uint32_t pbits[2];
|
|
pbits[0] = 0;
|
|
pbits[1] = 0;
|
|
|
|
evaluate_solution(&trialMinColor, &trialMaxColor, pbits, pParams, pResults, num_pixels, pPixels);
|
|
}
|
|
}
|
|
|
|
return pResults->m_best_overall_err;
|
|
}
|
|
|
|
// Note: In mode 6, m_has_alpha will only be true for transparent blocks.
|
|
static uint64_t color_cell_compression( uint32_t mode, const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults,
|
|
const bc7e_compress_block_params * pComp_params, uint32_t num_pixels, const color_quad_i * pPixels, bool refinement)
|
|
{
|
|
pResults->m_best_overall_err = UINT64_MAX;
|
|
pResults->m_used_lut = false; // set true below only if a one-color LUT result wins
|
|
|
|
if ((mode != 6) && (mode != 7))
|
|
{
|
|
assert(!pParams->m_has_alpha);
|
|
}
|
|
|
|
if ((mode <= 2) || (mode == 4) || (mode >= 6))
|
|
{
|
|
const uint32_t cr = pPixels[0].m_c[0];
|
|
const uint32_t cg = pPixels[0].m_c[1];
|
|
const uint32_t cb = pPixels[0].m_c[2];
|
|
const uint32_t ca = pPixels[0].m_c[3];
|
|
|
|
bool allSame = true;
|
|
for ( uint32_t i = 1; i < num_pixels; i++)
|
|
{
|
|
if ((cr != (uint32_t)pPixels[i].m_c[0]) || (cg != (uint32_t)pPixels[i].m_c[1]) || (cb != (uint32_t)pPixels[i].m_c[2]) || (ca != (uint32_t)pPixels[i].m_c[3]))
|
|
{
|
|
allSame = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
cif (allSame && pComp_params->m_use_luts)
|
|
{
|
|
pResults->m_used_lut = true; // solid/allSame subset -> one-color optimal-endpoint LUT
|
|
if (mode == 0)
|
|
return pack_mode0_to_one_color(pParams, pResults, cr, cg, cb, pResults->m_pSelectors, num_pixels, pPixels);
|
|
if (mode == 1)
|
|
return pack_mode1_to_one_color(pParams, pResults, cr, cg, cb, pResults->m_pSelectors, num_pixels, pPixels);
|
|
else if (mode == 6)
|
|
return pack_mode6_to_one_color(pParams, pResults, cr, cg, cb, ca, pResults->m_pSelectors, num_pixels, pPixels);
|
|
else if (mode == 7)
|
|
return pack_mode7_to_one_color(pParams, pResults, cr, cg, cb, ca, pResults->m_pSelectors, num_pixels, pPixels);
|
|
else
|
|
return pack_mode24_to_one_color(pParams, pResults, cr, cg, cb, pResults->m_pSelectors, num_pixels, pPixels);
|
|
}
|
|
}
|
|
|
|
vec4F meanColor, axis;
|
|
vec4F_set_scalar(&meanColor, 0.0f);
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
vec4F color = vec4F_from_color(&pPixels[i]);
|
|
meanColor = vec4F_add(&meanColor, &color);
|
|
}
|
|
|
|
vec4F meanColorScaled = vec4F_mul(&meanColor, 1.0f / (float)((int)num_pixels));
|
|
|
|
meanColor = vec4F_mul(&meanColor, 1.0f / (float)((int)num_pixels * 255.0f));
|
|
vec4F_saturate_in_place(&meanColor);
|
|
|
|
if (pParams->m_has_alpha)
|
|
{
|
|
vec4F v;
|
|
vec4F_set_scalar(&v, 0.0f);
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
vec4F color = vec4F_from_color(&pPixels[i]);
|
|
color = vec4F_sub(&color, &meanColorScaled);
|
|
|
|
vec4F a = vec4F_mul(&color, color.m_c[0]);
|
|
vec4F b = vec4F_mul(&color, color.m_c[1]);
|
|
vec4F c = vec4F_mul(&color, color.m_c[2]);
|
|
vec4F d = vec4F_mul(&color, color.m_c[3]);
|
|
|
|
vec4F n = i ? v : color;
|
|
vec4F_normalize_in_place(&n);
|
|
|
|
v.m_c[0] += vec4F_dot(&a, &n);
|
|
v.m_c[1] += vec4F_dot(&b, &n);
|
|
v.m_c[2] += vec4F_dot(&c, &n);
|
|
v.m_c[3] += vec4F_dot(&d, &n);
|
|
}
|
|
axis = v;
|
|
vec4F_normalize_in_place(&axis);
|
|
}
|
|
else
|
|
{
|
|
float cov[6];
|
|
cov[0] = 0; cov[1] = 0; cov[2] = 0;
|
|
cov[3] = 0; cov[4] = 0; cov[5] = 0;
|
|
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pV = &pPixels[i];
|
|
|
|
float r = pV->m_c[0] - meanColorScaled.m_c[0];
|
|
float g = pV->m_c[1] - meanColorScaled.m_c[1];
|
|
float b = pV->m_c[2] - meanColorScaled.m_c[2];
|
|
|
|
cov[0] += r*r;
|
|
cov[1] += r*g;
|
|
cov[2] += r*b;
|
|
cov[3] += g*g;
|
|
cov[4] += g*b;
|
|
cov[5] += b*b;
|
|
}
|
|
|
|
float vfr, vfg, vfb;
|
|
//vfr = hi[0] - lo[0];
|
|
//vfg = hi[1] - lo[1];
|
|
//vfb = hi[2] - lo[2];
|
|
// This is more stable.
|
|
vfr = .9f;
|
|
vfg = 1.0f;
|
|
vfb = .7f;
|
|
|
|
for ( uint32_t iter = 0; iter < 3; iter++)
|
|
{
|
|
float r = vfr*cov[0] + vfg*cov[1] + vfb*cov[2];
|
|
float g = vfr*cov[1] + vfg*cov[3] + vfb*cov[4];
|
|
float b = vfr*cov[2] + vfg*cov[4] + vfb*cov[5];
|
|
|
|
float m = maximumf(maximumf(abs(r), abs(g)), abs(b));
|
|
if (m > 1e-10f)
|
|
{
|
|
m = 1.0f / m;
|
|
r *= m;
|
|
g *= m;
|
|
b *= m;
|
|
}
|
|
|
|
//float delta = square(vfr - r) + square(vfg - g) + square(vfb - b);
|
|
|
|
vfr = r;
|
|
vfg = g;
|
|
vfb = b;
|
|
|
|
//if ((iter > 1) && (delta < 1e-8f))
|
|
// break;
|
|
}
|
|
|
|
float len = vfr*vfr + vfg*vfg + vfb*vfb;
|
|
|
|
if (len < 1e-10f)
|
|
vec4F_set_scalar(&axis, 0.0f);
|
|
else
|
|
{
|
|
len = 1.0f / sqrt(len);
|
|
vfr *= len;
|
|
vfg *= len;
|
|
vfb *= len;
|
|
vec4F_set(&axis, vfr, vfg, vfb, 0);
|
|
}
|
|
}
|
|
|
|
cif (vec4F_dot(&axis, &axis) < .5f)
|
|
{
|
|
if (pParams->m_perceptual)
|
|
vec4F_set(&axis, .213f, .715f, .072f, pParams->m_has_alpha ? .715f : 0);
|
|
else
|
|
vec4F_set(&axis, 1.0f, 1.0f, 1.0f, pParams->m_has_alpha ? 1.0f : 0);
|
|
vec4F_normalize_in_place(&axis);
|
|
}
|
|
|
|
float l = 1e+9f, h = -1e+9f;
|
|
|
|
cfor ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
vec4F color = vec4F_from_color(&pPixels[i]);
|
|
|
|
vec4F q = vec4F_sub(&color, &meanColorScaled);
|
|
float d = vec4F_dot(&q, &axis);
|
|
|
|
l = minimumf(l, d);
|
|
h = maximumf(h, d);
|
|
}
|
|
|
|
l *= (1.0f / 255.0f);
|
|
h *= (1.0f / 255.0f);
|
|
|
|
vec4F b0 = vec4F_mul(&axis, l);
|
|
vec4F b1 = vec4F_mul(&axis, h);
|
|
vec4F c0 = vec4F_add(&meanColor, &b0);
|
|
vec4F c1 = vec4F_add(&meanColor, &b1);
|
|
vec4F minColor = vec4F_saturate(&c0);
|
|
vec4F maxColor = vec4F_saturate(&c1);
|
|
|
|
vec4F whiteVec;
|
|
vec4F_set_scalar(&whiteVec, 1.0f);
|
|
if (vec4F_dot(&minColor, &whiteVec) > vec4F_dot(&maxColor, &whiteVec))
|
|
{
|
|
vec4F temp = minColor;
|
|
minColor = maxColor;
|
|
maxColor = temp;
|
|
}
|
|
|
|
if (!find_optimal_solution(mode, &minColor, &maxColor, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels))
|
|
return 0;
|
|
|
|
if (!refinement)
|
|
return pResults->m_best_overall_err;
|
|
|
|
for ( uint32_t i = 0; i < pComp_params->m_refinement_passes; i++)
|
|
{
|
|
vec4F xl, xh;
|
|
vec4F_set_scalar(&xl, 0.0f);
|
|
vec4F_set_scalar(&xh, 0.0f);
|
|
if (pParams->m_has_alpha)
|
|
compute_least_squares_endpoints_rgba(num_pixels, pResults->m_pSelectors, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
else
|
|
{
|
|
compute_least_squares_endpoints_rgb(num_pixels, pResults->m_pSelectors, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
|
|
xl.m_c[3] = 255.0f;
|
|
xh.m_c[3] = 255.0f;
|
|
}
|
|
|
|
xl = vec4F_mul(&xl, (1.0f / 255.0f));
|
|
xh = vec4F_mul(&xh, (1.0f / 255.0f));
|
|
|
|
if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels))
|
|
return 0;
|
|
}
|
|
|
|
if (pComp_params->m_uber_level > 0)
|
|
{
|
|
int selectors_temp[16], selectors_temp1[16];
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
selectors_temp[i] = pResults->m_pSelectors[i];
|
|
|
|
const int max_selector = pParams->m_num_selector_weights - 1;
|
|
|
|
uint32_t min_sel = 16;
|
|
uint32_t max_sel = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
uint32_t sel = selectors_temp[i];
|
|
min_sel = minimumu(min_sel, sel);
|
|
max_sel = maximumu(max_sel, sel);
|
|
}
|
|
|
|
vec4F xl, xh;
|
|
vec4F_set_scalar(&xl, 0.0f);
|
|
vec4F_set_scalar(&xh, 0.0f);
|
|
|
|
if (pComp_params->m_uber1_mask & 1)
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
uint32_t sel = selectors_temp[i];
|
|
if ((sel == min_sel) && (sel < (pParams->m_num_selector_weights - 1)))
|
|
sel++;
|
|
selectors_temp1[i] = sel;
|
|
}
|
|
|
|
if (pParams->m_has_alpha)
|
|
compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
else
|
|
{
|
|
compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
xl.m_c[3] = 255.0f;
|
|
xh.m_c[3] = 255.0f;
|
|
}
|
|
|
|
xl = vec4F_mul(&xl, (1.0f / 255.0f));
|
|
xh = vec4F_mul(&xh, (1.0f / 255.0f));
|
|
|
|
if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels))
|
|
return 0;
|
|
}
|
|
|
|
if (pComp_params->m_uber1_mask & 2)
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
uint32_t sel = selectors_temp[i];
|
|
if ((sel == max_sel) && (sel > 0))
|
|
sel--;
|
|
selectors_temp1[i] = sel;
|
|
}
|
|
|
|
if (pParams->m_has_alpha)
|
|
compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
else
|
|
{
|
|
compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
xl.m_c[3] = 255.0f;
|
|
xh.m_c[3] = 255.0f;
|
|
}
|
|
|
|
xl = vec4F_mul(&xl, (1.0f / 255.0f));
|
|
xh = vec4F_mul(&xh, (1.0f / 255.0f));
|
|
|
|
if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels))
|
|
return 0;
|
|
}
|
|
|
|
if (pComp_params->m_uber1_mask & 4)
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
uint32_t sel = selectors_temp[i];
|
|
if ((sel == min_sel) && (sel < (pParams->m_num_selector_weights - 1)))
|
|
sel++;
|
|
else if ((sel == max_sel) && (sel > 0))
|
|
sel--;
|
|
selectors_temp1[i] = sel;
|
|
}
|
|
|
|
if (pParams->m_has_alpha)
|
|
compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
else
|
|
{
|
|
compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
xl.m_c[3] = 255.0f;
|
|
xh.m_c[3] = 255.0f;
|
|
}
|
|
|
|
xl = vec4F_mul(&xl, (1.0f / 255.0f));
|
|
xh = vec4F_mul(&xh, (1.0f / 255.0f));
|
|
|
|
if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels))
|
|
return 0;
|
|
}
|
|
|
|
const uint32_t uber_err_thresh = (num_pixels * 56) >> 4;
|
|
if ((pComp_params->m_uber_level >= 2) && (pResults->m_best_overall_err > uber_err_thresh))
|
|
{
|
|
const int Q = (pComp_params->m_uber_level >= 4) ? (pComp_params->m_uber_level - 2) : 1;
|
|
for ( int ly = -Q; ly <= 1; ly++)
|
|
{
|
|
for ( int hy = max_selector - 1; hy <= (max_selector + Q); hy++)
|
|
{
|
|
if ((ly == 0) && (hy == max_selector))
|
|
continue;
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
selectors_temp1[i] = (int)clampf(floor((float)max_selector * ((float)(int)selectors_temp[i] - (float)ly) / ((float)hy - (float)ly) + .5f), 0, (float)max_selector);
|
|
|
|
vec4F_set_scalar(&xl, 0.0f);
|
|
vec4F_set_scalar(&xh, 0.0f);
|
|
if (pParams->m_has_alpha)
|
|
compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
else
|
|
{
|
|
compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels);
|
|
xl.m_c[3] = 255.0f;
|
|
xh.m_c[3] = 255.0f;
|
|
}
|
|
|
|
xl = vec4F_mul(&xl, (1.0f / 255.0f));
|
|
xh = vec4F_mul(&xh, (1.0f / 255.0f));
|
|
|
|
if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search && (pComp_params->m_uber_level >= 2), num_pixels, pPixels))
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((mode <= 2) || (mode == 4) || (mode >= 6)) && pComp_params->m_use_luts)
|
|
{
|
|
color_cell_compressor_results avg_results;
|
|
memset(&avg_results, 0, sizeof(avg_results));
|
|
|
|
avg_results.m_best_overall_err = pResults->m_best_overall_err;
|
|
avg_results.m_pSelectors = pResults->m_pSelectors;
|
|
avg_results.m_pSelectors_temp = pResults->m_pSelectors_temp;
|
|
|
|
const uint32_t r = (int)(.5f + meanColor.m_c[0] * 255.0f);
|
|
const uint32_t g = (int)(.5f + meanColor.m_c[1] * 255.0f);
|
|
const uint32_t b = (int)(.5f + meanColor.m_c[2] * 255.0f);
|
|
const uint32_t a = (int)(.5f + meanColor.m_c[3] * 255.0f);
|
|
|
|
uint64_t avg_err;
|
|
if (mode == 0)
|
|
avg_err = pack_mode0_to_one_color(pParams, &avg_results, r, g, b, pResults->m_pSelectors_temp, num_pixels, pPixels);
|
|
else if (mode == 1)
|
|
avg_err = pack_mode1_to_one_color(pParams, &avg_results, r, g, b, pResults->m_pSelectors_temp, num_pixels, pPixels);
|
|
else if (mode == 6)
|
|
avg_err = pack_mode6_to_one_color(pParams, &avg_results, r, g, b, a, pResults->m_pSelectors_temp, num_pixels, pPixels);
|
|
else if (mode == 7)
|
|
avg_err = pack_mode7_to_one_color(pParams, &avg_results, r, g, b, a, pResults->m_pSelectors_temp, num_pixels, pPixels);
|
|
else
|
|
avg_err = pack_mode24_to_one_color(pParams, &avg_results, r, g, b, pResults->m_pSelectors_temp, num_pixels, pPixels);
|
|
|
|
if (avg_err < pResults->m_best_overall_err)
|
|
{
|
|
pResults->m_best_overall_err = avg_err;
|
|
pResults->m_low_endpoint = avg_results.m_low_endpoint;
|
|
pResults->m_high_endpoint = avg_results.m_high_endpoint;
|
|
pResults->m_pbits[0] = avg_results.m_pbits[0];
|
|
pResults->m_pbits[1] = avg_results.m_pbits[1];
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
pResults->m_pSelectors[i] = pResults->m_pSelectors_temp[i];
|
|
|
|
pResults->m_used_lut = true; // average-color one-color LUT candidate won
|
|
}
|
|
}
|
|
|
|
return pResults->m_best_overall_err;
|
|
}
|
|
|
|
static uint64_t color_cell_compression_est( uint32_t mode, const color_cell_compressor_params * pParams, uint64_t best_err_so_far, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
NOTE_UNUSED(best_err_so_far);
|
|
assert((pParams->m_num_selector_weights == 4) || (pParams->m_num_selector_weights == 8));
|
|
|
|
float lr = 255, lg = 255, lb = 255;
|
|
float hr = 0, hg = 0, hb = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
|
|
float r = (float)(pC->m_c[0]);
|
|
float g = (float)(pC->m_c[1]);
|
|
float b = (float)(pC->m_c[2]);
|
|
|
|
lr = min(lr, r);
|
|
lg = min(lg, g);
|
|
lb = min(lb, b);
|
|
|
|
hr = max(hr, r);
|
|
hg = max(hg, g);
|
|
hb = max(hb, b);
|
|
}
|
|
|
|
const uint32_t N = 1 << g_bc7_color_index_bitcount[mode];
|
|
|
|
uint64_t total_err = 0;
|
|
|
|
float sr = lr;
|
|
float sg = lg;
|
|
float sb = lb;
|
|
|
|
float dir = hr - lr;
|
|
float dig = hg - lg;
|
|
float dib = hb - lb;
|
|
|
|
float far = dir;
|
|
float fag = dig;
|
|
float fab = dib;
|
|
|
|
float low = far * sr + fag * sg + fab * sb;
|
|
float high = far * hr + fag * hg + fab * hb;
|
|
|
|
float scale = ((float)N - 1) / ((float)(high - low) + BC7E_DENOM_BIAS);
|
|
float inv_n = 1.0f / ((float)N - 1);
|
|
|
|
float total_errf = 0;
|
|
|
|
// We don't handle perceptual very well here, but the difference is very slight (<.05 dB avg Luma PSNR across a large corpus) and the perf lost was high (2x slower).
|
|
if ((pParams->m_weights[0] != 1) || (pParams->m_weights[1] != 1) || (pParams->m_weights[2] != 1))
|
|
{
|
|
float wr = (float)(pParams->m_weights[0]);
|
|
float wg = (float)(pParams->m_weights[1]);
|
|
float wb = (float)(pParams->m_weights[2]);
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
|
|
float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2];
|
|
|
|
float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f);
|
|
|
|
float itr = sr + dir * s;
|
|
float itg = sg + dig * s;
|
|
float itb = sb + dib * s;
|
|
|
|
float dr = itr - (float)pC->m_c[0];
|
|
float dg = itg - (float)pC->m_c[1];
|
|
float db = itb - (float)pC->m_c[2];
|
|
|
|
total_errf += wr * dr * dr + wg * dg * dg + wb * db * db;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
|
|
float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2];
|
|
|
|
float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f);
|
|
|
|
float itr = sr + dir * s;
|
|
float itg = sg + dig * s;
|
|
float itb = sb + dib * s;
|
|
|
|
float dr = itr - (float)pC->m_c[0];
|
|
float dg = itg - (float)pC->m_c[1];
|
|
float db = itb - (float)pC->m_c[2];
|
|
|
|
total_errf += dr * dr + dg * dg + db * db;
|
|
}
|
|
}
|
|
|
|
total_err = (int64_t)total_errf;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint64_t color_cell_compression_est_mode7( uint32_t mode, const color_cell_compressor_params * pParams, uint64_t best_err_so_far, uint32_t num_pixels, const color_quad_i * pPixels)
|
|
{
|
|
NOTE_UNUSED(best_err_so_far);
|
|
NOTE_UNUSED(mode); // only referenced by the assert below (compiled out under NDEBUG)
|
|
assert((mode == 7) && (pParams->m_num_selector_weights == 4));
|
|
|
|
float lr = 255, lg = 255, lb = 255, la = 255;
|
|
float hr = 0, hg = 0, hb = 0, ha = 0;
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
|
|
float r = (float)(pC->m_c[0]);
|
|
float g = (float)(pC->m_c[1]);
|
|
float b = (float)(pC->m_c[2]);
|
|
float a = (float)(pC->m_c[3]);
|
|
|
|
lr = min(lr, r);
|
|
lg = min(lg, g);
|
|
lb = min(lb, b);
|
|
la = min(la, a);
|
|
|
|
hr = max(hr, r);
|
|
hg = max(hg, g);
|
|
hb = max(hb, b);
|
|
ha = max(ha, a);
|
|
}
|
|
|
|
const uint32_t N = 4;
|
|
|
|
uint64_t total_err = 0;
|
|
|
|
float sr = lr;
|
|
float sg = lg;
|
|
float sb = lb;
|
|
float sa = la;
|
|
|
|
float dir = hr - lr;
|
|
float dig = hg - lg;
|
|
float dib = hb - lb;
|
|
float dia = ha - la;
|
|
|
|
float far = dir;
|
|
float fag = dig;
|
|
float fab = dib;
|
|
float faa = dia;
|
|
|
|
float low = far * sr + fag * sg + fab * sb + faa * sa;
|
|
float high = far * hr + fag * hg + fab * hb + faa * ha;
|
|
|
|
float scale = ((float)N - 1) / ((float)(high - low) + BC7E_DENOM_BIAS);
|
|
float inv_n = 1.0f / ((float)N - 1);
|
|
|
|
float total_errf = 0;
|
|
|
|
// We don't handle perceptual very well here, but the difference is very slight (<.05 dB avg Luma PSNR across a large corpus) and the perf lost was high (2x slower).
|
|
if ( (!pParams->m_perceptual) && ((pParams->m_weights[0] != 1) || (pParams->m_weights[1] != 1) || (pParams->m_weights[2] != 1) || (pParams->m_weights[3] != 1)) )
|
|
{
|
|
float wr = (float)(pParams->m_weights[0]);
|
|
float wg = (float)(pParams->m_weights[1]);
|
|
float wb = (float)(pParams->m_weights[2]);
|
|
float wa = (float)(pParams->m_weights[3]);
|
|
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
|
|
float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2] + faa * (float)pC->m_c[3];
|
|
|
|
float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f);
|
|
|
|
float itr = sr + dir * s;
|
|
float itg = sg + dig * s;
|
|
float itb = sb + dib * s;
|
|
float ita = sa + dia * s;
|
|
|
|
float dr = itr - (float)pC->m_c[0];
|
|
float dg = itg - (float)pC->m_c[1];
|
|
float db = itb - (float)pC->m_c[2];
|
|
float da = ita - (float)pC->m_c[3];
|
|
|
|
total_errf += wr * dr * dr + wg * dg * dg + wb * db * db + wa * da * da;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( uint32_t i = 0; i < num_pixels; i++)
|
|
{
|
|
const color_quad_i * pC = &pPixels[i];
|
|
|
|
float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2] + faa * (float)pC->m_c[3];
|
|
|
|
float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f);
|
|
|
|
float itr = sr + dir * s;
|
|
float itg = sg + dig * s;
|
|
float itb = sb + dib * s;
|
|
float ita = sa + dia * s;
|
|
|
|
float dr = itr - (float)pC->m_c[0];
|
|
float dg = itg - (float)pC->m_c[1];
|
|
float db = itb - (float)pC->m_c[2];
|
|
float da = ita - (float)pC->m_c[3];
|
|
|
|
total_errf += dr * dr + dg * dg + db * db + da * da;
|
|
}
|
|
}
|
|
|
|
total_err = (int64_t)total_errf;
|
|
|
|
return total_err;
|
|
}
|
|
|
|
static uint32_t estimate_partition( uint32_t mode, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params)
|
|
{
|
|
const uint32_t total_subsets = g_bc7_num_subsets[mode];
|
|
uint32_t total_partitions = minimumu(pComp_params->m_max_partitions_mode[mode], 1U << g_bc7_partition_bits[mode]);
|
|
|
|
if (total_partitions <= 1)
|
|
return 0;
|
|
|
|
uint64_t best_err = UINT64_MAX;
|
|
uint32_t best_partition = 0;
|
|
|
|
color_cell_compressor_params params;
|
|
color_cell_compressor_params_clear(¶ms);
|
|
|
|
params.m_pSelector_weights = (g_bc7_color_index_bitcount[mode] == 2) ? g_bc7_weights2 : g_bc7_weights3;
|
|
params.m_num_selector_weights = 1 << g_bc7_color_index_bitcount[mode];
|
|
|
|
memcpy(params.m_weights, pComp_params->m_weights, sizeof(params.m_weights));
|
|
|
|
if (mode >= 6)
|
|
{
|
|
params.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0];
|
|
params.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1];
|
|
params.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2];
|
|
params.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3];
|
|
}
|
|
|
|
params.m_perceptual = pComp_params->m_perceptual;
|
|
|
|
for ( uint32_t partition = 0; partition < total_partitions; partition++)
|
|
{
|
|
const int * pPartition = (total_subsets == 3) ? &g_bc7_partition3[partition * 16] : &g_bc7_partition2[partition * 16];
|
|
|
|
color_quad_i subset_colors[3][16];
|
|
uint32_t subset_total_colors[3];
|
|
subset_total_colors[0] = 0;
|
|
subset_total_colors[1] = 0;
|
|
subset_total_colors[2] = 0;
|
|
|
|
for ( uint32_t index = 0; index < 16; index++)
|
|
{
|
|
const uint32_t p = pPartition[index];
|
|
|
|
subset_colors[p][subset_total_colors[p]] = pPixels[index];
|
|
subset_total_colors[p]++;
|
|
}
|
|
|
|
uint64_t total_subset_err = 0;
|
|
|
|
for ( uint32_t subset = 0; subset < total_subsets; subset++)
|
|
{
|
|
uint64_t err;
|
|
if (mode == 7)
|
|
err = color_cell_compression_est_mode7(mode, ¶ms, best_err, subset_total_colors[subset], &subset_colors[subset][0]);
|
|
else
|
|
err = color_cell_compression_est(mode, ¶ms, best_err, subset_total_colors[subset], &subset_colors[subset][0]);
|
|
|
|
total_subset_err += err;
|
|
|
|
} // subset
|
|
|
|
if (total_subset_err < best_err)
|
|
{
|
|
best_err = total_subset_err;
|
|
best_partition = partition;
|
|
if (!best_err)
|
|
break;
|
|
}
|
|
|
|
if (total_subsets == 2)
|
|
{
|
|
if ((partition == BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX) && (best_partition != BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX))
|
|
break;
|
|
}
|
|
|
|
} // partition
|
|
|
|
return best_partition;
|
|
}
|
|
|
|
struct solution
|
|
{
|
|
uint32_t m_index;
|
|
uint64_t m_err;
|
|
};
|
|
|
|
static uint32_t estimate_partition_list( uint32_t mode, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params,
|
|
solution * pSolutions, int32_t max_solutions)
|
|
{
|
|
const int32_t orig_max_solutions = max_solutions;
|
|
|
|
const uint32_t total_subsets = g_bc7_num_subsets[mode];
|
|
uint32_t total_partitions = minimumu(pComp_params->m_max_partitions_mode[mode], 1U << g_bc7_partition_bits[mode]);
|
|
|
|
if (total_partitions <= 1)
|
|
{
|
|
pSolutions[0].m_index = 0;
|
|
pSolutions[0].m_err = 0;
|
|
return 1;
|
|
}
|
|
else if (max_solutions >= (int)total_partitions)
|
|
{
|
|
for ( int i = 0; i < (int)total_partitions; i++)
|
|
{
|
|
pSolutions[i].m_index = i;
|
|
pSolutions[i].m_err = i;
|
|
}
|
|
return total_partitions;
|
|
}
|
|
|
|
const int32_t HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD = 4;
|
|
if (total_subsets == 2)
|
|
{
|
|
if (max_solutions < HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD)
|
|
max_solutions = HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD;
|
|
}
|
|
|
|
color_cell_compressor_params params;
|
|
color_cell_compressor_params_clear(¶ms);
|
|
|
|
params.m_pSelector_weights = (g_bc7_color_index_bitcount[mode] == 2) ? g_bc7_weights2 : g_bc7_weights3;
|
|
params.m_num_selector_weights = 1 << g_bc7_color_index_bitcount[mode];
|
|
|
|
memcpy(params.m_weights, pComp_params->m_weights, sizeof(params.m_weights));
|
|
|
|
if (mode >= 6)
|
|
{
|
|
params.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0];
|
|
params.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1];
|
|
params.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2];
|
|
params.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3];
|
|
}
|
|
|
|
params.m_perceptual = pComp_params->m_perceptual;
|
|
|
|
int32_t num_solutions = 0;
|
|
|
|
for ( uint32_t partition = 0; partition < total_partitions; partition++)
|
|
{
|
|
const int * pPartition = (total_subsets == 3) ? &g_bc7_partition3[partition * 16] : &g_bc7_partition2[partition * 16];
|
|
|
|
color_quad_i subset_colors[3][16];
|
|
uint32_t subset_total_colors[3];
|
|
subset_total_colors[0] = 0;
|
|
subset_total_colors[1] = 0;
|
|
subset_total_colors[2] = 0;
|
|
|
|
for ( uint32_t index = 0; index < 16; index++)
|
|
{
|
|
const uint32_t p = pPartition[index];
|
|
|
|
subset_colors[p][subset_total_colors[p]] = pPixels[index];
|
|
subset_total_colors[p]++;
|
|
}
|
|
|
|
uint64_t total_subset_err = 0;
|
|
|
|
for ( uint32_t subset = 0; subset < total_subsets; subset++)
|
|
{
|
|
uint64_t err;
|
|
if (mode == 7)
|
|
err = color_cell_compression_est_mode7(mode, ¶ms, UINT64_MAX, subset_total_colors[subset], &subset_colors[subset][0]);
|
|
else
|
|
err = color_cell_compression_est(mode, ¶ms, UINT64_MAX, subset_total_colors[subset], &subset_colors[subset][0]);
|
|
|
|
total_subset_err += err;
|
|
|
|
} // subset
|
|
|
|
int32_t i;
|
|
for (i = 0; i < num_solutions; i++)
|
|
{
|
|
if (total_subset_err < pSolutions[i].m_err)
|
|
break;
|
|
}
|
|
|
|
if (i < num_solutions)
|
|
{
|
|
int32_t solutions_to_move = (max_solutions - 1) - i;
|
|
int32_t num_elements_at_i = num_solutions - i;
|
|
if (solutions_to_move > num_elements_at_i)
|
|
solutions_to_move = num_elements_at_i;
|
|
|
|
assert(((i + 1) + solutions_to_move) <= max_solutions);
|
|
assert((i + solutions_to_move) <= num_solutions);
|
|
|
|
for (int32_t j = solutions_to_move - 1; j >= 0; --j)
|
|
{
|
|
pSolutions[i + j + 1] = pSolutions[i + j];
|
|
}
|
|
}
|
|
|
|
if (num_solutions < max_solutions)
|
|
num_solutions++;
|
|
|
|
if (i < num_solutions)
|
|
{
|
|
pSolutions[i].m_err = total_subset_err;
|
|
|
|
pSolutions[i].m_index = partition;
|
|
}
|
|
|
|
#if BC7E_NON_DETERMINISTIC
|
|
if ((total_subsets == 2) && (partition == BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX))
|
|
{
|
|
if (all(i >= HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD))
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
} // partition
|
|
|
|
#if 0
|
|
for ( int i = 0; i < num_solutions; i++)
|
|
{
|
|
assert(pSolutions[i].m_index < total_partitions);
|
|
}
|
|
|
|
for ( int i = 0; i < (num_solutions - 1); i++)
|
|
{
|
|
assert(pSolutions[i].m_err <= pSolutions[i + 1].m_err);
|
|
}
|
|
#endif
|
|
|
|
return min(num_solutions, orig_max_solutions);
|
|
}
|
|
|
|
static inline void set_block_bits(uint8_t *pBytes, uint32_t val, uint32_t num_bits, uint32_t * pCur_ofs)
|
|
{
|
|
assert(num_bits < 32);
|
|
uint32_t limit = 1U << num_bits;
|
|
assert(val < limit);
|
|
NOTE_UNUSED(limit); // only referenced by the assert above (compiled out under NDEBUG)
|
|
|
|
while (num_bits)
|
|
{
|
|
const uint32_t n = minimumu(8 - (*pCur_ofs & 7), num_bits);
|
|
|
|
pBytes[*pCur_ofs >> 3] |= (uint8_t)(val << (*pCur_ofs & 7));
|
|
|
|
val >>= n;
|
|
num_bits -= n;
|
|
*pCur_ofs += n;
|
|
}
|
|
|
|
assert(*pCur_ofs <= 128);
|
|
}
|
|
|
|
struct bc7_optimization_results
|
|
{
|
|
uint32_t m_mode;
|
|
uint32_t m_partition;
|
|
int m_selectors[16];
|
|
int m_alpha_selectors[16];
|
|
color_quad_i m_low[3];
|
|
color_quad_i m_high[3];
|
|
uint32_t m_pbits[3][2];
|
|
uint32_t m_rotation;
|
|
uint32_t m_index_selector;
|
|
// True if the WINNING encoding used the one-color optimal-endpoint LUT on any
|
|
// subset (OR'd across the winning mode's subsets). Set at each winner-update.
|
|
bool m_used_lut;
|
|
};
|
|
|
|
static void encode_bc7_block(void *pBlock, const bc7_optimization_results * pResults)
|
|
{
|
|
const uint32_t best_mode = pResults->m_mode;
|
|
|
|
const uint32_t total_subsets = g_bc7_num_subsets[best_mode];
|
|
|
|
const uint32_t total_partitions = 1 << g_bc7_partition_bits[best_mode];
|
|
|
|
const int *pPartition;
|
|
if (total_subsets == 1)
|
|
pPartition = &g_bc7_partition1[0];
|
|
else if (total_subsets == 2)
|
|
pPartition = &g_bc7_partition2[pResults->m_partition * 16];
|
|
else
|
|
pPartition = &g_bc7_partition3[pResults->m_partition * 16];
|
|
|
|
int color_selectors[16];
|
|
for ( int i = 0; i < 16; i++)
|
|
color_selectors[i] = pResults->m_selectors[i];
|
|
|
|
int alpha_selectors[16];
|
|
for ( int i = 0; i < 16; i++)
|
|
alpha_selectors[i] = pResults->m_alpha_selectors[i];
|
|
|
|
color_quad_i low[3], high[3];
|
|
low[0] = pResults->m_low[0];
|
|
low[1] = pResults->m_low[1];
|
|
low[2] = pResults->m_low[2];
|
|
|
|
high[0] = pResults->m_high[0];
|
|
high[1] = pResults->m_high[1];
|
|
high[2] = pResults->m_high[2];
|
|
|
|
uint32_t pbits[3][2];
|
|
for ( int i = 0; i < 3; i++)
|
|
{
|
|
pbits[i][0] = pResults->m_pbits[i][0];
|
|
pbits[i][1] = pResults->m_pbits[i][1];
|
|
}
|
|
|
|
int anchor[3];
|
|
anchor[0] = -1;
|
|
anchor[1] = -1;
|
|
anchor[2] = -1;
|
|
|
|
for ( uint32_t k = 0; k < total_subsets; k++)
|
|
{
|
|
uint32_t anchor_index = 0;
|
|
if (k)
|
|
{
|
|
if ((total_subsets == 3) && (k == 1))
|
|
{
|
|
anchor_index = g_bc7_table_anchor_index_third_subset_1[pResults->m_partition];
|
|
}
|
|
else if ((total_subsets == 3) && (k == 2))
|
|
{
|
|
anchor_index = g_bc7_table_anchor_index_third_subset_2[pResults->m_partition];
|
|
}
|
|
else
|
|
{
|
|
anchor_index = g_bc7_table_anchor_index_second_subset[pResults->m_partition];
|
|
}
|
|
}
|
|
|
|
anchor[k] = anchor_index;
|
|
|
|
const uint32_t color_index_bits = get_bc7_color_index_size(best_mode, pResults->m_index_selector);
|
|
const uint32_t num_color_indices = 1 << color_index_bits;
|
|
|
|
if (color_selectors[anchor_index] & (num_color_indices >> 1))
|
|
{
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
if ((uint32_t)pPartition[i] == k)
|
|
color_selectors[i] = (num_color_indices - 1) - color_selectors[i];
|
|
}
|
|
|
|
if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))
|
|
{
|
|
for ( uint32_t q = 0; q < 3; q++)
|
|
{
|
|
int t = low[k].m_c[q];
|
|
low[k].m_c[q] = high[k].m_c[q];
|
|
high[k].m_c[q] = t;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
color_quad_i tmp = low[k];
|
|
low[k] = high[k];
|
|
high[k] = tmp;
|
|
}
|
|
|
|
if (!g_bc7_mode_has_shared_p_bits[best_mode])
|
|
{
|
|
uint32_t t = pbits[k][0];
|
|
pbits[k][0] = pbits[k][1];
|
|
pbits[k][1] = t;
|
|
}
|
|
}
|
|
|
|
if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))
|
|
{
|
|
const uint32_t alpha_index_bits = get_bc7_alpha_index_size(best_mode, pResults->m_index_selector);
|
|
const uint32_t num_alpha_indices = 1 << alpha_index_bits;
|
|
|
|
if (alpha_selectors[anchor_index] & (num_alpha_indices >> 1))
|
|
{
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
if ((uint32_t)pPartition[i] == k)
|
|
alpha_selectors[i] = (num_alpha_indices - 1) - alpha_selectors[i];
|
|
}
|
|
|
|
int t = low[k].m_c[3];
|
|
low[k].m_c[3] = high[k].m_c[3];
|
|
high[k].m_c[3] = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t *pBlock_bytes = (uint8_t *)(pBlock);
|
|
memset(pBlock_bytes, 0, BC7E_BLOCK_SIZE);
|
|
|
|
uint32_t cur_bit_ofs = 0;
|
|
|
|
set_block_bits(pBlock_bytes, 1 << best_mode, best_mode + 1, &cur_bit_ofs);
|
|
|
|
if ((best_mode == 4) || (best_mode == 5))
|
|
set_block_bits(pBlock_bytes, pResults->m_rotation, 2, &cur_bit_ofs);
|
|
|
|
if (best_mode == 4)
|
|
set_block_bits(pBlock_bytes, pResults->m_index_selector, 1, &cur_bit_ofs);
|
|
|
|
if (total_partitions > 1)
|
|
set_block_bits(pBlock_bytes, pResults->m_partition, (total_partitions == 64) ? 6 : 4, &cur_bit_ofs);
|
|
|
|
const uint32_t total_comps = (best_mode >= 4) ? 4 : 3;
|
|
for ( uint32_t comp = 0; comp < total_comps; comp++)
|
|
{
|
|
for ( uint32_t subset = 0; subset < total_subsets; subset++)
|
|
{
|
|
set_block_bits(pBlock_bytes, low[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs);
|
|
set_block_bits(pBlock_bytes, high[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs);
|
|
}
|
|
}
|
|
|
|
if (g_bc7_mode_has_p_bits[best_mode])
|
|
{
|
|
for ( uint32_t subset = 0; subset < total_subsets; subset++)
|
|
{
|
|
set_block_bits(pBlock_bytes, pbits[subset][0], 1, &cur_bit_ofs);
|
|
if (!g_bc7_mode_has_shared_p_bits[best_mode])
|
|
set_block_bits(pBlock_bytes, pbits[subset][1], 1, &cur_bit_ofs);
|
|
}
|
|
}
|
|
|
|
for ( uint32_t y = 0; y < 4; y++)
|
|
{
|
|
for ( uint32_t x = 0; x < 4; x++)
|
|
{
|
|
int idx = x + y * 4;
|
|
|
|
uint32_t n = pResults->m_index_selector ? get_bc7_alpha_index_size(best_mode, pResults->m_index_selector) : get_bc7_color_index_size(best_mode, pResults->m_index_selector);
|
|
|
|
if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2]))
|
|
n--;
|
|
|
|
set_block_bits(pBlock_bytes, pResults->m_index_selector ? alpha_selectors[idx] : color_selectors[idx], n, &cur_bit_ofs);
|
|
}
|
|
}
|
|
|
|
if (get_bc7_mode_has_seperate_alpha_selectors(best_mode))
|
|
{
|
|
for ( uint32_t y = 0; y < 4; y++)
|
|
{
|
|
for ( uint32_t x = 0; x < 4; x++)
|
|
{
|
|
int idx = x + y * 4;
|
|
|
|
uint32_t n = pResults->m_index_selector ? get_bc7_color_index_size(best_mode, pResults->m_index_selector) : get_bc7_alpha_index_size(best_mode, pResults->m_index_selector);
|
|
|
|
if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2]))
|
|
n--;
|
|
|
|
set_block_bits(pBlock_bytes, pResults->m_index_selector ? color_selectors[idx] : alpha_selectors[idx], n, &cur_bit_ofs);
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(cur_bit_ofs == 128);
|
|
}
|
|
|
|
static inline void encode_bc7_block_mode6(void *pBlock, bc7_optimization_results * pResults)
|
|
{
|
|
color_quad_i low, high;
|
|
uint32_t pbits[2];
|
|
|
|
uint32_t invert_selectors = 0;
|
|
if (pResults->m_selectors[0] & 8)
|
|
{
|
|
invert_selectors = 15;
|
|
|
|
low = pResults->m_high[0];
|
|
high = pResults->m_low[0];
|
|
|
|
pbits[0] = pResults->m_pbits[0][1];
|
|
pbits[1] = pResults->m_pbits[0][0];
|
|
}
|
|
else
|
|
{
|
|
low = pResults->m_low[0];
|
|
high = pResults->m_high[0];
|
|
|
|
pbits[0] = pResults->m_pbits[0][0];
|
|
pbits[1] = pResults->m_pbits[0][1];
|
|
}
|
|
|
|
uint64_t l = 0, h = 0;
|
|
|
|
l = 1 << 6;
|
|
|
|
l |= (low.m_c[0] << 7);
|
|
l |= (high.m_c[0] << 14);
|
|
|
|
l |= (low.m_c[1] << 21);
|
|
l |= ((uint64_t)high.m_c[1] << 28);
|
|
|
|
l |= ((uint64_t)low.m_c[2] << 35);
|
|
l |= ((uint64_t)high.m_c[2] << 42);
|
|
|
|
l |= ((uint64_t)low.m_c[3] << 49);
|
|
l |= ((uint64_t)high.m_c[3] << 56);
|
|
|
|
l |= ((uint64_t)pbits[0] << 63);
|
|
|
|
h = pbits[1];
|
|
|
|
h |= ((invert_selectors ^ pResults->m_selectors[0]) << 1);
|
|
|
|
// TODO: Just invert all these bits in one single operation, not as individual
|
|
h |= ((invert_selectors ^ pResults->m_selectors[1]) << 4);
|
|
h |= ((invert_selectors ^ pResults->m_selectors[2]) << 8);
|
|
h |= ((invert_selectors ^ pResults->m_selectors[3]) << 12);
|
|
h |= ((invert_selectors ^ pResults->m_selectors[4]) << 16);
|
|
|
|
h |= ((invert_selectors ^ pResults->m_selectors[5]) << 20);
|
|
h |= ((invert_selectors ^ pResults->m_selectors[6]) << 24);
|
|
h |= ((invert_selectors ^ pResults->m_selectors[7]) << 28);
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[8]) << 32);
|
|
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[9]) << 36);
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[10]) << 40);
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[11]) << 44);
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[12]) << 48);
|
|
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[13]) << 52);
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[14]) << 56);
|
|
h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[15]) << 60);
|
|
|
|
((uint64_t *)(pBlock))[0] = l;
|
|
|
|
((uint64_t *)(pBlock))[1] = h;
|
|
}
|
|
|
|
static void handle_alpha_block_mode4(const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, uint32_t lo_a, uint32_t hi_a,
|
|
bc7_optimization_results * pOpt_results4, uint64_t * pMode4_err)
|
|
{
|
|
pParams->m_has_alpha = false;
|
|
pParams->m_comp_bits = 5;
|
|
pParams->m_has_pbits = false;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
for ( uint32_t index_selector = 0; index_selector < 2; index_selector++)
|
|
{
|
|
if ((pComp_params->m_mode4_index_mask & (1 << index_selector)) == 0)
|
|
continue;
|
|
|
|
if (index_selector)
|
|
{
|
|
pParams->m_pSelector_weights = g_bc7_weights3;
|
|
pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights3x[0];
|
|
pParams->m_num_selector_weights = 8;
|
|
}
|
|
else
|
|
{
|
|
pParams->m_pSelector_weights = g_bc7_weights2;
|
|
pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights2x[0];
|
|
pParams->m_num_selector_weights = 4;
|
|
}
|
|
|
|
color_cell_compressor_results results;
|
|
|
|
int selectors[16];
|
|
results.m_pSelectors = selectors;
|
|
|
|
int selectors_temp[16];
|
|
results.m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t trial_err = color_cell_compression(4, pParams, &results, pComp_params, 16, pPixels, true);
|
|
assert(trial_err == results.m_best_overall_err);
|
|
|
|
uint32_t la = minimumi((lo_a + 2) >> 2, 63);
|
|
uint32_t ha = minimumi((hi_a + 2) >> 2, 63);
|
|
|
|
if (la == ha)
|
|
{
|
|
if (lo_a != hi_a)
|
|
{
|
|
if (ha != 63)
|
|
ha++;
|
|
else if (la != 0)
|
|
la--;
|
|
}
|
|
}
|
|
|
|
uint64_t best_alpha_err = UINT64_MAX;
|
|
uint32_t best_la = 0, best_ha = 0;
|
|
int best_alpha_selectors[16] = {};
|
|
|
|
for ( int32_t pass = 0; pass < 2; pass++)
|
|
{
|
|
int32_t vals[8];
|
|
|
|
if (index_selector == 0)
|
|
{
|
|
vals[0] = (la << 2) | (la >> 4);
|
|
vals[7] = (ha << 2) | (ha >> 4);
|
|
|
|
for ( uint32_t i = 1; i < 7; i++)
|
|
vals[i] = (vals[0] * (64 - g_bc7_weights3[i]) + vals[7] * g_bc7_weights3[i] + 32) >> 6;
|
|
}
|
|
else
|
|
{
|
|
vals[0] = (la << 2) | (la >> 4);
|
|
vals[3] = (ha << 2) | (ha >> 4);
|
|
|
|
const int32_t w_s1 = 21, w_s2 = 43;
|
|
vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6;
|
|
vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6;
|
|
}
|
|
|
|
uint64_t trial_alpha_err = 0;
|
|
|
|
int trial_alpha_selectors[16];
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
const int32_t a = pPixels[i].m_c[3];
|
|
|
|
int s = 0;
|
|
int32_t be = iabs32(a - vals[0]);
|
|
|
|
int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; }
|
|
e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; }
|
|
e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; }
|
|
|
|
if (index_selector == 0)
|
|
{
|
|
e = iabs32(a - vals[4]); if (e < be) { be = e; s = 4; }
|
|
e = iabs32(a - vals[5]); if (e < be) { be = e; s = 5; }
|
|
e = iabs32(a - vals[6]); if (e < be) { be = e; s = 6; }
|
|
e = iabs32(a - vals[7]); if (e < be) { be = e; s = 7; }
|
|
}
|
|
|
|
trial_alpha_err += (be * be) * pParams->m_weights[3];
|
|
|
|
trial_alpha_selectors[i] = s;
|
|
}
|
|
|
|
if (trial_alpha_err < best_alpha_err)
|
|
{
|
|
best_alpha_err = trial_alpha_err;
|
|
best_la = la;
|
|
best_ha = ha;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
best_alpha_selectors[i] = trial_alpha_selectors[i];
|
|
}
|
|
|
|
if (pass == 0)
|
|
{
|
|
float xl, xh;
|
|
compute_least_squares_endpoints_a(16, trial_alpha_selectors, index_selector ? (const vec4F * )&g_bc7_weights2x[0] : (const vec4F * )&g_bc7_weights3x[0], &xl, &xh, pPixels);
|
|
if (xl > xh)
|
|
swapf(&xl, &xh);
|
|
la = clampi((int)floor(xl * (63.0f / 255.0f) + .5f), 0, 63);
|
|
ha = clampi((int)floor(xh * (63.0f / 255.0f) + .5f), 0, 63);
|
|
}
|
|
|
|
} // pass
|
|
|
|
if (pComp_params->m_uber_level > 0)
|
|
{
|
|
const int D = min((int)pComp_params->m_uber_level, 3);
|
|
for ( int ld = -D; ld <= D; ld++)
|
|
{
|
|
for ( int hd = -D; hd <= D; hd++)
|
|
{
|
|
la = clamp((int)best_la + ld, 0, 63);
|
|
ha = clamp((int)best_ha + hd, 0, 63);
|
|
|
|
int32_t vals[8];
|
|
|
|
if (index_selector == 0)
|
|
{
|
|
vals[0] = (la << 2) | (la >> 4);
|
|
vals[7] = (ha << 2) | (ha >> 4);
|
|
|
|
for ( uint32_t i = 1; i < 7; i++)
|
|
vals[i] = (vals[0] * (64 - g_bc7_weights3[i]) + vals[7] * g_bc7_weights3[i] + 32) >> 6;
|
|
}
|
|
else
|
|
{
|
|
vals[0] = (la << 2) | (la >> 4);
|
|
vals[3] = (ha << 2) | (ha >> 4);
|
|
|
|
const int32_t w_s1 = 21, w_s2 = 43;
|
|
vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6;
|
|
vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6;
|
|
}
|
|
|
|
uint64_t trial_alpha_err = 0;
|
|
|
|
int trial_alpha_selectors[16];
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
const int32_t a = pPixels[i].m_c[3];
|
|
|
|
int s = 0;
|
|
int32_t be = iabs32(a - vals[0]);
|
|
|
|
int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; }
|
|
e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; }
|
|
e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; }
|
|
|
|
if (index_selector == 0)
|
|
{
|
|
e = iabs32(a - vals[4]); if (e < be) { be = e; s = 4; }
|
|
e = iabs32(a - vals[5]); if (e < be) { be = e; s = 5; }
|
|
e = iabs32(a - vals[6]); if (e < be) { be = e; s = 6; }
|
|
e = iabs32(a - vals[7]); if (e < be) { be = e; s = 7; }
|
|
}
|
|
|
|
trial_alpha_err += (be * be) * pParams->m_weights[3];
|
|
|
|
trial_alpha_selectors[i] = s;
|
|
}
|
|
|
|
if (trial_alpha_err < best_alpha_err)
|
|
{
|
|
best_alpha_err = trial_alpha_err;
|
|
best_la = la;
|
|
best_ha = ha;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
best_alpha_selectors[i] = trial_alpha_selectors[i];
|
|
}
|
|
|
|
} // hd
|
|
|
|
} // ld
|
|
}
|
|
|
|
trial_err += best_alpha_err;
|
|
|
|
if (trial_err < *pMode4_err)
|
|
{
|
|
*pMode4_err = trial_err;
|
|
|
|
pOpt_results4->m_mode = 4;
|
|
pOpt_results4->m_index_selector = index_selector;
|
|
pOpt_results4->m_rotation = 0;
|
|
pOpt_results4->m_partition = 0;
|
|
pOpt_results4->m_used_lut = results.m_used_lut;
|
|
|
|
pOpt_results4->m_low[0] = results.m_low_endpoint;
|
|
pOpt_results4->m_high[0] = results.m_high_endpoint;
|
|
pOpt_results4->m_low[0].m_c[3] = best_la;
|
|
pOpt_results4->m_high[0].m_c[3] = best_ha;
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
pOpt_results4->m_selectors[i] = selectors[i];
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
pOpt_results4->m_alpha_selectors[i] = best_alpha_selectors[i];
|
|
}
|
|
|
|
} // index_selector
|
|
}
|
|
|
|
static void handle_alpha_block_mode5(const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, uint32_t lo_a, uint32_t hi_a,
|
|
bc7_optimization_results * pOpt_results5, uint64_t * pMode5_err)
|
|
{
|
|
pParams->m_pSelector_weights = g_bc7_weights2;
|
|
pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights2x[0];
|
|
pParams->m_num_selector_weights = 4;
|
|
|
|
pParams->m_comp_bits = 7;
|
|
pParams->m_has_alpha = false;
|
|
pParams->m_has_pbits = false;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
color_cell_compressor_results results5;
|
|
results5.m_pSelectors = pOpt_results5->m_selectors;
|
|
|
|
int selectors_temp[16];
|
|
results5.m_pSelectors_temp = selectors_temp;
|
|
|
|
*pMode5_err = color_cell_compression(5, pParams, &results5, pComp_params, 16, pPixels, true);
|
|
assert(*pMode5_err == results5.m_best_overall_err);
|
|
|
|
pOpt_results5->m_low[0] = results5.m_low_endpoint;
|
|
pOpt_results5->m_high[0] = results5.m_high_endpoint;
|
|
|
|
cif (lo_a == hi_a)
|
|
{
|
|
pOpt_results5->m_low[0].m_c[3] = lo_a;
|
|
pOpt_results5->m_high[0].m_c[3] = hi_a;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
pOpt_results5->m_alpha_selectors[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
uint64_t mode5_alpha_err = UINT64_MAX;
|
|
|
|
for ( uint32_t pass = 0; pass < 2; pass++)
|
|
{
|
|
int32_t vals[4];
|
|
vals[0] = lo_a;
|
|
vals[3] = hi_a;
|
|
|
|
const int32_t w_s1 = 21, w_s2 = 43;
|
|
vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6;
|
|
vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6;
|
|
|
|
int trial_alpha_selectors[16];
|
|
|
|
uint64_t trial_alpha_err = 0;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
const int32_t a = pPixels[i].m_c[3];
|
|
|
|
int s = 0;
|
|
int32_t be = iabs32(a - vals[0]);
|
|
int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; }
|
|
e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; }
|
|
e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; }
|
|
|
|
trial_alpha_selectors[i] = s;
|
|
|
|
trial_alpha_err += (be * be) * pParams->m_weights[3];
|
|
}
|
|
|
|
if (trial_alpha_err < mode5_alpha_err)
|
|
{
|
|
mode5_alpha_err = trial_alpha_err;
|
|
pOpt_results5->m_low[0].m_c[3] = lo_a;
|
|
pOpt_results5->m_high[0].m_c[3] = hi_a;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
pOpt_results5->m_alpha_selectors[i] = trial_alpha_selectors[i];
|
|
}
|
|
|
|
if (!pass)
|
|
{
|
|
float xl, xh;
|
|
compute_least_squares_endpoints_a(16, trial_alpha_selectors, (const vec4F * )&g_bc7_weights2x[0], &xl, &xh, pPixels);
|
|
|
|
uint32_t new_lo_a = clampi((int)floor(xl + .5f), 0, 255);
|
|
uint32_t new_hi_a = clampi((int)floor(xh + .5f), 0, 255);
|
|
if (new_lo_a > new_hi_a)
|
|
swapu(&new_lo_a, &new_hi_a);
|
|
|
|
if ((new_lo_a == lo_a) && (new_hi_a == hi_a))
|
|
break;
|
|
|
|
lo_a = new_lo_a;
|
|
hi_a = new_hi_a;
|
|
}
|
|
}
|
|
|
|
if (pComp_params->m_uber_level > 0)
|
|
{
|
|
const int D = min((int)pComp_params->m_uber_level, 3);
|
|
for ( int ld = -D; ld <= D; ld++)
|
|
{
|
|
for ( int hd = -D; hd <= D; hd++)
|
|
{
|
|
lo_a = clamp((int)pOpt_results5->m_low[0].m_c[3] + ld, 0, 255);
|
|
hi_a = clamp((int)pOpt_results5->m_high[0].m_c[3] + hd, 0, 255);
|
|
|
|
int32_t vals[4];
|
|
vals[0] = lo_a;
|
|
vals[3] = hi_a;
|
|
|
|
const int32_t w_s1 = 21, w_s2 = 43;
|
|
vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6;
|
|
vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6;
|
|
|
|
int trial_alpha_selectors[16];
|
|
|
|
uint64_t trial_alpha_err = 0;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
const int32_t a = pPixels[i].m_c[3];
|
|
|
|
int s = 0;
|
|
int32_t be = iabs32(a - vals[0]);
|
|
int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; }
|
|
e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; }
|
|
e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; }
|
|
|
|
trial_alpha_selectors[i] = s;
|
|
|
|
trial_alpha_err += (be * be) * pParams->m_weights[3];
|
|
}
|
|
|
|
if (trial_alpha_err < mode5_alpha_err)
|
|
{
|
|
mode5_alpha_err = trial_alpha_err;
|
|
pOpt_results5->m_low[0].m_c[3] = lo_a;
|
|
pOpt_results5->m_high[0].m_c[3] = hi_a;
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
pOpt_results5->m_alpha_selectors[i] = trial_alpha_selectors[i];
|
|
}
|
|
|
|
} // hd
|
|
|
|
} // ld
|
|
}
|
|
|
|
*pMode5_err += mode5_alpha_err;
|
|
}
|
|
|
|
pOpt_results5->m_mode = 5;
|
|
pOpt_results5->m_index_selector = 0;
|
|
pOpt_results5->m_rotation = 0;
|
|
pOpt_results5->m_partition = 0;
|
|
pOpt_results5->m_used_lut = results5.m_used_lut;
|
|
}
|
|
|
|
static void handle_alpha_block(void * pBlock, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, uint32_t lo_a, uint32_t hi_a, int forced_partition = -1, uint64_t* pBest_err = nullptr, bool* pUsed_lut = nullptr)
|
|
{
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
bc7_optimization_results opt_results = {};
|
|
|
|
uint64_t best_err = UINT64_MAX;
|
|
|
|
// Mode 4
|
|
if (pComp_params->m_alpha_settings.m_use_mode4)
|
|
{
|
|
color_cell_compressor_params params4 = *pParams;
|
|
|
|
const int num_rotations = (pComp_params->m_perceptual || (!pComp_params->m_alpha_settings.m_use_mode4_rotation)) ? 1 : 4;
|
|
for ( uint32_t rotation = 0; rotation < (uint32_t)num_rotations; rotation++)
|
|
{
|
|
if ((pComp_params->m_mode4_rotation_mask & (1 << rotation)) == 0)
|
|
continue;
|
|
|
|
memcpy(params4.m_weights, pParams->m_weights, sizeof(params4.m_weights));
|
|
if (rotation)
|
|
swapu(¶ms4.m_weights[rotation - 1], ¶ms4.m_weights[3]);
|
|
|
|
color_quad_i rot_pixels[16];
|
|
const color_quad_i * pTrial_pixels = pPixels;
|
|
uint32_t trial_lo_a = lo_a, trial_hi_a = hi_a;
|
|
if (rotation)
|
|
{
|
|
trial_lo_a = 255;
|
|
trial_hi_a = 0;
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
color_quad_i c = pPixels[i];
|
|
swapi(&c.m_c[3], &c.m_c[rotation - 1]);
|
|
rot_pixels[i] = c;
|
|
|
|
trial_lo_a = minimumu(trial_lo_a, c.m_c[3]);
|
|
trial_hi_a = maximumu(trial_hi_a, c.m_c[3]);
|
|
}
|
|
|
|
pTrial_pixels = rot_pixels;
|
|
}
|
|
|
|
bc7_optimization_results trial_opt_results4;
|
|
|
|
uint64_t trial_mode4_err = best_err;
|
|
|
|
handle_alpha_block_mode4(pTrial_pixels, pComp_params, ¶ms4, trial_lo_a, trial_hi_a, &trial_opt_results4, &trial_mode4_err);
|
|
|
|
if (trial_mode4_err < best_err)
|
|
{
|
|
best_err = trial_mode4_err;
|
|
|
|
opt_results.m_mode = 4;
|
|
opt_results.m_index_selector = trial_opt_results4.m_index_selector;
|
|
opt_results.m_rotation = rotation;
|
|
opt_results.m_partition = 0;
|
|
opt_results.m_used_lut = trial_opt_results4.m_used_lut;
|
|
|
|
opt_results.m_low[0] = trial_opt_results4.m_low[0];
|
|
opt_results.m_high[0] = trial_opt_results4.m_high[0];
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
opt_results.m_selectors[i] = trial_opt_results4.m_selectors[i];
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
opt_results.m_alpha_selectors[i] = trial_opt_results4.m_alpha_selectors[i];
|
|
}
|
|
} // rotation
|
|
}
|
|
|
|
// Mode 6
|
|
if (pComp_params->m_alpha_settings.m_use_mode6)
|
|
{
|
|
color_cell_compressor_params params6 = *pParams;
|
|
|
|
params6.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0];
|
|
params6.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1];
|
|
params6.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2];
|
|
params6.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3];
|
|
|
|
color_cell_compressor_results results6;
|
|
|
|
params6.m_pSelector_weights = g_bc7_weights4;
|
|
params6.m_pSelector_weightsx = (const vec4F *)&g_bc7_weights4x[0];
|
|
params6.m_num_selector_weights = 16;
|
|
|
|
params6.m_comp_bits = 7;
|
|
params6.m_has_pbits = true;
|
|
params6.m_endpoints_share_pbit = false;
|
|
params6.m_has_alpha = true;
|
|
|
|
int selectors[16];
|
|
results6.m_pSelectors = selectors;
|
|
|
|
int selectors_temp[16];
|
|
results6.m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t mode6_err = color_cell_compression(6, ¶ms6, &results6, pComp_params, 16, pPixels, true);
|
|
assert(mode6_err == results6.m_best_overall_err);
|
|
|
|
if (mode6_err < best_err)
|
|
{
|
|
best_err = mode6_err;
|
|
|
|
opt_results.m_mode = 6;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = 0;
|
|
|
|
opt_results.m_low[0] = results6.m_low_endpoint;
|
|
opt_results.m_high[0] = results6.m_high_endpoint;
|
|
|
|
opt_results.m_pbits[0][0] = results6.m_pbits[0];
|
|
opt_results.m_pbits[0][1] = results6.m_pbits[1];
|
|
opt_results.m_used_lut = results6.m_used_lut;
|
|
|
|
for ( int i = 0; i < 16; i++)
|
|
opt_results.m_selectors[i] = selectors[i];
|
|
}
|
|
}
|
|
|
|
// Mode 5
|
|
if (pComp_params->m_alpha_settings.m_use_mode5)
|
|
{
|
|
color_cell_compressor_params params5 = *pParams;
|
|
|
|
const int num_rotations = (pComp_params->m_perceptual || (!pComp_params->m_alpha_settings.m_use_mode5_rotation)) ? 1 : 4;
|
|
for ( uint32_t rotation = 0; rotation < (uint32_t)num_rotations; rotation++)
|
|
{
|
|
if ((pComp_params->m_mode5_rotation_mask & (1 << rotation)) == 0)
|
|
continue;
|
|
|
|
memcpy(params5.m_weights, pParams->m_weights, sizeof(params5.m_weights));
|
|
if (rotation)
|
|
swapu(¶ms5.m_weights[rotation - 1], ¶ms5.m_weights[3]);
|
|
|
|
color_quad_i rot_pixels[16];
|
|
const color_quad_i * pTrial_pixels = pPixels;
|
|
uint32_t trial_lo_a = lo_a, trial_hi_a = hi_a;
|
|
if (rotation)
|
|
{
|
|
trial_lo_a = 255;
|
|
trial_hi_a = 0;
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
color_quad_i c = pPixels[i];
|
|
swapi(&c.m_c[3], &c.m_c[rotation - 1]);
|
|
rot_pixels[i] = c;
|
|
|
|
trial_lo_a = minimumu(trial_lo_a, c.m_c[3]);
|
|
trial_hi_a = maximumu(trial_hi_a, c.m_c[3]);
|
|
}
|
|
|
|
pTrial_pixels = rot_pixels;
|
|
}
|
|
|
|
bc7_optimization_results trial_opt_results5;
|
|
|
|
uint64_t trial_mode5_err = 0;
|
|
|
|
handle_alpha_block_mode5(pTrial_pixels, pComp_params, ¶ms5, trial_lo_a, trial_hi_a, &trial_opt_results5, &trial_mode5_err);
|
|
|
|
if (trial_mode5_err < best_err)
|
|
{
|
|
best_err = trial_mode5_err;
|
|
|
|
opt_results = trial_opt_results5;
|
|
opt_results.m_rotation = rotation;
|
|
}
|
|
} // rotation
|
|
}
|
|
|
|
// Mode 7
|
|
if (pComp_params->m_alpha_settings.m_use_mode7)
|
|
{
|
|
solution solutions[BC7E_MAX_PARTITIONS7];
|
|
uint32_t num_solutions;
|
|
if (forced_partition >= 0)
|
|
{
|
|
solutions[0].m_index = forced_partition;
|
|
num_solutions = 1;
|
|
}
|
|
else
|
|
num_solutions = estimate_partition_list(7, pPixels, pComp_params, solutions, pComp_params->m_alpha_settings.m_max_mode7_partitions_to_try);
|
|
|
|
color_cell_compressor_params params7 = *pParams;
|
|
|
|
params7.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0];
|
|
params7.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1];
|
|
params7.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2];
|
|
params7.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3];
|
|
|
|
params7.m_pSelector_weights = g_bc7_weights2;
|
|
params7.m_pSelector_weightsx = (const vec4F *)&g_bc7_weights2x[0];
|
|
params7.m_num_selector_weights = 4;
|
|
|
|
params7.m_comp_bits = 5;
|
|
params7.m_has_pbits = true;
|
|
params7.m_endpoints_share_pbit = false;
|
|
|
|
params7.m_has_alpha = true;
|
|
|
|
int selectors_temp[16];
|
|
|
|
const bool disable_faster_part_selection = false;
|
|
|
|
for ( uint32_t solution_index = 0; solution_index < num_solutions; solution_index++)
|
|
{
|
|
const uint32_t trial_partition = solutions[solution_index].m_index;
|
|
assert(trial_partition < 64);
|
|
|
|
const int *pPartition = &g_bc7_partition2[trial_partition * 16];
|
|
|
|
color_quad_i subset_colors[2][16];
|
|
|
|
uint32_t subset_total_colors7[2];
|
|
subset_total_colors7[0] = 0;
|
|
subset_total_colors7[1] = 0;
|
|
|
|
int subset_pixel_index7[2][16];
|
|
int subset_selectors7[2][16];
|
|
color_cell_compressor_results subset_results7[2];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
assert(p < 2);
|
|
|
|
subset_colors[p][subset_total_colors7[p]] = pPixels[idx];
|
|
subset_pixel_index7[p][subset_total_colors7[p]] = idx;
|
|
subset_total_colors7[p]++;
|
|
}
|
|
|
|
uint64_t trial_err = 0;
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results7[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors7[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(7, ¶ms7, pResults, pComp_params, subset_total_colors7[subset], &subset_colors[subset][0], (num_solutions <= 2) || disable_faster_part_selection);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
trial_err += err;
|
|
if (trial_err > best_err)
|
|
break;
|
|
} // subset
|
|
|
|
if (trial_err < best_err)
|
|
{
|
|
best_err = trial_err;
|
|
|
|
opt_results.m_mode = 7;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = trial_partition;
|
|
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors7[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index7[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors7[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results7[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results7[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results7[subset].m_pbits[0];
|
|
opt_results.m_pbits[subset][1] = subset_results7[subset].m_pbits[1];
|
|
}
|
|
opt_results.m_used_lut = subset_results7[0].m_used_lut || subset_results7[1].m_used_lut;
|
|
}
|
|
|
|
} // solution_index
|
|
|
|
if ((num_solutions > 2) && (opt_results.m_mode == 7) && (!disable_faster_part_selection))
|
|
{
|
|
const uint32_t trial_partition = opt_results.m_partition;
|
|
assert(trial_partition < 64);
|
|
|
|
const int *pPartition = &g_bc7_partition2[trial_partition * 16];
|
|
|
|
color_quad_i subset_colors[2][16];
|
|
|
|
uint32_t subset_total_colors7[2];
|
|
subset_total_colors7[0] = 0;
|
|
subset_total_colors7[1] = 0;
|
|
|
|
int subset_pixel_index7[2][16];
|
|
int subset_selectors7[2][16];
|
|
color_cell_compressor_results subset_results7[2];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
assert(p < 2);
|
|
|
|
subset_colors[p][subset_total_colors7[p]] = pPixels[idx];
|
|
subset_pixel_index7[p][subset_total_colors7[p]] = idx;
|
|
subset_total_colors7[p]++;
|
|
}
|
|
|
|
uint64_t trial_err = 0;
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results7[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors7[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(7, ¶ms7, pResults, pComp_params, subset_total_colors7[subset], &subset_colors[subset][0], true);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
trial_err += err;
|
|
if (trial_err > best_err)
|
|
break;
|
|
} // subset
|
|
|
|
if (trial_err < best_err)
|
|
{
|
|
best_err = trial_err;
|
|
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors7[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index7[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors7[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results7[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results7[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results7[subset].m_pbits[0];
|
|
opt_results.m_pbits[subset][1] = subset_results7[subset].m_pbits[1];
|
|
}
|
|
opt_results.m_used_lut = subset_results7[0].m_used_lut || subset_results7[1].m_used_lut;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pBest_err) *pBest_err = best_err;
|
|
if (pUsed_lut) *pUsed_lut = opt_results.m_used_lut;
|
|
encode_bc7_block(pBlock, &opt_results);
|
|
}
|
|
|
|
static void handle_opaque_block(void * pBlock, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, int forced_partition = -1, uint64_t* pBest_err = nullptr, bool* pUsed_lut = nullptr)
|
|
{
|
|
int selectors_temp[16];
|
|
|
|
bc7_optimization_results opt_results = {};
|
|
|
|
uint64_t best_err = UINT64_MAX;
|
|
|
|
// Mode 6
|
|
if (pComp_params->m_opaque_settings.m_use_mode[6])
|
|
{
|
|
pParams->m_pSelector_weights = g_bc7_weights4;
|
|
pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights4x[0];
|
|
pParams->m_num_selector_weights = 16;
|
|
|
|
pParams->m_comp_bits = 7;
|
|
pParams->m_has_pbits = true;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
color_cell_compressor_results results6;
|
|
results6.m_pSelectors = opt_results.m_selectors;
|
|
results6.m_pSelectors_temp = selectors_temp;
|
|
|
|
best_err = color_cell_compression(6, pParams, &results6, pComp_params, 16, pPixels, true);
|
|
|
|
opt_results.m_mode = 6;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = 0;
|
|
opt_results.m_used_lut = results6.m_used_lut;
|
|
|
|
opt_results.m_low[0] = results6.m_low_endpoint;
|
|
opt_results.m_high[0] = results6.m_high_endpoint;
|
|
|
|
opt_results.m_pbits[0][0] = results6.m_pbits[0];
|
|
opt_results.m_pbits[0][1] = results6.m_pbits[1];
|
|
}
|
|
|
|
solution solutions2[BC7E_MAX_PARTITIONS3];
|
|
uint32_t num_solutions2 = 0;
|
|
if (pComp_params->m_opaque_settings.m_use_mode[1] || pComp_params->m_opaque_settings.m_use_mode[3])
|
|
{
|
|
if (forced_partition >= 0)
|
|
{
|
|
solutions2[0].m_index = forced_partition;
|
|
num_solutions2 = 1;
|
|
}
|
|
else if (pComp_params->m_opaque_settings.m_max_mode13_partitions_to_try == 1)
|
|
{
|
|
solutions2[0].m_index = estimate_partition(1, pPixels, pComp_params);
|
|
num_solutions2 = 1;
|
|
}
|
|
else
|
|
{
|
|
num_solutions2 = estimate_partition_list(1, pPixels, pComp_params, solutions2, pComp_params->m_opaque_settings.m_max_mode13_partitions_to_try);
|
|
}
|
|
}
|
|
|
|
const bool disable_faster_part_selection = false;
|
|
|
|
// Mode 1
|
|
if (pComp_params->m_opaque_settings.m_use_mode[1])
|
|
{
|
|
pParams->m_pSelector_weights = g_bc7_weights3;
|
|
pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights3x[0];
|
|
pParams->m_num_selector_weights = 8;
|
|
|
|
pParams->m_comp_bits = 6;
|
|
pParams->m_has_pbits = true;
|
|
pParams->m_endpoints_share_pbit = true;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
for ( uint32_t solution_index = 0; solution_index < num_solutions2; solution_index++)
|
|
{
|
|
const uint32_t trial_partition = solutions2[solution_index].m_index;
|
|
assert(trial_partition < 64);
|
|
|
|
const int *pPartition = &g_bc7_partition2[trial_partition * 16];
|
|
|
|
color_quad_i subset_colors[2][16];
|
|
|
|
uint32_t subset_total_colors1[2];
|
|
subset_total_colors1[0] = 0;
|
|
subset_total_colors1[1] = 0;
|
|
|
|
int subset_pixel_index1[2][16];
|
|
int subset_selectors1[2][16];
|
|
color_cell_compressor_results subset_results1[2];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
assert(p < 2);
|
|
|
|
subset_colors[p][subset_total_colors1[p]] = pPixels[idx];
|
|
subset_pixel_index1[p][subset_total_colors1[p]] = idx;
|
|
subset_total_colors1[p]++;
|
|
}
|
|
|
|
uint64_t trial_err = 0;
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results1[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors1[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(1, pParams, pResults, pComp_params, subset_total_colors1[subset], &subset_colors[subset][0], (num_solutions2 <= 2) || disable_faster_part_selection);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
trial_err += err;
|
|
if (trial_err > best_err)
|
|
break;
|
|
|
|
} // subset
|
|
|
|
if (trial_err < best_err)
|
|
{
|
|
best_err = trial_err;
|
|
|
|
opt_results.m_mode = 1;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = trial_partition;
|
|
opt_results.m_used_lut = subset_results1[0].m_used_lut || subset_results1[1].m_used_lut;
|
|
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors1[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index1[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors1[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results1[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results1[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results1[subset].m_pbits[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((num_solutions2 > 2) && (opt_results.m_mode == 1) && (!disable_faster_part_selection))
|
|
{
|
|
const uint32_t trial_partition = opt_results.m_partition;
|
|
assert(trial_partition < 64);
|
|
|
|
const int *pPartition = &g_bc7_partition2[trial_partition * 16];
|
|
|
|
color_quad_i subset_colors[2][16];
|
|
|
|
uint32_t subset_total_colors1[2];
|
|
subset_total_colors1[0] = 0;
|
|
subset_total_colors1[1] = 0;
|
|
|
|
int subset_pixel_index1[2][16];
|
|
int subset_selectors1[2][16];
|
|
color_cell_compressor_results subset_results1[2];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
assert(p < 2);
|
|
|
|
subset_colors[p][subset_total_colors1[p]] = pPixels[idx];
|
|
subset_pixel_index1[p][subset_total_colors1[p]] = idx;
|
|
subset_total_colors1[p]++;
|
|
}
|
|
|
|
uint64_t trial_err = 0;
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results1[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors1[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(1, pParams, pResults, pComp_params, subset_total_colors1[subset], &subset_colors[subset][0], true);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
trial_err += err;
|
|
if (trial_err > best_err)
|
|
break;
|
|
|
|
} // subset
|
|
|
|
if (trial_err < best_err)
|
|
{
|
|
best_err = trial_err;
|
|
opt_results.m_used_lut = subset_results1[0].m_used_lut || subset_results1[1].m_used_lut;
|
|
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors1[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index1[subset][i];
|
|
opt_results.m_selectors[pixel_index] = subset_selectors1[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results1[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results1[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results1[subset].m_pbits[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mode 0
|
|
if (pComp_params->m_opaque_settings.m_use_mode[0])
|
|
{
|
|
solution solutions3[BC7E_MAX_PARTITIONS0];
|
|
uint32_t num_solutions3 = 0;
|
|
if (forced_partition >= 0)
|
|
{
|
|
solutions3[0].m_index = forced_partition;
|
|
num_solutions3 = 1;
|
|
}
|
|
else if (pComp_params->m_opaque_settings.m_max_mode0_partitions_to_try == 1)
|
|
{
|
|
solutions3[0].m_index = estimate_partition(0, pPixels, pComp_params);
|
|
num_solutions3 = 1;
|
|
}
|
|
else
|
|
{
|
|
num_solutions3 = estimate_partition_list(0, pPixels, pComp_params, solutions3, pComp_params->m_opaque_settings.m_max_mode0_partitions_to_try);
|
|
}
|
|
|
|
pParams->m_pSelector_weights = g_bc7_weights3;
|
|
pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights3x[0];
|
|
pParams->m_num_selector_weights = 8;
|
|
|
|
pParams->m_comp_bits = 4;
|
|
pParams->m_has_pbits = true;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
for ( uint32_t solution_index = 0; solution_index < num_solutions3; solution_index++)
|
|
{
|
|
const uint32_t best_partition0 = solutions3[solution_index].m_index;
|
|
|
|
const int *pPartition = &g_bc7_partition3[best_partition0 * 16];
|
|
|
|
color_quad_i subset_colors[3][16];
|
|
|
|
uint32_t subset_total_colors0[3];
|
|
subset_total_colors0[0] = 0;
|
|
subset_total_colors0[1] = 0;
|
|
subset_total_colors0[2] = 0;
|
|
|
|
int subset_pixel_index0[3][16];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
|
|
subset_colors[p][subset_total_colors0[p]] = pPixels[idx];
|
|
subset_pixel_index0[p][subset_total_colors0[p]] = idx;
|
|
subset_total_colors0[p]++;
|
|
}
|
|
|
|
color_cell_compressor_results subset_results0[3];
|
|
int subset_selectors0[3][16];
|
|
|
|
uint64_t mode0_err = 0;
|
|
for ( uint32_t subset = 0; subset < 3; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results0[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors0[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(0, pParams, pResults, pComp_params, subset_total_colors0[subset], &subset_colors[subset][0], true);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
mode0_err += err;
|
|
if (mode0_err > best_err)
|
|
break;
|
|
} // subset
|
|
|
|
if (mode0_err < best_err)
|
|
{
|
|
best_err = mode0_err;
|
|
|
|
opt_results.m_mode = 0;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = best_partition0;
|
|
|
|
for ( uint32_t subset = 0; subset < 3; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors0[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index0[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors0[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results0[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results0[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results0[subset].m_pbits[0];
|
|
opt_results.m_pbits[subset][1] = subset_results0[subset].m_pbits[1];
|
|
}
|
|
opt_results.m_used_lut = subset_results0[0].m_used_lut || subset_results0[1].m_used_lut || subset_results0[2].m_used_lut;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mode 3
|
|
if (pComp_params->m_opaque_settings.m_use_mode[3])
|
|
{
|
|
pParams->m_pSelector_weights = g_bc7_weights2;
|
|
pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights2x[0];
|
|
pParams->m_num_selector_weights = 4;
|
|
|
|
pParams->m_comp_bits = 7;
|
|
pParams->m_has_pbits = true;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
for ( uint32_t solution_index = 0; solution_index < num_solutions2; solution_index++)
|
|
{
|
|
const uint32_t trial_partition = solutions2[solution_index].m_index;
|
|
assert(trial_partition < 64);
|
|
|
|
const int *pPartition = &g_bc7_partition2[trial_partition * 16];
|
|
|
|
color_quad_i subset_colors[2][16];
|
|
|
|
uint32_t subset_total_colors3[2];
|
|
subset_total_colors3[0] = 0;
|
|
subset_total_colors3[1] = 0;
|
|
|
|
int subset_pixel_index3[2][16];
|
|
int subset_selectors3[2][16];
|
|
color_cell_compressor_results subset_results3[2];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
assert(p < 2);
|
|
|
|
subset_colors[p][subset_total_colors3[p]] = pPixels[idx];
|
|
|
|
subset_pixel_index3[p][subset_total_colors3[p]] = idx;
|
|
|
|
subset_total_colors3[p]++;
|
|
}
|
|
|
|
uint64_t trial_err = 0;
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results3[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors3[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(3, pParams, pResults, pComp_params, subset_total_colors3[subset], &subset_colors[subset][0], (num_solutions2 <= 2) || disable_faster_part_selection);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
trial_err += err;
|
|
if (trial_err > best_err)
|
|
break;
|
|
} // subset
|
|
|
|
if (trial_err < best_err)
|
|
{
|
|
best_err = trial_err;
|
|
|
|
opt_results.m_mode = 3;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = trial_partition;
|
|
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors3[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index3[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors3[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results3[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results3[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results3[subset].m_pbits[0];
|
|
opt_results.m_pbits[subset][1] = subset_results3[subset].m_pbits[1];
|
|
}
|
|
opt_results.m_used_lut = subset_results3[0].m_used_lut || subset_results3[1].m_used_lut;
|
|
}
|
|
|
|
} // solution_index
|
|
|
|
if ((num_solutions2 > 2) && (opt_results.m_mode == 3) && (!disable_faster_part_selection))
|
|
{
|
|
const uint32_t trial_partition = opt_results.m_partition;
|
|
assert(trial_partition < 64);
|
|
|
|
const int *pPartition = &g_bc7_partition2[trial_partition * 16];
|
|
|
|
color_quad_i subset_colors[2][16];
|
|
|
|
uint32_t subset_total_colors3[2];
|
|
subset_total_colors3[0] = 0;
|
|
subset_total_colors3[1] = 0;
|
|
|
|
int subset_pixel_index3[2][16];
|
|
int subset_selectors3[2][16];
|
|
color_cell_compressor_results subset_results3[2];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
assert(p < 2);
|
|
|
|
subset_colors[p][subset_total_colors3[p]] = pPixels[idx];
|
|
|
|
subset_pixel_index3[p][subset_total_colors3[p]] = idx;
|
|
|
|
subset_total_colors3[p]++;
|
|
}
|
|
|
|
uint64_t trial_err = 0;
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results3[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors3[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(3, pParams, pResults, pComp_params, subset_total_colors3[subset], &subset_colors[subset][0], true);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
trial_err += err;
|
|
if (trial_err > best_err)
|
|
break;
|
|
} // subset
|
|
|
|
if (trial_err < best_err)
|
|
{
|
|
best_err = trial_err;
|
|
|
|
for ( uint32_t subset = 0; subset < 2; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors3[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index3[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors3[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results3[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results3[subset].m_high_endpoint;
|
|
|
|
opt_results.m_pbits[subset][0] = subset_results3[subset].m_pbits[0];
|
|
opt_results.m_pbits[subset][1] = subset_results3[subset].m_pbits[1];
|
|
}
|
|
opt_results.m_used_lut = subset_results3[0].m_used_lut || subset_results3[1].m_used_lut;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mode 5
|
|
if ((!pComp_params->m_perceptual) && (pComp_params->m_opaque_settings.m_use_mode[5]))
|
|
{
|
|
color_cell_compressor_params params5 = *pParams;
|
|
|
|
for ( uint32_t rotation = 0; rotation < 4; rotation++)
|
|
{
|
|
if ((pComp_params->m_mode5_rotation_mask & (1 << rotation)) == 0)
|
|
continue;
|
|
|
|
memcpy(params5.m_weights, pParams->m_weights, sizeof(params5.m_weights));
|
|
if (rotation)
|
|
swapu(¶ms5.m_weights[rotation - 1], ¶ms5.m_weights[3]);
|
|
|
|
color_quad_i rot_pixels[16];
|
|
const color_quad_i * pTrial_pixels = pPixels;
|
|
uint32_t trial_lo_a = 255, trial_hi_a = 255;
|
|
if (rotation)
|
|
{
|
|
trial_lo_a = 255;
|
|
trial_hi_a = 0;
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
color_quad_i c = pPixels[i];
|
|
swapi(&c.m_c[3], &c.m_c[rotation - 1]);
|
|
rot_pixels[i] = c;
|
|
|
|
trial_lo_a = minimumu(trial_lo_a, c.m_c[3]);
|
|
trial_hi_a = maximumu(trial_hi_a, c.m_c[3]);
|
|
}
|
|
|
|
pTrial_pixels = rot_pixels;
|
|
}
|
|
|
|
bc7_optimization_results trial_opt_results5;
|
|
|
|
uint64_t trial_mode5_err = 0;
|
|
|
|
handle_alpha_block_mode5(pTrial_pixels, pComp_params, ¶ms5, trial_lo_a, trial_hi_a, &trial_opt_results5, &trial_mode5_err);
|
|
|
|
if (trial_mode5_err < best_err)
|
|
{
|
|
best_err = trial_mode5_err;
|
|
|
|
opt_results = trial_opt_results5;
|
|
opt_results.m_rotation = rotation;
|
|
}
|
|
} // rotation
|
|
}
|
|
|
|
// Mode 2
|
|
if (pComp_params->m_opaque_settings.m_use_mode[2])
|
|
{
|
|
solution solutions3[BC7E_MAX_PARTITIONS2];
|
|
uint32_t num_solutions3 = 0;
|
|
if (forced_partition >= 0)
|
|
{
|
|
solutions3[0].m_index = forced_partition;
|
|
num_solutions3 = 1;
|
|
}
|
|
else if (pComp_params->m_opaque_settings.m_max_mode2_partitions_to_try == 1)
|
|
{
|
|
solutions3[0].m_index = estimate_partition(2, pPixels, pComp_params);
|
|
num_solutions3 = 1;
|
|
}
|
|
else
|
|
{
|
|
num_solutions3 = estimate_partition_list(2, pPixels, pComp_params, solutions3, pComp_params->m_opaque_settings.m_max_mode2_partitions_to_try);
|
|
}
|
|
|
|
pParams->m_pSelector_weights = g_bc7_weights2;
|
|
pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights2x[0];
|
|
pParams->m_num_selector_weights = 4;
|
|
|
|
pParams->m_comp_bits = 5;
|
|
pParams->m_has_pbits = false;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
for ( uint32_t solution_index = 0; solution_index < num_solutions3; solution_index++)
|
|
{
|
|
const int32_t best_partition2 = solutions3[solution_index].m_index;
|
|
|
|
uint32_t subset_total_colors2[3];
|
|
subset_total_colors2[0] = 0;
|
|
subset_total_colors2[1] = 0;
|
|
subset_total_colors2[2] = 0;
|
|
|
|
int subset_pixel_index2[3][16];
|
|
|
|
const int *pPartition = &g_bc7_partition3[best_partition2 * 16];
|
|
|
|
color_quad_i subset_colors[3][16];
|
|
|
|
for ( uint32_t idx = 0; idx < 16; idx++)
|
|
{
|
|
const uint32_t p = pPartition[idx];
|
|
|
|
subset_colors[p][subset_total_colors2[p]] = pPixels[idx];
|
|
|
|
subset_pixel_index2[p][subset_total_colors2[p]] = idx;
|
|
|
|
subset_total_colors2[p]++;
|
|
}
|
|
|
|
int subset_selectors2[3][16];
|
|
color_cell_compressor_results subset_results2[3];
|
|
|
|
uint64_t mode2_err = 0;
|
|
for ( uint32_t subset = 0; subset < 3; subset++)
|
|
{
|
|
color_cell_compressor_results * pResults = &subset_results2[subset];
|
|
|
|
pResults->m_pSelectors = &subset_selectors2[subset][0];
|
|
pResults->m_pSelectors_temp = selectors_temp;
|
|
|
|
uint64_t err = color_cell_compression(2, pParams, pResults, pComp_params, subset_total_colors2[subset], &subset_colors[subset][0], true);
|
|
assert(err == pResults->m_best_overall_err);
|
|
|
|
mode2_err += err;
|
|
if (mode2_err > best_err)
|
|
break;
|
|
} // subset
|
|
|
|
if (mode2_err < best_err)
|
|
{
|
|
best_err = mode2_err;
|
|
|
|
opt_results.m_mode = 2;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = best_partition2;
|
|
|
|
for ( uint32_t subset = 0; subset < 3; subset++)
|
|
{
|
|
for ( uint32_t i = 0; i < subset_total_colors2[subset]; i++)
|
|
{
|
|
const uint32_t pixel_index = subset_pixel_index2[subset][i];
|
|
|
|
opt_results.m_selectors[pixel_index] = subset_selectors2[subset][i];
|
|
}
|
|
|
|
opt_results.m_low[subset] = subset_results2[subset].m_low_endpoint;
|
|
opt_results.m_high[subset] = subset_results2[subset].m_high_endpoint;
|
|
}
|
|
opt_results.m_used_lut = subset_results2[0].m_used_lut || subset_results2[1].m_used_lut || subset_results2[2].m_used_lut;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mode 4
|
|
if ((!pComp_params->m_perceptual) && (pComp_params->m_opaque_settings.m_use_mode[4]))
|
|
{
|
|
color_cell_compressor_params params4 = *pParams;
|
|
|
|
for ( uint32_t rotation = 0; rotation < 4; rotation++)
|
|
{
|
|
if ((pComp_params->m_mode4_rotation_mask & (1 << rotation)) == 0)
|
|
continue;
|
|
|
|
memcpy(params4.m_weights, pParams->m_weights, sizeof(params4.m_weights));
|
|
if (rotation)
|
|
swapu(¶ms4.m_weights[rotation - 1], ¶ms4.m_weights[3]);
|
|
|
|
color_quad_i rot_pixels[16];
|
|
const color_quad_i * pTrial_pixels = pPixels;
|
|
uint32_t trial_lo_a = 255, trial_hi_a = 255;
|
|
if (rotation)
|
|
{
|
|
trial_lo_a = 255;
|
|
trial_hi_a = 0;
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
color_quad_i c = pPixels[i];
|
|
swapi(&c.m_c[3], &c.m_c[rotation - 1]);
|
|
rot_pixels[i] = c;
|
|
|
|
trial_lo_a = minimumu(trial_lo_a, c.m_c[3]);
|
|
trial_hi_a = maximumu(trial_hi_a, c.m_c[3]);
|
|
}
|
|
|
|
pTrial_pixels = rot_pixels;
|
|
}
|
|
|
|
bc7_optimization_results trial_opt_results4;
|
|
|
|
uint64_t trial_mode4_err = best_err;
|
|
|
|
handle_alpha_block_mode4(pTrial_pixels, pComp_params, ¶ms4, trial_lo_a, trial_hi_a, &trial_opt_results4, &trial_mode4_err);
|
|
|
|
if (trial_mode4_err < best_err)
|
|
{
|
|
best_err = trial_mode4_err;
|
|
|
|
opt_results.m_mode = 4;
|
|
opt_results.m_index_selector = trial_opt_results4.m_index_selector;
|
|
opt_results.m_rotation = rotation;
|
|
opt_results.m_partition = 0;
|
|
opt_results.m_used_lut = trial_opt_results4.m_used_lut;
|
|
|
|
opt_results.m_low[0] = trial_opt_results4.m_low[0];
|
|
opt_results.m_high[0] = trial_opt_results4.m_high[0];
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
opt_results.m_selectors[i] = trial_opt_results4.m_selectors[i];
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
opt_results.m_alpha_selectors[i] = trial_opt_results4.m_alpha_selectors[i];
|
|
}
|
|
} // rotation
|
|
}
|
|
|
|
if (pBest_err) *pBest_err = best_err;
|
|
if (pUsed_lut) *pUsed_lut = opt_results.m_used_lut;
|
|
encode_bc7_block(pBlock, &opt_results);
|
|
}
|
|
|
|
// all solid color blocks can be 100% perfectly encoded with just mode 5
|
|
static void handle_block_solid(void * pBlock, uint32_t cr, uint32_t cg, uint32_t cb, uint32_t ca, bool* pUsed_lut = nullptr)
|
|
{
|
|
if (pUsed_lut) *pUsed_lut = true; // solid blocks always use the mode-5 optimal-endpoint LUT
|
|
uint32_t er = g_bc7_mode_5_optimal_endpoints[cr];
|
|
uint32_t eg = g_bc7_mode_5_optimal_endpoints[cg];
|
|
uint32_t eb = g_bc7_mode_5_optimal_endpoints[cb];
|
|
color_quad_i lp, hp;
|
|
color_quad_i_set(&lp, er & 0xFF, eg & 0xFF, eb & 0xFF, ca);
|
|
color_quad_i_set(&hp, er >> 8, eg >> 8, eb >> 8, ca);
|
|
|
|
bc7_optimization_results opt;
|
|
opt.m_mode = 5;
|
|
opt.m_low[0] = lp;
|
|
opt.m_high[0] = hp;
|
|
opt.m_pbits[0][0] = 0;
|
|
opt.m_pbits[0][1] = 0;
|
|
opt.m_index_selector = 0;
|
|
opt.m_rotation = 0;
|
|
opt.m_partition = 0;
|
|
for ( int i = 0; i < 16; ++i)
|
|
opt.m_selectors[i] = BC7E_MODE_5_OPTIMAL_INDEX;
|
|
for ( int i = 0; i < 16; ++i)
|
|
opt.m_alpha_selectors[i] = 0;
|
|
encode_bc7_block(pBlock, &opt);
|
|
}
|
|
|
|
static void handle_opaque_block_mode6(void * pBlock, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, bool* pUsed_lut = nullptr)
|
|
{
|
|
int selectors_temp[16];
|
|
|
|
bc7_optimization_results opt_results = {};
|
|
|
|
uint64_t best_err = UINT64_MAX;
|
|
|
|
// Mode 6
|
|
pParams->m_pSelector_weights = g_bc7_weights4;
|
|
pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights4x[0];
|
|
pParams->m_num_selector_weights = 16;
|
|
|
|
pParams->m_comp_bits = 7;
|
|
pParams->m_has_pbits = true;
|
|
pParams->m_endpoints_share_pbit = false;
|
|
|
|
pParams->m_perceptual = pComp_params->m_perceptual;
|
|
|
|
color_cell_compressor_results results6;
|
|
results6.m_pSelectors = opt_results.m_selectors;
|
|
results6.m_pSelectors_temp = selectors_temp;
|
|
|
|
best_err = color_cell_compression(6, pParams, &results6, pComp_params, 16, pPixels, true);
|
|
NOTE_UNUSED(best_err); // mode 6 here is unconditional; the returned error isn't compared - silence clang -Wunused-but-set-variable
|
|
|
|
opt_results.m_mode = 6;
|
|
opt_results.m_index_selector = 0;
|
|
opt_results.m_rotation = 0;
|
|
opt_results.m_partition = 0;
|
|
|
|
opt_results.m_low[0] = results6.m_low_endpoint;
|
|
opt_results.m_high[0] = results6.m_high_endpoint;
|
|
|
|
opt_results.m_pbits[0][0] = results6.m_pbits[0];
|
|
opt_results.m_pbits[0][1] = results6.m_pbits[1];
|
|
|
|
if (pUsed_lut) *pUsed_lut = results6.m_used_lut;
|
|
encode_bc7_block_mode6(pBlock, &opt_results);
|
|
}
|
|
|
|
// Compress a single 4x4 RGBA block to ONE specified BC7 mode only. Additive API: it does
|
|
// not touch bc7e_compress_blocks(). Writes the 16-byte block to pBlock and returns its error.
|
|
// mode : 0..7
|
|
// partition : modes 0,1,2,3,7 -> partition pattern index, or -1 to auto-select the
|
|
// optimal one (existing estimate_partition* logic). Ignored for 4,5,6.
|
|
// rotation : modes 4,5 -> dual-plane component rotation [0..3] (0 = none).
|
|
// index_selector : mode 4 -> 0 or 1 (which index set is the scalar channel).
|
|
// Forcing an opaque mode (0-3) on a block with alpha drops alpha (those modes force A=255).
|
|
uint64_t bc7e_compress_block_single_mode(uint64_t* pBlock, const uint32_t* pPixelsRGBA, const bc7e_compress_block_params* pComp_params,
|
|
uint32_t mode, int partition, uint32_t rotation, uint32_t index_selector)
|
|
{
|
|
assert(g_codec_initialized);
|
|
assert(mode <= 7);
|
|
|
|
// Extract the 16 pixels + alpha range (mirrors bc7e_compress_blocks).
|
|
const color_quad_u8* pSrcPixels = (const color_quad_u8*)pPixelsRGBA;
|
|
color_quad_i temp_pixels[16];
|
|
int lo_a = 255, hi_a = 0;
|
|
for (uint32_t i = 0; i < 16; i++)
|
|
{
|
|
color_quad_u8 c = pSrcPixels[i];
|
|
temp_pixels[i].m_c[0] = c.m_c[0];
|
|
temp_pixels[i].m_c[1] = c.m_c[1];
|
|
temp_pixels[i].m_c[2] = c.m_c[2];
|
|
temp_pixels[i].m_c[3] = c.m_c[3];
|
|
lo_a = min(lo_a, (int)c.m_c[3]);
|
|
hi_a = max(hi_a, (int)c.m_c[3]);
|
|
}
|
|
|
|
color_cell_compressor_params ccparams;
|
|
color_cell_compressor_params_clear(&ccparams);
|
|
memcpy(ccparams.m_weights, pComp_params->m_weights, sizeof(ccparams.m_weights));
|
|
|
|
// Single-mode params: copy caller's settings, disable every mode, enable only this one.
|
|
bc7e_compress_block_params p = *pComp_params;
|
|
for (uint32_t i = 0; i < 7; i++)
|
|
p.m_opaque_settings.m_use_mode[i] = false;
|
|
p.m_alpha_settings.m_use_mode4 = false;
|
|
p.m_alpha_settings.m_use_mode5 = false;
|
|
p.m_alpha_settings.m_use_mode6 = false;
|
|
p.m_alpha_settings.m_use_mode7 = false;
|
|
p.m_mode6_only = false;
|
|
|
|
// Validate/clamp a caller-supplied partition to the mode's valid range; -1 means auto-select.
|
|
// Partition field width: mode 0 is 4 bits (16 patterns); modes 1,2,3,7 are 6 bits (64 patterns);
|
|
// modes 4,5,6 have no partition field.
|
|
int forced_partition = partition;
|
|
if (forced_partition >= 0)
|
|
{
|
|
const int num_partitions = 1 << g_bc7_partition_bits[mode]; // 1 for non-partitioned modes
|
|
assert((num_partitions > 1) && "partition index supplied for a non-partitioned BC7 mode (4/5/6)");
|
|
assert((forced_partition < num_partitions) && "partition index out of range for this BC7 mode (mode 0: 0-15, modes 1/2/3/7: 0-63)");
|
|
if (num_partitions <= 1)
|
|
forced_partition = -1; // mode has no partition field; ignore
|
|
else if (forced_partition >= num_partitions)
|
|
forced_partition = num_partitions - 1;
|
|
}
|
|
|
|
uint64_t best_err = UINT64_MAX;
|
|
|
|
if (mode <= 3)
|
|
{
|
|
// Opaque-only modes (alpha dropped / forced to 255).
|
|
p.m_opaque_settings.m_use_mode[mode] = true;
|
|
handle_opaque_block(pBlock, temp_pixels, &p, &ccparams, forced_partition, &best_err);
|
|
}
|
|
else if ((mode == 4) || (mode == 5))
|
|
{
|
|
// A non-zero rotation is only reachable under the handler's linear-metric gate, so
|
|
// force linear when one is explicitly requested (keeps the result valid + deterministic).
|
|
if (rotation != 0)
|
|
p.m_perceptual = false;
|
|
|
|
if (mode == 4)
|
|
{
|
|
p.m_alpha_settings.m_use_mode4 = true;
|
|
p.m_alpha_settings.m_use_mode4_rotation = true;
|
|
p.m_mode4_rotation_mask = 1u << (rotation & 3);
|
|
p.m_mode4_index_mask = 1u << (index_selector & 1);
|
|
}
|
|
else
|
|
{
|
|
p.m_alpha_settings.m_use_mode5 = true;
|
|
p.m_alpha_settings.m_use_mode5_rotation = true;
|
|
p.m_mode5_rotation_mask = 1u << (rotation & 3);
|
|
}
|
|
handle_alpha_block(pBlock, temp_pixels, &p, &ccparams, (uint32_t)lo_a, (uint32_t)hi_a, -1, &best_err);
|
|
}
|
|
else if (mode == 6)
|
|
{
|
|
p.m_alpha_settings.m_use_mode6 = true;
|
|
handle_alpha_block(pBlock, temp_pixels, &p, &ccparams, (uint32_t)lo_a, (uint32_t)hi_a, -1, &best_err);
|
|
}
|
|
else // mode == 7
|
|
{
|
|
p.m_alpha_settings.m_use_mode7 = true;
|
|
handle_alpha_block(pBlock, temp_pixels, &p, &ccparams, (uint32_t)lo_a, (uint32_t)hi_a, forced_partition, &best_err);
|
|
}
|
|
|
|
return best_err;
|
|
}
|
|
|
|
void bc7e_compress_blocks( uint32_t num_blocks, uint64_t * pBlocks, const uint32_t * pPixelsRGBA, const bc7e_compress_block_params * pComp_params, uint8_t * pUsed_lut)
|
|
{
|
|
if (!g_codec_initialized)
|
|
{
|
|
// Caller has forgotten to initialize the codec, or another thread is still working on that. We can't continue.
|
|
// What do we do here?
|
|
assert(0);
|
|
|
|
memset(pBlocks, 0, num_blocks * 16);
|
|
return;
|
|
}
|
|
|
|
color_cell_compressor_params params;
|
|
color_cell_compressor_params_clear(¶ms);
|
|
|
|
memcpy(params.m_weights, pComp_params->m_weights, sizeof(params.m_weights));
|
|
|
|
assert(pComp_params->m_mode4_rotation_mask != 0);
|
|
assert(pComp_params->m_mode4_index_mask != 0);
|
|
assert(pComp_params->m_mode5_rotation_mask != 0);
|
|
assert(pComp_params->m_uber1_mask != 0);
|
|
|
|
for (int32_t block_index = 0; block_index < (int32_t)num_blocks; block_index++)
|
|
{
|
|
const color_quad_u8 * pSrcPixels = &((const color_quad_u8 *)(pPixelsRGBA))[block_index * 16];
|
|
|
|
color_quad_i temp_pixels[16];
|
|
|
|
int lo_r = 255, hi_r = 0;
|
|
int lo_g = 255, hi_g = 0;
|
|
int lo_b = 255, hi_b = 0;
|
|
float lo_a = 255, hi_a = 0;
|
|
|
|
for ( uint32_t i = 0; i < 16; i++)
|
|
{
|
|
color_quad_u8 c = pSrcPixels[i];
|
|
|
|
int r = c.m_c[0];
|
|
int g = c.m_c[1];
|
|
int b = c.m_c[2];
|
|
int a = c.m_c[3];
|
|
|
|
temp_pixels[i].m_c[0] = r;
|
|
temp_pixels[i].m_c[1] = g;
|
|
temp_pixels[i].m_c[2] = b;
|
|
temp_pixels[i].m_c[3] = a;
|
|
|
|
lo_r = min(lo_r, r); hi_r = max(hi_r, r);
|
|
lo_g = min(lo_g, g); hi_g = max(hi_g, g);
|
|
lo_b = min(lo_b, b); hi_b = max(hi_b, b);
|
|
|
|
float fa = (float)(a);
|
|
|
|
lo_a = min(lo_a, fa);
|
|
hi_a = max(hi_a, fa);
|
|
}
|
|
|
|
bool all_same = lo_r==hi_r && lo_g==hi_g && lo_b==hi_b && lo_a==hi_a;
|
|
|
|
uint64_t * pBlock = &pBlocks[block_index * 2];
|
|
|
|
bool block_used_lut = false;
|
|
|
|
cif (all_same)
|
|
handle_block_solid(pBlock, lo_r, lo_g, lo_b, float_to_uint8(lo_a), &block_used_lut);
|
|
else
|
|
{
|
|
const bool has_alpha = (lo_a < 255);
|
|
// TODO: alpha block mode 6 only
|
|
cif (has_alpha)
|
|
handle_alpha_block(pBlock, temp_pixels, pComp_params, ¶ms, (int)lo_a, (int)hi_a, -1, nullptr, &block_used_lut);
|
|
else
|
|
{
|
|
if (pComp_params->m_mode6_only)
|
|
handle_opaque_block_mode6(pBlock, temp_pixels, pComp_params, ¶ms, &block_used_lut);
|
|
else
|
|
handle_opaque_block(pBlock, temp_pixels, pComp_params, ¶ms, -1, nullptr, &block_used_lut);
|
|
}
|
|
}
|
|
|
|
if (pUsed_lut)
|
|
pUsed_lut[block_index] = block_used_lut ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
void bc7e_compress_block_params_init(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
p->m_max_partitions_mode[0] = BC7E_MAX_PARTITIONS0;
|
|
p->m_max_partitions_mode[1] = BC7E_MAX_PARTITIONS1;
|
|
p->m_max_partitions_mode[2] = BC7E_MAX_PARTITIONS2;
|
|
p->m_max_partitions_mode[3] = BC7E_MAX_PARTITIONS3;
|
|
p->m_max_partitions_mode[4] = 0;
|
|
p->m_max_partitions_mode[5] = 0;
|
|
p->m_max_partitions_mode[6] = 0;
|
|
p->m_max_partitions_mode[7] = BC7E_MAX_PARTITIONS7;
|
|
|
|
p->m_use_luts = true; // default ON, matching bc7e's original behavior; callers may disable (e.g. XBC7 turns it off below lossless Q)
|
|
|
|
p->m_perceptual = perceptual;
|
|
if (perceptual)
|
|
{
|
|
p->m_weights[0] = 128;
|
|
p->m_weights[1] = 64;
|
|
p->m_weights[2] = 16;
|
|
p->m_weights[3] = 256;
|
|
}
|
|
else
|
|
{
|
|
p->m_weights[0] = 1;
|
|
p->m_weights[1] = 1;
|
|
p->m_weights[2] = 1;
|
|
p->m_weights[3] = 1;
|
|
}
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_mode6_only = false;
|
|
p->m_refinement_passes = 1;
|
|
p->m_mode4_rotation_mask = 0xF;
|
|
p->m_mode4_index_mask = 3;
|
|
p->m_mode5_rotation_mask = 0xF;
|
|
p->m_uber1_mask = 7;
|
|
for ( uint32_t i = 0; i < 7; i++)
|
|
p->m_opaque_settings.m_use_mode[i] = true;
|
|
p->m_opaque_settings.m_max_mode13_partitions_to_try = 1;
|
|
p->m_opaque_settings.m_max_mode0_partitions_to_try = 1;
|
|
p->m_opaque_settings.m_max_mode2_partitions_to_try = 1;
|
|
p->m_alpha_settings.m_use_mode4 = true;
|
|
p->m_alpha_settings.m_use_mode5 = true;
|
|
p->m_alpha_settings.m_use_mode6 = true;
|
|
p->m_alpha_settings.m_use_mode7 = true;
|
|
p->m_alpha_settings.m_use_mode4_rotation = true;
|
|
p->m_alpha_settings.m_use_mode5_rotation = true;
|
|
p->m_alpha_settings.m_max_mode7_partitions_to_try = 1;
|
|
p->m_alpha_settings.m_mode67_error_weight_mul[0] = 1;
|
|
p->m_alpha_settings.m_mode67_error_weight_mul[1] = 1;
|
|
p->m_alpha_settings.m_mode67_error_weight_mul[2] = 1;
|
|
p->m_alpha_settings.m_mode67_error_weight_mul[3] = 1;
|
|
p->m_uber_level = 0;
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_slowest(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
p->m_opaque_settings.m_max_mode13_partitions_to_try = 4;
|
|
p->m_opaque_settings.m_max_mode0_partitions_to_try = 4;
|
|
p->m_opaque_settings.m_max_mode2_partitions_to_try = 4;
|
|
p->m_alpha_settings.m_max_mode7_partitions_to_try = 4;
|
|
|
|
p->m_pbit_search = true;
|
|
p->m_uber_level = 4;
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_veryslow(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
p->m_opaque_settings.m_max_mode13_partitions_to_try = 2;
|
|
p->m_opaque_settings.m_max_mode0_partitions_to_try = 2;
|
|
p->m_opaque_settings.m_max_mode2_partitions_to_try = 2;
|
|
p->m_alpha_settings.m_max_mode7_partitions_to_try = 2;
|
|
|
|
p->m_pbit_search = true;
|
|
p->m_uber_level = 2;
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_slow(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
p->m_alpha_settings.m_max_mode7_partitions_to_try = 2;
|
|
|
|
p->m_pbit_search = true;
|
|
p->m_uber_level = 0;
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_basic(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
if (perceptual)
|
|
{
|
|
p->m_opaque_settings.m_use_mode[0] = false;
|
|
p->m_opaque_settings.m_use_mode[2] = false;
|
|
p->m_opaque_settings.m_use_mode[3] = false;
|
|
p->m_opaque_settings.m_use_mode[4] = false;
|
|
p->m_opaque_settings.m_use_mode[5] = false;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 1;
|
|
}
|
|
else
|
|
{
|
|
p->m_max_partitions_mode[1] = 32;
|
|
p->m_max_partitions_mode[2] = 32;
|
|
p->m_max_partitions_mode[3] = 32;
|
|
p->m_max_partitions_mode[7] = 32;
|
|
|
|
p->m_opaque_settings.m_use_mode[2] = false;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 1;
|
|
}
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_fast(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
if (perceptual)
|
|
{
|
|
p->m_opaque_settings.m_use_mode[0] = false;
|
|
p->m_opaque_settings.m_use_mode[2] = false;
|
|
p->m_opaque_settings.m_use_mode[3] = false;
|
|
p->m_opaque_settings.m_use_mode[4] = false;
|
|
p->m_opaque_settings.m_use_mode[5] = false;
|
|
|
|
p->m_alpha_settings.m_use_mode5 = false;
|
|
|
|
p->m_opaque_settings.m_max_mode13_partitions_to_try = 1;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 0;
|
|
}
|
|
else
|
|
{
|
|
p->m_opaque_settings.m_use_mode[0] = false;
|
|
p->m_opaque_settings.m_use_mode[2] = false;
|
|
p->m_opaque_settings.m_use_mode[4] = false;
|
|
p->m_opaque_settings.m_use_mode[5] = false;
|
|
|
|
p->m_alpha_settings.m_use_mode5 = false;
|
|
|
|
p->m_opaque_settings.m_max_mode13_partitions_to_try = 2;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 0;
|
|
}
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_veryfast(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
if (perceptual)
|
|
{
|
|
p->m_opaque_settings.m_use_mode[0] = false;
|
|
p->m_opaque_settings.m_use_mode[2] = false;
|
|
p->m_opaque_settings.m_use_mode[3] = false;
|
|
p->m_opaque_settings.m_use_mode[4] = false;
|
|
p->m_opaque_settings.m_use_mode[5] = false;
|
|
|
|
p->m_alpha_settings.m_use_mode5 = false;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 0;
|
|
}
|
|
else
|
|
{
|
|
p->m_opaque_settings.m_use_mode[2] = false;
|
|
p->m_opaque_settings.m_use_mode[4] = false;
|
|
p->m_opaque_settings.m_use_mode[5] = false;
|
|
|
|
p->m_alpha_settings.m_use_mode5 = false;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 0;
|
|
}
|
|
}
|
|
|
|
void bc7e_compress_block_params_init_ultrafast(bc7e_compress_block_params * p, bool perceptual)
|
|
{
|
|
bc7e_compress_block_params_init(p, perceptual);
|
|
|
|
p->m_mode6_only = true;
|
|
|
|
p->m_alpha_settings.m_use_mode4 = true;
|
|
p->m_alpha_settings.m_use_mode5 = true;
|
|
p->m_alpha_settings.m_use_mode7 = false;
|
|
|
|
p->m_mode4_rotation_mask = 1+4;
|
|
p->m_mode4_index_mask = 3;
|
|
p->m_mode5_rotation_mask = 1;
|
|
|
|
p->m_pbit_search = false;
|
|
p->m_uber_level = 0;
|
|
}
|
|
|
|
} // namespace bc7e_scalar
|