mirror of
https://github.com/yuzu-emu/FasTC.git
synced 2025-01-08 05:45:27 +00:00
Refactor shape and mode selection
We suffered another performance hit. This time it comes from the fact that we're copying around a lot of data based on what partition we're choosing. We can get rid of this a tad by only copying the data that we need once and then using getters/setters that selectively pull from an array based on our shape index.
This commit is contained in:
parent
26e816b3db
commit
cf937f2ad3
|
@ -86,14 +86,14 @@ namespace BPTCC {
|
||||||
// The enum is specialized to be power-of-two values so that an EBlockMode
|
// The enum is specialized to be power-of-two values so that an EBlockMode
|
||||||
// variable can be used as a bit mask.
|
// variable can be used as a bit mask.
|
||||||
enum EBlockMode {
|
enum EBlockMode {
|
||||||
eBlockMode_Zero = 0,
|
eBlockMode_Zero = 1,
|
||||||
eBlockMode_One = 1,
|
eBlockMode_One = 2,
|
||||||
eBlockMode_Two = 2,
|
eBlockMode_Two = 4,
|
||||||
eBlockMode_Three = 4,
|
eBlockMode_Three = 8,
|
||||||
eBlockMode_Four = 8,
|
eBlockMode_Four = 16,
|
||||||
eBlockMode_Five = 16,
|
eBlockMode_Five = 32,
|
||||||
eBlockMode_Six = 32,
|
eBlockMode_Six = 64,
|
||||||
eBlockMode_Seven = 64
|
eBlockMode_Seven = 128
|
||||||
};
|
};
|
||||||
|
|
||||||
// A shape selection can influence the results of the compressor by choosing
|
// A shape selection can influence the results of the compressor by choosing
|
||||||
|
@ -110,13 +110,18 @@ namespace BPTCC {
|
||||||
// This is the additional mask to prevent modes once shape selection
|
// This is the additional mask to prevent modes once shape selection
|
||||||
// is done. This value is &-ed with m_BlockModes from CompressionSettings
|
// is done. This value is &-ed with m_BlockModes from CompressionSettings
|
||||||
// to determine what the final considered blocks are.
|
// to determine what the final considered blocks are.
|
||||||
EBlockMode m_AdditionalModes;
|
uint32 m_SelectedModes;
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
ShapeSelection()
|
||||||
|
: m_SelectedModes(static_cast<EBlockMode>(0xFF))
|
||||||
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
// A shape selection function is one that selects a BPTC shape from a given
|
// A shape selection function is one that selects a BPTC shape from a given
|
||||||
// block position and pixel array.
|
// block position and pixel array.
|
||||||
typedef ShapeSelection
|
typedef ShapeSelection
|
||||||
(*ShapeSelectionFn)(uint32 x, uint32 y, uint32 pixels[16]);
|
(*ShapeSelectionFn)(uint32 x, uint32 y, const uint32 pixels[16]);
|
||||||
|
|
||||||
// Compression parameters used to control the BPTC compressor. Each of the
|
// Compression parameters used to control the BPTC compressor. Each of the
|
||||||
// values has a default, so this is not strictly required to perform
|
// values has a default, so this is not strictly required to perform
|
||||||
|
@ -135,11 +140,11 @@ namespace BPTCC {
|
||||||
// This variable is a bit mask of EBlockMode values and by default contains
|
// This variable is a bit mask of EBlockMode values and by default contains
|
||||||
// every mode. This setting can be used to further restrict the search space
|
// every mode. This setting can be used to further restrict the search space
|
||||||
// and increase compression times.
|
// and increase compression times.
|
||||||
EBlockMode m_BlockModes;
|
uint32 m_BlockModes;
|
||||||
|
|
||||||
CompressionSettings()
|
CompressionSettings()
|
||||||
: m_ShapeSelectionFn(NULL)
|
: m_ShapeSelectionFn(NULL)
|
||||||
, m_BlockModes(static_cast<EBlockMode>((1 << 7) - 1))
|
, m_BlockModes(static_cast<EBlockMode>(0xFF))
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,8 @@ class CompressionMode {
|
||||||
// This initializes the compression variables used in order to compress a list
|
// This initializes the compression variables used in order to compress a list
|
||||||
// of clusters. We can increase the speed a tad by specifying whether or not
|
// of clusters. We can increase the speed a tad by specifying whether or not
|
||||||
// the block is opaque or not.
|
// the block is opaque or not.
|
||||||
explicit CompressionMode(int mode, bool opaque = true)
|
explicit CompressionMode(int mode)
|
||||||
: m_IsOpaque(opaque)
|
: m_IsOpaque(mode < 4)
|
||||||
, m_Attributes(&(kModeAttributes[mode]))
|
, m_Attributes(&(kModeAttributes[mode]))
|
||||||
, m_RotateMode(0)
|
, m_RotateMode(0)
|
||||||
, m_IndexMode(0)
|
, m_IndexMode(0)
|
||||||
|
|
|
@ -103,8 +103,7 @@ using FasTC::BitStreamReadOnly;
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
// #define USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
|
|
||||||
enum EBlockStats {
|
enum EBlockStats {
|
||||||
eBlockStat_Path,
|
eBlockStat_Path,
|
||||||
|
@ -1250,7 +1249,6 @@ void CompressionMode::Pack(Params ¶ms, BitStream &stream) const {
|
||||||
const int nPartitionBits = GetNumberOfPartitionBits();
|
const int nPartitionBits = GetNumberOfPartitionBits();
|
||||||
const int nSubsets = GetNumberOfSubsets();
|
const int nSubsets = GetNumberOfSubsets();
|
||||||
|
|
||||||
|
|
||||||
// Mode #
|
// Mode #
|
||||||
stream.WriteBits(1 << kModeNumber, kModeNumber + 1);
|
stream.WriteBits(1 << kModeNumber, kModeNumber + 1);
|
||||||
|
|
||||||
|
@ -1791,114 +1789,15 @@ void CompressAtomic(FasTC::CompressionJobList &cjl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static double CompressTwoClusters(
|
|
||||||
int shapeIdx,
|
|
||||||
const RGBACluster *clusters,
|
|
||||||
uint8 *outBuf,
|
|
||||||
bool opaque,
|
|
||||||
double *errors = NULL,
|
|
||||||
int *modeChosen = NULL
|
|
||||||
) {
|
|
||||||
|
|
||||||
uint8 tempBuf1[16];
|
|
||||||
BitStream tmpStream1(tempBuf1, 128, 0);
|
|
||||||
double bestError =
|
|
||||||
CompressionMode(1, opaque).Compress(tmpStream1, shapeIdx, clusters);
|
|
||||||
|
|
||||||
if(errors) errors[1] = bestError;
|
|
||||||
if(modeChosen) *modeChosen = 1;
|
|
||||||
|
|
||||||
memcpy(outBuf, tempBuf1, 16);
|
|
||||||
if(bestError == 0.0) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 tempBuf3[16];
|
|
||||||
BitStream tmpStream3(tempBuf3, 128, 0);
|
|
||||||
|
|
||||||
double error =
|
|
||||||
CompressionMode(3, opaque).Compress(tmpStream3, shapeIdx, clusters);
|
|
||||||
|
|
||||||
if(errors) errors[3] = error;
|
|
||||||
if(error < bestError) {
|
|
||||||
if(modeChosen) *modeChosen = 3;
|
|
||||||
bestError = error;
|
|
||||||
memcpy(outBuf, tempBuf3, 16);
|
|
||||||
if(bestError == 0.0) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mode 3 offers more precision for RGB data. Mode 7 is really only if we
|
|
||||||
// have alpha.
|
|
||||||
if(!opaque) {
|
|
||||||
uint8 tempBuf7[16];
|
|
||||||
BitStream tmpStream7(tempBuf7, 128, 0);
|
|
||||||
|
|
||||||
error =
|
|
||||||
CompressionMode(7, opaque).Compress(tmpStream7, shapeIdx, clusters);
|
|
||||||
|
|
||||||
if(errors) errors[7] = error;
|
|
||||||
if(error < bestError) {
|
|
||||||
if(modeChosen) *modeChosen = 7;
|
|
||||||
memcpy(outBuf, tempBuf7, 16);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestError;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double CompressThreeClusters(
|
|
||||||
int shapeIdx,
|
|
||||||
const RGBACluster *clusters,
|
|
||||||
uint8 *outBuf,
|
|
||||||
bool opaque,
|
|
||||||
double *errors = NULL,
|
|
||||||
int *modeChosen = NULL
|
|
||||||
) {
|
|
||||||
uint8 tempBuf0[16];
|
|
||||||
BitStream tmpStream0(tempBuf0, 128, 0);
|
|
||||||
|
|
||||||
uint8 tempBuf2[16];
|
|
||||||
BitStream tmpStream2(tempBuf2, 128, 0);
|
|
||||||
|
|
||||||
double error, bestError = DBL_MAX;;
|
|
||||||
if(shapeIdx < 16) {
|
|
||||||
bestError =
|
|
||||||
CompressionMode(0, opaque).Compress(tmpStream0, shapeIdx, clusters);
|
|
||||||
|
|
||||||
if(errors) errors[0] = bestError;
|
|
||||||
} else {
|
|
||||||
if(errors) errors[0] = -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(modeChosen) *modeChosen = 0;
|
|
||||||
memcpy(outBuf, tempBuf0, 16);
|
|
||||||
if(bestError == 0.0) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
error =
|
|
||||||
CompressionMode(2, opaque).Compress(tmpStream2, shapeIdx, clusters);
|
|
||||||
|
|
||||||
if(errors) errors[2] = error;
|
|
||||||
if(error < bestError) {
|
|
||||||
if(modeChosen) *modeChosen = 2;
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestError;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PopulateTwoClustersForShape(
|
static void PopulateTwoClustersForShape(
|
||||||
const RGBACluster &points, int shapeIdx, RGBACluster *clusters
|
const uint32 points[16], int shapeIdx, RGBACluster *clusters
|
||||||
) {
|
) {
|
||||||
|
clusters[0].Reset();
|
||||||
|
clusters[1].Reset();
|
||||||
const uint16 shape = kShapeMask2[shapeIdx];
|
const uint16 shape = kShapeMask2[shapeIdx];
|
||||||
for(uint32 pt = 0; pt < kMaxNumDataPoints; pt++) {
|
for(uint32 pt = 0; pt < kMaxNumDataPoints; pt++) {
|
||||||
|
|
||||||
const RGBAVector &p = points.GetPoint(pt);
|
const RGBAVector p = RGBAVector(pt, points[pt]);
|
||||||
|
|
||||||
if((1 << pt) & shape)
|
if((1 << pt) & shape)
|
||||||
clusters[1].AddPoint(p);
|
clusters[1].AddPoint(p);
|
||||||
|
@ -1916,11 +1815,14 @@ static void PopulateTwoClustersForShape(
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PopulateThreeClustersForShape(
|
static void PopulateThreeClustersForShape(
|
||||||
const RGBACluster &points, int shapeIdx, RGBACluster *clusters
|
const uint32 points[16], int shapeIdx, RGBACluster *clusters
|
||||||
) {
|
) {
|
||||||
|
clusters[0].Reset();
|
||||||
|
clusters[1].Reset();
|
||||||
|
clusters[2].Reset();
|
||||||
for(uint32 pt = 0; pt < kMaxNumDataPoints; pt++) {
|
for(uint32 pt = 0; pt < kMaxNumDataPoints; pt++) {
|
||||||
|
|
||||||
const RGBAVector &p = points.GetPoint(pt);
|
const RGBAVector p = RGBAVector(pt, points[pt]);
|
||||||
|
|
||||||
if((1 << pt) & kShapeMask3[shapeIdx][0]) {
|
if((1 << pt) & kShapeMask3[shapeIdx][0]) {
|
||||||
if((1 << pt) & kShapeMask3[shapeIdx][1]) {
|
if((1 << pt) & kShapeMask3[shapeIdx][1]) {
|
||||||
|
@ -1955,18 +1857,8 @@ static double EstimateTwoClusterError(RGBACluster &c) {
|
||||||
const float *w = BPTCC::GetErrorMetric();
|
const float *w = BPTCC::GetErrorMetric();
|
||||||
|
|
||||||
double error = 0.0001;
|
double error = 0.0001;
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
double eigOne = c.GetPrincipalEigenvalue();
|
|
||||||
double eigTwo = c.GetSecondEigenvalue();
|
|
||||||
if(eigOne != 0.0) {
|
|
||||||
error += eigTwo / eigOne;
|
|
||||||
} else {
|
|
||||||
error += 1.0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
error += c.QuantizedError(Min, Max, 8,
|
error += c.QuantizedError(Min, Max, 8,
|
||||||
0xFFFFFFFF, RGBAVector(w[0], w[1], w[2], w[3]));
|
0xFFFFFFFF, RGBAVector(w[0], w[1], w[2], w[3]));
|
||||||
#endif
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1981,22 +1873,159 @@ static double EstimateThreeClusterError(RGBACluster &c) {
|
||||||
const float *w = BPTCC::GetErrorMetric();
|
const float *w = BPTCC::GetErrorMetric();
|
||||||
|
|
||||||
double error = 0.0001;
|
double error = 0.0001;
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
double eigOne = c.GetPrincipalEigenvalue();
|
|
||||||
double eigTwo = c.GetSecondEigenvalue();
|
|
||||||
|
|
||||||
if(eigOne != 0.0) {
|
|
||||||
error += eigTwo / eigOne;
|
|
||||||
} else {
|
|
||||||
error += 1.0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
error += c.QuantizedError(Min, Max, 4,
|
error += c.QuantizedError(Min, Max, 4,
|
||||||
0xFFFFFFFF, RGBAVector(w[0], w[1], w[2], w[3]));
|
0xFFFFFFFF, RGBAVector(w[0], w[1], w[2], w[3]));
|
||||||
#endif
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32 kTwoPartitionModes =
|
||||||
|
static_cast<uint32>(eBlockMode_One) |
|
||||||
|
static_cast<uint32>(eBlockMode_Three) |
|
||||||
|
static_cast<uint32>(eBlockMode_Seven);
|
||||||
|
static uint32 kThreePartitionModes =
|
||||||
|
static_cast<uint32>(eBlockMode_Zero) |
|
||||||
|
static_cast<uint32>(eBlockMode_Two);
|
||||||
|
static uint32 kAlphaModes =
|
||||||
|
static_cast<uint32>(eBlockMode_Four) |
|
||||||
|
static_cast<uint32>(eBlockMode_Five) |
|
||||||
|
static_cast<uint32>(eBlockMode_Six) |
|
||||||
|
static_cast<uint32>(eBlockMode_Seven);
|
||||||
|
|
||||||
|
static ShapeSelection BoxSelection(uint32 x, uint32 y,
|
||||||
|
const uint32 pixels[16]) {
|
||||||
|
|
||||||
|
ShapeSelection result;
|
||||||
|
|
||||||
|
bool opaque = true;
|
||||||
|
for(uint32 i = 0; i < 16; i++) {
|
||||||
|
uint32 a = (pixels[i] >> 24) & 0xFF;
|
||||||
|
opaque = opaque && (a >= 250); // For all intents and purposes...
|
||||||
|
}
|
||||||
|
|
||||||
|
// First we must figure out which shape to use. To do this, simply
|
||||||
|
// see which shape has the smallest sum of minimum bounding spheres.
|
||||||
|
double bestError[2] = { std::numeric_limits<double>::max(),
|
||||||
|
std::numeric_limits<double>::max() };
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < kNumShapes2; i++) {
|
||||||
|
RGBACluster clusters[2];
|
||||||
|
PopulateTwoClustersForShape(pixels, i, clusters);
|
||||||
|
|
||||||
|
double err = 0.0;
|
||||||
|
for(int ci = 0; ci < 2; ci++) {
|
||||||
|
err += EstimateTwoClusterError(clusters[ci]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err < bestError[0]) {
|
||||||
|
bestError[0] = err;
|
||||||
|
result.m_TwoShapeIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's small, we'll take it!
|
||||||
|
if(err < 1e-9) {
|
||||||
|
result.m_SelectedModes = kTwoPartitionModes;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are not 3 subset blocks that support alpha, so only check these
|
||||||
|
// if the entire block is opaque.
|
||||||
|
if(opaque) {
|
||||||
|
for(unsigned int i = 0; i < kNumShapes3; i++) {
|
||||||
|
|
||||||
|
RGBACluster clusters[3];
|
||||||
|
PopulateThreeClustersForShape(pixels, i, clusters);
|
||||||
|
|
||||||
|
double err = 0.0;
|
||||||
|
for(int ci = 0; ci < 3; ci++) {
|
||||||
|
err += EstimateThreeClusterError(clusters[ci]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err < bestError[1]) {
|
||||||
|
bestError[1] = err;
|
||||||
|
result.m_ThreeShapeIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's small, we'll take it!
|
||||||
|
if(err < 1e-9) {
|
||||||
|
result.m_SelectedModes = kThreePartitionModes;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's opaque, we get more value out of mode 6 than modes
|
||||||
|
// 4 and 5, so just ignore those.
|
||||||
|
result.m_SelectedModes &=
|
||||||
|
~(static_cast<uint32>(eBlockMode_Four) |
|
||||||
|
static_cast<uint32>(eBlockMode_Five));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Only some modes support alpha
|
||||||
|
result.m_SelectedModes &= kAlphaModes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CompressClusters(ShapeSelection selection, const uint32 pixels[16],
|
||||||
|
uint8 *outBuf, double *errors, int *modeChosen) {
|
||||||
|
RGBACluster clusters[3];
|
||||||
|
uint8 tmpBuf[16];
|
||||||
|
double bestError = std::numeric_limits<double>::max();
|
||||||
|
uint32 modes[8] = {0, 2, 1, 3, 7, 4, 5, 6};
|
||||||
|
|
||||||
|
bool populatedThree = false;
|
||||||
|
bool populatedTwo = false;
|
||||||
|
bool populatedOne = false;
|
||||||
|
|
||||||
|
// Block mode zero only has four bits for the partition index,
|
||||||
|
// so if the chosen three-partition shape is not within this range,
|
||||||
|
// then we shouldn't consider using this block mode...
|
||||||
|
if(selection.m_ThreeShapeIndex >= 16) {
|
||||||
|
selection.m_SelectedModes &= ~(static_cast<uint32>(eBlockMode_Zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32 modeIdx = 0; modeIdx < 8; modeIdx++) {
|
||||||
|
|
||||||
|
uint32 mode = modes[modeIdx];
|
||||||
|
if((selection.m_SelectedModes & (1 << mode)) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 shape = 0;
|
||||||
|
if(modeIdx < 2) {
|
||||||
|
shape = selection.m_ThreeShapeIndex;
|
||||||
|
if(!populatedThree) {
|
||||||
|
PopulateThreeClustersForShape(pixels, shape, clusters);
|
||||||
|
populatedThree = true;
|
||||||
|
}
|
||||||
|
} else if(modeIdx < 5) {
|
||||||
|
shape = selection.m_TwoShapeIndex;
|
||||||
|
if(!populatedTwo) {
|
||||||
|
PopulateTwoClustersForShape(pixels, shape, clusters);
|
||||||
|
populatedTwo = true;
|
||||||
|
}
|
||||||
|
} else if(!populatedOne) {
|
||||||
|
clusters[0].Reset();
|
||||||
|
for(uint32 i = 0; i < 16; i++) {
|
||||||
|
clusters[0].AddPoint(RGBAVector(i, pixels[i]));
|
||||||
|
}
|
||||||
|
populatedOne = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitStream tmpStream(tmpBuf, 128, 0);
|
||||||
|
double error = CompressionMode(mode).Compress(tmpStream, shape, clusters);
|
||||||
|
|
||||||
|
if(errors) errors[mode] = error;
|
||||||
|
if(error < bestError) {
|
||||||
|
memcpy(outBuf, tmpBuf, sizeof(tmpBuf));
|
||||||
|
bestError = error;
|
||||||
|
if(modeChosen)
|
||||||
|
*modeChosen = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void CompressBC7Block(const uint32 block[16], uint8 *outBuf,
|
static void CompressBC7Block(const uint32 block[16], uint8 *outBuf,
|
||||||
const CompressionSettings settings) {
|
const CompressionSettings settings) {
|
||||||
// All a single color?
|
// All a single color?
|
||||||
|
@ -2007,15 +2036,11 @@ static void CompressBC7Block(const uint32 block[16], uint8 *outBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
RGBACluster blockCluster;
|
RGBACluster blockCluster;
|
||||||
bool opaque = true;
|
|
||||||
bool transparent = true;
|
bool transparent = true;
|
||||||
|
|
||||||
for(uint32 i = 0; i < kMaxNumDataPoints; i++) {
|
for(uint32 i = 0; i < kMaxNumDataPoints; i++) {
|
||||||
RGBAVector p = RGBAVector(i, block[i]);
|
RGBAVector p = RGBAVector(i, block[i]);
|
||||||
blockCluster.AddPoint(p);
|
blockCluster.AddPoint(p);
|
||||||
if(fabs(p.A() - 255.0f) > 1e-10)
|
|
||||||
opaque = false;
|
|
||||||
|
|
||||||
if(p.A() > 0.0f)
|
if(p.A() > 0.0f)
|
||||||
transparent = false;
|
transparent = false;
|
||||||
}
|
}
|
||||||
|
@ -2027,122 +2052,16 @@ static void CompressBC7Block(const uint32 block[16], uint8 *outBuf,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we must figure out which shape to use. To do this, simply
|
ShapeSelectionFn selectionFn = BoxSelection;
|
||||||
// see which shape has the smallest sum of minimum bounding spheres.
|
if(settings.m_ShapeSelectionFn != NULL) {
|
||||||
double bestError[2] = { DBL_MAX, DBL_MAX };
|
selectionFn = settings.m_ShapeSelectionFn;
|
||||||
int bestShapeIdx[2] = { -1, -1 };
|
|
||||||
RGBACluster bestClusters[2][3];
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < kNumShapes2; i++) {
|
|
||||||
RGBACluster clusters[2];
|
|
||||||
PopulateTwoClustersForShape(blockCluster, i, clusters);
|
|
||||||
|
|
||||||
double err = 0.0;
|
|
||||||
for(int ci = 0; ci < 2; ci++) {
|
|
||||||
err += EstimateTwoClusterError(clusters[ci]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's small, we'll take it!
|
|
||||||
if(err < 1e-9) {
|
|
||||||
CompressTwoClusters(i, clusters, outBuf, opaque);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(err < bestError[0]) {
|
|
||||||
bestError[0] = err;
|
|
||||||
bestShapeIdx[0] = i;
|
|
||||||
bestClusters[0][0] = clusters[0];
|
|
||||||
bestClusters[0][1] = clusters[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
assert(selectionFn);
|
||||||
|
|
||||||
// There are not 3 subset blocks that support alpha, so only check these
|
ShapeSelection selection = selectionFn(0, 0, block);
|
||||||
// if the entire block is opaque.
|
selection.m_SelectedModes &= settings.m_BlockModes;
|
||||||
if(opaque) {
|
assert(selection.m_SelectedModes);
|
||||||
for(unsigned int i = 0; i < kNumShapes3; i++) {
|
CompressClusters(selection, block, outBuf, NULL, NULL);
|
||||||
|
|
||||||
RGBACluster clusters[3];
|
|
||||||
PopulateThreeClustersForShape(blockCluster, i, clusters);
|
|
||||||
|
|
||||||
double err = 0.0;
|
|
||||||
for(int ci = 0; ci < 3; ci++) {
|
|
||||||
err += EstimateThreeClusterError(clusters[ci]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's small, we'll take it!
|
|
||||||
if(err < 1e-9) {
|
|
||||||
CompressThreeClusters(i, clusters, outBuf, opaque);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(err < bestError[1]) {
|
|
||||||
bestError[1] = err;
|
|
||||||
bestShapeIdx[1] = i;
|
|
||||||
bestClusters[1][0] = clusters[0];
|
|
||||||
bestClusters[1][1] = clusters[1];
|
|
||||||
bestClusters[1][2] = clusters[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 tempBuf1[16], tempBuf2[16];
|
|
||||||
|
|
||||||
BitStream tempStream1 (tempBuf1, 128, 0);
|
|
||||||
CompressionMode compressor(6, opaque);
|
|
||||||
double best = compressor.Compress(tempStream1, 0, &blockCluster);
|
|
||||||
if(best == 0.0f) {
|
|
||||||
memcpy(outBuf, tempBuf1, 16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check modes 4 and 5 if the block isn't opaque...
|
|
||||||
if(!opaque) {
|
|
||||||
for(int mode = 4; mode <= 5; mode++) {
|
|
||||||
|
|
||||||
BitStream tempStream2(tempBuf2, 128, 0);
|
|
||||||
CompressionMode compressorTry(mode, opaque);
|
|
||||||
|
|
||||||
double error = compressorTry.Compress(tempStream2, 0, &blockCluster);
|
|
||||||
if(error < best) {
|
|
||||||
|
|
||||||
best = error;
|
|
||||||
|
|
||||||
if(best == 0.0f) {
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
memcpy(tempBuf1, tempBuf2, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double error =
|
|
||||||
CompressTwoClusters(bestShapeIdx[0], bestClusters[0], tempBuf2, opaque);
|
|
||||||
if(error < best) {
|
|
||||||
|
|
||||||
best = error;
|
|
||||||
if(error == 0.0f) {
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
memcpy(tempBuf1, tempBuf2, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(opaque) {
|
|
||||||
const double newError =
|
|
||||||
CompressThreeClusters(bestShapeIdx[1],
|
|
||||||
bestClusters[1],
|
|
||||||
tempBuf2,
|
|
||||||
opaque);
|
|
||||||
if(newError < best) {
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(outBuf, tempBuf1, 16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static double EstimateTwoClusterErrorStats(
|
static double EstimateTwoClusterErrorStats(
|
||||||
|
@ -2179,17 +2098,7 @@ static double EstimateTwoClusterErrorStats(
|
||||||
}
|
}
|
||||||
|
|
||||||
double error = 0.0001;
|
double error = 0.0001;
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
double eigOne = c.GetPrincipalEigenvalue();
|
|
||||||
double eigTwo = c.GetSecondEigenvalue();
|
|
||||||
if(eigOne != 0.0) {
|
|
||||||
error += eigTwo / eigOne;
|
|
||||||
} else {
|
|
||||||
error += 1.0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
error += std::min(err1, err3);
|
error += std::min(err1, err3);
|
||||||
#endif
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2226,18 +2135,7 @@ static double EstimateThreeClusterErrorStats(
|
||||||
}
|
}
|
||||||
|
|
||||||
double error = 0.0001;
|
double error = 0.0001;
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
double eigOne = c.GetPrincipalEigenvalue();
|
|
||||||
double eigTwo = c.GetSecondEigenvalue();
|
|
||||||
|
|
||||||
if(eigOne != 0.0) {
|
|
||||||
error += eigTwo / eigOne;
|
|
||||||
} else {
|
|
||||||
error += 1.0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
error += std::min(err0, err2);
|
error += std::min(err0, err2);
|
||||||
#endif
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2364,31 +2262,19 @@ static void CompressBC7Block(
|
||||||
Min, Max, 4, 0xFEFEFEFE, RGBAVector(w[0], w[1], w[2], w[3])
|
Min, Max, 4, 0xFEFEFEFE, RGBAVector(w[0], w[1], w[2], w[3])
|
||||||
);
|
);
|
||||||
UpdateErrorEstimate(modeEstimate, 6, err);
|
UpdateErrorEstimate(modeEstimate, 6, err);
|
||||||
|
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
double eigOne = blockCluster.GetPrincipalEigenvalue();
|
|
||||||
double eigTwo = blockCluster.GetSecondEigenvalue();
|
|
||||||
double error;
|
|
||||||
if(eigOne != 0.0) {
|
|
||||||
error = eigTwo / eigOne;
|
|
||||||
} else {
|
|
||||||
error = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintStream(logStream, kBlockStatString[eBlockStat_SingleShapeEstimate], error);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we must figure out which shape to use. To do this, simply
|
// First we must figure out which shape to use. To do this, simply
|
||||||
// see which shape has the smallest sum of minimum bounding spheres.
|
// see which shape has the smallest sum of minimum bounding spheres.
|
||||||
double bestError[2] = { DBL_MAX, DBL_MAX };
|
double bestError[2] = { DBL_MAX, DBL_MAX };
|
||||||
int bestShapeIdx[2] = { -1, -1 };
|
|
||||||
RGBACluster bestClusters[2][3];
|
ShapeSelection selection;
|
||||||
|
uint32 path = 0;
|
||||||
|
|
||||||
for(unsigned int i = 0; i < kNumShapes2; i++) {
|
for(unsigned int i = 0; i < kNumShapes2; i++) {
|
||||||
RGBACluster clusters[2];
|
RGBACluster clusters[2];
|
||||||
PopulateTwoClustersForShape(blockCluster, i, clusters);
|
PopulateTwoClustersForShape(block, i, clusters);
|
||||||
|
|
||||||
double err = 0.0;
|
double err = 0.0;
|
||||||
double errEstimate[2] = { -1.0, -1.0 };
|
double errEstimate[2] = { -1.0, -1.0 };
|
||||||
|
@ -2407,10 +2293,6 @@ static void CompressBC7Block(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
err /= 2.0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(errEstimate[0] != -1.0) {
|
if(errEstimate[0] != -1.0) {
|
||||||
UpdateErrorEstimate(modeEstimate, 1, errEstimate[0]);
|
UpdateErrorEstimate(modeEstimate, 1, errEstimate[0]);
|
||||||
}
|
}
|
||||||
|
@ -2427,21 +2309,15 @@ static void CompressBC7Block(
|
||||||
|
|
||||||
// If it's small, we'll take it!
|
// If it's small, we'll take it!
|
||||||
if(err < 1e-9) {
|
if(err < 1e-9) {
|
||||||
int modeChosen;
|
path = 2;
|
||||||
CompressTwoClusters(
|
selection.m_TwoShapeIndex = i;
|
||||||
i, clusters, outBuf, opaque, modeError, &modeChosen
|
selection.m_SelectedModes = kTwoPartitionModes;
|
||||||
);
|
break;
|
||||||
bestMode = modeChosen;
|
|
||||||
|
|
||||||
PrintStat(logStream, kBlockStatString[eBlockStat_Path], 2);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(err < bestError[0]) {
|
if(err < bestError[0]) {
|
||||||
bestError[0] = err;
|
bestError[0] = err;
|
||||||
bestShapeIdx[0] = i;
|
selection.m_TwoShapeIndex = i;
|
||||||
bestClusters[0][0] = clusters[0];
|
|
||||||
bestClusters[0][1] = clusters[1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2451,7 +2327,7 @@ static void CompressBC7Block(
|
||||||
for(unsigned int i = 0; i < kNumShapes3; i++) {
|
for(unsigned int i = 0; i < kNumShapes3; i++) {
|
||||||
|
|
||||||
RGBACluster clusters[3];
|
RGBACluster clusters[3];
|
||||||
PopulateThreeClustersForShape(blockCluster, i, clusters);
|
PopulateThreeClustersForShape(block, i, clusters);
|
||||||
|
|
||||||
double err = 0.0;
|
double err = 0.0;
|
||||||
double errEstimate[2] = { -1.0, -1.0 };
|
double errEstimate[2] = { -1.0, -1.0 };
|
||||||
|
@ -2470,10 +2346,6 @@ static void CompressBC7Block(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PCA_FOR_SHAPE_ESTIMATION
|
|
||||||
err /= 3.0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(errEstimate[0] != -1.0) {
|
if(errEstimate[0] != -1.0) {
|
||||||
UpdateErrorEstimate(modeEstimate, 0, errEstimate[0]);
|
UpdateErrorEstimate(modeEstimate, 0, errEstimate[0]);
|
||||||
}
|
}
|
||||||
|
@ -2490,94 +2362,26 @@ static void CompressBC7Block(
|
||||||
|
|
||||||
// If it's small, we'll take it!
|
// If it's small, we'll take it!
|
||||||
if(err < 1e-9) {
|
if(err < 1e-9) {
|
||||||
int modeChosen;
|
path = 2;
|
||||||
CompressThreeClusters(
|
selection.m_TwoShapeIndex = i;
|
||||||
i, clusters, outBuf, opaque, modeError, &modeChosen
|
selection.m_SelectedModes = kThreePartitionModes;
|
||||||
);
|
break;
|
||||||
bestMode = modeChosen;
|
|
||||||
|
|
||||||
PrintStat(logStream, kBlockStatString[eBlockStat_Path], 2);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(err < bestError[1]) {
|
if(err < bestError[1]) {
|
||||||
bestError[1] = err;
|
bestError[1] = err;
|
||||||
bestShapeIdx[1] = i;
|
selection.m_ThreeShapeIndex = i;
|
||||||
bestClusters[1][0] = clusters[0];
|
|
||||||
bestClusters[1][1] = clusters[1];
|
|
||||||
bestClusters[1][2] = clusters[2];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintStat(logStream, kBlockStatString[eBlockStat_Path], 3);
|
if(path == 0) path = 3;
|
||||||
|
|
||||||
uint8 tempBuf1[16], tempBuf2[16];
|
selection.m_SelectedModes &= settings.m_BlockModes;
|
||||||
|
assert(selection.m_SelectedModes);
|
||||||
|
CompressClusters(selection, block, outBuf, modeError, &bestMode);
|
||||||
|
|
||||||
BitStream tempStream1 (tempBuf1, 128, 0);
|
PrintStat(logStream, kBlockStatString[eBlockStat_Path], path);
|
||||||
CompressionMode compressor(6, opaque);
|
|
||||||
double best = compressor.Compress(tempStream1, 0, &blockCluster);
|
|
||||||
modeError[6] = best;
|
|
||||||
bestMode = 6;
|
|
||||||
if(best == 0.0f) {
|
|
||||||
memcpy(outBuf, tempBuf1, 16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check modes 4 and 5 if the block isn't opaque...
|
|
||||||
if(!opaque) {
|
|
||||||
for(int mode = 4; mode <= 5; mode++) {
|
|
||||||
|
|
||||||
BitStream tempStream2(tempBuf2, 128, 0);
|
|
||||||
CompressionMode compressorTry(mode, opaque);
|
|
||||||
|
|
||||||
double error = compressorTry.Compress(tempStream2, 0, &blockCluster);
|
|
||||||
if(error < best) {
|
|
||||||
|
|
||||||
bestMode = mode;
|
|
||||||
best = error;
|
|
||||||
|
|
||||||
if(best == 0.0f) {
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
memcpy(tempBuf1, tempBuf2, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int modeChosen;
|
|
||||||
double error = CompressTwoClusters(
|
|
||||||
bestShapeIdx[0], bestClusters[0], tempBuf2, opaque, modeError, &modeChosen
|
|
||||||
);
|
|
||||||
if(error < best) {
|
|
||||||
|
|
||||||
bestMode = modeChosen;
|
|
||||||
best = error;
|
|
||||||
|
|
||||||
if(error == 0.0f) {
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
memcpy(tempBuf1, tempBuf2, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(opaque) {
|
|
||||||
const double newError = CompressThreeClusters(
|
|
||||||
bestShapeIdx[1], bestClusters[1],
|
|
||||||
tempBuf2, opaque, modeError, &modeChosen
|
|
||||||
);
|
|
||||||
if(newError < best) {
|
|
||||||
|
|
||||||
bestMode = modeChosen;
|
|
||||||
memcpy(outBuf, tempBuf2, 16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(outBuf, tempBuf1, 16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecompressBC7Block(const uint8 block[16], uint32 outBuf[16]) {
|
static void DecompressBC7Block(const uint8 block[16], uint32 outBuf[16]) {
|
||||||
|
|
|
@ -177,6 +177,8 @@ public:
|
||||||
bool AllSamePoint() const { return m_Max == m_Min; }
|
bool AllSamePoint() const { return m_Max == m_Min; }
|
||||||
int GetPointBitString() const { return m_PointBitString; }
|
int GetPointBitString() const { return m_PointBitString; }
|
||||||
|
|
||||||
|
void Reset() { *this = RGBACluster(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The number of points in the cluster.
|
// The number of points in the cluster.
|
||||||
uint32 m_NumPoints;
|
uint32 m_NumPoints;
|
||||||
|
|
Loading…
Reference in a new issue