FasTC/CLTool/main.cpp

2127 lines
70 KiB
C++
Raw Normal View History

//--------------------------------------------------------------------------------------
// Copyright 2011 Intel Corporation
// All Rights Reserved
//
// Permission is granted to use, copy, distribute and prepare derivative works of this
// software for any purpose and without fee, provided, that the above copyright notice
// and this statement appear in all copies. Intel makes no representations about the
// suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS."
// INTEL SPECIFICALLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, AND ALL LIABILITY,
// INCLUDING CONSEQUENTIAL AND OTHER INDIRECT DAMAGES, FOR THE USE OF THIS SOFTWARE,
// INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PROPRIETARY RIGHTS, AND INCLUDING THE
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Intel does not
// assume any responsibility for any errors which may appear in this software nor any
// responsibility to update it.
//
//--------------------------------------------------------------------------------------
#include "DXUT.h"
#include "DXUTcamera.h"
#include "DXUTgui.h"
#include "DXUTsettingsDlg.h"
#include "SDKmisc.h"
#include "SDKMesh.h"
#include "DXTCompressorDLL.h" // DXT compressor DLL.
#include "BC7CompressorDLL.h" // BC7 compressor DLL.
#include "StopWatch.h" // Timer.
#include "TaskMgrTBB.h" // TBB task manager.
#include <tchar.h>
#include <strsafe.h>
#define ALIGN16(x) __declspec(align(16)) x
#define ALIGN32(x) __declspec(align(32)) x
// DXT compressor type.
enum ECompressorType
{
eCompType_DXT1,
eCompType_DXT5,
eCompType_BC7,
kNumCompressorTypes
};
const TCHAR *kCompressorTypeStr[kNumCompressorTypes] = {
_T("DXT1/BC1"),
_T("DXT5/BC3"),
_T("BC7"),
};
enum EInstructionSet
{
eInstrSet_Scalar
, eInstrSet_SSE
, eInstrSet_AVX2
, kNumInstructionSets
};
const TCHAR *kInstructionSetStr[kNumInstructionSets] = {
_T("Scalar"),
_T("SSE"),
_T("AVX2"),
};
enum EThreadMode
{
eThreadMode_None,
eThreadMode_TBB,
eThreadMode_Win32,
kNumThreadModes
};
const TCHAR *kThreadModeStr[kNumThreadModes] = {
_T("None"),
_T("TBB"),
_T("Win32")
};
static BOOL g_DXT1Available = TRUE;
static BOOL g_AVX2Available = FALSE;
static BOOL g_DX11Available = FALSE;
const struct ECompressionScheme {
const ECompressorType type;
const EInstructionSet instrSet;
const EThreadMode threadMode;
const BOOL &availabilityOverride;
} kCompressionSchemes[] = {
{ eCompType_DXT1, eInstrSet_Scalar, eThreadMode_None, g_DXT1Available },
{ eCompType_DXT1, eInstrSet_Scalar, eThreadMode_TBB, g_DXT1Available },
{ eCompType_DXT1, eInstrSet_Scalar, eThreadMode_Win32, g_DXT1Available },
{ eCompType_DXT1, eInstrSet_SSE, eThreadMode_None, g_DXT1Available },
{ eCompType_DXT1, eInstrSet_SSE, eThreadMode_TBB, g_DXT1Available },
{ eCompType_DXT1, eInstrSet_SSE, eThreadMode_Win32, g_DXT1Available },
{ eCompType_DXT5, eInstrSet_Scalar, eThreadMode_None, g_DXT1Available },
{ eCompType_DXT5, eInstrSet_Scalar, eThreadMode_TBB, g_DXT1Available },
{ eCompType_DXT5, eInstrSet_Scalar, eThreadMode_Win32, g_DXT1Available },
{ eCompType_DXT5, eInstrSet_SSE, eThreadMode_None, g_DXT1Available },
{ eCompType_DXT5, eInstrSet_SSE, eThreadMode_TBB, g_DXT1Available },
{ eCompType_DXT5, eInstrSet_SSE, eThreadMode_Win32, g_DXT1Available },
{ eCompType_BC7, eInstrSet_Scalar, eThreadMode_None, g_DX11Available },
{ eCompType_BC7, eInstrSet_Scalar, eThreadMode_Win32, g_DX11Available },
{ eCompType_BC7, eInstrSet_SSE, eThreadMode_None, g_DX11Available },
{ eCompType_BC7, eInstrSet_SSE, eThreadMode_Win32, g_DX11Available },
{ eCompType_DXT1, eInstrSet_AVX2, eThreadMode_None, g_AVX2Available },
{ eCompType_DXT1, eInstrSet_AVX2, eThreadMode_TBB, g_AVX2Available },
{ eCompType_DXT1, eInstrSet_AVX2, eThreadMode_Win32, g_AVX2Available },
{ eCompType_DXT5, eInstrSet_AVX2, eThreadMode_None, g_AVX2Available },
{ eCompType_DXT5, eInstrSet_AVX2, eThreadMode_TBB, g_AVX2Available },
{ eCompType_DXT5, eInstrSet_AVX2, eThreadMode_Win32, g_AVX2Available },
};
const int kNumCompressionSchemes = sizeof(kCompressionSchemes) / sizeof(kCompressionSchemes[0]);
const ECompressionScheme *gCompressionScheme = kCompressionSchemes;
// Textured vertex.
struct Vertex
{
D3DXVECTOR3 position;
D3DXVECTOR2 texCoord;
};
// Global variables
CDXUTDialogResourceManager gDialogResourceManager; // manager for shared resources of dialogs
CD3DSettingsDlg gD3DSettingsDlg; // Device settings dialog
CDXUTDialog gHUD; // manages the 3D
CDXUTDialog gSampleUI; // dialog for sample specific controls
bool gShowHelp = false; // If true, it renders the UI control text
CDXUTTextHelper* gTxtHelper = NULL;
double gCompTime = 0.0;
double gCompRate = 0.0;
int gBlocksPerTask = 256;
int gFrameNum = 0;
int gFrameDelay = 100;
int gTexWidth = 0;
int gTexHeight = 0;
double gError = 0.0;
#ifdef REPORT_RMSE
static const WCHAR *kErrorStr = L"Root Mean Squared Error";
#else
static const WCHAR *kErrorStr = L"Peak Signal/Noise Ratio";
#endif
ID3D11DepthStencilState* gDepthStencilState = NULL;
UINT gStencilReference = 0;
ID3D11InputLayout* gVertexLayout = NULL;
ID3D11Buffer* gVertexBuffer = NULL;
ID3D11Buffer* gQuadVB = NULL;
ID3D11Buffer* gIndexBuffer = NULL;
ID3D11VertexShader* gVertexShader = NULL;
ID3D11PixelShader* gRenderFramePS = NULL;
ID3D11PixelShader* gRenderTexturePS = NULL;
ID3D11SamplerState* gSamPoint = NULL;
ID3D11ShaderResourceView* gUncompressedSRV = NULL; // Shader resource view for the uncompressed texture resource.
ID3D11ShaderResourceView* gCompressedSRV = NULL; // Shader resource view for the compressed texture resource.
ID3D11ShaderResourceView* gErrorSRV = NULL; // Shader resource view for the error texture.
// Win32 thread API
const int kMaxWinThreads = 16;
enum EThreadState {
eThreadState_WaitForData,
eThreadState_DataLoaded,
eThreadState_Running,
eThreadState_Done
};
typedef void (* CompressionFunc)(const BYTE* inBuf, BYTE* outBuf, int width, int height);
struct WinThreadData {
EThreadState state;
int threadIdx;
const BYTE *inBuf;
BYTE *outBuf;
int width;
int height;
void (*cmpFunc)(const BYTE* inBuf, BYTE* outBuf, int width, int height);
// Defaults..
WinThreadData() :
state(eThreadState_Done),
threadIdx(-1),
inBuf(NULL),
outBuf(NULL),
width(-1),
height(-1),
cmpFunc(NULL)
{ }
} gWinThreadData[kMaxWinThreads];
HANDLE gWinThreadWorkEvent[kMaxWinThreads];
HANDLE gWinThreadStartEvent = NULL;
HANDLE gWinThreadDoneEvent = NULL;
int gNumWinThreads = 0;
DWORD gNumProcessors = 1; // We have at least one processor.
DWORD dwThreadIdArray[kMaxWinThreads];
HANDLE hThreadArray[kMaxWinThreads];
// UI control IDs
#define IDC_TOGGLEFULLSCREEN 1
#define IDC_TOGGLEREF 2
#define IDC_CHANGEDEVICE 3
#define IDC_UNCOMPRESSEDTEXT 4
#define IDC_COMPRESSEDTEXT 5
#define IDC_ERRORTEXT 6
#define IDC_SIZETEXT 7
#define IDC_TIMETEXT 8
#define IDC_RATETEXT 9
#define IDC_TBB 10
#define IDC_SIMD 11
#define IDC_COMPRESSOR 12
#define IDC_BLOCKSPERTASKTEXT 13
#define IDC_BLOCKSPERTASK 14
#define IDC_LOADTEXTURE 15
#define IDC_RECOMPRESS 16
#define IDC_RMSETEXT 17
// Forward declarations
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext );
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
void* pUserContext );
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
bool CALLBACK IsD3D11DeviceAcceptable(const CD3D11EnumAdapterInfo *AdapterInfo, UINT Output, const CD3D11EnumDeviceInfo *DeviceInfo,
DXGI_FORMAT BackBufferFormat, bool bWindowed, void* pUserContext );
HRESULT CALLBACK OnD3D11CreateDevice( ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
void* pUserContext );
HRESULT CALLBACK OnD3D11ResizedSwapChain( ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain,
const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void CALLBACK OnD3D11ReleasingSwapChain( void* pUserContext );
void CALLBACK OnD3D11DestroyDevice( void* pUserContext );
void CALLBACK OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, double fTime,
float fElapsedTime, void* pUserContext );
void InitApp();
void RenderText();
void UpdateBlockSlider();
void UpdateCompressionAlgorithms();
void UpdateThreadingMode();
void UpdateCompressionModes();
void UpdateAllowedSettings();
void SetCompressionScheme(EInstructionSet instrSet, ECompressorType compType, EThreadMode threadMode);
HRESULT CreateTextures(LPTSTR file);
void DestroyTextures();
HRESULT LoadTexture(LPTSTR file);
HRESULT PadTexture(ID3D11ShaderResourceView** textureSRV);
HRESULT SaveTexture(ID3D11ShaderResourceView* textureSRV, LPTSTR file);
HRESULT CompressTexture(ID3D11ShaderResourceView* uncompressedSRV, ID3D11ShaderResourceView** compressedSRV);
HRESULT ComputeError(ID3D11ShaderResourceView* uncompressedSRV, ID3D11ShaderResourceView* compressedSRV, ID3D11ShaderResourceView** errorSRV);
HRESULT RecompressTexture();
void ComputeRMSE(const BYTE *errorData, const INT width, const INT height);
void InitWin32Threads();
void DestroyThreads();
void StoreDepthStencilState();
void RestoreDepthStencilState();
HRESULT DisableDepthTest();
namespace DXTC
{
VOID CompressImageDXT(const BYTE* inBuf, BYTE* outBuf, INT width, INT height);
VOID CompressImageDXTNoThread(const BYTE* inBuf, BYTE* outBuf, INT width, INT height);
VOID CompressImageDXTTBB(const BYTE* inBuf, BYTE* outBuf, INT width, INT height);
VOID CompressImageDXTWIN(const BYTE* inBuf, BYTE* outBuf, INT width, INT height);
DWORD WINAPI CompressImageDXTWinThread( LPVOID lpParam );
}
#ifdef ENABLE_AVX2
#ifdef _M_X64
/* On x64, we can't have inline assembly in C files, see avxtest.asm */
extern "C" int __stdcall supports_AVX2();
#else ifdef WIN32
/* AVX2 instructions require 64 bit mode. */
extern "C" int __stdcall supports_AVX2() {
return 0;
}
#endif // _M_X64
#endif // ENABLE_AVX2
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
#ifdef ENABLE_AVX2
g_AVX2Available = supports_AVX2();
#endif
// Make sure that the event array is set to null...
memset(gWinThreadWorkEvent, 0, sizeof(gWinThreadWorkEvent));
// Figure out how many cores there are on this machine
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
gNumProcessors = sysinfo.dwNumberOfProcessors;
// Make sure all of our threads are empty.
for(int i = 0; i < kMaxWinThreads; i++) {
hThreadArray[i] = NULL;
}
// Set DXUT callbacks
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackKeyboard( OnKeyboard );
DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackD3D11DeviceAcceptable( IsD3D11DeviceAcceptable );
DXUTSetCallbackD3D11DeviceCreated( OnD3D11CreateDevice );
DXUTSetCallbackD3D11SwapChainResized( OnD3D11ResizedSwapChain );
DXUTSetCallbackD3D11FrameRender( OnD3D11FrameRender );
DXUTSetCallbackD3D11SwapChainReleasing( OnD3D11ReleasingSwapChain );
DXUTSetCallbackD3D11DeviceDestroyed( OnD3D11DestroyDevice );
InitApp();
DXUTInit( true, true, NULL );
DXUTSetCursorSettings( true, true );
DXUTCreateWindow( L"Fast Texture Compressor" );
// Try to create a device with DX11 feature set
DXUTCreateDevice (D3D_FEATURE_LEVEL_11_0, true, 1280, 1024 );
// If we don't have an adequate driver, then we revert to DX10 feature set...
DXUTDeviceSettings settings = DXUTGetDeviceSettings();
if(settings.d3d11.DriverType == D3D_DRIVER_TYPE_UNKNOWN || settings.d3d11.DriverType == D3D_DRIVER_TYPE_NULL) {
DXUTCreateDevice(D3D_FEATURE_LEVEL_10_1, true, 1280, 1024);
// !HACK! Force enumeration here in order to relocate hardware with new feature level
DXUTGetD3D11Enumeration(true);
DXUTCreateDevice(D3D_FEATURE_LEVEL_10_1, true, 1280, 1024);
const TCHAR *noDx11msg = _T("Your hardware does not seem to support DX11. BC7 Compression is disabled.");
MessageBox(NULL, noDx11msg, _T("Error"), MB_OK);
}
else {
g_DX11Available = TRUE;
}
// Now that we know what things are allowed, update the available options.
UpdateAllowedSettings();
DXUTMainLoop();
// Destroy all of the threads...
DestroyThreads();
return DXUTGetExitCode();
}
// Initialize the app
void InitApp()
{
// Initialize dialogs
gD3DSettingsDlg.Init(&gDialogResourceManager);
gHUD.Init(&gDialogResourceManager);
gSampleUI.Init(&gDialogResourceManager);
gHUD.SetCallback(OnGUIEvent);
int x = 0;
int y = 10;
gHUD.AddButton(IDC_TOGGLEFULLSCREEN, L"Toggle full screen", x, y, 170, 23);
gHUD.AddButton(IDC_TOGGLEREF, L"Toggle REF (F3)", x, y += 26, 170, 23, VK_F3);
gHUD.AddButton(IDC_CHANGEDEVICE, L"Change device (F2)", x, y += 26, 170, 23, VK_F2);
gSampleUI.SetCallback(OnGUIEvent);
x = 0;
y = 0;
gSampleUI.AddStatic(IDC_UNCOMPRESSEDTEXT, L"Uncompressed", x, y, 125, 22);
gSampleUI.AddStatic(IDC_COMPRESSEDTEXT, L"Compressed", x, y, 125, 22);
gSampleUI.AddStatic(IDC_ERRORTEXT, L"Error", x, y, 125, 22);
WCHAR wstr[MAX_PATH];
swprintf_s(wstr, MAX_PATH, L"Texture Size: %d x %d", gTexWidth, gTexHeight);
gSampleUI.AddStatic(IDC_SIZETEXT, wstr, x, y, 125, 22);
swprintf_s(wstr, MAX_PATH, L"%s: %.2f", kErrorStr, gError);
gSampleUI.AddStatic(IDC_RMSETEXT, wstr, x, y, 125, 22);
swprintf_s(wstr, MAX_PATH, L"Compression Time: %0.2f ms", gCompTime);
gSampleUI.AddStatic(IDC_TIMETEXT, wstr, x, y, 125, 22);
swprintf_s(wstr, MAX_PATH, L"Compression Rate: %0.2f Mp/s", gCompRate);
gSampleUI.AddStatic(IDC_RATETEXT, wstr, x, y, 125, 22);
gSampleUI.AddComboBox(IDC_TBB, x, y, 95, 22);
gSampleUI.AddComboBox(IDC_SIMD, x, y, 140, 22);
gSampleUI.AddComboBox(IDC_COMPRESSOR, x, y, 105, 22);
swprintf_s(wstr, MAX_PATH, L"Blocks Per Task: %d", gBlocksPerTask);
gSampleUI.AddStatic(IDC_BLOCKSPERTASKTEXT, wstr, x, y, 125, 22);
gSampleUI.AddSlider(IDC_BLOCKSPERTASK, x, y, 256, 22, 1, 512, gBlocksPerTask);
gSampleUI.AddButton(IDC_LOADTEXTURE, L"Load Texture", x, y, 125, 22);
gSampleUI.AddButton(IDC_RECOMPRESS, L"Recompress", x, y, 125, 22);
}
// Called right before creating a D3D11 device, allowing the app to modify the device settings as needed
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
// Uncomment this to get debug information from D3D11
//pDeviceSettings->d3d11.CreateFlags |= D3D11_CREATE_DEVICE_DEBUG;
// For the first device created if its a REF device, optionally display a warning dialog box
static bool s_bFirstTime = true;
if( s_bFirstTime )
{
s_bFirstTime = false;
if( ( DXUT_D3D11_DEVICE == pDeviceSettings->ver &&
pDeviceSettings->d3d11.DriverType == D3D_DRIVER_TYPE_REFERENCE ) )
{
DXUTDisplaySwitchingToREFWarning( pDeviceSettings->ver );
}
}
return true;
}
// Handle updates to the scene.
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
}
// Render the help and statistics text
void RenderText()
{
UINT nBackBufferHeight = ( DXUTIsAppRenderingWithD3D9() ) ? DXUTGetD3D9BackBufferSurfaceDesc()->Height :
DXUTGetDXGIBackBufferSurfaceDesc()->Height;
gTxtHelper->Begin();
gTxtHelper->SetInsertionPos( 2, 0 );
gTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
gTxtHelper->DrawTextLine( DXUTGetFrameStats( false ) );
gTxtHelper->DrawTextLine( DXUTGetDeviceStats() );
// Draw help
if( gShowHelp )
{
gTxtHelper->SetInsertionPos( 2, nBackBufferHeight - 20 * 6 );
gTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
gTxtHelper->DrawTextLine( L"Controls:" );
gTxtHelper->SetInsertionPos( 20, nBackBufferHeight - 20 * 5 );
gTxtHelper->DrawTextLine( L"Hide help: F1\n"
L"Quit: ESC\n" );
}
else
{
gTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
gTxtHelper->DrawTextLine( L"Press F1 for help" );
}
gTxtHelper->End();
}
// Handle messages to the application
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
void* pUserContext )
{
// Pass messages to dialog resource manager calls so GUI state is updated correctly
*pbNoFurtherProcessing = gDialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
if( *pbNoFurtherProcessing )
return 0;
// Pass messages to settings dialog if its active
if( gD3DSettingsDlg.IsActive() )
{
gD3DSettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
return 0;
}
// Give the dialogs a chance to handle the message first
*pbNoFurtherProcessing = gHUD.MsgProc( hWnd, uMsg, wParam, lParam );
if( *pbNoFurtherProcessing )
return 0;
*pbNoFurtherProcessing = gSampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
if( *pbNoFurtherProcessing )
return 0;
return 0;
}
// Handle key presses
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
if( bKeyDown )
{
switch( nChar )
{
case VK_F1:
gShowHelp = !gShowHelp; break;
}
}
}
// Handles the GUI events
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
switch( nControlID )
{
case IDC_TOGGLEFULLSCREEN:
{
DXUTToggleFullScreen();
break;
}
case IDC_TOGGLEREF:
{
DXUTToggleREF();
break;
}
case IDC_CHANGEDEVICE:
{
gD3DSettingsDlg.SetActive( !gD3DSettingsDlg.IsActive() );
break;
}
case IDC_TIMETEXT:
{
WCHAR wstr[MAX_PATH];
swprintf_s(wstr, MAX_PATH, L"Compression Time: %0.2f ms", gCompTime);
gSampleUI.GetStatic(IDC_TIMETEXT)->SetText(wstr);
break;
}
case IDC_RATETEXT:
{
WCHAR wstr[MAX_PATH];
swprintf_s(wstr, MAX_PATH, L"Compression Rate: %0.2f Mp/s", gCompRate);
gSampleUI.GetStatic(IDC_RATETEXT)->SetText(wstr);
break;
}
case IDC_RMSETEXT:
{
WCHAR wstr[MAX_PATH];
swprintf_s(wstr, MAX_PATH, L"%s: %.2f", kErrorStr, gError);
gSampleUI.GetStatic(IDC_RMSETEXT)->SetText(wstr);
break;
}
case IDC_TBB:
{
// Shut down all previous threading abilities.
DestroyThreads();
EInstructionSet instrSet = gCompressionScheme->instrSet;
ECompressorType compType = gCompressionScheme->type;
EThreadMode newMode = (EThreadMode)(INT_PTR)gSampleUI.GetComboBox(IDC_TBB)->GetSelectedData();
switch(newMode) {
case eThreadMode_TBB:
// Initialize the TBB task manager.
gTaskMgr.Init();
break;
case eThreadMode_Win32:
InitWin32Threads();
break;
case eThreadMode_None:
// Do nothing, our threads are fine.
break;
}
SetCompressionScheme(instrSet, compType, newMode);
UpdateAllowedSettings();
// Recompress the texture.
RecompressTexture();
break;
}
case IDC_SIMD:
{
EThreadMode threadMode = gCompressionScheme->threadMode;
ECompressorType compType = gCompressionScheme->type;
EInstructionSet newInstrSet = (EInstructionSet)(INT_PTR)gSampleUI.GetComboBox(IDC_SIMD)->GetSelectedData();
// If we selected AVX2, then the total number of blocks when using AVX2 changes, so we need
// to reflect that in the slider.
UpdateBlockSlider();
SetCompressionScheme(newInstrSet, compType, threadMode);
UpdateAllowedSettings();
// Recompress the texture.
RecompressTexture();
break;
}
case IDC_COMPRESSOR:
{
EThreadMode threadMode = gCompressionScheme->threadMode;
EInstructionSet instrSet = gCompressionScheme->instrSet;
ECompressorType newCompType = (ECompressorType)(INT_PTR)gSampleUI.GetComboBox(IDC_COMPRESSOR)->GetSelectedData();
SetCompressionScheme(instrSet, newCompType, threadMode);
UpdateAllowedSettings();
// Recompress the texture.
RecompressTexture();
break;
}
case IDC_BLOCKSPERTASK:
{
gBlocksPerTask = gSampleUI.GetSlider(IDC_BLOCKSPERTASK)->GetValue();
WCHAR wstr[MAX_PATH];
swprintf_s(wstr, MAX_PATH, L"Blocks Per Task: %d", gBlocksPerTask);
gSampleUI.GetStatic(IDC_BLOCKSPERTASKTEXT)->SetText(wstr);
// Recompress the texture.
RecompressTexture();
break;
}
case IDC_LOADTEXTURE:
{
// Store the current working directory.
TCHAR workingDirectory[MAX_PATH];
GetCurrentDirectory(MAX_PATH, workingDirectory);
// Open a file dialog.
OPENFILENAME openFileName;
WCHAR file[MAX_PATH];
file[0] = 0;
ZeroMemory(&openFileName, sizeof(OPENFILENAME));
openFileName.lStructSize = sizeof(OPENFILENAME);
openFileName.lpstrFile = file;
openFileName.nMaxFile = MAX_PATH;
openFileName.lpstrFilter = L"DDS\0*.dds\0\0";
openFileName.nFilterIndex = 1;
openFileName.lpstrInitialDir = NULL;
openFileName.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if(GetOpenFileName(&openFileName))
{
CreateTextures(openFileName.lpstrFile);
}
// Restore the working directory. GetOpenFileName changes the current working directory which causes problems with relative paths to assets.
SetCurrentDirectory(workingDirectory);
break;
}
case IDC_RECOMPRESS:
{
// Recompress the texture.
RecompressTexture();
break;
}
}
}
// Reject any D3D11 devices that aren't acceptable by returning false
bool CALLBACK IsD3D11DeviceAcceptable( const CD3D11EnumAdapterInfo *AdapterInfo, UINT Output, const CD3D11EnumDeviceInfo *DeviceInfo,
DXGI_FORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
return true;
}
// Find and compile the specified shader
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
HRESULT hr = S_OK;
// find the file
WCHAR str[MAX_PATH];
V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, szFileName ) );
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
// Setting this flag improves the shader debugging experience, but still allows
// the shaders to be optimized and to run exactly the way they will run in
// the release configuration of this program.
dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif
ID3DBlob* pErrorBlob;
hr = D3DX11CompileFromFile( str, NULL, NULL, szEntryPoint, szShaderModel,
dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
if( FAILED(hr) )
{
if( pErrorBlob != NULL )
OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
SAFE_RELEASE( pErrorBlob );
return hr;
}
SAFE_RELEASE( pErrorBlob );
return S_OK;
}
// Create any D3D11 resources that aren't dependent on the back buffer
HRESULT CALLBACK OnD3D11CreateDevice( ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
void* pUserContext )
{
HRESULT hr;
ID3D11DeviceContext* pd3dImmediateContext = DXUTGetD3D11DeviceContext();
V_RETURN(gDialogResourceManager.OnD3D11CreateDevice(pd3dDevice, pd3dImmediateContext));
V_RETURN(gD3DSettingsDlg.OnD3D11CreateDevice(pd3dDevice));
gTxtHelper = new CDXUTTextHelper(pd3dDevice, pd3dImmediateContext, &gDialogResourceManager, 15);
// Create a vertex shader.
ID3DBlob* vertexShaderBuffer = NULL;
V_RETURN(CompileShaderFromFile(L"FastTextureCompressor\\FastTextureCompressor.hlsl", "PassThroughVS", "vs_4_0", &vertexShaderBuffer));
V_RETURN(pd3dDevice->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &gVertexShader));
// Create a pixel shader that renders the composite frame.
ID3DBlob* pixelShaderBuffer = NULL;
V_RETURN(CompileShaderFromFile(L"FastTextureCompressor\\FastTextureCompressor.hlsl", "RenderFramePS", "ps_4_0", &pixelShaderBuffer));
V_RETURN(pd3dDevice->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &gRenderFramePS));
// Create a pixel shader that renders the error texture.
V_RETURN(CompileShaderFromFile(L"FastTextureCompressor\\FastTextureCompressor.hlsl", "RenderTexturePS", "ps_4_0", &pixelShaderBuffer));
V_RETURN(pd3dDevice->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &gRenderTexturePS));
// Create our vertex input layout
const D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
V_RETURN(pd3dDevice->CreateInputLayout(layout, ARRAYSIZE(layout), vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &gVertexLayout));
SAFE_RELEASE(vertexShaderBuffer);
SAFE_RELEASE(pixelShaderBuffer);
// Create a vertex buffer for three textured quads.
D3DXVECTOR2 quadSize(0.32f, 0.32f);
D3DXVECTOR2 quadOrigin(-0.66f, -0.0f);
Vertex tripleQuadVertices[18];
ZeroMemory(tripleQuadVertices, sizeof(tripleQuadVertices));
for(int i = 0; i < 18; i += 6)
{
tripleQuadVertices[i].position = D3DXVECTOR3(quadOrigin.x - quadSize.x, quadOrigin.y + quadSize.y, 0.0f);
tripleQuadVertices[i].texCoord = D3DXVECTOR2(0.0f, 0.0f);
tripleQuadVertices[i + 1].position = D3DXVECTOR3(quadOrigin.x + quadSize.x, quadOrigin.y + quadSize.y, 0.0f);
tripleQuadVertices[i + 1].texCoord = D3DXVECTOR2(1.0f, 0.0f);
tripleQuadVertices[i + 2].position = D3DXVECTOR3(quadOrigin.x + quadSize.x, quadOrigin.y - quadSize.y, 0.0f);
tripleQuadVertices[i + 2].texCoord = D3DXVECTOR2(1.0f, 1.0f);
tripleQuadVertices[i + 3].position = D3DXVECTOR3(quadOrigin.x + quadSize.x, quadOrigin.y - quadSize.y, 0.0f);
tripleQuadVertices[i + 3].texCoord = D3DXVECTOR2(1.0f, 1.0f);
tripleQuadVertices[i + 4].position = D3DXVECTOR3(quadOrigin.x - quadSize.x, quadOrigin.y - quadSize.y, 0.0f);
tripleQuadVertices[i + 4].texCoord = D3DXVECTOR2(0.0f, 1.0f);
tripleQuadVertices[i + 5].position = D3DXVECTOR3(quadOrigin.x - quadSize.x, quadOrigin.y + quadSize.y, 0.0f);
tripleQuadVertices[i + 5].texCoord = D3DXVECTOR2(0.0f, 0.0f);
quadOrigin.x += 0.66f;
}
D3D11_BUFFER_DESC bufDesc;
ZeroMemory(&bufDesc, sizeof(bufDesc));
bufDesc.Usage = D3D11_USAGE_DEFAULT;
bufDesc.ByteWidth = sizeof(tripleQuadVertices);
bufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(data));
data.pSysMem = tripleQuadVertices;
V_RETURN(pd3dDevice->CreateBuffer(&bufDesc, &data, &gVertexBuffer));
// Create a vertex buffer for a single textured quad.
quadSize = D3DXVECTOR2(1.0f, 1.0f);
quadOrigin = D3DXVECTOR2(0.0f, 0.0f);
Vertex singleQuadVertices[6];
singleQuadVertices[0].position = D3DXVECTOR3(quadOrigin.x - quadSize.x, quadOrigin.y + quadSize.y, 0.0f);
singleQuadVertices[0].texCoord = D3DXVECTOR2(0.0f, 0.0f);
singleQuadVertices[1].position = D3DXVECTOR3(quadOrigin.x + quadSize.x, quadOrigin.y + quadSize.y, 0.0f);
singleQuadVertices[1].texCoord = D3DXVECTOR2(1.0f, 0.0f);
singleQuadVertices[2].position = D3DXVECTOR3(quadOrigin.x + quadSize.x, quadOrigin.y - quadSize.y, 0.0f);
singleQuadVertices[2].texCoord = D3DXVECTOR2(1.0f, 1.0f);
singleQuadVertices[3].position = D3DXVECTOR3(quadOrigin.x + quadSize.x, quadOrigin.y - quadSize.y, 0.0f);
singleQuadVertices[3].texCoord = D3DXVECTOR2(1.0f, 1.0f);
singleQuadVertices[4].position = D3DXVECTOR3(quadOrigin.x - quadSize.x, quadOrigin.y - quadSize.y, 0.0f);
singleQuadVertices[4].texCoord = D3DXVECTOR2(0.0f, 1.0f);
singleQuadVertices[5].position = D3DXVECTOR3(quadOrigin.x - quadSize.x, quadOrigin.y + quadSize.y, 0.0f);
singleQuadVertices[5].texCoord = D3DXVECTOR2(0.0f, 0.0f);
ZeroMemory(&bufDesc, sizeof(bufDesc));
bufDesc.Usage = D3D11_USAGE_DEFAULT;
bufDesc.ByteWidth = sizeof(singleQuadVertices);
bufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufDesc.CPUAccessFlags = 0;
ZeroMemory(&data, sizeof(data));
data.pSysMem = singleQuadVertices;
V_RETURN(pd3dDevice->CreateBuffer(&bufDesc, &data, &gQuadVB));
// Create a sampler state
D3D11_SAMPLER_DESC SamDesc;
SamDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
SamDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
SamDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
SamDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
SamDesc.MipLODBias = 0.0f;
SamDesc.MaxAnisotropy = 1;
SamDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
SamDesc.BorderColor[0] = SamDesc.BorderColor[1] = SamDesc.BorderColor[2] = SamDesc.BorderColor[3] = 0;
SamDesc.MinLOD = 0;
SamDesc.MaxLOD = D3D11_FLOAT32_MAX;
V_RETURN(pd3dDevice->CreateSamplerState(&SamDesc, &gSamPoint));
// Load and initialize the textures.
WCHAR path[MAX_PATH];
V_RETURN(DXUTFindDXSDKMediaFileCch(path, MAX_PATH, L"Images\\texture.dds"));
V_RETURN(CreateTextures(path));
return S_OK;
}
// Create any D3D11 resources that depend on the back buffer
HRESULT CALLBACK OnD3D11ResizedSwapChain( ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain,
const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
HRESULT hr;
V_RETURN( gDialogResourceManager.OnD3D11ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );
V_RETURN( gD3DSettingsDlg.OnD3D11ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );
gHUD.SetLocation( pBackBufferSurfaceDesc->Width - 170, 0 );
gHUD.SetSize( 170, 170 );
gSampleUI.SetLocation( 0, 0 );
gSampleUI.SetSize( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height );
int oneThirdWidth = int(gSampleUI.GetWidth() / 3.0f);
int oneThirdHeight = int(gSampleUI.GetHeight() / 3.0f);
int x = 20;
int y = oneThirdHeight - 20;
gSampleUI.GetStatic(IDC_UNCOMPRESSEDTEXT)->SetLocation(x, y);
gSampleUI.GetStatic(IDC_COMPRESSEDTEXT)->SetLocation(x += oneThirdWidth, y);
gSampleUI.GetStatic(IDC_ERRORTEXT)->SetLocation(x += oneThirdWidth, y);
x = gSampleUI.GetWidth() - 276;
y = gSampleUI.GetHeight() - 216;
gSampleUI.GetStatic(IDC_SIZETEXT)->SetLocation(x, y);
gSampleUI.GetStatic(IDC_RMSETEXT)->SetLocation(x, y += 26);
gSampleUI.GetStatic(IDC_TIMETEXT)->SetLocation(x, y += 26);
gSampleUI.GetStatic(IDC_RATETEXT)->SetLocation(x, y += 26);
gSampleUI.GetComboBox(IDC_SIMD)->SetLocation(x, y += 26);
gSampleUI.GetComboBox(IDC_COMPRESSOR)->SetLocation(x + 150, y);
gSampleUI.GetStatic(IDC_BLOCKSPERTASKTEXT)->SetLocation(x, y += 26);
gSampleUI.GetComboBox(IDC_TBB)->SetLocation(x + 160, y);
gSampleUI.GetSlider(IDC_BLOCKSPERTASK)->SetLocation(x, y += 26);
gSampleUI.GetButton(IDC_LOADTEXTURE)->SetLocation(x, y += 26);
gSampleUI.GetButton(IDC_RECOMPRESS)->SetLocation(x + 131, y);
return S_OK;
}
// Render the scene using the D3D11 device
void CALLBACK OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, double fTime,
float fElapsedTime, void* pUserContext )
{
// Recompress the texture gFrameDelay frames after the app has started. This produces more accurate timing of the
// compression algorithm.
if(gFrameNum == gFrameDelay)
{
RecompressTexture();
gFrameNum++;
}
else if(gFrameNum < gFrameDelay)
{
gFrameNum++;
}
// If the settings dialog is being shown, then render it instead of rendering the app's scene
if( gD3DSettingsDlg.IsActive() )
{
gD3DSettingsDlg.OnRender( fElapsedTime );
return;
}
// Clear the render target and depth stencil
float ClearColor[4] = { 0.02f, 0.02f, 0.02f, 1.0f };
ID3D11RenderTargetView* pRTV = DXUTGetD3D11RenderTargetView();
pd3dImmediateContext->ClearRenderTargetView( pRTV, ClearColor );
ID3D11DepthStencilView* pDSV = DXUTGetD3D11DepthStencilView();
pd3dImmediateContext->ClearDepthStencilView( pDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );
// Set the input layout.
pd3dImmediateContext->IASetInputLayout( gVertexLayout );
// Set the vertex buffer.
UINT stride = sizeof( Vertex );
UINT offset = 0;
pd3dImmediateContext->IASetVertexBuffers( 0, 1, &gVertexBuffer, &stride, &offset );
// Set the primitive topology
pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
// Set the shaders
pd3dImmediateContext->VSSetShader( gVertexShader, NULL, 0 );
pd3dImmediateContext->PSSetShader( gRenderFramePS, NULL, 0 );
// Set the texture sampler.
pd3dImmediateContext->PSSetSamplers( 0, 1, &gSamPoint );
// Render the uncompressed texture.
pd3dImmediateContext->PSSetShaderResources( 0, 1, &gUncompressedSRV );
pd3dImmediateContext->Draw( 6, 0 );
// Render the compressed texture.
pd3dImmediateContext->PSSetShaderResources( 0, 1, &gCompressedSRV );
pd3dImmediateContext->Draw( 6, 6 );
// Render the error texture.
pd3dImmediateContext->PSSetShaderResources( 0, 1, &gErrorSRV );
pd3dImmediateContext->Draw( 6, 12 );
DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" );
HRESULT hr;
V(gHUD.OnRender( fElapsedTime ));
V(gSampleUI.OnRender( fElapsedTime ));
RenderText();
DXUT_EndPerfEvent();
}
// Release D3D11 resources created in OnD3D11ResizedSwapChain
void CALLBACK OnD3D11ReleasingSwapChain( void* pUserContext )
{
gDialogResourceManager.OnD3D11ReleasingSwapChain();
}
// Release D3D11 resources created in OnD3D11CreateDevice
void CALLBACK OnD3D11DestroyDevice( void* pUserContext )
{
gDialogResourceManager.OnD3D11DestroyDevice();
gD3DSettingsDlg.OnD3D11DestroyDevice();
//CDXUTDirectionWidget::StaticOnD3D11DestroyDevice();
DXUTGetGlobalResourceCache().OnDestroyDevice();
SAFE_DELETE( gTxtHelper );
SAFE_RELEASE( gVertexLayout );
SAFE_RELEASE( gVertexBuffer );
SAFE_RELEASE( gQuadVB );
SAFE_RELEASE( gIndexBuffer );
SAFE_RELEASE( gVertexShader );
SAFE_RELEASE( gRenderFramePS );
SAFE_RELEASE( gRenderTexturePS );
SAFE_RELEASE( gSamPoint );
DestroyTextures();
}
// Free previously allocated texture resources and create new texture resources.
HRESULT CreateTextures(LPTSTR file)
{
// Destroy any previously created textures.
DestroyTextures();
// Load the uncompressed texture.
HRESULT hr;
V_RETURN(LoadTexture(file));
// Compress the texture.
V_RETURN(CompressTexture(gUncompressedSRV, &gCompressedSRV));
// Compute the error in the compressed texture.
V_RETURN(ComputeError(gUncompressedSRV, gCompressedSRV, &gErrorSRV));
return S_OK;
}
// Destroy texture resources.
void DestroyTextures()
{
SAFE_RELEASE(gErrorSRV);
SAFE_RELEASE(gCompressedSRV);
SAFE_RELEASE(gUncompressedSRV);
}
// This functions loads a texture and prepares it for compression. The compressor only works on texture
// dimensions that are divisible by 4. Textures that are not divisible by 4 are resized and padded with the edge values.
HRESULT LoadTexture(LPTSTR file)
{
// Load the uncrompressed texture.
// The loadInfo structure disables mipmapping by setting MipLevels to 1.
D3DX11_IMAGE_LOAD_INFO loadInfo;
ZeroMemory(&loadInfo, sizeof(D3DX11_IMAGE_LOAD_INFO));
loadInfo.Width = D3DX11_DEFAULT;
loadInfo.Height = D3DX11_DEFAULT;
loadInfo.Depth = D3DX11_DEFAULT;
loadInfo.FirstMipLevel = D3DX11_DEFAULT;
loadInfo.MipLevels = 1;
loadInfo.Usage = (D3D11_USAGE) D3DX11_DEFAULT;
loadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
loadInfo.CpuAccessFlags = D3DX11_DEFAULT;
loadInfo.MiscFlags = D3DX11_DEFAULT;
loadInfo.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
loadInfo.Filter = D3DX11_FILTER_POINT | D3DX11_FILTER_SRGB;
loadInfo.MipFilter = D3DX11_DEFAULT;
loadInfo.pSrcInfo = NULL;
HRESULT hr;
V_RETURN(D3DX11CreateShaderResourceViewFromFile(DXUTGetD3D11Device(), file, &loadInfo, NULL, &gUncompressedSRV, NULL));
// Pad the texture.
V_RETURN(PadTexture(&gUncompressedSRV));
// Query the texture description.
ID3D11Texture2D* tex;
gUncompressedSRV->GetResource((ID3D11Resource**)&tex);
D3D11_TEXTURE2D_DESC texDesc;
tex->GetDesc(&texDesc);
SAFE_RELEASE(tex);
// Update the UI's texture width and height.
gTexWidth = texDesc.Width;
gTexHeight = texDesc.Height;
WCHAR wstr[MAX_PATH];
swprintf_s(wstr, MAX_PATH, L"Texture Size: %d x %d", gTexWidth, gTexHeight);
gSampleUI.GetStatic(IDC_SIZETEXT)->SetText(wstr);
// gSampleUI.SendEvent(IDC_SIZETEXT, true, gSampleUI.GetStatic(IDC_SIZETEXT));
UpdateBlockSlider();
return S_OK;
}
void SetCompressionScheme(EInstructionSet instrSet, ECompressorType compType, EThreadMode threadMode) {
bool foundMatch = false;
for(int i = 0; i < kNumCompressionSchemes; i++) {
bool match = true;
match = match && kCompressionSchemes[i].instrSet == instrSet;
match = match && kCompressionSchemes[i].type == compType;
match = match && kCompressionSchemes[i].threadMode == threadMode;
if(match) {
gCompressionScheme = &(kCompressionSchemes[i]);
foundMatch = true;
break;
}
}
if(!foundMatch) {
OutputDebugString(L"ERROR: Did not find match for compression scheme, not changing.\n");
}
}
void UpdateCompressionModes() {
CDXUTComboBox *comboBox = gSampleUI.GetComboBox(IDC_COMPRESSOR);
comboBox->RemoveAllItems();
// If we're updating the compression modes, then see
// what we currently have selected and keep everything else constant.
EThreadMode currThreadMode = gCompressionScheme->threadMode;
EInstructionSet currInstrSet = gCompressionScheme->instrSet;
bool added[kNumCompressorTypes];
memset(added, 0, sizeof(added));
for(int i = 0; i < kNumCompressionSchemes; i++) {
bool match = kCompressionSchemes[i].instrSet == currInstrSet;
match = match && kCompressionSchemes[i].threadMode == currThreadMode;
match = match && kCompressionSchemes[i].availabilityOverride;
if(match) {
ECompressorType compType = kCompressionSchemes[i].type;
if(!added[compType]) {
comboBox->AddItem(kCompressorTypeStr[compType], (void*)(INT_PTR)compType);
added[compType] = true;
}
}
}
comboBox->SetSelectedByData((void *)(INT_PTR)(gCompressionScheme->type));
}
void UpdateCompressionAlgorithms() {
CDXUTComboBox *comboBox = gSampleUI.GetComboBox(IDC_SIMD);
comboBox->RemoveAllItems();
// If we're updating the compression algorithms, then see
// what we currently have selected and keep everything else constant.
EThreadMode currThreadMode = gCompressionScheme->threadMode;
ECompressorType currType = gCompressionScheme->type;
bool added[kNumInstructionSets];
memset(added, 0, sizeof(added));
for(int i = 0; i < kNumCompressionSchemes; i++) {
bool match = kCompressionSchemes[i].type == currType;
match = match && kCompressionSchemes[i].threadMode == currThreadMode;
match = match && kCompressionSchemes[i].availabilityOverride;
if(match) {
EInstructionSet instrSet = kCompressionSchemes[i].instrSet;
if(!added[instrSet]) {
comboBox->AddItem(kInstructionSetStr[instrSet], (void*)(INT_PTR)instrSet);
added[instrSet] = true;
}
}
}
comboBox->SetSelectedByData((void *)(INT_PTR)(gCompressionScheme->instrSet));
}
void UpdateThreadingMode() {
CDXUTComboBox *comboBox = gSampleUI.GetComboBox(IDC_TBB);
comboBox->RemoveAllItems();
// If we're updating the compression algorithms, then see
// what we currently have selected and keep everything else constant.
EInstructionSet currInstrSet = gCompressionScheme->instrSet;
ECompressorType currType = gCompressionScheme->type;
bool added[kNumThreadModes];
memset(added, 0, sizeof(added));
for(int i = 0; i < kNumCompressionSchemes; i++) {
bool match = kCompressionSchemes[i].type == currType;
match = match && kCompressionSchemes[i].instrSet == currInstrSet;
match = match && kCompressionSchemes[i].availabilityOverride;
if(match) {
EThreadMode threadMode = kCompressionSchemes[i].threadMode;
if(!added[threadMode]) {
comboBox->AddItem(kThreadModeStr[threadMode], (void*)(INT_PTR)threadMode);
added[threadMode] = true;
}
}
}
comboBox->SetSelectedByData((void *)(INT_PTR)(gCompressionScheme->threadMode));
}
void UpdateAllowedSettings() {
UpdateCompressionModes();
UpdateCompressionAlgorithms();
UpdateThreadingMode();
}
void UpdateBlockSlider() {
int blockRows = gTexHeight / 4;
int blockCols = gTexWidth / 4;
if(gCompressionScheme->instrSet == eInstrSet_AVX2) {
blockCols /= 2;
}
int numBlocks = blockRows * blockCols;
int blksPerProc = numBlocks / gNumProcessors;
gSampleUI.GetSlider(IDC_BLOCKSPERTASK)->SetRange(1, blksPerProc);
}
// Pad the texture to dimensions that are divisible by 4.
HRESULT PadTexture(ID3D11ShaderResourceView** textureSRV)
{
// Query the texture description.
ID3D11Texture2D* tex;
(*textureSRV)->GetResource((ID3D11Resource**)&tex);
D3D11_TEXTURE2D_DESC texDesc;
tex->GetDesc(&texDesc);
// Exit if the texture dimensions are divisible by 4.
if((texDesc.Width % 4 == 0) && (texDesc.Height % 4 == 0))
{
SAFE_RELEASE(tex);
return S_OK;
}
// Compute the size of the padded texture.
UINT padWidth = texDesc.Width / 4 * 4 + 4;
UINT padHeight = texDesc.Height / 4 * 4 + 4;
// Create a buffer for the padded texels.
BYTE* padTexels = new BYTE[padWidth * padHeight * 4];
// Create a staging resource for the texture.
HRESULT hr;
ID3D11Device* device = DXUTGetD3D11Device();
D3D11_TEXTURE2D_DESC stgTexDesc;
memcpy(&stgTexDesc, &texDesc, sizeof(D3D11_TEXTURE2D_DESC));
stgTexDesc.Usage = D3D11_USAGE_STAGING;
stgTexDesc.BindFlags = 0;
stgTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
ID3D11Texture2D* stgTex;
V_RETURN(device->CreateTexture2D(&stgTexDesc, NULL, &stgTex));
// Copy the texture into the staging resource.
ID3D11DeviceContext* deviceContext = DXUTGetD3D11DeviceContext();
deviceContext->CopyResource(stgTex, tex);
// Map the staging resource.
D3D11_MAPPED_SUBRESOURCE texData;
V_RETURN(deviceContext->Map(stgTex, D3D11CalcSubresource(0, 0, 1), D3D11_MAP_READ_WRITE, 0, &texData));
// Copy the beginning of each row.
BYTE* texels = (BYTE*)texData.pData;
for(UINT row = 0; row < stgTexDesc.Height; row++)
{
UINT rowStart = row * texData.RowPitch;
UINT padRowStart = row * padWidth * 4;
memcpy(padTexels + padRowStart, texels + rowStart, stgTexDesc.Width * 4);
// Pad the end of each row.
if(padWidth > stgTexDesc.Width)
{
BYTE* padVal = texels + rowStart + (stgTexDesc.Width - 1) * 4;
for(UINT padCol = stgTexDesc.Width; padCol < padWidth; padCol++)
{
UINT padColStart = padCol * 4;
memcpy(padTexels + padRowStart + padColStart, padVal, 4);
}
}
}
// Pad the end of each column.
if(padHeight > stgTexDesc.Height)
{
UINT lastRow = (stgTexDesc.Height - 1);
UINT lastRowStart = lastRow * padWidth * 4;
BYTE* padVal = padTexels + lastRowStart;
for(UINT padRow = stgTexDesc.Height; padRow < padHeight; padRow++)
{
UINT padRowStart = padRow * padWidth * 4;
memcpy(padTexels + padRowStart, padVal, padWidth * 4);
}
}
// Unmap the staging resources.
deviceContext->Unmap(stgTex, D3D11CalcSubresource(0, 0, 1));
// Create a padded texture.
D3D11_TEXTURE2D_DESC padTexDesc;
memcpy(&padTexDesc, &texDesc, sizeof(D3D11_TEXTURE2D_DESC));
padTexDesc.Width = padWidth;
padTexDesc.Height = padHeight;
D3D11_SUBRESOURCE_DATA padTexData;
ZeroMemory(&padTexData, sizeof(D3D11_SUBRESOURCE_DATA));
padTexData.pSysMem = padTexels;
padTexData.SysMemPitch = padWidth * sizeof(BYTE) * 4;
ID3D11Texture2D* padTex;
V_RETURN(device->CreateTexture2D(&padTexDesc, &padTexData, &padTex));
// Delete the padded texel buffer.
delete [] padTexels;
// Release the shader resource view for the texture.
SAFE_RELEASE(*textureSRV);
// Create a shader resource view for the padded texture.
D3D11_SHADER_RESOURCE_VIEW_DESC padTexSRVDesc;
padTexSRVDesc.Format = padTexDesc.Format;
padTexSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
padTexSRVDesc.Texture2D.MipLevels = padTexDesc.MipLevels;
padTexSRVDesc.Texture2D.MostDetailedMip = padTexDesc.MipLevels - 1;
V_RETURN(device->CreateShaderResourceView(padTex, &padTexSRVDesc, textureSRV));
// Release resources.
SAFE_RELEASE(padTex);
SAFE_RELEASE(stgTex);
SAFE_RELEASE(tex);
return S_OK;
}
// Save a texture to a file.
HRESULT SaveTexture(ID3D11ShaderResourceView* textureSRV, LPTSTR file)
{
// Get the texture resource.
ID3D11Resource* texRes;
textureSRV->GetResource(&texRes);
if(texRes == NULL)
{
return E_POINTER;
}
// Save the texture to a file.
HRESULT hr;
V_RETURN(D3DX11SaveTextureToFile(DXUTGetD3D11DeviceContext(), texRes, D3DX11_IFF_DDS, file));
// Release the texture resources.
SAFE_RELEASE(texRes);
return S_OK;
}
// Compress a texture.
HRESULT CompressTexture(ID3D11ShaderResourceView* uncompressedSRV, ID3D11ShaderResourceView** compressedSRV)
{
// Query the texture description of the uncompressed texture.
ID3D11Resource* uncompRes;
gUncompressedSRV->GetResource(&uncompRes);
D3D11_TEXTURE2D_DESC uncompTexDesc;
((ID3D11Texture2D*)uncompRes)->GetDesc(&uncompTexDesc);
// Create a 2D texture for the compressed texture.
HRESULT hr;
ID3D11Texture2D* compTex;
D3D11_TEXTURE2D_DESC compTexDesc;
memcpy(&compTexDesc, &uncompTexDesc, sizeof(D3D11_TEXTURE2D_DESC));
switch(gCompressionScheme->type) {
default:
case eCompType_DXT1:
compTexDesc.Format = DXGI_FORMAT_BC1_UNORM_SRGB;
break;
case eCompType_DXT5:
compTexDesc.Format = DXGI_FORMAT_BC3_UNORM_SRGB;
break;
case eCompType_BC7:
compTexDesc.Format = DXGI_FORMAT_BC7_UNORM_SRGB;
break;
}
ID3D11Device* device = DXUTGetD3D11Device();
V_RETURN(device->CreateTexture2D(&compTexDesc, NULL, &compTex));
// Create a shader resource view for the compressed texture.
SAFE_RELEASE(*compressedSRV);
D3D11_SHADER_RESOURCE_VIEW_DESC compSRVDesc;
compSRVDesc.Format = compTexDesc.Format;
compSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
compSRVDesc.Texture2D.MipLevels = compTexDesc.MipLevels;
compSRVDesc.Texture2D.MostDetailedMip = compTexDesc.MipLevels - 1;
V_RETURN(device->CreateShaderResourceView(compTex, &compSRVDesc, compressedSRV));
// Create a staging resource for the compressed texture.
compTexDesc.Usage = D3D11_USAGE_STAGING;
compTexDesc.BindFlags = 0;
compTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
ID3D11Texture2D* compStgTex;
V_RETURN(device->CreateTexture2D(&compTexDesc, NULL, &compStgTex));
// Create a staging resource for the uncompressed texture.
uncompTexDesc.Usage = D3D11_USAGE_STAGING;
uncompTexDesc.BindFlags = 0;
uncompTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
ID3D11Texture2D* uncompStgTex;
V_RETURN(device->CreateTexture2D(&uncompTexDesc, NULL, &uncompStgTex));
// Copy the uncompressed texture into the staging resource.
ID3D11DeviceContext* deviceContext = DXUTGetD3D11DeviceContext();
deviceContext->CopyResource(uncompStgTex, uncompRes);
// Map the staging resources.
D3D11_MAPPED_SUBRESOURCE uncompData;
V_RETURN(deviceContext->Map(uncompStgTex, D3D11CalcSubresource(0, 0, 1), D3D11_MAP_READ_WRITE, 0, &uncompData));
D3D11_MAPPED_SUBRESOURCE compData;
V_RETURN(deviceContext->Map(compStgTex, D3D11CalcSubresource(0, 0, 1), D3D11_MAP_READ_WRITE, 0, &compData));
// Time the compression.
StopWatch stopWatch;
stopWatch.Start();
const int kNumCompressions = 1;
for(int cmpNum = 0; cmpNum < kNumCompressions; cmpNum++) {
// Compress the uncompressed texels.
DXTC::CompressImageDXT((BYTE*)uncompData.pData, (BYTE*)compData.pData, uncompTexDesc.Width, uncompTexDesc.Height);
}
// Update the compression time.
stopWatch.Stop();
gCompTime = stopWatch.TimeInMilliseconds();
gSampleUI.SendEvent(IDC_TIMETEXT, true, gSampleUI.GetStatic(IDC_TIMETEXT));
// Compute the compression rate.
INT numPixels = compTexDesc.Width * compTexDesc.Height * kNumCompressions;
gCompRate = (double)numPixels / stopWatch.TimeInSeconds() / 1000000.0;
gSampleUI.SendEvent(IDC_RATETEXT, true, gSampleUI.GetStatic(IDC_RATETEXT));
stopWatch.Reset();
// Unmap the staging resources.
deviceContext->Unmap(compStgTex, D3D11CalcSubresource(0, 0, 1));
deviceContext->Unmap(uncompStgTex, D3D11CalcSubresource(0, 0, 1));
// Copy the staging resourse into the compressed texture.
deviceContext->CopyResource(compTex, compStgTex);
// Release resources.
SAFE_RELEASE(uncompStgTex);
SAFE_RELEASE(compStgTex);
SAFE_RELEASE(compTex);
SAFE_RELEASE(uncompRes);
return S_OK;
}
#define CHECK_WIN_THREAD_FUNC(x) \
do { \
if(NULL == (x)) { \
wchar_t wstr[256]; \
swprintf_s(wstr, L"Error detected from call %s at line %d of main.cpp", _T(#x), __LINE__); \
ReportWinThreadError(wstr); \
} \
} \
while(0)
void ReportWinThreadError(const wchar_t *str) {
// Retrieve the system error message for the last-error code.
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message.
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR)str) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
str, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK);
// Free error-handling buffer allocations.
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
void InitWin32Threads() {
// Already initialized?
if(gNumWinThreads > 0) {
return;
}
SetLastError(0);
gNumWinThreads = gNumProcessors;
if(gNumWinThreads >= MAXIMUM_WAIT_OBJECTS)
gNumWinThreads = MAXIMUM_WAIT_OBJECTS;
// Create the synchronization events.
for(int i = 0; i < gNumWinThreads; i++) {
CHECK_WIN_THREAD_FUNC(gWinThreadWorkEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL));
}
CHECK_WIN_THREAD_FUNC(gWinThreadStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL));
CHECK_WIN_THREAD_FUNC(gWinThreadDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL));
// Create threads
for(int threadIdx = 0; threadIdx < gNumWinThreads; threadIdx++) {
gWinThreadData[threadIdx].state = eThreadState_WaitForData;
CHECK_WIN_THREAD_FUNC(hThreadArray[threadIdx] = CreateThread(NULL, 0, DXTC::CompressImageDXTWinThread, &gWinThreadData[threadIdx], 0, &dwThreadIdArray[threadIdx]));
}
}
void DestroyThreads() {
switch(gCompressionScheme->threadMode) {
case eThreadMode_TBB:
{
// Shutdown the TBB task manager.
gTaskMgr.Shutdown();
}
break;
case eThreadMode_Win32:
{
// Release all windows threads that may be active...
for(int i=0; i < gNumWinThreads; i++) {
gWinThreadData[i].state = eThreadState_Done;
}
// Send the event for the threads to start.
CHECK_WIN_THREAD_FUNC(ResetEvent(gWinThreadDoneEvent));
CHECK_WIN_THREAD_FUNC(SetEvent(gWinThreadStartEvent));
// Wait for all the threads to finish....
DWORD dwWaitRet = WaitForMultipleObjects(gNumWinThreads, hThreadArray, TRUE, INFINITE);
if(WAIT_FAILED == dwWaitRet)
ReportWinThreadError(L"DestroyThreads() -- WaitForMultipleObjects");
// !HACK! This doesn't actually do anything. There is either a bug in the
// Intel compiler or the windows run-time that causes the threads to not
// be cleaned up properly if the following two lines of code are not present.
// Since we're passing INFINITE to WaitForMultipleObjects, that function will
// never time out and per-microsoft spec, should never give this return value...
// Even with these lines, the bug does not consistently disappear unless you
// clean and rebuild. Heigenbug?
//
// If we compile with MSVC, then the following two lines are not necessary.
else if(WAIT_TIMEOUT == dwWaitRet)
OutputDebugString(L"DestroyThreads() -- WaitForMultipleObjects -- TIMEOUT");
// Reset the start event
CHECK_WIN_THREAD_FUNC(ResetEvent(gWinThreadStartEvent));
CHECK_WIN_THREAD_FUNC(SetEvent(gWinThreadDoneEvent));
// Close all thread handles.
for(int i=0; i < gNumWinThreads; i++) {
CHECK_WIN_THREAD_FUNC(CloseHandle(hThreadArray[i]));
}
for(int i =0; i < kMaxWinThreads; i++ ){
hThreadArray[i] = NULL;
}
// Close all event handles...
CHECK_WIN_THREAD_FUNC(CloseHandle(gWinThreadDoneEvent));
gWinThreadDoneEvent = NULL;
CHECK_WIN_THREAD_FUNC(CloseHandle(gWinThreadStartEvent));
gWinThreadStartEvent = NULL;
for(int i = 0; i < gNumWinThreads; i++) {
CHECK_WIN_THREAD_FUNC(CloseHandle(gWinThreadWorkEvent[i]));
}
for(int i = 0; i < kMaxWinThreads; i++) {
gWinThreadWorkEvent[i] = NULL;
}
gNumWinThreads = 0;
}
break;
case eThreadMode_None:
// Do nothing.
break;
}
}
static inline DXGI_FORMAT GetNonSRGBFormat(DXGI_FORMAT f) {
switch(f) {
case DXGI_FORMAT_BC1_UNORM_SRGB: return DXGI_FORMAT_BC1_UNORM;
case DXGI_FORMAT_BC3_UNORM_SRGB: return DXGI_FORMAT_BC3_UNORM;
case DXGI_FORMAT_BC7_UNORM_SRGB: return DXGI_FORMAT_BC7_UNORM;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM;
default: assert(!"Unknown format!");
}
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
// Compute an "error" texture that represents the absolute difference in color between an
// uncompressed texture and a compressed texture.
HRESULT ComputeError(ID3D11ShaderResourceView* uncompressedSRV, ID3D11ShaderResourceView* compressedSRV, ID3D11ShaderResourceView** errorSRV)
{
HRESULT hr;
// Query the texture description of the uncompressed texture.
ID3D11Resource* uncompRes;
gUncompressedSRV->GetResource(&uncompRes);
D3D11_TEXTURE2D_DESC uncompTexDesc;
((ID3D11Texture2D*)uncompRes)->GetDesc(&uncompTexDesc);
// Query the texture description of the uncompressed texture.
ID3D11Resource* compRes;
gCompressedSRV->GetResource(&compRes);
D3D11_TEXTURE2D_DESC compTexDesc;
((ID3D11Texture2D*)compRes)->GetDesc(&compTexDesc);
// Create a 2D resource without gamma correction for the two textures.
compTexDesc.Format = GetNonSRGBFormat(compTexDesc.Format);
uncompTexDesc.Format = GetNonSRGBFormat(uncompTexDesc.Format);
ID3D11Device* device = DXUTGetD3D11Device();
ID3D11Texture2D* uncompTex;
device->CreateTexture2D(&uncompTexDesc, NULL, &uncompTex);
ID3D11Texture2D* compTex;
device->CreateTexture2D(&compTexDesc, NULL, &compTex);
// Create a shader resource view for the two textures.
D3D11_SHADER_RESOURCE_VIEW_DESC compSRVDesc;
compSRVDesc.Format = compTexDesc.Format;
compSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
compSRVDesc.Texture2D.MipLevels = compTexDesc.MipLevels;
compSRVDesc.Texture2D.MostDetailedMip = compTexDesc.MipLevels - 1;
ID3D11ShaderResourceView *compSRV;
V_RETURN(device->CreateShaderResourceView(compTex, &compSRVDesc, &compSRV));
D3D11_SHADER_RESOURCE_VIEW_DESC uncompSRVDesc;
uncompSRVDesc.Format = uncompTexDesc.Format;
uncompSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
uncompSRVDesc.Texture2D.MipLevels = uncompTexDesc.MipLevels;
uncompSRVDesc.Texture2D.MostDetailedMip = uncompTexDesc.MipLevels - 1;
ID3D11ShaderResourceView *uncompSRV;
V_RETURN(device->CreateShaderResourceView(uncompTex, &uncompSRVDesc, &uncompSRV));
// Create a 2D texture for the error texture.
ID3D11Texture2D* errorTex;
D3D11_TEXTURE2D_DESC errorTexDesc;
memcpy(&errorTexDesc, &uncompTexDesc, sizeof(D3D11_TEXTURE2D_DESC));
errorTexDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
V_RETURN(device->CreateTexture2D(&errorTexDesc, NULL, &errorTex));
// Create a render target view for the error texture.
D3D11_RENDER_TARGET_VIEW_DESC errorRTVDesc;
errorRTVDesc.Format = errorTexDesc.Format;
errorRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
errorRTVDesc.Texture2D.MipSlice = 0;
ID3D11RenderTargetView* errorRTV;
V_RETURN(device->CreateRenderTargetView(errorTex, &errorRTVDesc, &errorRTV));
// Create a shader resource view for the error texture.
D3D11_SHADER_RESOURCE_VIEW_DESC errorSRVDesc;
errorSRVDesc.Format = errorTexDesc.Format;
errorSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
errorSRVDesc.Texture2D.MipLevels = errorTexDesc.MipLevels;
errorSRVDesc.Texture2D.MostDetailedMip = errorTexDesc.MipLevels - 1;
V_RETURN(device->CreateShaderResourceView(errorTex, &errorSRVDesc, errorSRV));
// Create a query for the GPU operations...
D3D11_QUERY_DESC GPUQueryDesc;
GPUQueryDesc.Query = D3D11_QUERY_EVENT;
GPUQueryDesc.MiscFlags = 0;
#ifdef _DEBUG
D3D11_QUERY_DESC OcclusionQueryDesc;
OcclusionQueryDesc.Query = D3D11_QUERY_OCCLUSION;
OcclusionQueryDesc.MiscFlags = 0;
D3D11_QUERY_DESC StatsQueryDesc;
StatsQueryDesc.Query = D3D11_QUERY_PIPELINE_STATISTICS;
StatsQueryDesc.MiscFlags = 0;
#endif
ID3D11Query *GPUQuery;
V_RETURN(device->CreateQuery(&GPUQueryDesc, &GPUQuery));
ID3D11DeviceContext* deviceContext = DXUTGetD3D11DeviceContext();
deviceContext->CopyResource(compTex, compRes);
deviceContext->CopyResource(uncompTex, uncompRes);
#ifdef _DEBUG
ID3D11Query *OcclusionQuery, *StatsQuery;
V_RETURN(device->CreateQuery(&OcclusionQueryDesc, &OcclusionQuery));
V_RETURN(device->CreateQuery(&StatsQueryDesc, &StatsQuery));
deviceContext->Begin(OcclusionQuery);
deviceContext->Begin(StatsQuery);
#endif
// Set the viewport to a 1:1 mapping of pixels to texels.
D3D11_VIEWPORT viewport;
viewport.Width = (FLOAT)errorTexDesc.Width;
viewport.Height = (FLOAT)errorTexDesc.Height;
viewport.MinDepth = 0;
viewport.MaxDepth = 1;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
deviceContext->RSSetViewports(1, &viewport);
// Bind the render target view of the error texture.
ID3D11RenderTargetView* RTV[1] = { errorRTV };
deviceContext->OMSetRenderTargets(1, RTV, NULL);
// Clear the render target.
FLOAT color[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
deviceContext->ClearRenderTargetView(errorRTV, color);
// Set the input layout.
deviceContext->IASetInputLayout(gVertexLayout);
// Set vertex buffer
UINT stride = sizeof(Vertex);
UINT offset = 0;
deviceContext->IASetVertexBuffers(0, 1, &gQuadVB, &stride, &offset);
// Set the primitive topology
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Set the shaders
deviceContext->VSSetShader(gVertexShader, NULL, 0);
deviceContext->PSSetShader(gRenderTexturePS, NULL, 0);
// Set the texture sampler.
deviceContext->PSSetSamplers(0, 1, &gSamPoint);
// Bind the textures.
ID3D11ShaderResourceView* SRV[2] = { compSRV, uncompSRV};
deviceContext->PSSetShaderResources(0, 2, SRV);
// Store the depth/stencil state.
StoreDepthStencilState();
// Disable depth testing.
V_RETURN(DisableDepthTest());
// Render a quad.
deviceContext->Draw(6, 0);
// Restore the depth/stencil state.
RestoreDepthStencilState();
// Reset the render target.
RTV[0] = DXUTGetD3D11RenderTargetView();
deviceContext->OMSetRenderTargets(1, RTV, DXUTGetD3D11DepthStencilView());
// Reset the viewport.
viewport.Width = (FLOAT)DXUTGetDXGIBackBufferSurfaceDesc()->Width;
viewport.Height = (FLOAT)DXUTGetDXGIBackBufferSurfaceDesc()->Height;
deviceContext->RSSetViewports(1, &viewport);
deviceContext->End(GPUQuery);
#ifdef _DEBUG
deviceContext->End(OcclusionQuery);
deviceContext->End(StatsQuery);
#endif
BOOL finishedGPU = false;
// If we do not have a d3d 11 context, we will still hit this line and try to
// finish using the GPU. If this happens this enters an infinite loop.
int infLoopPrevention = 0;
while(!finishedGPU && ++infLoopPrevention < 10000) {
HRESULT ret;
V_RETURN(ret = deviceContext->GetData(GPUQuery, &finishedGPU, sizeof(BOOL), 0));
if(ret != S_OK)
Sleep(1);
}
#ifdef _DEBUG
UINT64 nPixelsWritten = 0;
deviceContext->GetData(OcclusionQuery, (void *)&nPixelsWritten, sizeof(UINT64), 0);
D3D11_QUERY_DATA_PIPELINE_STATISTICS stats;
deviceContext->GetData(StatsQuery, (void *)&stats, sizeof(D3D11_QUERY_DATA_PIPELINE_STATISTICS), 0);
TCHAR nPixelsWrittenMsg[256];
_stprintf(nPixelsWrittenMsg, _T("Pixels rendered during error computation: %d\n"), nPixelsWritten);
OutputDebugString(nPixelsWrittenMsg);
#endif
// Create a copy of the error texture that is accessible by the CPU
ID3D11Texture2D* errorTexCopy;
D3D11_TEXTURE2D_DESC errorTexCopyDesc;
memcpy(&errorTexCopyDesc, &uncompTexDesc, sizeof(D3D11_TEXTURE2D_DESC));
errorTexCopyDesc.Usage = D3D11_USAGE_STAGING;
errorTexCopyDesc.BindFlags = 0;
errorTexCopyDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
V_RETURN(device->CreateTexture2D(&errorTexCopyDesc, NULL, &errorTexCopy));
// Copy the error texture into the copy....
deviceContext->CopyResource(errorTexCopy, errorTex);
// Map the staging resource.
D3D11_MAPPED_SUBRESOURCE errorData;
V_RETURN(deviceContext->Map(errorTexCopy, D3D11CalcSubresource(0, 0, 1), D3D11_MAP_READ, 0, &errorData));
// Calculate PSNR
ComputeRMSE((const BYTE *)(errorData.pData), errorTexCopyDesc.Width, errorTexCopyDesc.Height);
gSampleUI.SendEvent(IDC_RMSETEXT, true, gSampleUI.GetStatic(IDC_RMSETEXT));
// Unmap the staging resources.
deviceContext->Unmap(errorTexCopy, D3D11CalcSubresource(0, 0, 1));
// Release resources.
SAFE_RELEASE(errorRTV);
SAFE_RELEASE(errorTex);
SAFE_RELEASE(errorTexCopy);
SAFE_RELEASE(uncompRes);
SAFE_RELEASE(compRes);
SAFE_RELEASE(GPUQuery);
#ifdef _DEBUG
SAFE_RELEASE(OcclusionQuery);
SAFE_RELEASE(StatsQuery);
#endif
SAFE_RELEASE(compSRV);
SAFE_RELEASE(uncompSRV);
SAFE_RELEASE(compTex);
SAFE_RELEASE(uncompTex);
return S_OK;
}
// Recompresses the already loaded texture and recomputes the error.
HRESULT RecompressTexture()
{
// Destroy any previously created textures.
SAFE_RELEASE(gErrorSRV);
SAFE_RELEASE(gCompressedSRV);
// Compress the texture.
HRESULT hr;
V_RETURN(CompressTexture(gUncompressedSRV, &gCompressedSRV));
// Compute the error in the compressed texture.
V_RETURN(ComputeError(gUncompressedSRV, gCompressedSRV, &gErrorSRV));
return S_OK;
}
// Store the depth-stencil state.
void StoreDepthStencilState()
{
DXUTGetD3D11DeviceContext()->OMGetDepthStencilState(&gDepthStencilState, &gStencilReference);
}
// Restore the depth-stencil state.
void RestoreDepthStencilState()
{
DXUTGetD3D11DeviceContext()->OMSetDepthStencilState(gDepthStencilState, gStencilReference);
}
// Disable depth testing.
HRESULT DisableDepthTest()
{
D3D11_DEPTH_STENCIL_DESC depStenDesc;
ZeroMemory(&depStenDesc, sizeof(D3D11_DEPTH_STENCIL_DESC));
depStenDesc.DepthEnable = FALSE;
depStenDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depStenDesc.DepthFunc = D3D11_COMPARISON_LESS;
depStenDesc.StencilEnable = FALSE;
depStenDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
depStenDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
depStenDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depStenDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
depStenDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depStenDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
depStenDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depStenDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
depStenDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depStenDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
ID3D11DepthStencilState* depStenState;
HRESULT hr;
V_RETURN(DXUTGetD3D11Device()->CreateDepthStencilState(&depStenDesc, &depStenState));
DXUTGetD3D11DeviceContext()->OMSetDepthStencilState(depStenState, 0);
SAFE_RELEASE(depStenState);
return S_OK;
}
void ComputeRMSE(const BYTE *errorData, const INT width, const INT height) {
const float *w = BC7C::GetErrorMetric();
const double wr = w[0];
const double wg = w[1];
const double wb = w[2];
double MSE = 0.0;
for(int i = 0; i < width; i++) {
for(int j = 0; j < height; j++) {
const INT pixel = ((const INT *)errorData)[j * width + i];
double dr = double(pixel & 0xFF) * wr;
double dg = double((pixel >> 8) & 0xFF) * wg;
double db = double((pixel >> 16) & 0xFF) * wb;
const double pixelMSE = (double(dr) * double(dr)) + (double(dg) * double(dg)) + (double(db) * double(db));
MSE += pixelMSE;
}
}
MSE /= (double(width) * double(height));
#ifdef REPORT_RMSE
gError = sqrt(MSE);
#else
double MAXI = (255.0 * wr) * (255.0 * wr) + (255.0 * wg) * (255.0 * wg) + (255.0 * wb) * (255.0 * wb);
gError= 10 * log10(MAXI/MSE);
#endif
}
namespace DXTC
{
VOID CompressImageDXT(const BYTE* inBuf, BYTE* outBuf, INT width, INT height) {
// If we aren't multi-cored, then just run everything serially.
if(gNumProcessors <= 1) {
CompressImageDXTNoThread(inBuf, outBuf, width, height);
return;
}
switch(gCompressionScheme->threadMode) {
case eThreadMode_None:
CompressImageDXTNoThread(inBuf, outBuf, width, height);
break;
case eThreadMode_TBB:
CompressImageDXTTBB(inBuf, outBuf, width, height);
break;
case eThreadMode_Win32:
CompressImageDXTWIN(inBuf, outBuf, width, height);
break;
}
}
CompressionFunc GetCompressionFunc() {
switch(gCompressionScheme->instrSet)
{
case eInstrSet_SSE:
{
switch(gCompressionScheme->type) {
case eCompType_DXT1: return DXTC::CompressImageDXT1SSE2;
case eCompType_DXT5: return DXTC::CompressImageDXT5SSE2;
case eCompType_BC7: return BC7C::CompressImageBC7SIMD;
}
}
break;
case eInstrSet_Scalar:
{
switch(gCompressionScheme->type) {
case eCompType_DXT1: return DXTC::CompressImageDXT1;
case eCompType_DXT5: return DXTC::CompressImageDXT5;
case eCompType_BC7: return BC7C::CompressImageBC7;
}
}
break;
#ifdef ENABLE_AVX2
case eInstrSet_AVX2:
{
switch(gCompressionScheme->type) {
case eCompType_DXT1: return DXTC::CompressImageDXT1AVX2;
case eCompType_DXT5: return DXTC::CompressImageDXT5AVX2;
}
}
#endif
}
return NULL;
}
void CompressImageDXTNoThread(const BYTE* inBuf, BYTE* outBuf, INT width, INT height) {
CompressionFunc cmpFunc = GetCompressionFunc();
if(cmpFunc == NULL) {
OutputDebugString(L"DXTC::CompressImageDXTNoThread -- Compression Scheme not implemented!\n");
return;
}
// Do the compression.
(*cmpFunc)(inBuf, outBuf, width, height);
}
// Use the TBB task manager to compress an image with DXT compression.
VOID CompressImageDXTTBB(const BYTE* inBuf, BYTE* outBuf, INT width, INT height)
{
// Initialize the data.
DXTTaskData data;
data.inBuf = inBuf;
data.outBuf = outBuf;
data.width = width;
data.height = height;
data.numBlocks = width * height / 16;
if(gCompressionScheme->instrSet == eInstrSet_AVX2) {
data.numBlocks = width * height / 32;
}
data.kBlocksPerTask = gBlocksPerTask;
// Compute the task count.
UINT taskCount = (UINT)ceil((float)data.numBlocks / gBlocksPerTask);
// Create the task set.
TASKSETFUNC taskFunc = NULL;
switch(gCompressionScheme->instrSet)
{
case eInstrSet_SSE:
{
switch(gCompressionScheme->type) {
case eCompType_DXT1: taskFunc = DXTC::CompressImageDXT1SSE2Task; break;
case eCompType_DXT5: taskFunc = DXTC::CompressImageDXT5SSE2Task; break;
}
}
break;
case eInstrSet_Scalar:
{
switch(gCompressionScheme->type) {
case eCompType_DXT1: taskFunc = DXTC::CompressImageDXT1Task; break;
case eCompType_DXT5: taskFunc = DXTC::CompressImageDXT5Task; break;
}
}
break;
#ifdef ENABLE_AVX2
case eInstrSet_AVX2:
{
switch(gCompressionScheme->type) {
case eCompType_DXT1: taskFunc = DXTC::CompressImageDXT1AVX2Task; break;
case eCompType_DXT5: taskFunc = DXTC::CompressImageDXT5AVX2Task; break;
}
}
break;
#endif
}
TASKSETHANDLE taskSet;
gTaskMgr.CreateTaskSet(taskFunc, &data, taskCount, NULL, 0, "Fast Texture Compression", &taskSet);
if(taskSet == TASKSETHANDLE_INVALID)
{
return;
}
// Wait for the task set.
gTaskMgr.WaitForSet(taskSet);
// Release the task set.
gTaskMgr.ReleaseHandle(taskSet);
taskSet = TASKSETHANDLE_INVALID;
}
int GetBlocksPerLoop() {
if(gCompressionScheme->instrSet == eInstrSet_AVX2)
return 2;
return 1;
}
int GetBytesPerBlock() {
switch(gCompressionScheme->type) {
default:
case eCompType_DXT1:
return 8;
case eCompType_DXT5:
case eCompType_BC7:
return 16;
}
}
VOID CompressImageDXTWIN(const BYTE* inBuf, BYTE* outBuf, INT width, INT height) {
const int numThreads = gNumWinThreads;
const int blocksPerLoop = GetBlocksPerLoop();
const int bytesPerBlock = GetBytesPerBlock();
// We want to split the data evenly among all threads.
const int kNumPixels = width * height;
const int kNumBlocks = kNumPixels >> (3 + blocksPerLoop);
const int kBlocksPerRow = width >> (1 + blocksPerLoop);
const int kBlocksPerThread = kNumBlocks / numThreads;
const int kBlocksPerColumn = height >> 2;
const int kBlockRowsPerThread = kBlocksPerThread / kBlocksPerRow;
const int kBlockColsPerThread = kBlocksPerThread % kBlocksPerRow;
const int kOffsetPerThread = kBlockRowsPerThread * width * 4 * 4 + kBlockColsPerThread * 4 * 4 * (blocksPerLoop);
const int kHeightPerThread = (blocksPerLoop * 16 * kBlocksPerThread) / width;
CompressionFunc cmpFunc = GetCompressionFunc();
if(cmpFunc == NULL) {
OutputDebugString(L"DXTC::CompressImageDXTNoThread -- Compression Scheme not implemented!\n");
return;
}
// Load the threads.
for(int threadIdx = 0; threadIdx < numThreads; threadIdx++) {
WinThreadData *data = &gWinThreadData[threadIdx];
data->inBuf = inBuf + (threadIdx * kOffsetPerThread);
data->outBuf = outBuf + (threadIdx * kBlocksPerThread * blocksPerLoop * bytesPerBlock);
data->width = width;
data->height = kHeightPerThread;
data->cmpFunc = cmpFunc;
data->state = eThreadState_DataLoaded;
data->threadIdx = threadIdx;
}
// Send the event for the threads to start.
CHECK_WIN_THREAD_FUNC(ResetEvent(gWinThreadDoneEvent));
CHECK_WIN_THREAD_FUNC(SetEvent(gWinThreadStartEvent));
// Wait for all the threads to finish
if(WAIT_FAILED == WaitForMultipleObjects(numThreads, gWinThreadWorkEvent, TRUE, INFINITE))
ReportWinThreadError(TEXT("CompressImageDXTWIN -- WaitForMultipleObjects"));
// Reset the start event
CHECK_WIN_THREAD_FUNC(ResetEvent(gWinThreadStartEvent));
CHECK_WIN_THREAD_FUNC(SetEvent(gWinThreadDoneEvent));
}
DWORD WINAPI CompressImageDXTWinThread( LPVOID lpParam ) {
WinThreadData *data = (WinThreadData *)lpParam;
while(data->state != eThreadState_Done) {
if(WAIT_FAILED == WaitForSingleObject(gWinThreadStartEvent, INFINITE))
ReportWinThreadError(TEXT("CompressImageDXTWinThread -- WaitForSingleObject"));
if(data->state == eThreadState_Done)
break;
data->state = eThreadState_Running;
(*(data->cmpFunc))(data->inBuf, data->outBuf, data->width, data->height);
data->state = eThreadState_WaitForData;
HANDLE workEvent = gWinThreadWorkEvent[data->threadIdx];
if(WAIT_FAILED == SignalObjectAndWait(workEvent, gWinThreadDoneEvent, INFINITE, FALSE))
ReportWinThreadError(TEXT("CompressImageDXTWinThread -- SignalObjectAndWait"));
}
return 0;
}
}