Plug in the infrastructure to start passing images to be compressed.

This commit is contained in:
Pavel Krajcevski 2013-09-20 12:43:59 -04:00
parent 995c237e5e
commit 1093447055
9 changed files with 166 additions and 67 deletions

View file

@ -53,6 +53,7 @@
void PrintUsage() { void PrintUsage() {
fprintf(stderr, "Usage: tc [OPTIONS] imagefile\n"); fprintf(stderr, "Usage: tc [OPTIONS] imagefile\n");
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, "\t-f\t\tFormat to use. Either \"BPTC\" or \"PVRTC\". Default: BPTC\n");
fprintf(stderr, "\t-l\t\tSave an output log.\n"); fprintf(stderr, "\t-l\t\tSave an output log.\n");
fprintf(stderr, "\t-q <quality>\tSet compression quality level. Default: 50\n"); fprintf(stderr, "\t-q <quality>\tSet compression quality level. Default: 50\n");
fprintf(stderr, "\t-n <num>\tCompress the image num times and give the average time and PSNR. Default: 1\n"); fprintf(stderr, "\t-n <num>\tCompress the image num times and give the average time and PSNR. Default: 1\n");
@ -92,7 +93,8 @@ int main(int argc, char **argv) {
bool bUseSIMD = false; bool bUseSIMD = false;
bool bSaveLog = false; bool bSaveLog = false;
bool bUseAtomics = false; bool bUseAtomics = false;
ECompressionFormat format = eCompressionFormat_BPTC;
bool knowArg = false; bool knowArg = false;
do { do {
knowArg = false; knowArg = false;
@ -110,6 +112,23 @@ int main(int argc, char **argv) {
continue; continue;
} }
if(strcmp(argv[fileArg], "-f") == 0) {
fileArg++;
if(fileArg == argc) {
PrintUsage();
exit(1);
} else {
if(!strcmp(argv[fileArg], "PVRTC")) {
format = eCompressionFormat_PVRTC;
}
}
fileArg++;
knowArg = true;
continue;
}
if(strcmp(argv[fileArg], "-l") == 0) { if(strcmp(argv[fileArg], "-l") == 0) {
fileArg++; fileArg++;
bSaveLog = true; bSaveLog = true;
@ -195,6 +214,7 @@ int main(int argc, char **argv) {
} }
SCompressionSettings settings; SCompressionSettings settings;
settings.format = format;
settings.bUseSIMD = bUseSIMD; settings.bUseSIMD = bUseSIMD;
settings.bUseAtomics = bUseAtomics; settings.bUseAtomics = bUseAtomics;
settings.iNumThreads = numThreads; settings.iNumThreads = numThreads;

View file

@ -71,6 +71,9 @@ ELSE()
SET( LINK_FLAGS -lrt ${LINK_FLAGS} ) SET( LINK_FLAGS -lrt ${LINK_FLAGS} )
ENDIF() ENDIF()
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR} )
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/PVRTCEncoder/include )
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/BPTCEncoder/include ) INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/BPTCEncoder/include )
INCLUDE_DIRECTORIES( ${FasTC_BINARY_DIR}/BPTCEncoder/include ) INCLUDE_DIRECTORIES( ${FasTC_BINARY_DIR}/BPTCEncoder/include )
@ -149,6 +152,7 @@ ADD_LIBRARY( FasTCCore
TARGET_LINK_LIBRARIES( FasTCCore FasTCIO ) TARGET_LINK_LIBRARIES( FasTCCore FasTCIO )
TARGET_LINK_LIBRARIES( FasTCCore BPTCEncoder ) TARGET_LINK_LIBRARIES( FasTCCore BPTCEncoder )
TARGET_LINK_LIBRARIES( FasTCCore PVRTCEncoder )
IF( THREAD_API MATCHES "Boost" ) IF( THREAD_API MATCHES "Boost" )
TARGET_LINK_LIBRARIES( FasTCCore ${Boost_LIBRARIES} ) TARGET_LINK_LIBRARIES( FasTCCore ${Boost_LIBRARIES} )

View file

@ -44,10 +44,13 @@
#ifndef _COMPRESSED_IMAGE_H_ #ifndef _COMPRESSED_IMAGE_H_
#define _COMPRESSED_IMAGE_H_ #define _COMPRESSED_IMAGE_H_
#include "TexCompTypes.h"
enum ECompressionFormat { enum ECompressionFormat {
eCompressionFormat_DXT1, eCompressionFormat_DXT1,
eCompressionFormat_DXT5, eCompressionFormat_DXT5,
eCompressionFormat_BPTC, eCompressionFormat_BPTC,
eCompressionFormat_PVRTC,
kNumCompressionFormats kNumCompressionFormats
}; };
@ -55,15 +58,15 @@ enum ECompressionFormat {
class CompressedImage { class CompressedImage {
private: private:
unsigned int m_Width; uint32 m_Width;
unsigned int m_Height; uint32 m_Height;
ECompressionFormat m_Format; ECompressionFormat m_Format;
unsigned char *m_Data; uint8 *m_Data;
unsigned int m_DataSz; uint32 m_DataSz;
void InitData(const unsigned char *withData); void InitData(const uint8 *withData);
public: public:
CompressedImage(); CompressedImage();
@ -71,23 +74,29 @@ class CompressedImage {
// the passed format. The size of the data is expected to conform // the passed format. The size of the data is expected to conform
// to the width, height, and format specified. // to the width, height, and format specified.
CompressedImage( CompressedImage(
const unsigned int width, const uint32 width,
const unsigned int height, const uint32 height,
const ECompressionFormat format, const ECompressionFormat format,
const unsigned char *data const uint8 *data
); );
unsigned int GetHeight() const { return m_Height; } uint32 GetHeight() const { return m_Height; }
unsigned int GetWidth() const { return m_Width; } uint32 GetWidth() const { return m_Width; }
CompressedImage( const CompressedImage &other ); CompressedImage( const CompressedImage &other );
~CompressedImage(); ~CompressedImage();
static uint32 GetCompressedSize(uint32 uncompressedSize, ECompressionFormat format);
static uint32 GetUncompressedSize(uint32 compressedSize, ECompressionFormat format) {
uint32 cmp = GetCompressedSize(compressedSize, format);
return compressedSize * (compressedSize / cmp);
}
// Decompress the compressed image data into outBuf. outBufSz is expected // Decompress the compressed image data into outBuf. outBufSz is expected
// to be the proper size determined by the width, height, and format. // to be the proper size determined by the width, height, and format.
// !FIXME! We should have a function to explicitly return the in/out buf // !FIXME! We should have a function to explicitly return the in/out buf
// size for a given compressed image. // size for a given compressed image.
bool DecompressImage(unsigned char *outBuf, unsigned int outBufSz) const; bool DecompressImage(uint8 *outBuf, uint32 outBufSz) const;
}; };
#endif // _COMPRESSED_IMAGE_H_ #endif // _COMPRESSED_IMAGE_H_

View file

@ -93,7 +93,8 @@ struct SCompressionSettings {
extern bool CompressImageData( extern bool CompressImageData(
const unsigned char *data, const unsigned char *data,
const unsigned int dataSz, const unsigned int width,
const unsigned int height,
unsigned char *cmpData, unsigned char *cmpData,
const unsigned int cmpDataSz, const unsigned int cmpDataSz,
const SCompressionSettings &settings const SCompressionSettings &settings

View file

@ -50,6 +50,7 @@
#include "TexCompTypes.h" #include "TexCompTypes.h"
#include "BC7Compressor.h" #include "BC7Compressor.h"
#include "PVRTCCompressor.h"
CompressedImage::CompressedImage() CompressedImage::CompressedImage()
: m_Width(0) : m_Width(0)
@ -85,16 +86,7 @@ CompressedImage::CompressedImage(
} }
void CompressedImage::InitData(const unsigned char *withData) { void CompressedImage::InitData(const unsigned char *withData) {
m_DataSz = 0; m_DataSz = GetCompressedSize(m_Width * m_Height * 4, m_Format);
int uncompDataSz = m_Width * m_Height * 4;
switch(m_Format) {
default: assert(!"Not implemented!"); // Fall through V
case eCompressionFormat_DXT1: m_DataSz = uncompDataSz / 8; break;
case eCompressionFormat_DXT5: m_DataSz = uncompDataSz / 4; break;
case eCompressionFormat_BPTC: m_DataSz = uncompDataSz / 4; break;
}
if(m_DataSz > 0) { if(m_DataSz > 0) {
m_Data = new unsigned char[m_DataSz]; m_Data = new unsigned char[m_DataSz];
memcpy(m_Data, withData, m_DataSz); memcpy(m_Data, withData, m_DataSz);
@ -111,14 +103,7 @@ CompressedImage::~CompressedImage() {
bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBufSz) const { bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBufSz) const {
// First make sure that we have enough data // First make sure that we have enough data
uint32 dataSz = 0; uint32 dataSz = GetUncompressedSize(m_DataSz, m_Format);
switch(m_Format) {
default: assert(!"Not implemented!"); // Fall through V
case eCompressionFormat_DXT1: dataSz = m_DataSz * 8; break;
case eCompressionFormat_DXT5: dataSz = m_DataSz * 4; break;
case eCompressionFormat_BPTC: dataSz = m_DataSz * 4; break;
}
if(dataSz > outBufSz) { if(dataSz > outBufSz) {
fprintf(stderr, "Not enough space to store entire decompressed image! " fprintf(stderr, "Not enough space to store entire decompressed image! "
"Got %d bytes, but need %d!\n", outBufSz, dataSz); "Got %d bytes, but need %d!\n", outBufSz, dataSz);
@ -126,6 +111,13 @@ bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBuf
} }
switch(m_Format) { switch(m_Format) {
case eCompressionFormat_PVRTC:
{
DecompressionJob dj (m_Data, outBuf, m_Width, m_Height);
PVRTCC::Decompress(dj);
}
break;
case eCompressionFormat_BPTC: case eCompressionFormat_BPTC:
{ {
DecompressionJob dj (m_Data, outBuf, m_Width, m_Height); DecompressionJob dj (m_Data, outBuf, m_Width, m_Height);
@ -142,3 +134,26 @@ bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBuf
return true; return true;
} }
uint32 CompressedImage::GetCompressedSize(uint32 uncompressedSize, ECompressionFormat format) {
assert(uncompressedSize % 8 == 0);
uint32 cmpDataSzNeeded = 0;
switch(format) {
default:
assert(!"Not implemented!");
// Fall through V
case eCompressionFormat_DXT1:
case eCompressionFormat_PVRTC:
cmpDataSzNeeded = uncompressedSize / 8;
break;
case eCompressionFormat_DXT5:
case eCompressionFormat_BPTC:
cmpDataSzNeeded = uncompressedSize / 4;
break;
}
return cmpDataSzNeeded;
}

View file

@ -149,16 +149,10 @@ CompressedImage *Image::Compress(const SCompressionSettings &settings) const {
assert(dataSz > 0); assert(dataSz > 0);
// Allocate data based on the compression method // Allocate data based on the compression method
int cmpDataSz = 0; int cmpDataSz = CompressedImage::GetCompressedSize(dataSz, settings.format);
switch(settings.format) {
default: assert(!"Not implemented!"); // Fall Through V
case eCompressionFormat_DXT1: cmpDataSz = dataSz / 8; break;
case eCompressionFormat_DXT5: cmpDataSz = dataSz / 4; break;
case eCompressionFormat_BPTC: cmpDataSz = dataSz / 4; break;
}
unsigned char *cmpData = new unsigned char[cmpDataSz]; unsigned char *cmpData = new unsigned char[cmpDataSz];
CompressImageData(m_PixelData, dataSz, cmpData, cmpDataSz, settings); CompressImageData(m_PixelData, GetWidth(), GetHeight(), cmpData, cmpDataSz, settings);
outImg = new CompressedImage(GetWidth(), GetHeight(), settings.format, cmpData); outImg = new CompressedImage(GetWidth(), GetHeight(), settings.format, cmpData);

View file

@ -49,6 +49,7 @@
#include <cstdio> #include <cstdio>
#include <cassert> #include <cassert>
#include "PVRTCCompressor.h"
#include "BC7Compressor.h" #include "BC7Compressor.h"
#include "Thread.h" #include "Thread.h"
#include "WorkerQueue.h" #include "WorkerQueue.h"
@ -68,6 +69,10 @@ static inline T sad(const T &a, const T &b) {
return (a > b)? a - b : b - a; return (a > b)? a - b : b - a;
} }
static void CompressPVRTC(const CompressionJob &cj) {
PVRTCC::Compress(cj);
}
SCompressionSettings:: SCompressionSettings() SCompressionSettings:: SCompressionSettings()
: format(eCompressionFormat_BPTC) : format(eCompressionFormat_BPTC)
, bUseSIMD(false) , bUseSIMD(false)
@ -86,6 +91,12 @@ static CompressionFuncWithStats ChooseFuncFromSettingsWithStats(const SCompress
} }
break; break;
case eCompressionFormat_PVRTC:
{
// !FIXME! actually implement one of these methods...
return NULL;
}
default: default:
{ {
assert(!"Not implemented!"); assert(!"Not implemented!");
@ -109,6 +120,11 @@ static CompressionFunc ChooseFuncFromSettings(const SCompressionSettings &s) {
} }
break; break;
case eCompressionFormat_PVRTC:
{
return CompressPVRTC;
}
default: default:
{ {
assert(!"Not implemented!"); assert(!"Not implemented!");
@ -123,8 +139,9 @@ static void ReportError(const char *msg) {
} }
static double CompressImageInSerial( static double CompressImageInSerial(
const unsigned char *imgData, const uint8 *imgData,
const unsigned int imgDataSz, const uint32 imgWidth,
const uint32 imgHeight,
const SCompressionSettings &settings, const SCompressionSettings &settings,
unsigned char *outBuf unsigned char *outBuf
) { ) {
@ -133,14 +150,14 @@ static double CompressImageInSerial(
double cmpTimeTotal = 0.0; double cmpTimeTotal = 0.0;
StopWatch stopWatch = StopWatch();
for(int i = 0; i < settings.iNumCompressions; i++) { for(int i = 0; i < settings.iNumCompressions; i++) {
StopWatch stopWatch = StopWatch();
stopWatch.Reset(); stopWatch.Reset();
stopWatch.Start(); stopWatch.Start();
// !FIXME! We're assuming that we have 4x4 blocks here... // !FIXME! We're assuming that we have 4x4 blocks here...
CompressionJob cj (imgData, outBuf, imgDataSz / 16, 4); CompressionJob cj (imgData, outBuf, imgWidth, imgHeight);
if(fStats && settings.pStatManager) { if(fStats && settings.pStatManager) {
(*fStats)(cj, *(settings.pStatManager)); (*fStats)(cj, *(settings.pStatManager));
} }
@ -332,12 +349,15 @@ static double CompressImageWithWorkerQueue(
bool CompressImageData( bool CompressImageData(
const unsigned char *data, const unsigned char *data,
const unsigned int dataSz, const unsigned int width,
const unsigned int height,
unsigned char *cmpData, unsigned char *cmpData,
const unsigned int cmpDataSz, const unsigned int cmpDataSz,
const SCompressionSettings &settings const SCompressionSettings &settings
) { ) {
uint32 dataSz = width * height * 4;
// Make sure that platform supports SSE if they chose this // Make sure that platform supports SSE if they chose this
// option... // option...
#ifndef HAS_SSE_41 #ifndef HAS_SSE_41
@ -352,15 +372,23 @@ bool CompressImageData(
return false; return false;
} }
// Allocate data based on the compression method uint32 numThreads = settings.iNumThreads;
uint32 cmpDataSzNeeded = 0; if(settings.format == eCompressionFormat_PVRTC &&
switch(settings.format) { (settings.iNumThreads > 1 || settings.pStatManager)) {
default: assert(!"Not implemented!"); // Fall through V if(settings.iNumThreads > 1) {
case eCompressionFormat_DXT1: cmpDataSzNeeded = dataSz / 8; break; ReportError("WARNING - PVRTC compressor does not support multithreading.");
case eCompressionFormat_DXT5: cmpDataSzNeeded = dataSz / 4; break; numThreads = 1;
case eCompressionFormat_BPTC: cmpDataSzNeeded = dataSz / 4; break; }
if(settings.pStatManager) {
ReportError("WARNING - PVRTC compressor does not support stat collection.");
}
} }
// Allocate data based on the compression method
uint32 cmpDataSzNeeded =
CompressedImage::GetCompressedSize(dataSz, settings.format);
if(cmpDataSzNeeded == 0) { if(cmpDataSzNeeded == 0) {
ReportError("Unknown compression format"); ReportError("Unknown compression format");
return false; return false;
@ -375,21 +403,17 @@ bool CompressImageData(
double cmpMSTime = 0.0; double cmpMSTime = 0.0;
if(settings.iNumThreads > 1) { if(numThreads > 1) {
if(settings.bUseAtomics) { if(settings.bUseAtomics) {
//!KLUDGE!
unsigned int height = 4;
unsigned int width = dataSz / 16;
cmpMSTime = CompressImageWithAtomics(data, width, height, settings, cmpData); cmpMSTime = CompressImageWithAtomics(data, width, height, settings, cmpData);
} } else if(settings.iJobSize > 0) {
else if(settings.iJobSize > 0)
cmpMSTime = CompressImageWithWorkerQueue(data, dataSz, settings, cmpData); cmpMSTime = CompressImageWithWorkerQueue(data, dataSz, settings, cmpData);
else } else {
cmpMSTime = CompressImageWithThreads(data, dataSz, settings, cmpData); cmpMSTime = CompressImageWithThreads(data, dataSz, settings, cmpData);
}
} }
else { else {
cmpMSTime = CompressImageInSerial(data, dataSz, settings, cmpData); cmpMSTime = CompressImageInSerial(data, width, height, settings, cmpData);
} }
// Report compression time // Report compression time

View file

@ -76,7 +76,7 @@ namespace PVRTCC {
// Takes a stream of uncompressed RGBA8 data and compresses it into PVRTC // Takes a stream of uncompressed RGBA8 data and compresses it into PVRTC
// version one. The width and height must be specified in order to properly // version one. The width and height must be specified in order to properly
// decompress the data. // decompress the data.
void Compress(const DecompressionJob &, void Compress(const CompressionJob &,
bool bTwoBitMode = false, bool bTwoBitMode = false,
const EWrapMode wrapMode = eWrapMode_Wrap); const EWrapMode wrapMode = eWrapMode_Wrap);

View file

@ -57,20 +57,52 @@
namespace PVRTCC { namespace PVRTCC {
void Compress(const DecompressionJob &dcj, void Compress(const CompressionJob &dcj,
bool bTwoBitMode, bool bTwoBitMode,
const EWrapMode wrapMode) { const EWrapMode wrapMode) {
Image img(dcj.height, dcj.width); Image img(dcj.height, dcj.width);
for(uint32 j = 0; j < dcj.height; j++) { uint32 nPixels = dcj.height * dcj.width;
for(uint32 i = 0; i < dcj.width; i++) { for(uint32 i = 0; i < nPixels; i++) {
const uint32 *pixels = reinterpret_cast<const uint32 *>(dcj.inBuf); // Assume block stream order (whyyyy)
img(i, j).UnpackRGBA(pixels[j * dcj.width + i]); uint32 blockIdx = i / 16;
} uint32 blockWidth = dcj.width / 4;
uint32 blockX = blockIdx % blockWidth;
uint32 blockY = blockIdx / blockWidth;
uint32 x = blockX * 4 + (i % 4);
uint32 y = blockY * 4 + (i % 16) / 4;
const uint32 *pixels = reinterpret_cast<const uint32 *>(dcj.inBuf);
img(x, y).UnpackRGBA(pixels[i]);
} }
Image original = img;
img.DebugOutput("Original");
// Downscale it using anisotropic diffusion based scheme in order to preserve // Downscale it using anisotropic diffusion based scheme in order to preserve
// image features, then reupscale and compute deltas. Use deltas to generate // image features, then reupscale and compute deltas. Use deltas to generate
// initial A & B images followed by modulation data. // initial A & B images followed by modulation data.
img.ContentAwareDownscale(1, 1, eWrapMode_Wrap, true);
img.DebugOutput("DownscaledOnce");
img.ContentAwareDownscale(1, 1, eWrapMode_Wrap, false);
img.DebugOutput("DownscaledTwice");
Image downscaled = img;
// Upscale it again
img.BilinearUpscale(2, 2, eWrapMode_Wrap);
img.DebugOutput("Reconstruction");
// Compute difference...
Image difference = img;
for(uint32 j = 0; j < dcj.height; j++) {
for(uint32 i = 0; i < dcj.width; i++) {
for(uint32 c = 0; c < 4; c++) {
difference(i, j).Component(c) -= img(i, j).Component(c);
}
}
}
} }
} // namespace PVRTCC } // namespace PVRTCC