Work toward packaging dxt1 compressor as a single header library.

pull/312/head
Ignacio 4 years ago
parent 1a6e70c9a0
commit daff42781d

@ -62,17 +62,17 @@ namespace nv
};
};
NVMATH_API Vector3 rgb9e5_to_vector3(FloatRGB9E5 v);
NVMATH_API FloatRGB9E5 vector3_to_rgb9e5(const Vector3 & v);
Vector3 rgb9e5_to_vector3(FloatRGB9E5 v);
FloatRGB9E5 vector3_to_rgb9e5(const Vector3 & v);
NVMATH_API float float11_to_float32(uint v);
NVMATH_API float float10_to_float32(uint v);
float float11_to_float32(uint v);
float float10_to_float32(uint v);
NVMATH_API Vector3 r11g11b10_to_vector3(FloatR11G11B10 v);
NVMATH_API FloatR11G11B10 vector3_to_r11g11b10(const Vector3 & v);
Vector3 r11g11b10_to_vector3(FloatR11G11B10 v);
FloatR11G11B10 vector3_to_r11g11b10(const Vector3 & v);
NVMATH_API Vector3 rgbe8_to_vector3(FloatRGBE8 v);
NVMATH_API FloatRGBE8 vector3_to_rgbe8(const Vector3 & v);
Vector3 rgbe8_to_vector3(FloatRGBE8 v);
FloatRGBE8 vector3_to_rgbe8(const Vector3 & v);
} // nv

@ -1,7 +1,6 @@
// MIT license see full LICENSE text at end of file
#include "ClusterFit.h"
#include "nvmath/Fitting.h"
#include "nvmath/Vector.inl"
#include <float.h> // FLT_MAX
@ -9,6 +8,96 @@
using namespace nv;
static Vector3 computeCentroid(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric)
{
Vector3 centroid(0.0f);
float total = 0.0f;
for (int i = 0; i < n; i++)
{
total += weights[i];
centroid += weights[i] * points[i];
}
centroid *= (1.0f / total);
return centroid;
}
static Vector3 computeCovariance(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric, float *__restrict covariance)
{
// compute the centroid
Vector3 centroid = computeCentroid(n, points, weights, metric);
// compute covariance matrix
for (int i = 0; i < 6; i++)
{
covariance[i] = 0.0f;
}
for (int i = 0; i < n; i++)
{
Vector3 a = (points[i] - centroid) * metric; // @@ I think weight should be squared, but that seems to increase the error slightly.
Vector3 b = weights[i] * a;
covariance[0] += a.x * b.x;
covariance[1] += a.x * b.y;
covariance[2] += a.x * b.z;
covariance[3] += a.y * b.y;
covariance[4] += a.y * b.z;
covariance[5] += a.z * b.z;
}
return centroid;
}
// @@ We should be able to do something cheaper...
static Vector3 estimatePrincipalComponent(const float * __restrict matrix)
{
const Vector3 row0(matrix[0], matrix[1], matrix[2]);
const Vector3 row1(matrix[1], matrix[3], matrix[4]);
const Vector3 row2(matrix[2], matrix[4], matrix[5]);
float r0 = lengthSquared(row0);
float r1 = lengthSquared(row1);
float r2 = lengthSquared(row2);
if (r0 > r1 && r0 > r2) return row0;
if (r1 > r2) return row1;
return row2;
}
static inline Vector3 firstEigenVector_PowerMethod(const float *__restrict matrix)
{
if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0)
{
return Vector3(0.0f);
}
Vector3 v = estimatePrincipalComponent(matrix);
const int NUM = 8;
for (int i = 0; i < NUM; i++)
{
float x = v.x * matrix[0] + v.y * matrix[1] + v.z * matrix[2];
float y = v.x * matrix[1] + v.y * matrix[3] + v.z * matrix[4];
float z = v.x * matrix[2] + v.y * matrix[4] + v.z * matrix[5];
float norm = max(max(x, y), z);
v = Vector3(x, y, z) * (1.0f / norm);
}
return v;
}
static Vector3 computePrincipalComponent_PowerMethod(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric)
{
float matrix[6];
computeCovariance(n, points, weights, metric, matrix);
return firstEigenVector_PowerMethod(matrix);
}
void ClusterFit::setColorSet(const Vector3 * colors, const float * weights, int count)
{
// initialise the best error
@ -23,8 +112,7 @@ void ClusterFit::setColorSet(const Vector3 * colors, const float * weights, int
m_count = count;
// I've tried using a lower quality approximation of the principal direction, but the best fit line seems to produce best results.
Vector3 principal = Fit::computePrincipalComponent_PowerMethod(count, colors, weights, metric);
//Vector3 principal = Fit::computePrincipalComponent_EigenSolver(count, colors, weights, metric);
Vector3 principal = computePrincipalComponent_PowerMethod(count, colors, weights, metric);
// build the list of values
int order[16];

@ -1,76 +1,86 @@
// MIT license see full LICENSE text at end of file
#pragma once
#include "nvmath/SimdVector.h"
#include "nvmath/Vector.h"
#include "nvcore/Memory.h"
// Use SIMD version if altivec or SSE are available.
#define NVTT_USE_SIMD (NV_USE_ALTIVEC || NV_USE_SSE)
//#define NVTT_USE_SIMD 0
namespace nv {
struct ColorSet;
class ClusterFit
{
public:
ClusterFit() {}
void setColorSet(const Vector3 * colors, const float * weights, int count);
void setColorWeights(const Vector4 & w);
float bestError() const;
bool compress3(Vector3 * start, Vector3 * end);
bool compress4(Vector3 * start, Vector3 * end);
private:
uint m_count;
// IC: Color and weight arrays are larger than necessary to avoid compiler warning.
#if NVTT_USE_SIMD
NV_ALIGN_16 SimdVector m_weighted[17]; // color | weight
SimdVector m_metric; // vec3
SimdVector m_metricSqr; // vec3
SimdVector m_xxsum; // color | weight
SimdVector m_xsum; // color | weight (wsum)
SimdVector m_besterror; // scalar
#else
Vector3 m_weighted[17];
float m_weights[17];
Vector3 m_metric;
Vector3 m_metricSqr;
Vector3 m_xxsum;
Vector3 m_xsum;
float m_wsum;
float m_besterror;
#endif
};
} // nv namespace
// Copyright (c) 2006-2020 Ignacio Castano icastano@nvidia.com
// Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// MIT license see full LICENSE text at end of file
#pragma once
#include "nvmath/SimdVector.h"
#include "nvmath/Vector.h"
// Use SIMD version if altivec or SSE are available.
#define NVTT_USE_SIMD (NV_USE_ALTIVEC || NV_USE_SSE)
//#define NVTT_USE_SIMD 0
#include <xmmintrin.h>
#if (NV_USE_SSE > 1)
#include <emmintrin.h>
#endif
#ifndef NV_ALIGN_16
#if NV_CC_GNUC
# define NV_ALIGN_16 __attribute__ ((__aligned__ (16)))
#else
# define NV_ALIGN_16 __declspec(align(16))
#endif
#endif
namespace nv {
class ClusterFit
{
public:
ClusterFit() {}
void setColorSet(const Vector3 * colors, const float * weights, int count);
void setColorWeights(const Vector4 & w);
float bestError() const;
bool compress3(Vector3 * start, Vector3 * end);
bool compress4(Vector3 * start, Vector3 * end);
private:
uint m_count;
// IC: Color and weight arrays are larger than necessary to avoid compiler warning.
#if NVTT_USE_SIMD
NV_ALIGN_16 SimdVector m_weighted[17]; // color | weight
SimdVector m_metric; // vec3
SimdVector m_metricSqr; // vec3
SimdVector m_xxsum; // color | weight
SimdVector m_xsum; // color | weight (wsum)
SimdVector m_besterror; // scalar
#else
Vector3 m_weighted[17];
float m_weights[17];
Vector3 m_metric;
Vector3 m_metricSqr;
Vector3 m_xxsum;
Vector3 m_xsum;
float m_wsum;
float m_besterror;
#endif
};
} // nv namespace
// Copyright (c) 2006-2020 Ignacio Castano icastano@nvidia.com
// Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,17 +1,8 @@
#include "CompressorDXT1.h"
#include "SingleColorLookup.h"
#include "ClusterFit.h"
#include "nvimage/ColorBlock.h"
#include "nvimage/BlockDXT.h"
#include "nvmath/Color.inl"
#include "nvmath/Vector.inl"
#include "nvmath/Fitting.h"
#include "nvmath/ftoi.h"
#include "nvcore/Utils.h" // swap
#include "nvmath/nvmath.h"
#include <string.h> // memset
#include <float.h> // FLT_MAX
@ -19,6 +10,104 @@
using namespace nv;
/// Swap two values.
/*template <typename T>
inline void swap(T & a, T & b)
{
T temp(a);
a = b;
b = temp;
}*/
///////////////////////////////////////////////////////////////////////////////////////////////////
// Basic Types
struct Color16 {
union {
struct {
uint16 b : 5;
uint16 g : 6;
uint16 r : 5;
};
uint16 u;
};
};
struct Color32 {
union {
struct {
uint8 b, g, r, a;
};
uint32 u;
};
};
namespace nv {
struct BlockDXT1 {
Color16 col0;
Color16 col1;
uint32 indices;
};
/*struct Vector3 {
float x, y, z;
};*/
inline Vector3 operator*(Vector3 v, float s) {
return { v.x * s, v.y * s, v.z * s };
}
inline Vector3 operator*(float s, Vector3 v) {
return { v.x * s, v.y * s, v.z * s };
}
inline Vector3 operator*(Vector3 a, Vector3 b) {
return { a.x * b.x, a.y * b.y, a.z * b.z };
}
inline float dot(Vector3 a, Vector3 b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
inline Vector3 operator+(Vector3 a, Vector3 b) {
return { a.x + b.x, a.y + b.y, a.z + b.z };
}
inline Vector3 operator-(Vector3 a, Vector3 b) {
return { a.x - b.x, a.y - b.y, a.z - b.z };
}
inline Vector3 operator/(Vector3 v, float s) {
return { v.x / s, v.y / s, v.z / s };
}
/*inline float saturate(float x) {
return x < 0 ? 0 : (x > 1 ? 1 : x);
}*/
inline Vector3 saturate(Vector3 v) {
return { saturate(v.x), saturate(v.y), saturate(v.z) };
}
inline Vector3 min(Vector3 a, Vector3 b) {
return { min(a.x, b.x), min(a.y, b.y), min(a.z, b.z) };
}
inline Vector3 max(Vector3 a, Vector3 b) {
return { max(a.x, b.x), max(a.y, b.y), max(a.z, b.z) };
}
inline bool operator==(const Vector3 & a, const Vector3 & b) {
return memcmp(&a, &b, sizeof(Vector3));
}
inline void Vector3::set(float x, float y, float z) {
this->x = x; this->y = y; this->z = z;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Color conversion functions.
@ -54,16 +143,18 @@ static const float midpoints6[64] = {
static Color16 vector3_to_color16(const Vector3 & v) {
// Truncate.
uint r = ftoi_trunc(clamp(v.x * 31.0f, 0.0f, 31.0f));
uint g = ftoi_trunc(clamp(v.y * 63.0f, 0.0f, 63.0f));
uint b = ftoi_trunc(clamp(v.z * 31.0f, 0.0f, 31.0f));
uint r = uint(clamp(v.x * 31.0f, 0.0f, 31.0f));
uint g = uint(clamp(v.y * 63.0f, 0.0f, 63.0f));
uint b = uint(clamp(v.z * 31.0f, 0.0f, 31.0f));
// Round exactly according to 565 bit-expansion.
r += (v.x > midpoints5[r]);
g += (v.y > midpoints6[g]);
b += (v.z > midpoints5[b]);
return Color16((r << 11) | (g << 5) | b);
Color16 c;
c.u = (r << 11) | (g << 5) | b;
return c;
}
@ -87,12 +178,12 @@ inline Vector3 color_to_vector3(Color32 c)
return Vector3(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f);
}
inline Color32 vector3_to_color(Vector3 v)
inline Color32 vector3_to_color32(Vector3 v)
{
Color32 color;
color.r = U8(ftoi_round(saturate(v.x) * 255));
color.g = U8(ftoi_round(saturate(v.y) * 255));
color.b = U8(ftoi_round(saturate(v.z) * 255));
color.r = uint8(saturate(v.x) * 255 + 0.5f);
color.g = uint8(saturate(v.y) * 255 + 0.5f);
color.b = uint8(saturate(v.z) * 255 + 0.5f);
color.a = 255;
return color;
}
@ -101,15 +192,6 @@ inline Color32 vector3_to_color(Vector3 v)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Input block processing.
/*inline static void color_block_to_vector_block(const ColorBlock & rgba, Vector3 block[16])
{
for (int i = 0; i < 16; i++)
{
const Color32 c = rgba.color(i);
block[i] = Vector3(c.r, c.g, c.b);
}
}*/
// Find first valid color.
/*static bool find_valid_color_rgb(const Vector3 * colors, const float * weights, int count, Vector3 * valid_color)
{
@ -201,6 +283,107 @@ static int reduce_colors(const uint8 * input_colors, Vector3 * colors, float * w
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Palette evaluation.
#define DECODER 0
inline void evaluate_palette4(Color16 c0, Color16 c1, Color32 palette[4], bool d3d9_bias) {
#if DECODER == 0 || DECODER == 1
palette[2].r = (2 * palette[0].r + palette[1].r + d3d9_bias) / 3;
palette[2].g = (2 * palette[0].g + palette[1].g + d3d9_bias) / 3;
palette[2].b = (2 * palette[0].b + palette[1].b + d3d9_bias) / 3;
palette[3].r = (2 * palette[1].r + palette[0].r + d3d9_bias) / 3;
palette[3].g = (2 * palette[1].g + palette[0].g + d3d9_bias) / 3;
palette[3].b = (2 * palette[1].b + palette[0].b + d3d9_bias) / 3;
#else
int dg = palette[1].g - palette[0].g;
palette[2].r = ((2 * c0.r + c1.r) * 22) / 8;
palette[2].g = (256 * palette[0].g + dg * 80 + dg / 4 + 128) / 256;
palette[2].b = ((2 * c0.b + c1.b) * 22) / 8;
palette[3].r = ((2 * c1.r + c0.r) * 22) / 8;
palette[3].g = (256 * palette[1].g - dg * 80 - dg / 4 + 128) / 256;
palette[3].b = ((2 * c1.b + c0.b) * 22) / 8;
#endif
}
inline void evaluate_palette3(Color16 c0, Color16 c1, Color32 palette[4]) {
#if DECODER == 0 || DECODER == 1
palette[2].r = (palette[0].r + palette[1].r) / 2;
palette[2].g = (palette[0].g + palette[1].g) / 2;
palette[2].b = (palette[0].b + palette[1].b) / 2;
#else
int dg = palette[1].g - palette[0].g;
palette[2].r = ((c0.r + c1.r) * 33) / 8;
palette[2].g = (256 * palette[0].g + dg * 128 + dg / 4 + 128) / 256;
palette[2].b = ((c0.b + c1.b) * 33) / 8;
#endif
palette[3].r = 0;
palette[3].g = 0;
palette[3].b = 0;
}
static void evaluate_palette(Color16 c0, Color16 c1, Color32 palette[4], bool d3d9_bias) {
palette[0] = bitexpand_color16_to_color32(c0);
palette[1] = bitexpand_color16_to_color32(c1);
if (c0.u > c1.u) {
evaluate_palette4(c0, c1, palette, d3d9_bias);
}
else {
evaluate_palette3(c0, c1, palette);
}
}
static void evaluate_palette_nv(Color16 c0, Color16 c1, Color32 palette[4]) {
palette[0].r = (3 * c0.r * 22) / 8;
palette[0].g = (c0.g << 2) | (c0.g >> 4);
palette[0].b = (3 * c0.b * 22) / 8;
palette[1].a = 255;
palette[1].r = (3 * c1.r * 22) / 8;
palette[1].g = (c1.g << 2) | (c1.g >> 4);
palette[1].b = (3 * c1.b * 22) / 8;
palette[1].a = 255;
int gdiff = palette[1].g - palette[0].g;
if (c0.u > c1.u) {
palette[2].r = ((2 * c0.r + c1.r) * 22) / 8;
palette[2].g = (256 * palette[0].g + gdiff / 4 + 128 + gdiff * 80) / 256;
palette[2].b = ((2 * c0.b + c1.b) * 22) / 8;
palette[2].a = 0xFF;
palette[3].r = ((2 * c1.r + c0.r) * 22) / 8;
palette[3].g = (256 * palette[1].g - gdiff / 4 + 128 - gdiff * 80) / 256;
palette[3].b = ((2 * c1.b + c0.b) * 22) / 8;
palette[3].a = 0xFF;
}
else {
palette[2].r = ((c0.r + c1.r) * 33) / 8;
palette[2].g = (256 * palette[0].g + gdiff / 4 + 128 + gdiff * 128) / 256;
palette[2].b = ((c0.b + c1.b) * 33) / 8;
palette[2].a = 0xFF;
palette[3].u = 0;
}
}
static void evaluate_palette(Color16 c0, Color16 c1, Color32 palette[4]) {
#if DECODER == 0
evaluate_palette(c0, c1, palette, false);
#elif DECODER == 1
evaluate_palette(c0, c1, palette, true);
#elif DECODER == 2
evaluate_palette_nv(c0, c1, palette);
#endif
}
static void evaluate_palette(Color16 c0, Color16 c1, Vector3 palette[4]) {
Color32 palette32[4];
evaluate_palette(c0, c1, palette32);
for (int i = 0; i < 4; i++) {
palette[i] = color_to_vector3(palette32[i]);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Error evaluation.
@ -245,8 +428,8 @@ static int evaluate_mse(const Color32 palette[4], const Color32 & c) {
// Returns MSE error in [0-255] range.
static int evaluate_mse(const BlockDXT1 * output, Color32 color, int index) {
Color32 palette[4];
//output->evaluatePalette(palette, /*d3d9=*/false);
output->evaluatePaletteNV5x(palette);
evaluate_palette(output->col0, output->col1, palette);
//evaluate_palette_nv(output->col0, output->col1, palette);
return evaluate_mse(palette[index], color);
}
@ -296,8 +479,8 @@ static float evaluate_mse(const BlockDXT1 * output, const Vector3 colors[16]) {
static float evaluate_mse(const Vector4 input_colors[16], const float input_weights[16], const Vector3 & color_weights, const BlockDXT1 * output) {
Color32 palette[4];
output->evaluatePalette(palette, /*d3d9=*/false);
//output->evaluatePaletteNV5x(palette);
evaluate_palette(output->col0, output->col1, palette);
//evaluate_palette_nv5x(output->col0, output->col1, palette);
// convert palette to float.
/*Vector3 vector_palette[4];
@ -317,105 +500,30 @@ static float evaluate_mse(const Vector4 input_colors[16], const float input_weig
float nv::evaluate_dxt1_error(const uint8 rgba_block[16*4], const BlockDXT1 * block, int decoder) {
Color32 palette[4];
if (decoder == 2) {
block->evaluatePaletteNV5x(palette);
evaluate_palette_nv(block->col0, block->col1, palette);
}
else {
block->evaluatePalette(palette, /*d3d9=*/decoder);
evaluate_palette(block->col0, block->col1, palette, /*d3d9=*/decoder);
}
// evaluate error for each index.
float error = 0.0f;
for (int i = 0; i < 16; i++) {
int index = (block->indices >> (2 * i)) & 3;
Color32 c(rgba_block[4 * i + 0], rgba_block[4 * i + 1], rgba_block[4 * i + 2]);
Color32 c;
c.r = rgba_block[4 * i + 0];
c.g = rgba_block[4 * i + 1];
c.b = rgba_block[4 * i + 2];
c.a = 255;
error += evaluate_mse(palette[index], c);
}
return error;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Palette evaluation.
#define DECODER 0
inline void evaluate_palette4(Color16 c0, Color16 c1, Color32 palette[4]) {
#if DECODER == 0
palette[2].r = (2 * palette[0].r + palette[1].r) / 3;
palette[2].g = (2 * palette[0].g + palette[1].g) / 3;
palette[2].b = (2 * palette[0].b + palette[1].b) / 3;
palette[3].r = (2 * palette[1].r + palette[0].r) / 3;
palette[3].g = (2 * palette[1].g + palette[0].g) / 3;
palette[3].b = (2 * palette[1].b + palette[0].b) / 3;
#elif DECODER == 1
palette[2].r = (2 * palette[0].r + palette[1].r + 1) / 3;
palette[2].g = (2 * palette[0].g + palette[1].g + 1) / 3;
palette[2].b = (2 * palette[0].b + palette[1].b + 1) / 3;
palette[3].r = (2 * palette[1].r + palette[0].r + 1) / 3;
palette[3].g = (2 * palette[1].g + palette[0].g + 1) / 3;
palette[3].b = (2 * palette[1].b + palette[0].b + 1) / 3;
#else
int dg = palette[1].g - palette[0].g;
palette[2].r = ((2 * c0.r + c1.r) * 22) / 8;
palette[2].g = (256 * palette[0].g + dg * 80 + dg / 4 + 128) / 256;
palette[2].b = ((2 * c0.b + c1.b) * 22) / 8;
palette[3].r = ((2 * c1.r + c0.r) * 22) / 8;
palette[3].g = (256 * palette[1].g - dg * 80 - dg / 4 + 128) / 256;
palette[3].b = ((2 * c1.b + c0.b) * 22) / 8;
#endif
}
inline void evaluate_palette3(Color16 c0, Color16 c1, Color32 palette[4]) {
#if DECODER == 0 || DECODER == 1
palette[2].r = (palette[0].r + palette[1].r) / 2;
palette[2].g = (palette[0].g + palette[1].g) / 2;
palette[2].b = (palette[0].b + palette[1].b) / 2;
#else
int dg = palette[1].g - palette[0].g;
palette[2].r = ((c0.r + c1.r) * 33) / 8;
palette[2].g = (256 * palette[0].g + dg * 128 + dg / 4 + 128) / 256;
palette[2].b = ((c0.b + c1.b) * 33) / 8;
#endif
palette[3].r = 0;
palette[3].g = 0;
palette[3].b = 0;
}
static void evaluate_palette(Color16 c0, Color16 c1, Color32 palette[4]) {
palette[0] = bitexpand_color16_to_color32(c0);
palette[1] = bitexpand_color16_to_color32(c1);
if (c0.u > c1.u) {
evaluate_palette4(c0, c1, palette);
}
else {
evaluate_palette3(c0, c1, palette);
}
}
static void evaluate_palette(Color16 c0, Color16 c1, Vector3 palette[4]) {
Color32 palette32[4];
evaluate_palette(c0, c1, palette32);
for (int i = 0; i < 4; i++) {
palette[i] = color_to_vector3(palette32[i]);
}
}
/*static void evaluate_palette3(Color16 c0, Color16 c1, Vector3 palette[4]) {
nvDebugCheck(c0.u > c1.u);
Color32 palette32[4];
evaluate_palette(c0, c1, palette32);
for (int i = 0; i < 4; i++) {
palette[i] = color_to_vector3(palette32[i]);
}
}*/
// Index selection
static uint compute_indices4(const Vector4 input_colors[16], const Vector3 & color_weights, const Vector3 palette[4]) {
@ -678,10 +786,12 @@ inline static void select_diagonal(const Vector3 * colors, int count, Vector3 *
}
center /= count;*/
Vector2 covariance = Vector2(0);
float cov_xz = 0.0f;
float cov_yz = 0.0f;
for (int i = 0; i < count; i++) {
Vector3 t = colors[i] - center;
covariance += t.xy() * t.z;
cov_xz += t.x * t.z;
cov_yz += t.y * t.z;
}
float x0 = c0->x;
@ -689,10 +799,10 @@ inline static void select_diagonal(const Vector3 * colors, int count, Vector3 *
float x1 = c1->x;
float y1 = c1->y;
if (covariance.x < 0) {
if (cov_xz < 0) {
swap(x0, x1);
}
if (covariance.y < 0) {
if (cov_yz < 0) {
swap(y0, y1);
}
@ -702,22 +812,89 @@ inline static void select_diagonal(const Vector3 * colors, int count, Vector3 *
inline static void inset_bbox(Vector3 * restrict c0, Vector3 * restrict c1)
{
Vector3 inset = (*c0 - *c1) / 16.0f - (8.0f / 255.0f) / 16.0f;
Vector3 inset = (*c0 - *c1) / 16.0f - Vector3((8.0f / 255.0f) / 16.0f);
*c0 = saturate(*c0 - inset);
*c1 = saturate(*c1 + inset);
}
// Single color lookup tables from:
// https://github.com/nothings/stb/blob/master/stb_dxt.h
static uint8 match5[256][2];
static uint8 match6[256][2];
static int Mul8Bit(int a, int b)
{
int t = a * b + 128;
return (t + (t >> 8)) >> 8;
}
static inline int Lerp13(int a, int b)
{
#ifdef DXT_USE_ROUNDING_BIAS
// with rounding bias
return a + Mul8Bit(b - a, 0x55);
#else
// without rounding bias
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed.
return (a * 2 + b) / 3;
#endif
}
static void PrepareOptTable(uint8 * table, const uint8 * expand, int size)
{
for (int i = 0; i < 256; i++) {
int bestErr = 256 * 100;
for (int min = 0; min < size; min++) {
for (int max = 0; max < size; max++) {
int mine = expand[min];
int maxe = expand[max];
int err = abs(Lerp13(maxe, mine) - i) * 100;
// DX10 spec says that interpolation must be within 3% of "correct" result,
// add this as error term. (normally we'd expect a random distribution of
// +-1.5% error, but nowhere in the spec does it say that the error has to be
// unbiased - better safe than sorry).
err += abs(max - min) * 3;
if (err < bestErr) {
bestErr = err;
table[i * 2 + 0] = max;
table[i * 2 + 1] = min;
}
}
}
}
}
// @@ Make this explicit.
NV_AT_STARTUP(nv::init_dxt1());
void nv::init_dxt1()
{
// Prepare single color lookup tables.
uint8 expand5[32];
uint8 expand6[64];
for (int i = 0; i < 32; i++) expand5[i] = (i << 3) | (i >> 2);
for (int i = 0; i < 64; i++) expand6[i] = (i << 2) | (i >> 4);
PrepareOptTable(&match5[0][0], expand5, 32);
PrepareOptTable(&match6[0][0], expand6, 64);
}
// Single color compressor, based on:
// https://mollyrocket.com/forums/viewtopic.php?t=392
static void compress_dxt1_single_color_optimal(Color32 c, BlockDXT1 * output)
{
output->col0.r = OMatch5[c.r][0];
output->col0.g = OMatch6[c.g][0];
output->col0.b = OMatch5[c.b][0];
output->col1.r = OMatch5[c.r][1];
output->col1.g = OMatch6[c.g][1];
output->col1.b = OMatch5[c.b][1];
output->col0.r = match5[c.r][0];
output->col0.g = match6[c.g][0];
output->col0.b = match5[c.b][0];
output->col1.r = match5[c.r][1];
output->col1.g = match6[c.g][1];
output->col1.b = match5[c.b][1];
output->indices = 0xaaaaaaaa;
if (output->col0.u < output->col1.u)
@ -728,24 +905,23 @@ static void compress_dxt1_single_color_optimal(Color32 c, BlockDXT1 * output)
}
float nv::compress_dxt1_single_color_optimal(Color32 c, BlockDXT1 * output)
/*float nv::compress_dxt1_single_color_optimal(Color32 c, BlockDXT1 * output)
{
::compress_dxt1_single_color_optimal(c, output);
// Multiply by 16^2, the weight associated to a single color.
// Divide by 255*255 to covert error to [0-1] range.
return (256.0f / (255*255)) * evaluate_mse(output, c, output->indices & 3);
}
}*/
float nv::compress_dxt1_single_color_optimal(const Vector3 & color, BlockDXT1 * output)
/*float nv::compress_dxt1_single_color_optimal(const Vector3 & color, BlockDXT1 * output)
{
return compress_dxt1_single_color_optimal(vector3_to_color(color), output);
}
return compress_dxt1_single_color_optimal(vector3_to_color32(color), output);
}*/
// Compress block using the average color.
float nv::compress_dxt1_single_color(const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output)
float nv::compress_dxt1_single_color(const nv::Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output)
{
// Compute block average.
Vector3 color_sum(0);
@ -757,7 +933,7 @@ float nv::compress_dxt1_single_color(const Vector3 * colors, const float * weigh
}
// Compress optimally.
::compress_dxt1_single_color_optimal(vector3_to_color(color_sum / weight_sum), output);
::compress_dxt1_single_color_optimal(vector3_to_color32(color_sum / weight_sum), output);
// Decompress block color.
Color32 palette[4];
@ -787,12 +963,12 @@ float nv::compress_dxt1_bounding_box_exhaustive(const Vector4 input_colors[16],
}
// Convert to 5:6:5
int min_r = ftoi_floor(31 * min_color.x);
int min_g = ftoi_floor(63 * min_color.y);
int min_b = ftoi_floor(31 * min_color.z);
int max_r = ftoi_ceil(31 * max_color.x);
int max_g = ftoi_ceil(63 * max_color.y);
int max_b = ftoi_ceil(31 * max_color.z);
int min_r = int(31 * min_color.x);
int min_g = int(63 * min_color.y);
int min_b = int(31 * min_color.z);
int max_r = int(31 * max_color.x + 1);
int max_g = int(63 * max_color.y + 1);
int max_b = int(31 * max_color.z + 1);
// Expand the box.
int range_r = max_r - min_r;
@ -818,7 +994,7 @@ float nv::compress_dxt1_bounding_box_exhaustive(const Vector4 input_colors[16],
// @@ Convert to fixed point before building box?
Color32 colors32[16];
for (int i = 0; i < count; i++) {
colors32[i] = toColor32(Vector4(colors[i], 1));
colors32[i] = vector3_to_color32(colors[i]);
}
float best_error = FLT_MAX;
@ -843,7 +1019,7 @@ float nv::compress_dxt1_bounding_box_exhaustive(const Vector4 input_colors[16],
if (c0.u > c1.u) {
// Evaluate error in 4 color mode.
evaluate_palette4(c0, c1, palette);
evaluate_palette4(c0, c1, palette, false);
}
else {
if (three_color_mode) {
@ -942,19 +1118,6 @@ void nv::compress_dxt1_cluster_fit(const Vector4 input_colors[16], const Vector3
return mask;
}*/
inline uint32 mod3(uint32 a) {
a = (a >> 16) + (a & 0xFFFF); /* sum base 2**16 digits a <= 0x1FFFE */
a = (a >> 8) + (a & 0xFF); /* sum base 2**8 digits a <= 0x2FD */
a = (a >> 4) + (a & 0xF); /* sum base 2**4 digits a <= 0x3C; worst case 0x3B */
a = (a >> 2) + (a & 0x3); /* sum base 2**2 digits a <= 0x1D; worst case 0x1B */
a = (a >> 2) + (a & 0x3); /* sum base 2**2 digits a <= 0x9; worst case 0x7 */
a = (a >> 2) + (a & 0x3); /* sum base 2**2 digits a <= 0x4 */
if (a > 2) a = a - 3;
return a;
}
float nv::compress_dxt1(const Vector4 input_colors[16], const float input_weights[16], const Vector3 & color_weights, bool three_color_mode, bool hq, BlockDXT1 * output)
{
Vector3 colors[16];
@ -1004,7 +1167,8 @@ float nv::compress_dxt1(const Vector4 input_colors[16], const float input_weight
// Cluster fit cannot handle single color blocks, so encode them optimally if we haven't encoded them already.
if (error == FLT_MAX && count == 1) {
error = compress_dxt1_single_color_optimal(colors[0], output);
::compress_dxt1_single_color_optimal(vector3_to_color32(colors[0]), output);
return evaluate_mse(input_colors, input_weights, color_weights, output);
}
if (count > 1) {
@ -1107,6 +1271,11 @@ float nv::compress_dxt1(const Vector4 input_colors[16], const float input_weight
refined.col1.b += delta[2];
}
if (!three_color_mode) {
if (refined.col0.u == refined.col1.u) refined.col1.g += 1;
if (refined.col0.u < refined.col1.u) swap(refined.col0.u, refined.col1.u);
}
Vector3 palette[4];
evaluate_palette(output->col0, output->col1, palette);
@ -1159,7 +1328,7 @@ float nv::compress_dxt1_fast(const Vector4 input_colors[16], const float input_w
Vector3 c0, c1;
fit_colors_bbox(colors, count, &c0, &c1);
if (c0 == c1) {
::compress_dxt1_single_color_optimal(vector3_to_color(c0), output);
::compress_dxt1_single_color_optimal(vector3_to_color32(c0), output);
return evaluate_mse(input_colors, input_weights, color_weights, output);
}
inset_bbox(&c0, &c1);
@ -1208,7 +1377,7 @@ void nv::compress_dxt1_fast2(const uint8 input_colors[16*4], BlockDXT1 * output)
//select_diagonal(colors, count, &c0, &c1);
fit_colors_bbox(vec_colors, 16, &c0, &c1);
if (c0 == c1) {
::compress_dxt1_single_color_optimal(vector3_to_color(c0), output);
::compress_dxt1_single_color_optimal(vector3_to_color32(c0), output);
return;
}
inset_bbox(&c0, &c1);
@ -1222,11 +1391,11 @@ void nv::compress_dxt1_fast2(const uint8 input_colors[16*4], BlockDXT1 * output)
}
static int Mul8Bit(int a, int b)
/*static int Mul8Bit(int a, int b)
{
int t = a * b + 128;
return (t + (t >> 8)) >> 8;
}
}*/
static bool compute_least_squares_endpoints(const uint8 *block, uint32 mask, Vector3 *pmax, Vector3 *pmin)
{
@ -1487,7 +1656,10 @@ void nv::compress_dxt1_fast_geld(const uint8 input_colors[16 * 4], BlockDXT1 * b
Vector3 c0, c1;
if (!compute_least_squares_endpoints(input_colors, selectors, &c0, &c1)) {
// @@ Single color compressor.
Color32 c(lr, lg, lb);
Color32 c;
c.r = lr;
c.g = lg;
c.b = lb;
::compress_dxt1_single_color_optimal(c, block);
}
else {
@ -1512,7 +1684,7 @@ void nv::compress_dxt1_fast_geld(const uint8 input_colors[16 * 4], BlockDXT1 * b
//select_diagonal(colors, count, &c0, &c1);
fit_colors_bbox(vec_colors, 16, &c0, &c1);
if (c0 == c1) {
::compress_dxt1_single_color_optimal(vector3_to_color(c0), output);
::compress_dxt1_single_color_optimal(vector3_to_color32(c0), output);
return;
}
inset_bbox(&c0, &c1);

@ -1,15 +1,13 @@
namespace nv {
class Color32;
struct BlockDXT1;
class Vector3;
class Vector4;
// All these functions return MSE.
void init_dxt1();
float compress_dxt1_single_color_optimal(Color32 c, BlockDXT1 * output);
float compress_dxt1_single_color_optimal(const Vector3 & color, BlockDXT1 * output);
// All these functions return MSE.
float compress_dxt1_single_color(const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output);
//float compress_dxt1_least_squares_fit(const Vector4 input_colors[16], const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output);

Loading…
Cancel
Save