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