From c1222d75f967c1acb00bac35df917859f2adf256 Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Fri, 21 Sep 2012 12:39:09 -0400 Subject: [PATCH] Reorganize a lot of code. Should probably split image and image file classes to separate IO operations and whatnot. --- CLTool/src/clunix.cpp | 3 +- Core/include/TexComp.h | 48 ++++++++++++++++++++----- Core/src/TexComp.cpp | 75 +++++++++++++++++++++------------------- Core/src/ThreadGroup.cpp | 13 +++---- Core/src/ThreadGroup.h | 5 +-- IO/include/ImageFile.h | 8 ++++- IO/src/ImageFile.cpp | 24 +++++++++++++ 7 files changed, 121 insertions(+), 55 deletions(-) diff --git a/CLTool/src/clunix.cpp b/CLTool/src/clunix.cpp index 5a2e0b3..7a5a0e6 100644 --- a/CLTool/src/clunix.cpp +++ b/CLTool/src/clunix.cpp @@ -3,6 +3,7 @@ #include #include "TexComp.h" +#include "ImageFile.h" void PrintUsage() { fprintf(stderr, "Usage: tc [-s|-t ] \n"); @@ -82,7 +83,7 @@ int main(int argc, char **argv) { settings.iQuality = quality; settings.iNumCompressions = numCompressions; - CompressedImage *ci = CompressImage(file, settings); + CompressedImage *ci = file.Compress(settings); if(NULL == ci) { fprintf(stderr, "Error compressing image!\n"); return 1; diff --git a/Core/include/TexComp.h b/Core/include/TexComp.h index d8ab436..012df35 100644 --- a/Core/include/TexComp.h +++ b/Core/include/TexComp.h @@ -1,30 +1,62 @@ #ifndef _TEX_COMP_H_ #define _TEX_COMP_H_ -#include "ImageFile.h" #include "CompressedImage.h" +// Forward declarations +class ImageFile; +class CompressedImage; + struct SCompressionSettings { SCompressionSettings(); // defaults + + // The compression format for the image. ECompressionFormat format; + + // The flag that requests us to use SIMD, if it is available bool bUseSIMD; + + // The number of threads to spawn in order to process the data int iNumThreads; + + // Some compression formats take a measurement of quality when + // compressing an image. If the format supports it, this value + // will be used for quality purposes. int iQuality; + + // The number of compressions to perform. The program will compress + // the image this many times, and then take the average of the timing. int iNumCompressions; + + // This setting measures the number of blocks that a thread + // will process at any given time. If this value is zero, + // which is the default, the work will be divided by the + // number of threads, and each thread will do it's job and + // exit. + int iJobSize; }; -extern CompressedImage * CompressImage( - const ImageFile &, +extern bool CompressImageData( + const unsigned char *data, + const unsigned int dataSz, + unsigned char *cmpData, + const unsigned int cmpDataSz, const SCompressionSettings &settings ); +// A compression function format. It takes the raw data and image dimensions and +// returns the compressed image data into outData. It is assumed that there is +// enough space allocated for outData to store the compressed data. Allocation +// is dependent on the compression format. typedef void (* CompressionFunc)( - const unsigned char *inData, - unsigned char *outData, - unsigned int width, - unsigned int height + const unsigned char *inData, // Raw image data + unsigned char *outData, // Buffer to store compressed data. + unsigned int width, // Image width + unsigned int height // Image height ); -extern double ComputePSNR(const CompressedImage &, const ImageFile &); +// This function computes the Peak Signal to Noise Ratio between a +// compressed image and a raw image. +extern double ComputePSNR(const CompressedImage &ci, const ImageFile &file); #endif //_TEX_COMP_H_ diff --git a/Core/src/TexComp.cpp b/Core/src/TexComp.cpp index b256d6e..d3e6cdb 100644 --- a/Core/src/TexComp.cpp +++ b/Core/src/TexComp.cpp @@ -1,6 +1,7 @@ #include "BC7Compressor.h" #include "TexComp.h" #include "ThreadGroup.h" +#include "ImageFile.h" #include #include @@ -22,6 +23,11 @@ static void clamp(T &x, const T &minX, const T &maxX) { x = max(min(maxX, x), minX); } +template +static inline T sad(const T &a, const T &b) { + return (a > b)? a - b : b - a; +} + SCompressionSettings:: SCompressionSettings() : format(eCompressionFormat_BPTC) , bUseSIMD(false) @@ -58,7 +64,8 @@ static void ReportError(const char *msg) { } static double CompressImageInSerial( - const ImageFile &img, + const unsigned char *imgData, + const unsigned int imgDataSz, const SCompressionSettings &settings, const CompressionFunc f, unsigned char *outBuf @@ -71,7 +78,8 @@ static double CompressImageInSerial( stopWatch.Reset(); stopWatch.Start(); - (*f)(img.GetPixels(), outBuf, img.GetWidth(), img.GetHeight()); + // !FIXME! We're assuming that we have 4x4 blocks here... + (*f)(imgData, outBuf, imgDataSz / 16, 4); stopWatch.Stop(); @@ -83,16 +91,17 @@ static double CompressImageInSerial( } static double CompressImageWithThreads( - const ImageFile &img, + const unsigned char *imgData, + const unsigned int imgDataSz, const SCompressionSettings &settings, const CompressionFunc f, unsigned char *outBuf ) { - ThreadGroup tgrp (settings.iNumThreads, img, f, outBuf); + ThreadGroup tgrp (settings.iNumThreads, imgData, imgDataSz, f, outBuf); if(!(tgrp.PrepareThreads())) { assert(!"Thread group failed to prepare threads?!"); - return NULL; + return -1.0f; } double cmpTimeTotal = 0.0; @@ -120,10 +129,13 @@ static double CompressImageWithWorkerQueue( unsigned char *outBuf ) { return 0.0; -} +} -CompressedImage * CompressImage( - const ImageFile &img, +bool CompressImageData( + const unsigned char *data, + const unsigned int dataSz, + unsigned char *cmpData, + const unsigned int cmpDataSz, const SCompressionSettings &settings ) { @@ -132,27 +144,31 @@ CompressedImage * CompressImage( #ifndef HAS_SSE_41 if(settings.bUseSIMD) { ReportError("Platform does not support SIMD!\n"); - return NULL; + return false; } #endif - const unsigned int dataSz = img.GetWidth() * img.GetHeight() * 4; + if(dataSz <= 0) { + ReportError("No data sent to compress!"); + return false; + } // Allocate data based on the compression method - int cmpDataSz = 0; + int cmpDataSzNeeded = 0; switch(settings.format) { - case eCompressionFormat_DXT1: cmpDataSz = dataSz / 8; - case eCompressionFormat_DXT5: cmpDataSz = dataSz / 4; - case eCompressionFormat_BPTC: cmpDataSz = dataSz / 4; + case eCompressionFormat_DXT1: cmpDataSzNeeded = dataSz / 8; + case eCompressionFormat_DXT5: cmpDataSzNeeded = dataSz / 4; + case eCompressionFormat_BPTC: cmpDataSzNeeded = dataSz / 4; } - if(cmpDataSz == 0) { + if(cmpDataSzNeeded == 0) { ReportError("Unknown compression format"); - return NULL; + return false; + } + else if(cmpDataSzNeeded > cmpDataSz) { + ReportError("Not enough space for compressed data!"); + return false; } - - CompressedImage *outImg = NULL; - unsigned char *cmpData = new unsigned char[cmpDataSz]; CompressionFunc f = ChooseFuncFromSettings(settings); if(f) { @@ -160,32 +176,21 @@ CompressedImage * CompressImage( double cmpMSTime = 0.0; if(settings.iNumThreads > 1) { - cmpMSTime = CompressImageWithThreads(img, settings, f, cmpData); + cmpMSTime = CompressImageWithThreads(data, dataSz, settings, f, cmpData); } else { - cmpMSTime = CompressImageInSerial(img, settings, f, cmpData); + cmpMSTime = CompressImageInSerial(data, dataSz, settings, f, cmpData); } - outImg = new CompressedImage(img.GetWidth(), img.GetHeight(), settings.format, cmpData); - // Report compression time fprintf(stdout, "Compression time: %0.3f ms\n", cmpMSTime); } else { ReportError("Could not find adequate compression function for specified settings"); - delete [] cmpData; - return NULL; + return false; } - // Cleanup - delete [] cmpData; - - return outImg; -} - -template -static inline T sad(const T &a, const T &b) { - return (a > b)? a - b : b - a; + return true; } double ComputePSNR(const CompressedImage &ci, const ImageFile &file) { @@ -196,7 +201,7 @@ double ComputePSNR(const CompressedImage &ci, const ImageFile &file) { return -1.0f; } - const unsigned char *rawData = file.GetPixels(); + const unsigned char *rawData = file.RawData(); const double wr = 1.0; const double wg = 1.0; diff --git a/Core/src/ThreadGroup.cpp b/Core/src/ThreadGroup.cpp index 7926899..4263619 100644 --- a/Core/src/ThreadGroup.cpp +++ b/Core/src/ThreadGroup.cpp @@ -52,14 +52,15 @@ void CmpThread::operator()() { } -ThreadGroup::ThreadGroup( int numThreads, const ImageFile &image, CompressionFunc func, unsigned char *outBuf ) +ThreadGroup::ThreadGroup( int numThreads, const unsigned char *inBuf, unsigned int inBufSz, CompressionFunc func, unsigned char *outBuf ) : m_StartBarrier(new boost::barrier(numThreads + 1)) , m_FinishMutex(new boost::mutex()) , m_FinishCV(new boost::condition_variable()) , m_NumThreads(numThreads) , m_ActiveThreads(0) , m_Func(func) - , m_Image(image) + , m_ImageDataSz(inBufSz) + , m_ImageData(inBuf) , m_OutBuf(outBuf) , m_ThreadState(eThreadState_Done) , m_ExitFlag(false) @@ -107,13 +108,9 @@ bool ThreadGroup::PrepareThreads() { return true; } - // Make sure that the image dimensions are multiples of 4 - assert((m_Image.GetWidth() & 3) == 0); - assert((m_Image.GetHeight() & 3) == 0); - // We can assume that the image data is in block stream order // so, the size of the data given to each thread will be (nb*4)x4 - int numBlocks = (m_Image.GetWidth() * m_Image.GetHeight()) / 16; + int numBlocks = m_ImageDataSz / 64; int blocksProcessed = 0; int blocksPerThread = (numBlocks/m_NumThreads) + ((numBlocks % m_NumThreads)? 1 : 0); @@ -135,7 +132,7 @@ bool ThreadGroup::PrepareThreads() { t.m_Width = numBlocksThisThread * 4; t.m_CmpFunc = m_Func; t.m_OutBuf = m_OutBuf + (blocksProcessed * GetCompressedBlockSize()); - t.m_InBuf = m_Image.GetPixels() + (blocksProcessed * GetUncompressedBlockSize()); + t.m_InBuf = m_ImageData + (blocksProcessed * GetUncompressedBlockSize()); blocksProcessed += numBlocksThisThread; diff --git a/Core/src/ThreadGroup.h b/Core/src/ThreadGroup.h index 5435ffc..32894ac 100644 --- a/Core/src/ThreadGroup.h +++ b/Core/src/ThreadGroup.h @@ -42,7 +42,7 @@ public: class ThreadGroup { public: - ThreadGroup( int numThreads, const ImageFile &, CompressionFunc func, unsigned char *outBuf ); + ThreadGroup( int numThreads, const unsigned char *inBuf, unsigned int inBufSz, CompressionFunc func, unsigned char *outBuf ); ~ThreadGroup(); bool PrepareThreads(); @@ -74,7 +74,8 @@ class ThreadGroup { boost::thread *m_ThreadHandles[kMaxNumThreads]; // State variables. - const ImageFile &m_Image; + const unsigned int m_ImageDataSz; + const unsigned char *const m_ImageData; const CompressionFunc m_Func; unsigned char *m_OutBuf; diff --git a/IO/include/ImageFile.h b/IO/include/ImageFile.h index ea10efe..26641c1 100644 --- a/IO/include/ImageFile.h +++ b/IO/include/ImageFile.h @@ -3,6 +3,10 @@ #include "ImageFileFormat.h" +// Forward declare +class CompressedImage; + +// Class definition class ImageFile { public: @@ -13,7 +17,9 @@ public: unsigned int GetWidth() const { return m_Width; } unsigned int GetHeight() const { return m_Height; } - const unsigned char *GetPixels() const { return m_PixelData; } + CompressedImage *Compress(const SCompressionSettings &) const; + + const unsigned char *RawData() const { return m_PixelData; } private: unsigned int m_Handle; diff --git a/IO/src/ImageFile.cpp b/IO/src/ImageFile.cpp index 5fb75da..82034fa 100644 --- a/IO/src/ImageFile.cpp +++ b/IO/src/ImageFile.cpp @@ -4,8 +4,10 @@ #include #include +#include "TexComp.h" #include "ImageFile.h" #include "ImageLoader.h" +#include "CompressedImage.h" #ifdef PNG_FOUND # include "ImageLoaderPNG.h" @@ -302,3 +304,25 @@ unsigned char *ImageFile::ReadFileData(const char *filename) { return rawData; } #endif + +CompressedImage *ImageFile::Compress(const SCompressionSettings &settings) const { + CompressedImage *outImg = NULL; + const unsigned int dataSz = GetWidth() * GetHeight() * 4; + + assert(dataSz > 0); + + // Allocate data based on the compression method + int cmpDataSz = 0; + switch(settings.format) { + case eCompressionFormat_DXT1: cmpDataSz = dataSz / 8; + case eCompressionFormat_DXT5: cmpDataSz = dataSz / 4; + case eCompressionFormat_BPTC: cmpDataSz = dataSz / 4; + } + + unsigned char *cmpData = new unsigned char[cmpDataSz]; + CompressImageData(m_PixelData, dataSz, cmpData, cmpDataSz, settings); + + outImg = new CompressedImage(GetWidth(), GetHeight(), settings.format, cmpData); + return outImg; +} +