diff --git a/CLTool/src/clunix.cpp b/CLTool/src/clunix.cpp index aa39d52..a064c1f 100644 --- a/CLTool/src/clunix.cpp +++ b/CLTool/src/clunix.cpp @@ -53,6 +53,7 @@ void PrintUsage() { fprintf(stderr, "Usage: tc [OPTIONS] imagefile\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-q \tSet compression quality level. Default: 50\n"); fprintf(stderr, "\t-n \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 bSaveLog = false; bool bUseAtomics = false; - + ECompressionFormat format = eCompressionFormat_BPTC; + bool knowArg = false; do { knowArg = false; @@ -110,6 +112,23 @@ int main(int argc, char **argv) { 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) { fileArg++; bSaveLog = true; @@ -195,6 +214,7 @@ int main(int argc, char **argv) { } SCompressionSettings settings; + settings.format = format; settings.bUseSIMD = bUseSIMD; settings.bUseAtomics = bUseAtomics; settings.iNumThreads = numThreads; diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 0e33516..21d1f2e 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -71,6 +71,9 @@ ELSE() SET( LINK_FLAGS -lrt ${LINK_FLAGS} ) ENDIF() +INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR} ) +INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/PVRTCEncoder/include ) + INCLUDE_DIRECTORIES( ${FasTC_SOURCE_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 BPTCEncoder ) +TARGET_LINK_LIBRARIES( FasTCCore PVRTCEncoder ) IF( THREAD_API MATCHES "Boost" ) TARGET_LINK_LIBRARIES( FasTCCore ${Boost_LIBRARIES} ) diff --git a/Core/include/CompressedImage.h b/Core/include/CompressedImage.h index 831a50c..8799d11 100644 --- a/Core/include/CompressedImage.h +++ b/Core/include/CompressedImage.h @@ -44,10 +44,13 @@ #ifndef _COMPRESSED_IMAGE_H_ #define _COMPRESSED_IMAGE_H_ +#include "TexCompTypes.h" + enum ECompressionFormat { eCompressionFormat_DXT1, eCompressionFormat_DXT5, eCompressionFormat_BPTC, + eCompressionFormat_PVRTC, kNumCompressionFormats }; @@ -55,15 +58,15 @@ enum ECompressionFormat { class CompressedImage { private: - unsigned int m_Width; - unsigned int m_Height; + uint32 m_Width; + uint32 m_Height; ECompressionFormat m_Format; - unsigned char *m_Data; - unsigned int m_DataSz; + uint8 *m_Data; + uint32 m_DataSz; - void InitData(const unsigned char *withData); + void InitData(const uint8 *withData); public: CompressedImage(); @@ -71,23 +74,29 @@ class CompressedImage { // the passed format. The size of the data is expected to conform // to the width, height, and format specified. CompressedImage( - const unsigned int width, - const unsigned int height, + const uint32 width, + const uint32 height, const ECompressionFormat format, - const unsigned char *data + const uint8 *data ); - unsigned int GetHeight() const { return m_Height; } - unsigned int GetWidth() const { return m_Width; } + uint32 GetHeight() const { return m_Height; } + uint32 GetWidth() const { return m_Width; } CompressedImage( const CompressedImage &other ); ~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 // 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 // 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_ diff --git a/Core/include/TexComp.h b/Core/include/TexComp.h index 1ad8c84..873a228 100644 --- a/Core/include/TexComp.h +++ b/Core/include/TexComp.h @@ -93,7 +93,8 @@ struct SCompressionSettings { extern bool CompressImageData( const unsigned char *data, - const unsigned int dataSz, + const unsigned int width, + const unsigned int height, unsigned char *cmpData, const unsigned int cmpDataSz, const SCompressionSettings &settings diff --git a/Core/src/CompressedImage.cpp b/Core/src/CompressedImage.cpp index 0f4f393..a30d9ba 100644 --- a/Core/src/CompressedImage.cpp +++ b/Core/src/CompressedImage.cpp @@ -50,6 +50,7 @@ #include "TexCompTypes.h" #include "BC7Compressor.h" +#include "PVRTCCompressor.h" CompressedImage::CompressedImage() : m_Width(0) @@ -85,16 +86,7 @@ CompressedImage::CompressedImage( } void CompressedImage::InitData(const unsigned char *withData) { - m_DataSz = 0; - 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; - } - + m_DataSz = GetCompressedSize(m_Width * m_Height * 4, m_Format); if(m_DataSz > 0) { m_Data = new unsigned char[m_DataSz]; memcpy(m_Data, withData, m_DataSz); @@ -111,14 +103,7 @@ CompressedImage::~CompressedImage() { bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBufSz) const { // First make sure that we have enough data - uint32 dataSz = 0; - 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; - } - + uint32 dataSz = GetUncompressedSize(m_DataSz, m_Format); if(dataSz > outBufSz) { fprintf(stderr, "Not enough space to store entire decompressed image! " "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) { + case eCompressionFormat_PVRTC: + { + DecompressionJob dj (m_Data, outBuf, m_Width, m_Height); + PVRTCC::Decompress(dj); + } + break; + case eCompressionFormat_BPTC: { DecompressionJob dj (m_Data, outBuf, m_Width, m_Height); @@ -142,3 +134,26 @@ bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBuf 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; +} + diff --git a/Core/src/Image.cpp b/Core/src/Image.cpp index 663469b..a918706 100644 --- a/Core/src/Image.cpp +++ b/Core/src/Image.cpp @@ -149,16 +149,10 @@ CompressedImage *Image::Compress(const SCompressionSettings &settings) const { assert(dataSz > 0); // Allocate data based on the compression method - int cmpDataSz = 0; - 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; - } + int cmpDataSz = CompressedImage::GetCompressedSize(dataSz, settings.format); 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); diff --git a/Core/src/TexComp.cpp b/Core/src/TexComp.cpp index 3cf0bf5..3cc25ac 100644 --- a/Core/src/TexComp.cpp +++ b/Core/src/TexComp.cpp @@ -49,6 +49,7 @@ #include #include +#include "PVRTCCompressor.h" #include "BC7Compressor.h" #include "Thread.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; } +static void CompressPVRTC(const CompressionJob &cj) { + PVRTCC::Compress(cj); +} + SCompressionSettings:: SCompressionSettings() : format(eCompressionFormat_BPTC) , bUseSIMD(false) @@ -86,6 +91,12 @@ static CompressionFuncWithStats ChooseFuncFromSettingsWithStats(const SCompress } break; + case eCompressionFormat_PVRTC: + { + // !FIXME! actually implement one of these methods... + return NULL; + } + default: { assert(!"Not implemented!"); @@ -109,6 +120,11 @@ static CompressionFunc ChooseFuncFromSettings(const SCompressionSettings &s) { } break; + case eCompressionFormat_PVRTC: + { + return CompressPVRTC; + } + default: { assert(!"Not implemented!"); @@ -123,8 +139,9 @@ static void ReportError(const char *msg) { } static double CompressImageInSerial( - const unsigned char *imgData, - const unsigned int imgDataSz, + const uint8 *imgData, + const uint32 imgWidth, + const uint32 imgHeight, const SCompressionSettings &settings, unsigned char *outBuf ) { @@ -133,14 +150,14 @@ static double CompressImageInSerial( double cmpTimeTotal = 0.0; + StopWatch stopWatch = StopWatch(); for(int i = 0; i < settings.iNumCompressions; i++) { - StopWatch stopWatch = StopWatch(); stopWatch.Reset(); stopWatch.Start(); // !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) { (*fStats)(cj, *(settings.pStatManager)); } @@ -332,12 +349,15 @@ static double CompressImageWithWorkerQueue( bool CompressImageData( const unsigned char *data, - const unsigned int dataSz, + const unsigned int width, + const unsigned int height, unsigned char *cmpData, const unsigned int cmpDataSz, const SCompressionSettings &settings ) { + uint32 dataSz = width * height * 4; + // Make sure that platform supports SSE if they chose this // option... #ifndef HAS_SSE_41 @@ -352,15 +372,23 @@ bool CompressImageData( return false; } - // Allocate data based on the compression method - uint32 cmpDataSzNeeded = 0; - switch(settings.format) { - default: assert(!"Not implemented!"); // Fall through V - case eCompressionFormat_DXT1: cmpDataSzNeeded = dataSz / 8; break; - case eCompressionFormat_DXT5: cmpDataSzNeeded = dataSz / 4; break; - case eCompressionFormat_BPTC: cmpDataSzNeeded = dataSz / 4; break; + uint32 numThreads = settings.iNumThreads; + if(settings.format == eCompressionFormat_PVRTC && + (settings.iNumThreads > 1 || settings.pStatManager)) { + if(settings.iNumThreads > 1) { + ReportError("WARNING - PVRTC compressor does not support multithreading."); + numThreads = 1; + } + + 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) { ReportError("Unknown compression format"); return false; @@ -375,21 +403,17 @@ bool CompressImageData( double cmpMSTime = 0.0; - if(settings.iNumThreads > 1) { + if(numThreads > 1) { if(settings.bUseAtomics) { - //!KLUDGE! - unsigned int height = 4; - unsigned int width = dataSz / 16; - cmpMSTime = CompressImageWithAtomics(data, width, height, settings, cmpData); - } - else if(settings.iJobSize > 0) + } else if(settings.iJobSize > 0) { cmpMSTime = CompressImageWithWorkerQueue(data, dataSz, settings, cmpData); - else + } else { cmpMSTime = CompressImageWithThreads(data, dataSz, settings, cmpData); + } } else { - cmpMSTime = CompressImageInSerial(data, dataSz, settings, cmpData); + cmpMSTime = CompressImageInSerial(data, width, height, settings, cmpData); } // Report compression time diff --git a/PVRTCEncoder/include/PVRTCCompressor.h b/PVRTCEncoder/include/PVRTCCompressor.h index c9001cd..657c71a 100644 --- a/PVRTCEncoder/include/PVRTCCompressor.h +++ b/PVRTCEncoder/include/PVRTCCompressor.h @@ -76,7 +76,7 @@ namespace PVRTCC { // 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 // decompress the data. - void Compress(const DecompressionJob &, + void Compress(const CompressionJob &, bool bTwoBitMode = false, const EWrapMode wrapMode = eWrapMode_Wrap); diff --git a/PVRTCEncoder/src/Compressor.cpp b/PVRTCEncoder/src/Compressor.cpp index b17484f..b26c51e 100644 --- a/PVRTCEncoder/src/Compressor.cpp +++ b/PVRTCEncoder/src/Compressor.cpp @@ -57,20 +57,52 @@ namespace PVRTCC { - void Compress(const DecompressionJob &dcj, + void Compress(const CompressionJob &dcj, bool bTwoBitMode, const EWrapMode wrapMode) { Image img(dcj.height, dcj.width); - for(uint32 j = 0; j < dcj.height; j++) { - for(uint32 i = 0; i < dcj.width; i++) { - const uint32 *pixels = reinterpret_cast(dcj.inBuf); - img(i, j).UnpackRGBA(pixels[j * dcj.width + i]); - } + uint32 nPixels = dcj.height * dcj.width; + for(uint32 i = 0; i < nPixels; i++) { + // Assume block stream order (whyyyy) + 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(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 // image features, then reupscale and compute deltas. Use deltas to generate // 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