MEMTIME.CPP

//--------------------------------------------------------------------------; 
//
// File: memtime.cpp
//
// Description:
// This code times various things about system memory and video
// memory. It is intended to show the advantages and disadvantages
// of different drawing methoids.
// Please refer to the documented for a more detailed discriptions of
// what is going on.
//
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
//---------------------------------------------------------------------------
//
// Copyright (c) 1994 - 1997 Microsoft Corporation. All Rights Reserved.
//
//---------------------------------------------------------------------------

#include<windows.h>
#include<windowsx.h>
#include<commdlg.h>
#include <ddraw.h>

#include"memtime.h"

/*----------------------------------------------------------------------------*\
| |
| g l o b a l v a r i a b l e s |
| |
\*----------------------------------------------------------------------------*/
static char szAppName[]="Direct Draw video/system memory timing tests";

static HINSTANCE hInstApp;
static HWND hwndApp;
static HACCEL hAccelApp;
static HPALETTE hpalApp;

static DWORD ScreenHeight, ScreenWidth;
static PCHAR pSource, pDest;

static LPDIRECTDRAW pDirectDrawObj;
static LPDIRECTDRAWSURFACE pOnScreenBuf;
static LPDIRECTDRAWSURFACE pOffScreenBuf;
static LPDIRECTDRAWSURFACE pSysMemBuf;

static ULONG ModeXFrameCount;
static ULONG ModeYFrameCount;
static ULONG StretchFrameCount;

/*----------------------------------------------------------------------------

Timers

*/

extern "C" MemFunc DwordMemCopy_Pitch;
extern "C" MemFunc ByteMemCopy_Pitch;

extern "C" MemFunc DwordMemFill_Pitch;
extern "C" MemFunc ByteMemFill_Pitch;

extern "C" MemFunc VertMemSto_Pitch;


TIMING_RESULT SystemTests[]=
{
{0, DwordMemCopy_Pitch, "System to System dword copy"},
{0, ByteMemCopy_Pitch, "System to System byte copy"},

{0, DwordMemFill_Pitch, "System dword Fill"},
{0, ByteMemFill_Pitch, "System byte Fill"},

{0, VertMemSto_Pitch, "System vertical byte fill"}
};


TIMING_RESULT VideoTests[]=
{
{0, DwordMemCopy_Pitch, "System to Video dword Copy"},
{0, ByteMemCopy_Pitch, "System to Video byte Copy"},

{0, DwordMemFill_Pitch, "Video dword Fill"},
{0, ByteMemFill_Pitch, "Video byte Fill"},

{0, VertMemSto_Pitch, "Video vertical byte fill"}
};




/*----------------------------------------------------------------------------

defines

*/

#define NUMBER_OF(aa) (sizeof(aa)/sizeof((aa)[0]))


/*----------------------------------------------------------------------------*\
|
| f u n c t i o n d e f i n i t i o n s
|
\*----------------------------------------------------------------------------*/

LRESULT CALLBACK AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
int ErrMsg (LPSTR sz,...);
LONG AppCommand (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
void PrintTimingResults( void );

void AppExit(void);
BOOL AppIdle(void);
BOOL DirectDrawInit();
VOID DoSystemTest( PTIMING_RESULT pTimeObject );
VOID DoVideoTests( PTIMING_RESULT pTimeObject );
ULONG GetFPS( int width, int height );
ULONG GetStretchFPS( int width, int height );

/*----------------------------------------------------------------------------*\
| AppAbout( hDlg, uiMessage, wParam, lParam )
|
| Description:
| This function handles messages belonging to the "About" dialog box.
| The only message that it looks for is WM_COMMAND, indicating the use
| has pressed the "OK" button. When this happens, it takes down
| the dialog box.
|
| Arguments:
| hDlg window handle of about dialog window
| uiMessage message number
| wParam message-dependent
| lParam message-dependent
|
| Returns:
| TRUE if message has been processed, else FALSE
|
\*----------------------------------------------------------------------------*/
BOOL CALLBACK
AppAbout( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch (msg) {
case WM_COMMAND:
if (LOWORD(wParam) == IDOK) {
EndDialog(hwnd,TRUE);
}
break;
case WM_INITDIALOG:
return TRUE;
}
return FALSE;
}

/*----------------------------------------------------------------------------*\
| AppInit( hInst, hPrev)
|
| Description:
| This is called when the application is first loaded into
| memory. It performs all initialization that doesn't need to be done
| once per instance.
|
| Arguments:
| hInstance instance handle of current instance
| hPrev instance handle of previous instance
|
| Returns:
| TRUE if successful, FALSE if not
|
\*----------------------------------------------------------------------------*/
BOOL
AppInit(HINSTANCE hInst, HINSTANCE hPrev, int sw, LPSTR szCmdLine )
{
WNDCLASS cls;
HRESULT ddrval;
DDSURFACEDESC ddsd;
BOOL returnValue = FALSE;

/* Save instance handle for DialogBoxs */
hInstApp = hInst;

hAccelApp = LoadAccelerators(hInst, "AppAccel");

if (!hPrev) {
/*
* Register a class for the main application window
*/
cls.hCursor = LoadCursor(NULL,IDC_ARROW);
cls.hIcon = LoadIcon(hInst,"AppIcon");
cls.lpszMenuName = "AppMenu";
cls.lpszClassName = szAppName;
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
cls.hInstance = hInst;
cls.style = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
cls.lpfnWndProc = (WNDPROC)AppWndProc;
cls.cbWndExtra = 0;
cls.cbClsExtra = 0;

if (RegisterClass(&cls)) {
hwndApp = CreateWindow (
szAppName, // Class name
szAppName, // Caption
WS_OVERLAPPEDWINDOW, // Style bits
50, 50, // Position
400+20,400+75, // Size
(HWND)NULL, // Parent window (no parent)
(HMENU)NULL, // use class menu
hInst, // handle to window instance
(LPSTR)NULL); // no params to pass on
ShowWindow(hwndApp,sw);

// init our direct draw stuff
ddrval = DirectDrawCreate( NULL, &pDirectDrawObj, NULL );
if( DD_OK == ddrval) {
// clear out the surface desctiptions
ZeroMemory( &ddsd, sizeof( ddsd ) );
// set the size
ddsd.dwSize = sizeof( ddsd );

// figure out what display mode we are in now
ddrval = pDirectDrawObj->GetDisplayMode( &ddsd );
if( DD_OK == ddrval ) {
// make sure we're in a 8-bit mode
if( ddsd.ddpfPixelFormat.dwRGBBitCount >= 8) {
ScreenHeight = ddsd.dwHeight;
ScreenWidth = ddsd.dwWidth;

// if we are biger than 800x600 it's to much memory
// to move around. Clip it
if(ScreenHeight > 600) ScreenHeight = 600;
if(ScreenWidth > 800) ScreenWidth = 800;

if( (ScreenWidth < 800) || (ScreenHeight < 600) ) {
ErrMsg("Resolutions smaller than 800x600 may yeild different numbers because of L2 cache effects");
}

// allocate some memory we are going to play with
pSource = (PCHAR)GlobalAllocPtr(GHND, ScreenHeight * ScreenWidth);
pDest = (PCHAR)GlobalAllocPtr(GHND, ScreenHeight * ScreenWidth);

// make sure the memory worked and it's dword alligned.
// (we're in Win32, when is not going to be dword alligned?)
if( pSource && pDest && !((DWORD)pSource & 0x3) && !((DWORD)pDest & 0x3)) {
returnValue = TRUE;
} else {
ErrMsg("Memory allocation failure");
}
} else {
ErrMsg("You must be in a 8-bit (or better) color mode to run this app");
}
} else {
ErrMsg("DirectDraw failure getting the display mode");
}
} else {
ErrMsg("DirectDraw create failure");
}
} else {
ErrMsg("Register Class failure");
}
}
return returnValue;
}

/*----------------------------------------------------------------------------*\
| BandwidthTestsSetup
|
| Description:
| Setup the surfaces for the bandwidth tests
|
| Returns:
| TRUE if successful, FALSE if not
\*----------------------------------------------------------------------------*/
BOOL
BandwidthTestsSetup( VOID )
{
DDSURFACEDESC ddsd;
BOOL returnValue = FALSE;
HRESULT ddrval;

if( pOnScreenBuf ) pOnScreenBuf->Release();
if( pOffScreenBuf ) pOffScreenBuf->Release();
if( pSysMemBuf ) pSysMemBuf->Release();

// Tell the world we love it and want to share
ddrval = pDirectDrawObj->SetCooperativeLevel( hwndApp, DDSCL_NORMAL );
if( DD_OK == ddrval ) {
// clear out out stuff
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );

ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

ddsd.dwHeight = ScreenHeight;
ddsd.dwWidth = ScreenWidth;
// create our off screen video memory.
ddrval = pDirectDrawObj->CreateSurface( &ddsd, &pOffScreenBuf, NULL );
if( DD_OK == ddrval ) {
// yay!
returnValue = TRUE;
} else {
ErrMsg("Failure creating video surface");
}
} else {
ErrMsg("Failure setting Co-op mode");
}
return returnValue;
}
/*----------------------------------------------------------------------------*\
| AppExit()
|
| Description:
| app is just about to exit, cleanup
|
\*----------------------------------------------------------------------------*/
void AppExit()
{
if( pSource ) GlobalFreePtr(pSource);
if( pDest ) GlobalFreePtr(pDest);
if( pOffScreenBuf ) pOffScreenBuf->Release();
if( pOnScreenBuf ) pOnScreenBuf->Release();
if( pSysMemBuf ) pSysMemBuf->Release();
if( pDirectDrawObj ) pDirectDrawObj->Release();

}

/*----------------------------------------------------------------------------*\
| WinMain( hInst, hPrev, lpszCmdLine, cmdShow ) |
|
| Description:
| The main procedure for the App. After initializing, it just goes
| into a message-processing loop until it gets a WM_QUIT message
| (meaning the app was closed). |
|
| Arguments:
| hInst instance handle of this instance of the app
| hPrev instance handle of previous instance, NULL if first
| szCmdLine ->null-terminated command line
| cmdShow specifies how the window is initially displayed
|
| Returns:
| The exit code as specified in the WM_QUIT message.
|
\*----------------------------------------------------------------------------*/
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
MSG msg;

/* Call initialization procedure */
if (!AppInit(hInst,hPrev,sw,szCmdLine))
return FALSE;

/*
* Polling messages from event queue
*/
for (;;) {
if(!GetMessage(&msg, NULL, 0, 0)) {
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}


/*----------------------------------------------------------------------------*\
| AppPaint(hwnd, hdc)
|
| Description:
| The paint function. Right now this does nothing.
|
| Arguments:
| hwnd window painting into
| hdc display context to paint to
|
| Returns:
| nothing
|
\*----------------------------------------------------------------------------*/
AppPaint (HWND hwnd, HDC hdc)
{
TEXTMETRIC tm;
int x,y;
RECT rc;

#define PRINT_STUFF(sz) (TextOut(hdc, x, y, sz, lstrlen(sz)), y += tm.tmHeight+1)

SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
GetTextMetrics(hdc, &tm);

GetClientRect(hwnd,&rc);

SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hdc,GetSysColor(COLOR_WINDOW));

// setup start pos
x=4;
y=4;

PRINT_STUFF("Select 'Time All' to run tests");
y += (tm.tmHeight+1) * 2;
PRINT_STUFF("The screen will go blank during some tests");

return TRUE;
}

/*----------------------------------------------------------------------------*\
| AppWndProc( hwnd, uiMessage, wParam, lParam )
|
| Description:
| The window proc for the app's main (tiled) window.
| This processes all of the parent window's messages.
|
\*----------------------------------------------------------------------------*/
LRESULT CALLBACK AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;

switch (msg) {
case WM_COMMAND:
return AppCommand(hwnd,msg,wParam,lParam);
break;

case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);
AppPaint( hwnd, hdc );
break;

case WM_DESTROY:
hAccelApp = NULL;
PostQuitMessage(0);
break;

}
return DefWindowProc(hwnd,msg,wParam,lParam);
}

/*----------------------------------------------------------------------------*\
| AppCommand(hwnd, msg, wParam, lParam )
|
| Description:
| handles WM_COMMAND messages for the main window (hwndApp)
| of the parent window's messages.
|
\*----------------------------------------------------------------------------*/
LONG AppCommand (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
char achFileName[128] = "";

switch(wParam) {
case MENU_ABOUT:
DialogBox(hInstApp,"AppAbout",hwnd,(DLGPROC)AppAbout);
break;


// do all the timing tests
case MENU_TIMEALL:
{
HCURSOR Arror = SetCursor(LoadCursor(0,IDC_WAIT));

ShowCursor(FALSE); //take care of animated Plus! cursors

DWORD PriorityClass = GetPriorityClass(GetCurrentProcess());
int ThreadPriority = GetThreadPriority(GetCurrentThread());

if( NUMBER_OF(SystemTests) != NUMBER_OF(VideoTests))
{
ErrMsg("test number mismatch");
break;
}

// Jack this thread up to realtime.
// NOTE: This is very bad from for the real world. The rule is
// "The Higher your ptiority, the less work your thread should do".
// We are doing a lot of work. But this is only a test app, so who cares.
SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);

// setup the surfaces for the bandwidth tests
BandwidthTestsSetup();
// Walk through the list of timers, and call them all

for(int Counter = 0;Counter < NUMBER_OF(SystemTests) ;Counter++) {

SetWindowText(hwndApp,SystemTests[Counter].pDescription);

DoSystemTest( &SystemTests[Counter] );

SetWindowText(hwndApp,VideoTests[Counter].pDescription);

DoVideoTests( &VideoTests[Counter] );

}

// do 320x240 frame rate tests
ModeXFrameCount = GetFPS(320,240);
// do 640x480 frame rate tests
ModeYFrameCount = GetFPS(640,480);
// do 320x240 stretch to 640x480 frame rate tests
StretchFrameCount = GetStretchFPS(640,480);

// return ourselves to normal
SetPriorityClass(GetCurrentProcess(),PriorityClass);
SetThreadPriority(GetCurrentThread(),ThreadPriority);

SetCursor(Arror);
SetWindowText(hwndApp,szAppName);

ShowCursor(TRUE);
PrintTimingResults();
}
break;

case MENU_EXIT:
PostMessage(hwnd,WM_CLOSE,0,0L);
break;
}
return 0L;
}

/*----------------------------------------------------------------------------*\
| ErrMsg - Opens a Message box with a error message in it. The user can
| select the OK button to continue
\*----------------------------------------------------------------------------*/
int ErrMsg (LPSTR sz,...)
{
char ach[128];
va_list va;

va_start( va, sz );
wvsprintf ( ach, sz, va ); /* Format the string */
va_end( va );
MessageBox(hwndApp,ach,szAppName,MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
return FALSE;
}




/*----------------------------------------------------------------------------*\
| PrintTimingResults( void )
|
| Description:
| Print the results of the timting tests
|
\*----------------------------------------------------------------------------*/
#include <stdio.h>

VOID
PrintTimingResults( void )
{
double result;
LARGE_INTEGER perfFreq;
char OutputBuffer[2048];
char TempOutputBuffer[2048];
#define PRINT_DOUBLE(d) {sprintf(TempOutputBuffer,"%12.8f",d);strcat(OutputBuffer,TempOutputBuffer);}
#define PRINT_STRING(s) {sprintf(TempOutputBuffer,"%s",s);strcat(OutputBuffer,TempOutputBuffer);}

OutputBuffer[0]=0;
TempOutputBuffer[0]=0;

// get the frequemcy out of timer
QueryPerformanceFrequency( &perfFreq );

// Turn all the time resutls into MB per second
for(int Counter = 0; Counter < NUMBER_OF(SystemTests) ; Counter++) {
if(SystemTests[Counter].Time) {
result = (double)SystemTests[Counter].Iterations;
result /= (double)SystemTests[Counter].Time / (double)perfFreq.LowPart;
result /= (double)1000000.0;
PRINT_DOUBLE(result);
} else {
PRINT_STRING("N/A");
}
PRINT_STRING(" MB/sec \t");
PRINT_STRING(SystemTests[Counter].pDescription);
PRINT_STRING("\n");

if(VideoTests[Counter].Time) {
result = (double)VideoTests[Counter].Iterations;
result /= (double)VideoTests[Counter].Time / (double)perfFreq.LowPart;
result /= (double)1000000.0;
PRINT_DOUBLE(result);
} else {
PRINT_STRING("N/A");
}
PRINT_STRING(" MB/sec \t");
PRINT_STRING(VideoTests[Counter].pDescription);
PRINT_STRING("\n");

}

if( ModeXFrameCount ) {
PRINT_DOUBLE((double)ModeXFrameCount/5.0);
} else {
PRINT_STRING("N/A");
}
PRINT_STRING(" FPS \t\t");
PRINT_STRING("320x240 Frame Rate");
PRINT_STRING("\n");

if( ModeYFrameCount ) {
PRINT_DOUBLE((double)ModeYFrameCount/5.0);
} else {
PRINT_STRING("N/A");
}
PRINT_STRING(" FPS \t\t");
PRINT_STRING("640x480 Frame Rate");
PRINT_STRING("\n");

if( StretchFrameCount ) {
PRINT_DOUBLE((double)StretchFrameCount/5.0);
} else {
PRINT_STRING("N/A");
}
PRINT_STRING(" FPS \t\t");
PRINT_STRING("320x240 stretched to 640x480");
PRINT_STRING("\n");

#ifndef LOR_TESTS
// display the results
MessageBox(0, OutputBuffer ,"Timing Results",MB_OK);
#endif

}

VOID DoSystemTest( PTIMING_RESULT pTimeObject )
{
LARGE_INTEGER startTime, endTime;

QueryPerformanceCounter( &startTime );

// do the memory copy
pTimeObject->Iterations = (*pTimeObject->pTimer)(pSource, pDest, ScreenHeight, ScreenWidth, ScreenWidth, 100 );

QueryPerformanceCounter( &endTime );

// if it takes more than 32bits of clock ticks
// it's not worth profiling.
pTimeObject->Time = endTime.LowPart - startTime.LowPart;
}

VOID DoVideoTests( PTIMING_RESULT pTimeObject )
{
DDSURFACEDESC ddsd;
HRESULT returnValue;
LARGE_INTEGER startTime, endTime;

ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );


// lock the video memory surfce
// NOTE: We are about to do a lock for a REALLY long time.
// this is really bad form in the real world.
//
returnValue = pOffScreenBuf->Lock( NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL);
if( returnValue == DD_OK )
{
if( ddsd.lPitch && ddsd.dwWidth && ddsd.dwHeight && ddsd.lpSurface)
{
// get the time, and do the copy
QueryPerformanceCounter( &startTime );

pTimeObject->Iterations = (*pTimeObject->pTimer)( pSource, ddsd.lpSurface, ddsd.dwHeight, ddsd.dwWidth, ddsd.lPitch, 100 );

QueryPerformanceCounter( &endTime );

pOffScreenBuf->Unlock( ddsd.lpSurface );
// stor the delta time
pTimeObject->Time = endTime.LowPart - startTime.LowPart;
}
else
{
pOffScreenBuf->Unlock( ddsd.lpSurface );
ErrMsg("Lock returned bogus Session Description stuff");
}
}
else
ErrMsg("Can't lock surface");

return;
}

BOOL
InitFullScreen(
DWORD Height,
DWORD Width
)
{
DDSURFACEDESC ddsd;
DDSCAPS ddscaps;
HRESULT ddrval;
ULONG returnValue = FALSE;

// Kill our current offscreen surface.
if( pOffScreenBuf ) {
ddrval = pOffScreenBuf->Release();
if( ddrval != DD_OK ) {
ErrMsg("Can't release offscreen buf");
}
}


// Get exclusive mode
ddrval = pDirectDrawObj->SetCooperativeLevel( hwndApp, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_NOWINDOWCHANGES );

if( ddrval == DD_OK ){
// set the display mode to the requested mode
ddrval = pDirectDrawObj->SetDisplayMode( Height, Width, 8);
if(ddrval == DD_OK){
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
// get the primary buffer
ddrval = pDirectDrawObj->CreateSurface( &ddsd, &pOnScreenBuf, NULL );

if( ddrval == DD_OK ) {
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
// the back buffer might be in system memory depending
// on the card and the mode.
ddrval = pOnScreenBuf->GetAttachedSurface(&ddscaps, &pOffScreenBuf);

if( ddrval == DD_OK ) {
returnValue = TRUE;
}
}
} else {
ErrMsg("can't set display mode %d x %d",Width,Height);
}
}
return returnValue;
}


BOOL
CreateSysMemSurface( VOID )
{
DDSURFACEDESC ddsd;
ULONG returnValue;

// clear out out stuff
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );

// build a surface in system memory
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;

ddsd.dwHeight = 240;
ddsd.dwWidth = 320;
returnValue = pDirectDrawObj->CreateSurface( &ddsd, &pSysMemBuf, NULL );
if( DD_OK == returnValue ) {
return TRUE;
}

return FALSE;
}

ULONG
GetStretchFPS( int width, int height )
{
ULONG time;
HRESULT ddrval;
DDSURFACEDESC ddsd;
DDBLTFX ddbltfx;
ULONG FrameCount;

// set the mode (and create the surfaces)
if( InitFullScreen( width, height ) ) {
// get the system memory surface
if( CreateSysMemSurface() ) {

// build the ROP for the blt
ZeroMemory( &ddbltfx, sizeof(ddbltfx) );
ddbltfx.dwSize = sizeof( ddbltfx );
ddbltfx.dwROP = SRCCOPY;

time = timeGetTime();
// wait for a seconds for the mode to settle out
// (I don't need to do this.. I'm just paranoid)
while( (timeGetTime() - time) < 1000);
FrameCount = 0;
time = timeGetTime();

// Initialize the surface description structure
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize=sizeof(ddsd);

ddrval = 0;
// do this for 5 seconds
while( ((timeGetTime() - time) < 5000) && (DD_OK == ddrval) ) {
// simluate rendering and off screen frame (with a copy)
ddrval = pSysMemBuf->Lock( NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
if (FAILED(ddrval))
{
FrameCount=0;
break;
}

DwordMemCopy_Pitch( pSource, ddsd.lpSurface, ddsd.dwHeight, ddsd.dwWidth, ddsd.lPitch, 1);

ddrval = pSysMemBuf->Unlock( NULL );
if (FAILED(ddrval))
{
FrameCount=0;
break;
}

// blt the system buffer into the back buffer
ddrval = pOffScreenBuf->Blt( NULL, pSysMemBuf, NULL, DDBLT_ROP, &ddbltfx);
if (FAILED(ddrval))
{
FrameCount=0;
break;
}

// flip the back buffer on screen
ddrval = pOnScreenBuf->Flip( NULL, DDFLIP_WAIT );
if (FAILED(ddrval))
{
FrameCount=0;
break;
}

FrameCount++;
}
}
pDirectDrawObj->RestoreDisplayMode();
if( DD_OK != ddrval ) {
ErrMsg("Test failure %X",ddrval);
}

pOffScreenBuf->Release();
pOffScreenBuf = NULL;
pOnScreenBuf->Release();
pOnScreenBuf = NULL;
pSysMemBuf->Release();
pSysMemBuf = NULL;
}
return FrameCount;
}

ULONG GetFPS( int width, int height )
{

ULONG time; 
DDSURFACEDESC ddsd;
HRESULT ddrval;
ULONG FrameCount;

ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );

// set the mode (and setup the surfaces)
if( InitFullScreen( width, height ) )
{
time = timeGetTime();
// wait for a seconds for the mode to settle out
// (I don't need to do this, I'm just paranoid)
while( (timeGetTime() - time) < 1000);
ddrval = DD_OK;
FrameCount = 0;
time = timeGetTime();
while( ((timeGetTime() - time) < 5000) && (ddrval == DD_OK) )
{
// simluate a render into the back buffer with a copy
ddrval = pOffScreenBuf->Lock( NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
if (FAILED(ddrval))
{
FrameCount=0;
break;
}
// Stuff some Junk into the offscreen surface
DwordMemCopy_Pitch( pSource, ddsd.lpSurface, ddsd.dwHeight, ddsd.dwWidth, ddsd.lPitch, 1 );

ddrval = pOffScreenBuf->Unlock( NULL );
if (FAILED(ddrval))
{
FrameCount=0;
break;
}

// flip the off screen surface on screen
ddrval = pOnScreenBuf->Flip( NULL, DDFLIP_WAIT );
if (FAILED(ddrval))
{
FrameCount=0;
break;
}

FrameCount++;
}
pDirectDrawObj->RestoreDisplayMode();
if( FAILED(ddrval) )
ErrMsg("Test failure %X",ddrval);
pOffScreenBuf->Release();
pOffScreenBuf = NULL;
pOnScreenBuf->Release();
pOnScreenBuf = NULL;
}

return FrameCount;
}

/*****************************************************************************
*
* dprintf() is called by the DPF macro if DEBUG is defined at compile time.
*
* The messages will be send to COM1: like any debug message. To
* enable debug output, add the following to WIN.INI :
*
* [debug]
* QA=1
*
****************************************************************************/

#ifdef DEBUG

#define MODNAME "QA"


#define _WINDLL
#include <stdarg.h>

void FAR CDECL dprintf(LPSTR szFormat, ...)
{
char ach[128];
va_list va;

static BOOL fDebug = -1;

if (fDebug == -1) {
fDebug = GetProfileIntA("Debug", MODNAME, TRUE);
}

if (!fDebug) return;



lstrcpyA(ach, MODNAME ": ");
va_start(va, szFormat);
wvsprintfA(ach+lstrlenA(ach),szFormat,(LPSTR)va);
va_end(va);
lstrcatA(ach, "\r\n");

OutputDebugStringA(ach);
}

#endif