Blame |
Last modification |
View Log
| RSS feed
#include "D3D8Helper.h"
#include "D3DTester.h"
#include "DirectXErrorString.h"
#include "Common.h"
#include "D3DInterface.h"
#include "SexyAppBase.h"
using namespace Sexy;
static const int gD3DTestTextureWidth = 64;
static const int gD3DTestTextureHeight = 64;
static bool gD3DTestHas32BitTexture = false;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
D3DTestImage::D3DTestImage()
{
mBits = NULL;
mWidth = 0;
mHeight = 0;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
D3DTestImage::D3DTestImage(int theWidth, int theHeight)
{
mBits = NULL;
mWidth = 0;
mHeight = 0;
Create(theWidth,theHeight);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
D3DTestImage::~D3DTestImage()
{
FreeImage();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::Create(int theWidth, int theHeight)
{
FreeImage();
if(theWidth>0 && theHeight>0)
{
mBits = new DWORD[theWidth*theHeight];
mWidth = theWidth;
mHeight = theHeight;
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::FreeImage()
{
delete [] mBits;
mWidth = 0;
mHeight = 0;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
const D3DTestImage& D3DTestImage::operator=(const D3DTestImage &theImage)
{
if (&theImage==this)
return *this;
Create(theImage.GetWidth(), theImage.GetHeight());
memcpy(mBits, theImage.GetBits(), mWidth*mHeight*4);
return *this;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTestImage::CompareEqual(const D3DTestImage &theImage) const
{
if (theImage.GetWidth() != GetWidth())
return false;
if (theImage.GetHeight() != GetHeight())
return false;
return memcmp(theImage.GetBits(),GetBits(),mWidth*mHeight*4)==0;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::FillRect(int x, int y, int theWidth, int theHeight, DWORD theColor)
{
DWORD *aRow = mBits + y*mWidth + x;
for(int j=0; j<theHeight; j++)
{
DWORD *aPixel = aRow;
for(int i=0; i<theWidth; i++)
*aPixel++ = theColor;
aRow += mWidth;
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::MakeVerticalBands()
{
for(int i=0; i<mWidth; i++)
FillRect(i,0,1,mHeight,i&1?0xFFFFFFFF:0xFF000000);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::CopyToTexture8888(LPDIRECTDRAWSURFACE7 theTexture, int offx, int offy, int texWidth, int texHeight)
{
DDSURFACEDESC2 aDesc;
aDesc.dwSize = sizeof(aDesc);
D3DTester::CheckDXError(theTexture->Lock(NULL,&aDesc,DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY,NULL),"Lock Texture");
int aWidth = min(texWidth,(GetWidth()-offx));
int aHeight = min(texHeight,(GetHeight()-offy));
if(aWidth < texWidth || aHeight < texHeight)
memset(aDesc.lpSurface, 0, aDesc.lPitch*aDesc.dwHeight);
if(aWidth>0 && aHeight>0)
{
DWORD *srcRow = GetBits() + offy * GetWidth() + offx;
char *dstRow = (char*)aDesc.lpSurface;
for(int y=0; y<aHeight; y++)
{
DWORD *src = srcRow;
DWORD *dst = (DWORD*)dstRow;
for(int x=0; x<aWidth; x++)
*dst++ = *src++;
srcRow += GetWidth();
dstRow += aDesc.lPitch;
}
}
D3DTester::CheckDXError(theTexture->Unlock(NULL),"Texture Unlock");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::CopyToTexture4444(LPDIRECTDRAWSURFACE7 theTexture, int offx, int offy, int texWidth, int texHeight)
{
DDSURFACEDESC2 aDesc;
aDesc.dwSize = sizeof(aDesc);
D3DTester::CheckDXError(theTexture->Lock(NULL,&aDesc,DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY,NULL),"Lock Texture");
int aWidth = min(texWidth,(GetWidth()-offx));
int aHeight = min(texHeight,(GetHeight()-offy));
if(aWidth < texWidth || aHeight < texHeight)
memset(aDesc.lpSurface, 0, aDesc.lPitch*aDesc.dwHeight);
if(aWidth>0 && aHeight>0)
{
DWORD *srcRow = GetBits() + offy * GetWidth() + offx;
char *dstRow = (char*)aDesc.lpSurface;
for(int y=0; y<aHeight; y++)
{
DWORD *src = srcRow;
ushort *dst = (ushort*)dstRow;
for(int x=0; x<aWidth; x++)
{
DWORD aPixel = *src++;
*dst++ = ((aPixel>>16)&0xF000) | ((aPixel>>12)&0x0F00) | ((aPixel>>8)&0x00F0) | ((aPixel>>4)&0x000F);
}
srcRow += GetWidth();
dstRow += aDesc.lPitch;
}
}
D3DTester::CheckDXError(theTexture->Unlock(NULL),"Texture Unlock");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::DrawPieceToDevice(LPDIRECT3DDEVICE7 theDevice, LPDIRECTDRAWSURFACE7 theTexture, float x, float y, int offx, int offy, int texWidth, int texHeight, DWORD theColor)
{
float maxU = (float)texWidth/gD3DTestTextureWidth;
float maxV = (float)texHeight/gD3DTestTextureHeight;
if (gD3DTestHas32BitTexture)
CopyToTexture8888(theTexture, offx, offy, texWidth, texHeight);
else
CopyToTexture4444(theTexture, offx, offy, texWidth, texHeight);
x -= 0.5f;
y -= 0.5f;
D3DTLVERTEX aVertex[4] =
{
{ x, y, 0, 1, theColor, 0, 0, 0 },
{ x, y+texHeight, 0, 1, theColor, 0, 0, maxV },
{ x+texWidth, y, 0, 1, theColor, 0, maxU, 0 },
{ x+texWidth, y+texHeight, 0, 1, theColor, 0, maxU, maxV }
};
D3DTester::CheckDXError(theDevice->SetTexture(0, theTexture),"SetTexture theTexture");
D3DTester::CheckDXError(theDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_TLVERTEX, aVertex, 4, D3DDP_WAIT),"DrawPrimitive");
D3DTester::CheckDXError(theDevice->SetTexture(0, NULL),"SetTexture NULL");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTestImage::DrawToDevice(LPDIRECT3DDEVICE7 theDevice, LPDIRECTDRAWSURFACE7 theTexture, int x, int y, DWORD theColor)
{
int aWidth = GetWidth();
int aHeight = GetHeight();
int aTexWidth = min(64,gD3DTestTextureWidth);
int aTexHeight = min(64,gD3DTestTextureHeight);
for(int j=0; j<aHeight; j+=aTexHeight)
{
for(int i=0; i<aWidth; i+=aTexWidth)
{
DrawPieceToDevice(theDevice, theTexture, (float)x+i,(float)y+j,i,j,aTexWidth,aTexHeight,theColor);
}
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int D3DTestImage::ColorDistance(DWORD c1, DWORD c2)
{
int r1 = (c1&0xff0000)>>16;
int g1 = (c1&0x00ff00)>>8;
int b1 = (c1&0xff);
int r2 = (c2&0xff0000)>>16;
int g2 = (c2&0x00ff00)>>8;
int b2 = (c2&0xff);
return (r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTestImage::IsUniformColor(DWORD theColor, int &theNumMistakes, int testWidth, int testHeight) const
{
theNumMistakes = 0;
const DWORD *aRow = GetBits();
DWORD aLastPixel = *aRow;
bool isUniform = true;
for(int i=0; i<testHeight; i++)
{
const DWORD *aSrc = aRow;
for(int j=0; j<testWidth; j++)
{
DWORD aPixel = *aSrc++;
if(aLastPixel!=aPixel)
isUniform = false;
if(ColorDistance(aPixel,theColor)>COLOR_TOLERANCE)
theNumMistakes++;
}
aRow += GetWidth();
}
return isUniform;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int D3DTestImage::CheckUniformBands(int testWidth, int testHeight, int xoff, int yoff)
{
int aNumMistakes = 0;
const DWORD *aRow = GetBits() + yoff*GetWidth() + xoff;
DWORD aLastPixel = *aRow;
bool isUniform = true;
for(int i=0; i<testHeight; i++)
{
const DWORD *aSrc = aRow;
for(int j=0; j<testWidth; j++)
{
DWORD aPixel = *aSrc++;
if(aLastPixel!=aPixel)
isUniform = false;
if(ColorDistance(aPixel,(j&1)?0xFFFFFF:0x000000)>COLOR_TOLERANCE)
aNumMistakes++;
}
aRow += GetWidth();
}
return aNumMistakes;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static int D3DTestHighBit(DWORD theMask)
{
int aBit = 31;
while(aBit>0)
{
if((1<<aBit) & theMask)
break;
aBit--;
}
return aBit;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
template <class PixelType>
static void D3DTestPixelConvert(D3DTestImage &theImage, DDSURFACEDESC2 &theDesc, PixelType)
{
int rMask = theDesc.ddpfPixelFormat.dwRBitMask;
int gMask = theDesc.ddpfPixelFormat.dwGBitMask;
int bMask = theDesc.ddpfPixelFormat.dwBBitMask;
int redShift = 23-D3DTestHighBit(rMask);
int greenShift = 15-D3DTestHighBit(gMask);
int blueShift = 7-D3DTestHighBit(bMask);
char *srcRow = (char*)theDesc.lpSurface;
DWORD *dstRow = theImage.GetBits();
for(int j=0; j<theImage.GetHeight(); j++)
{
PixelType *src = (PixelType*)srcRow;
DWORD *dst = dstRow;
for(int i=0; i<theImage.GetWidth(); i++)
{
PixelType aPixel = *src++;
int r = aPixel & rMask;
int g = aPixel & gMask;
int b = aPixel & bMask;
if(redShift>0) r<<=redShift; else r>>=-redShift;
if(greenShift>0) g<<=greenShift; else g>>=-greenShift;
if(blueShift>0) b<<=blueShift; else b>>=-blueShift;
*dst++ = 0xFF000000 | r | g | b;
}
srcRow += theDesc.lPitch;
dstRow += theImage.GetWidth();
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void D3DTestPixelConvert24(D3DTestImage &theImage, DDSURFACEDESC2 &theDesc)
{
int rMask = theDesc.ddpfPixelFormat.dwRBitMask;
int gMask = theDesc.ddpfPixelFormat.dwGBitMask;
int bMask = theDesc.ddpfPixelFormat.dwBBitMask;
int redShift = 23-D3DTestHighBit(rMask);
int greenShift = 15-D3DTestHighBit(gMask);
int blueShift = 7-D3DTestHighBit(bMask);
char *srcRow = (char*)theDesc.lpSurface;
DWORD *dstRow = theImage.GetBits();
for(int j=0; j<theImage.GetHeight(); j++)
{
char *src = srcRow;
DWORD *dst = dstRow;
for(int i=0; i<theImage.GetWidth(); i++)
{
DWORD aPixel = *((DWORD*)src)&0xFFFFFF;
src += 3;
int r = aPixel & rMask;
int g = aPixel & gMask;
int b = aPixel & bMask;
if(redShift>0) r<<=redShift; else r>>=-redShift;
if(greenShift>0) g<<=greenShift; else g>>=-greenShift;
if(blueShift>0) b<<=blueShift; else b>>=-blueShift;
*dst++ = 0xFF000000 | r | g | b;
}
srcRow += theDesc.lPitch;
dstRow += theImage.GetWidth();
}
}
#define SafeSetRenderState(x,y)\
CheckDXError(mD3DDevice7->SetRenderState(x,y),#x ", " #y)
#define SafeSetTextureStageState(i,x,y)\
CheckDXError(mD3DDevice7->SetTextureStageState(i,x,y),#x ", " #y)
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
D3DTester::D3DTester()
{
mDD7 = NULL;
mPrimarySurface = NULL;
mTextureSurface = NULL;
mTextureSurface2 = NULL;
mD3D7 = NULL;
mD3DDevice7 = NULL;
mRegKey = NULL;
memset(&mDisplayGUID, 0, sizeof(GUID));
mMinVidMemory = 0;
mRecommendedVidMemory = 0;
mDriverYear = 0;
mCheckRegistry = true;
mResultsChanged = false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
D3DTester::~D3DTester()
{
Cleanup();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::CheckRegistry()
{
mResultsChanged = true;
DWORD aSize, aType;
if (mRegKey==NULL)
return false;
// Check Test Version
DWORD aVersion = 0;
aSize = sizeof(aVersion);
aType = REG_DWORD;
if (RegQueryValueExA(mRegKey, "Version", 0, &aType, (uchar*) &aVersion, &aSize) != ERROR_SUCCESS)
return false;
if (aVersion != TEST_VERSION)
return false;
// Check Min Vid Memory
DWORD aMinVidMemory = 0;
aSize = sizeof(aMinVidMemory);
aType = REG_DWORD;
if (RegQueryValueExA(mRegKey, "MinVidMemory", 0, &aType, (uchar*) &aMinVidMemory, &aSize) != ERROR_SUCCESS)
return false;
if (aMinVidMemory != mMinVidMemory)
return false;
// Check Recommended Vid Memory
DWORD aRecVidMemory = 0;
aSize = sizeof(aRecVidMemory);
aType = REG_DWORD;
if (RegQueryValueExA(mRegKey, "RecVidMemory", 0, &aType, (uchar*) &aRecVidMemory, &aSize) != ERROR_SUCCESS)
return false;
if (aRecVidMemory != mRecommendedVidMemory)
return false;
// Check GUID
UUID aGUID;
aSize = sizeof(aGUID);
aType = REG_BINARY;
if (RegQueryValueExA(mRegKey, "DisplayGUID", 0, &aType, (uchar*) &aGUID, &aSize) != ERROR_SUCCESS)
return false;
if (aSize != sizeof(aGUID))
return false;
if (memcmp(&aGUID,&mDisplayGUID,sizeof(aGUID)) != 0) // different video card or driver
return false;
// Get failure reason
char aBuf[4096];
aType = REG_SZ;
aSize = 4096;
if (RegQueryValueExA(mRegKey, "FailureReason", 0, &aType, (uchar*) aBuf, &aSize) != ERROR_SUCCESS)
return false;
mFailureReason = aBuf;
// Get warining
aType = REG_SZ;
aSize = 4096;
if (RegQueryValueExA(mRegKey, "Warning", 0, &aType, (uchar*) aBuf, &aSize) != ERROR_SUCCESS)
return false;
mWarning = aBuf;
mResultsChanged = false;
if (!mFailureReason.empty())
return false;
else
return true;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTester::WriteToRegistry()
{
DWORD aSize, aType;
if (mRegKey==NULL)
return;
// Write Test Version
DWORD aVersion = TEST_VERSION;
aSize = sizeof(aVersion);
aType = REG_DWORD;
RegSetValueExA(mRegKey, "Version", 0, aType, (uchar*) &aVersion, aSize);
// Write Min Vid Memory
DWORD aMinVidMemory = mMinVidMemory;
aSize = sizeof(aMinVidMemory);
aType = REG_DWORD;
RegSetValueExA(mRegKey, "MinVidMemory", 0, aType, (uchar*) &aMinVidMemory, aSize);
// Write Recommended Vid Memory
DWORD aRecVidMemory = mRecommendedVidMemory;
aSize = sizeof(aRecVidMemory);
aType = REG_DWORD;
RegSetValueExA(mRegKey, "RecVidMemory", 0, aType, (uchar*) &aRecVidMemory, aSize);
// Write GUID
aSize = sizeof(mDisplayGUID);
aType = REG_BINARY;
RegSetValueExA(mRegKey, "DisplayGUID", 0, aType, (uchar*) &mDisplayGUID, aSize);
// Write failure reason
aType = REG_SZ;
aSize = mFailureReason.length()+1;
RegSetValueExA(mRegKey, "FailureReason", 0, aType, (uchar*) mFailureReason.c_str(), aSize);
// Write warining
aType = REG_SZ;
aSize = mWarning.length()+1;
RegSetValueExA(mRegKey, "Warning", 0, aType, (uchar*) mWarning.c_str(), aSize);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::FileContains(FILE* theFile, const char* theString)
{
bool found = false;
char aBuf[4096];
while (!feof(theFile))
{
if (fgets(aBuf,4000,theFile)==NULL)
break;
std::string aStr = Trim(aBuf);
if (!aStr.empty() && StrFindNoCase(theString,aStr.c_str()) >= 0)
{
found = true;
break;
}
}
return found;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::IsSupportedCard(const char *theDisplayDesc)
{
// Look for 'bad' exception list
FILE* aFile = fopen("vhwb.dat","r");
if (aFile != NULL)
{
bool found = FileContains(aFile, theDisplayDesc);
fclose(aFile);
if (found)
return false;
}
// Look for 'good' supported list
aFile = fopen("vhw.dat","r");
if (aFile==NULL) // default checks
{
if (mDriverYear>=2002)
return true;
if (StrFindNoCase(theDisplayDesc,"nvidia") >= 0)
return true;
if (StrFindNoCase(theDisplayDesc,"radeon") >= 0)
return true;
if (StrFindNoCase(theDisplayDesc,"ati ") >= 0)
return true;
return false;
}
bool found = FileContains(aFile, theDisplayDesc);
fclose(aFile);
return found;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::Init(HWND theHWND, LPDIRECTDRAW7 theDDraw)
{
Cleanup();
mDriverYear = 0;
if (mCheckRegistry)
{
std::string aKey = RemoveTrailingSlash("SOFTWARE\\" + gSexyAppBase->mRegKey) + "\\Test3D";
RegCreateKeyExA(HKEY_CURRENT_USER, aKey.c_str(),0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&mRegKey,NULL);
}
try
{
if (theDDraw==NULL)
{
extern HMODULE gDDrawDLL;
typedef HRESULT (WINAPI *DirectDrawCreateExFunc)(GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter);
DirectDrawCreateExFunc aDirectDrawCreateExFunc = (DirectDrawCreateExFunc)GetProcAddress(gDDrawDLL,"DirectDrawCreateEx");
if (aDirectDrawCreateExFunc == NULL)
return Fail("No DirectDrawCreateEx");
CheckDXError(aDirectDrawCreateExFunc(NULL, (LPVOID*)&mDD7, IID_IDirectDraw7, NULL),"DirectDrawCreateEx");
}
else
{
theDDraw->AddRef();
mDD7 = theDDraw;
}
if (!GetD3D8AdapterInfo(mDisplayGUID,mDisplayDriver,mDisplayDescription))
{
// Get Device GUID
DDDEVICEIDENTIFIER2 aDeviceInfo;
CheckDXError(mDD7->GetDeviceIdentifier(&aDeviceInfo,0), "GetDeviceIdentifier");
mDisplayGUID = aDeviceInfo.guidDeviceIdentifier;
mDisplayDriver = aDeviceInfo.szDriver;
mDisplayDescription = aDeviceInfo.szDescription;
}
// Test Video Memory
DWORD dwTotal, dwFree;
DDSCAPS2 ddsCaps;
ZeroMemory(&ddsCaps,sizeof(ddsCaps));
ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
HDC aDC = GetDC(NULL);
int aWidth = GetDeviceCaps(aDC, HORZRES);
int aHeight = GetDeviceCaps(aDC, VERTRES);
int aBPP = GetDeviceCaps( aDC, BITSPIXEL );
ReleaseDC(NULL, aDC);
HRESULT aResult = mDD7->GetAvailableVidMem(&ddsCaps, &dwTotal, &dwFree);
if (!SUCCEEDED(aResult))
{
Warn(StrFormat("GetAvailableVidMem failed: %s",GetDirectXErrorString(aResult).c_str()));
}
else
{
dwTotal += (aBPP/8)*aWidth*aHeight;
dwTotal /= (1024*1024);
if (dwTotal < mMinVidMemory)
return Fail("Not enough video memory.");
else if (dwTotal < mRecommendedVidMemory)
Warn("Low video memory.");
}
// Check registry to see if we've already done the test
if (mCheckRegistry)
{
if (CheckRegistry())
return false;
mShouldWriteToRegistry = true;
}
mFailureReason = "";
mWarning = "";
// Get date on driver dll
std::string aPath = mDisplayDriver;
if (aPath.find_first_of("/\\")==std::string::npos)
{
char aBuf[_MAX_PATH+1];
if (GetSystemDirectoryA(aBuf,sizeof(aBuf)-1))
aPath = AddTrailingSlash(aBuf,true) + aPath;
}
FILETIME aFileTime;
memset(&aFileTime, 0, sizeof(aFileTime));
HANDLE aFileHandle = CreateFileA(aPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (aFileHandle != INVALID_HANDLE_VALUE)
{
SYSTEMTIME aSystemTime;
if (GetFileTime(aFileHandle, NULL, NULL, &aFileTime) && FileTimeToSystemTime(&aFileTime,&aSystemTime))
mDriverYear = aSystemTime.wYear;
CloseHandle(aFileHandle);
}
// Check supported cards
if (!IsSupportedCard(mDisplayDescription.c_str()))
Warn(StrFormat("Unsupported video card: %s",mDisplayDescription.c_str()));
// Get Direct3D7 to test 3d capabilities
CheckDXError(mDD7->QueryInterface(IID_IDirect3D7, (LPVOID*)&mD3D7),"QueryInterface IID_IDirect3D7");
CheckDXError(mDD7->SetCooperativeLevel(theHWND, DDSCL_NORMAL),"SetCooperativeLevel");
// Create Primary Surface for test rendering
DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
ddsd.dwWidth = 100;
ddsd.dwHeight = 100;
CheckDXError(mDD7->CreateSurface(&ddsd, &mPrimarySurface, NULL),"CreateSurface (Primary)");
mTestImage.Create(ddsd.dwWidth,ddsd.dwHeight);
CheckDXError(mD3D7->CreateDevice(IID_IDirect3DHALDevice, mPrimarySurface, &mD3DDevice7),"CreateDevice");
DWORD aFormat = 0;
CheckDXError(mD3DDevice7->EnumTextureFormats(PixelFormatsCallback,&aFormat),"EnumTextureFormats");
if (!(aFormat & PixelFormat_A8R8G8B8))
{
Warn("A8R8G8B8 texture format not supported.");
if (!(aFormat & PixelFormat_A4R4G4B4))
return Fail("A4R4G4B4 and A8R8G8B8 texture formats not supported.");
gD3DTestHas32BitTexture = false;
}
else
gD3DTestHas32BitTexture = true;
// Create Texture Surface
DDSURFACEDESC2 aDesc;
ZeroMemory(&aDesc, sizeof(aDesc));
aDesc.dwSize = sizeof(aDesc);
aDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
aDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
aDesc.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE;
aDesc.dwWidth = 64;
aDesc.dwHeight = 64;
aDesc.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
aDesc.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
if (gD3DTestHas32BitTexture)
{
aDesc.ddpfPixelFormat.dwRGBBitCount = 32;
aDesc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000;
aDesc.ddpfPixelFormat.dwRBitMask = 0x00FF0000;
aDesc.ddpfPixelFormat.dwGBitMask = 0x0000FF00;
aDesc.ddpfPixelFormat.dwBBitMask = 0x000000FF;
}
else
{
aDesc.ddpfPixelFormat.dwRGBBitCount = 16;
aDesc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xF000;
aDesc.ddpfPixelFormat.dwRBitMask = 0x0F00;
aDesc.ddpfPixelFormat.dwGBitMask = 0x00F0;
aDesc.ddpfPixelFormat.dwBBitMask = 0x000F;
}
CheckDXError(mDD7->CreateSurface(&aDesc, &mTextureSurface, NULL), "CreateSurface (TextureSurface1)");
CheckDXError(mDD7->CreateSurface(&aDesc, &mTextureSurface2, NULL), "CreateSurface (TextureSurfacd2)");
}
catch(TestException &ex)
{
return Fail(ex.mMsg);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
HRESULT CALLBACK D3DTester::PixelFormatsCallback(LPDDPIXELFORMAT theFormat, LPVOID lpContext)
{
*((DWORD*)lpContext) |= D3DInterface::GetDDPixelFormat(theFormat);
return D3DENUMRET_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTester::Cleanup()
{
if (mDD7)
{
mDD7->Release();
mDD7 = NULL;
}
if (mD3D7)
{
mD3D7->Release();
mD3D7 = NULL;
}
if (mD3DDevice7)
{
mD3DDevice7->Release();
mD3DDevice7 = NULL;
}
if (mPrimarySurface)
{
mPrimarySurface->Release();
mPrimarySurface = NULL;
}
if (mTextureSurface)
{
mTextureSurface->Release();
mTextureSurface = NULL;
}
if (mTextureSurface2)
{
mTextureSurface2->Release();
mTextureSurface2 = NULL;
}
if (mRegKey)
{
RegCloseKey(mRegKey);
mRegKey = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::Fail(const std::string &theStr)
{
mFailureReason = theStr;
return false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::Warn(const std::string &theStr)
{
mWarning = theStr;
return true;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTester::CheckDXError(HRESULT theResult, const char *theMsg)
{
if (FAILED(theResult))
{
std::string aMsg = "DXError - ";
aMsg += theMsg;
aMsg += ": ";
aMsg += GetDirectXErrorString(theResult);
throw TestException(aMsg);
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTester::CopyPrimaryToTestImage()
{
DDBLTFX aBltFX;
ZeroMemory(&aBltFX, sizeof(aBltFX));
aBltFX.dwSize = sizeof(aBltFX);
DDSURFACEDESC2 aDesc;
memset(&aDesc, 0, sizeof(aDesc));
aDesc.dwSize = sizeof(aDesc);
D3DTester::CheckDXError(mPrimarySurface->Lock(NULL,&aDesc,DDLOCK_WAIT,NULL),"CopyPrimary Lock");
if(aDesc.ddpfPixelFormat.dwRGBBitCount==32)
D3DTestPixelConvert<DWORD>(mTestImage,aDesc,0);
else if(aDesc.ddpfPixelFormat.dwRGBBitCount==16)
D3DTestPixelConvert<unsigned short>(mTestImage,aDesc,0);
else if(aDesc.ddpfPixelFormat.dwRGBBitCount==24)
throw TestException("Can't test 24-bit mode.");
// D3DTestPixelConvert24(mTestImage,aDesc);
else
{
mPrimarySurface->Unlock(NULL);
throw TestException("Invalid Color Depth");
}
mPrimarySurface->Unlock(NULL);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::TestAlphaBlend()
{
try
{
CheckDXError(mD3DDevice7->BeginScene());
CheckDXError(mD3DDevice7->Clear(0, NULL, D3DCLEAR_TARGET ,0xff000000, 1.0f, 0L),"Clear");
SafeSetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
SafeSetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
D3DTestImage anImage(64,64);
anImage.FillRect(0,0,64,64,0xFF0000FF);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
anImage.FillRect(0,0,64,64,0x80FF0000);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
mD3DDevice7->EndScene();
CopyPrimaryToTestImage();
}
catch(TestException &ex)
{
mD3DDevice7->EndScene();
return Fail(ex.mMsg);
}
int aNumErrors = 0;
bool isUniform = mTestImage.IsUniformColor(0x7f007f,aNumErrors,10,10);
if (aNumErrors==0)
{
if (isUniform)
return true;
else
return Warn("Alpha blend test not uniform.");
}
else
return Fail("Alpha blend Not Supported");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::TestAdditiveBlend()
{
try
{
CheckDXError(mD3DDevice7->BeginScene());
CheckDXError(mD3DDevice7->Clear(0, NULL, D3DCLEAR_TARGET ,0xff000000, 1.0f, 0L),"Clear");
SafeSetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
SafeSetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE);
SafeSetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
SafeSetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
D3DTestImage anImage(64,64);
anImage.FillRect(0,0,64,64,0xFF404040);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
anImage.FillRect(0,0,64,64,0xFF404040);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
mD3DDevice7->EndScene();
CopyPrimaryToTestImage();
}
catch(TestException &ex)
{
mD3DDevice7->EndScene();
return Fail(ex.mMsg);
}
int aNumErrors = 0;
bool isUniform = mTestImage.IsUniformColor(0x808080,aNumErrors,10,10);
if(aNumErrors==0)
{
if (isUniform)
return true;
else
return Warn("Additive blend test not uniform.");
}
else
return Fail("Additive blend not supported.");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::TestAlphaAddBlend()
{
try
{
CheckDXError(mD3DDevice7->BeginScene());
CheckDXError(mD3DDevice7->Clear(0, NULL, D3DCLEAR_TARGET ,0xff000000, 1.0f, 0L),"Clear");
SafeSetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
SafeSetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
SafeSetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
D3DTestImage anImage(64,64);
anImage.FillRect(0,0,64,64,0xFF404040);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
anImage.FillRect(0,0,64,64,0x80808080);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
mD3DDevice7->EndScene();
CopyPrimaryToTestImage();
}
catch(TestException &ex)
{
mD3DDevice7->EndScene();
return Fail(ex.mMsg);
}
int aNumErrors = 0;
bool isUniform = mTestImage.IsUniformColor(0x808080,aNumErrors,10,10);
if(aNumErrors==0)
{
if (isUniform)
return true;
else
return Warn("AlphaAdd blend test not uniform.");
}
else
return Fail("AlphaAdd blend not supported.");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::TestAlphaModulate()
{
try
{
CheckDXError(mD3DDevice7->BeginScene());
CheckDXError(mD3DDevice7->Clear(0, NULL, D3DCLEAR_TARGET ,0xff000000, 1.0f, 0L),"Clear");
SafeSetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
SafeSetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
SafeSetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_MODULATE);
D3DTestImage anImage(64,64);
anImage.FillRect(0,0,64,64,0xFF0000FF);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0);
anImage.FillRect(0,0,64,64,0xFFFF0000);
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, 0, 0, 0x80FFFFFF);
mD3DDevice7->EndScene();
CopyPrimaryToTestImage();
}
catch(TestException &ex)
{
mD3DDevice7->EndScene();
return Fail(ex.mMsg);
}
int aNumErrors = 0;
bool isUniform = mTestImage.IsUniformColor(0x7f007f,aNumErrors,10,10);
if(aNumErrors==0)
{
if (isUniform)
return true;
else
return Warn("AlphaModulated blend test not uniform.");
}
else
return Fail("AlphaModulated blend not supported.");
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::TestClipProblem()
{
try
{
CheckDXError(mD3DDevice7->BeginScene());
CheckDXError(mD3DDevice7->Clear(0, NULL, D3DCLEAR_TARGET ,0xff000000, 1.0f, 0L),"Clear");
SafeSetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
SafeSetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
SafeSetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
D3DTestImage anImage(64,64);
anImage.MakeVerticalBands();
anImage.DrawToDevice(mD3DDevice7, mTextureSurface, -2, -2);
mD3DDevice7->EndScene();
CopyPrimaryToTestImage();
}
catch(TestException &ex)
{
mD3DDevice7->EndScene();
return Fail(ex.mMsg);
}
int aNumErrors = mTestImage.CheckUniformBands(62,62);
if (aNumErrors==0)
return true;
else
return Warn("Clip problem detected.");
}
#undef SafeSetRenderState
#undef SafeSetTextureStageState
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool D3DTester::DoTest()
{
if (!TestAlphaBlend())
return false;
if (!TestAdditiveBlend())
return false;
if (!TestAlphaAddBlend())
return false;
if (!TestAlphaModulate())
return false;
if (!TestClipProblem())
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTester::SetVidMemoryConstraints(DWORD theMin, DWORD theRecommended)
{
mMinVidMemory = theMin;
mRecommendedVidMemory = theRecommended;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void D3DTester::TestD3D(HWND theHWND, LPDIRECTDRAW7 theDDraw)
{
if (!gSexyAppBase->mPlayingDemoBuffer)
{
mShouldWriteToRegistry = false;
if (Init(theHWND, theDDraw))
{
DoTest();
}
else
{
if ((mCheckRegistry) && (!mShouldWriteToRegistry))
{
mResultsChanged = RegQueryValueExA(mRegKey, "FailureReason", 0, NULL, NULL, NULL) != ERROR_SUCCESS;
mShouldWriteToRegistry = true;
}
}
if (mShouldWriteToRegistry)
WriteToRegistry();
Cleanup();
}
gSexyAppBase->DemoSyncString(&mFailureReason);
gSexyAppBase->DemoSyncString(&mWarning);
gSexyAppBase->DemoSyncBool(&mResultsChanged);
}