/******************************Module*Header*******************************\ * Module Name: perfsuite.cpp * * Copyright (c) 1991-1999 Microsoft Corporation * * Contains the test prototypes and includes * \**************************************************************************/ #include "perftest.h" #include /***************************************************************************\ * TestSuite::TestSuite * \***************************************************************************/ TestSuite::TestSuite() { } /***************************************************************************\ * TestSuite::~TestSuite * \***************************************************************************/ TestSuite::~TestSuite() { } /***************************************************************************\ * TestSuite::InitializeDestination * * Create the destination to be used by the tests. Could be a particular * format for the screen, a bitmap, or a DIB. * * Returns: * * *bitmapResult - if a GDI+ Bitmap is to be used (use g.GetHDC() to draw * to via GDI) * *hbitmapResult - if a GDI bitmap is to be used (use Graphics(hdc) to * draw to via GDI+) * both NULL - if the screen is to be used * \***************************************************************************/ BOOL TestSuite::InitializeDestination( DestinationType destinationIndex, Bitmap **bitmapResult, HBITMAP *hbitmapResult ) { Graphics *g = NULL; HDC hdc = 0; INT screenDepth = 0; PixelFormat bitmapFormat = PixelFormatMax; ULONG *bitfields; Bitmap *bitmap; HBITMAP hbitmap; union { BITMAPINFO bitmapInfo; BYTE padding[sizeof(BITMAPINFO) + 3*sizeof(RGBQUAD)]; }; // Clear all state remembered or returned: ModeSet = FALSE; bitmap = NULL; hbitmap = NULL; HalftonePalette = NULL; // Initialize our DIB format in case we use it: RtlZeroMemory(&bitmapInfo, sizeof(bitmapInfo)); bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.bmiHeader.biWidth = TestWidth; bitmapInfo.bmiHeader.biHeight = TestHeight; bitmapInfo.bmiHeader.biPlanes = 1; bitfields = reinterpret_cast(&bitmapInfo.bmiColors[0]); // First handle any destinations that need to change the color depth: switch (destinationIndex) { case Destination_Screen_Current: break; case Destination_Screen_800_600_8bpp_HalftonePalette: HalftonePalette = DllExports::GdipCreateHalftonePalette(); if (!HalftonePalette) { return FALSE; } screenDepth = 8; break; case Destination_Screen_800_600_8bpp_DefaultPalette: screenDepth = 8; break; case Destination_Screen_800_600_16bpp: screenDepth = 16; break; case Destination_Screen_800_600_24bpp: screenDepth = 24; break; case Destination_Screen_800_600_32bpp: screenDepth = 32; break; case Destination_CompatibleBitmap_8bpp: // We want to emulate a compatible bitmap at 8bpp. Because of palette // issues, we really have to switch to 8bpp mode to do that. screenDepth = 8; break; case Destination_DIB_15bpp: bitmapInfo.bmiHeader.biBitCount = 16; bitmapInfo.bmiHeader.biCompression = BI_BITFIELDS; bitfields[0] = 0x7c00; bitfields[1] = 0x03e0; bitfields[2] = 0x001f; break; case Destination_DIB_16bpp: bitmapInfo.bmiHeader.biBitCount = 16; bitmapInfo.bmiHeader.biCompression = BI_BITFIELDS; bitfields[0] = 0xf800; bitfields[1] = 0x07e0; bitfields[2] = 0x001f; break; case Destination_DIB_24bpp: bitmapInfo.bmiHeader.biBitCount = 24; bitmapInfo.bmiHeader.biCompression = BI_RGB; break; case Destination_DIB_32bpp: bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biCompression = BI_RGB; break; case Destination_Bitmap_32bpp_ARGB: bitmapFormat = PixelFormat32bppARGB; break; case Destination_Bitmap_32bpp_PARGB: bitmapFormat = PixelFormat32bppPARGB; break; default: return FALSE; } // Now that we've figured out what to do, actually create our stuff: if (bitmapInfo.bmiHeader.biBitCount != 0) { // It's a DIB: VOID* drawBits; HDC hdcScreen = GetDC(NULL); hbitmap = CreateDIBSection(hdcScreen, &bitmapInfo, DIB_RGB_COLORS, (VOID**) &drawBits, NULL, 0); ReleaseDC(NULL, hdcScreen); if (!hbitmap) return(FALSE); } else if (bitmapFormat != PixelFormatMax) { // It's a Bitmap: bitmap = new Bitmap(TestWidth, TestHeight, bitmapFormat); if (!bitmap) return(FALSE); } else { // It's to the screen (or a weird 8bpp compatible bitmap): if (screenDepth != 0) { // We have to do a mode change: DEVMODE devMode; devMode.dmSize = sizeof(DEVMODE); devMode.dmBitsPerPel = screenDepth; devMode.dmPelsWidth = TestWidth; devMode.dmPelsHeight = TestHeight; devMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; // Note that we invoke CDS_FULLSCREEN to tell the system that // the mode change is temporary (and so that User won't resize // all the windows on the desktop): if (ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { return(FALSE); } // Remember that the mode was set: ModeSet = TRUE; // Wait several seconds to allow other OS threads to page in and // repaint the task bar, etc. We don't want that polluting our // perf numbers. Sleep(5000); } // Handle that 8bpp comaptible bitmap special case: if (destinationIndex == Destination_CompatibleBitmap_8bpp) { HDC hdcScreen = GetDC(NULL); hbitmap = CreateCompatibleBitmap(hdcScreen, TestWidth, TestHeight); ReleaseDC(NULL, hdcScreen); if (!hbitmap) return(FALSE); } } *hbitmapResult = hbitmap; *bitmapResult = bitmap; return(TRUE); } /***************************************************************************\ * TestSuite::UninitializeDestination * \***************************************************************************/ VOID TestSuite::UninitializeDestination( DestinationType destinationIndex, Bitmap *bitmap, HBITMAP hbitmap ) { if (ModeSet) { ChangeDisplaySettings(NULL, 0); } if (HalftonePalette) { DeleteObject(HalftonePalette); } DeleteObject(hbitmap); delete bitmap; } /***************************************************************************\ * TestSuite::InitializeApi * * If 'Api_GdiPlus', returns a 'Graphics*' that can be used to render to * the specified surface. * * If 'Api_Gdi', returns an 'HDC' that can be use to render to the specified * surface. * * The surface is tried in the following order: * * 1. Bitmap* (if non-NULL) * 2. HBITMAP (if non-NULL) * 3. HWND * \***************************************************************************/ BOOL TestSuite::InitializeApi( ApiType apiIndex, Bitmap *bitmap, HBITMAP hbitmap, HWND hwnd, Graphics **gResult, HDC *hdcResult) { Graphics *g = NULL; HDC hdc = NULL; OldPalette = NULL; if (bitmap != NULL) { g = new Graphics(bitmap); if (!g) return(FALSE); if (apiIndex == Api_Gdi) { hdc = g->GetHDC(); if (!hdc) { delete g; return(FALSE); } } } else if (hbitmap != NULL) { HDC hdcScreen = GetDC(hwnd); hdc = CreateCompatibleDC(hdcScreen); SelectObject(hdc, hbitmap); ReleaseDC(hwnd, hdcScreen); if (apiIndex == Api_GdiPlus) { g = new Graphics(hdc); if (!g) { DeleteObject(hdc); return(FALSE); } } } else { hdc = GetDC(hwnd); if (!hdc) return(FALSE); if (HalftonePalette) { OldPalette = SelectPalette(hdc, HalftonePalette, FALSE); RealizePalette(hdc); } if (apiIndex == Api_GdiPlus) { g = new Graphics(hdc); if (!g) return(FALSE); } } *gResult = g; *hdcResult = hdc; return(TRUE); } /***************************************************************************\ * TestSuite::UninitializeApi * \***************************************************************************/ VOID TestSuite::UninitializeApi( ApiType apiIndex, Bitmap *bitmap, HBITMAP hbitmap, HWND hwnd, Graphics *g, HDC hdc) { if (bitmap != NULL) { if (apiIndex == Api_Gdi) g->ReleaseHDC(hdc); delete g; } else if (hbitmap != NULL) { if (apiIndex == Api_GdiPlus) delete g; DeleteObject(hdc); } else { if (apiIndex == Api_GdiPlus) delete g; if (OldPalette) { SelectPalette(hdc, OldPalette, FALSE); OldPalette = NULL; } ReleaseDC(hwnd, hdc); } } /***************************************************************************\ * TestSuite::InitializeState * \***************************************************************************/ BOOL TestSuite::InitializeState( ApiType apiIndex, StateType stateIndex, Graphics *g, HDC hdc) { if (apiIndex == Api_GdiPlus) { SavedState = g->Save(); if (!SavedState) return(FALSE); switch (stateIndex) { case State_Antialias: g->SetSmoothingMode(SmoothingModeAntiAlias); g->SetTextRenderingHint(TextRenderingHintAntiAlias); break; } } else { // Do stuff to 'hdc' } return(TRUE); } /***************************************************************************\ * TestSuite::UninitializeState * \***************************************************************************/ VOID TestSuite::UninitializeState( ApiType apiIndex, StateType stateIndex, Graphics *g, HDC hdc) { if (apiIndex == Api_GdiPlus) { g->Restore(SavedState); } else { // Do stuff to 'hdc' } } /***************************************************************************\ * TestSuite::Run * \***************************************************************************/ void TestSuite::Run(HWND hwnd) { INT i; Graphics *g; HDC hdc; INT destinationIndex; INT apiIndex; INT stateIndex; INT testIndex; TCHAR string[2048]; Bitmap *bitmap; HBITMAP hbitmap; CurrentTestIndex=0; // Maximize the window: ShowWindow(hwnd, SW_MAXIMIZE); // Zero out the results matrix for (i = 0; i < ResultCount(); i++) { ResultsList[i].Score = 0; } // Go through the matrix of tests to find stuff to run for (destinationIndex = 0; destinationIndex < Destination_Count; destinationIndex++) { if (!DestinationList[destinationIndex].Enabled) continue; if (!InitializeDestination((DestinationType) destinationIndex, &bitmap, &hbitmap)) continue; for (apiIndex = 0; apiIndex < Api_Count; apiIndex++) { if (!ApiList[apiIndex].Enabled) continue; if (!InitializeApi((ApiType) apiIndex, bitmap, hbitmap, hwnd, &g, &hdc)) continue; for (stateIndex = 0; stateIndex < State_Count; stateIndex++) { if (!StateList[stateIndex].Enabled) continue; if (!InitializeState((ApiType) apiIndex, (StateType) stateIndex, g, hdc)) continue; for (testIndex = 0; testIndex < Test_Count; testIndex++) { if (!TestList[testIndex].Enabled) continue; _stprintf(string, _T("[%s] [%s] [%s] [%s]"), ApiList[apiIndex].Description, DestinationList[destinationIndex].Description, StateList[stateIndex].Description, TestList[testIndex].TestEntry->Description); SetWindowText(hwnd, string); if (Icecap && FoundIcecap) { // Save the test information so that we can // add it to the profile CurrentTestIndex++; #if UNICODE WideCharToMultiByte( CP_ACP, 0, string, -1, CurrentTestDescription, 2048, NULL, NULL); #else strncpy(CurrentTestDescription, string, 2048); #endif } // Woo hoo, everything is now set up and we're ready // to run a test! if (apiIndex == Api_GdiPlus) { GraphicsState oldState = g->Save(); ResultsList[ResultIndex(destinationIndex, apiIndex, stateIndex, testIndex)].Score = TestList[testIndex].TestEntry->Function(g, NULL); g->Restore(oldState); } else { SaveDC(hdc); ResultsList[ResultIndex(destinationIndex, apiIndex, stateIndex, testIndex)].Score = TestList[testIndex].TestEntry->Function(NULL, hdc); RestoreDC(hdc, -1); } // Copy the result to the screen if it was from a bitmap: if (bitmap) { Graphics gScreen(hwnd); gScreen.DrawImage(bitmap, 0, 0); } else if (hbitmap) { // This will use the source 'hdc', which may have a // transform set on it. Oh well! HDC hdcScreen = GetDC(hwnd); BitBlt(hdcScreen, 0, 0, TestWidth, TestHeight, hdc, 0, 0, SRCCOPY); ReleaseDC(hwnd, hdcScreen); } } UninitializeState((ApiType) apiIndex, (StateType) stateIndex, g, hdc); } UninitializeApi((ApiType) apiIndex, bitmap, hbitmap, hwnd, g, hdc); } UninitializeDestination((DestinationType) destinationIndex, bitmap, hbitmap); } // We're done! CreatePerformanceReport(ResultsList, ExcelOut); } /******************************Public*Routine******************************\ * bFillBitmapInfo * * Fills in the fields of a BITMAPINFO so that we can create a bitmap * that matches the format of the display. * * This is done by creating a compatible bitmap and calling GetDIBits * to return the color masks. This is done with two calls. The first * call passes in biBitCount = 0 to GetDIBits which will fill in the * base BITMAPINFOHEADER data. The second call to GetDIBits (passing * in the BITMAPINFO filled in by the first call) will return the color * table or bitmasks, as appropriate. * * Returns: * TRUE if successful, FALSE otherwise. * * History: * * 20-Jan-2000 [gilmanw] * Removed code to set color table for 8bpp and less DIBs since calling * code will not create such DIBs. * * 07-Jun-1995 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ static BOOL bFillBitmapInfo(HDC hdc, BITMAPINFO *pbmi) { HBITMAP hbm; BOOL bRet = FALSE; // // Create a dummy bitmap from which we can query color format info // about the device surface. // if ( (hbm = CreateCompatibleBitmap(hdc, 1, 1)) != NULL ) { pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // // Call first time to fill in BITMAPINFO header. // GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS); if ( pbmi->bmiHeader.biCompression == BI_BITFIELDS ) { // // Call a second time to get the color masks. // It's a GetDIBits Win32 "feature". // GetDIBits(hdc, hbm, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS); } bRet = TRUE; DeleteObject(hbm); } return bRet; } /******************************Public*Routine******************************\ * CreateCompatibleDIB2 * * Create a DIB section with a optimal format w.r.t. the specified device. * * Parameters * * hdc * * Specifies display DC used to determine format. Must be a direct DC * (not an info or memory DC). * * width * * Specifies the width of the bitmap. * * height * * Specifies the height of the bitmap. * * Return Value * * The return value is the handle to the bitmap created. If the function * fails, the return value is NULL. * * Comments * * For devices that are <= 8bpp, a normal compatible bitmap is * created (i.e., CreateCompatibleBitmap is called). I have a * different version of this function that will create <= 8bpp * DIBs. However, DIBs have the property that their color table * has precedence over the palette selected into the DC whereas * a bitmap from CreateCompatibleBitmap uses the palette selected * into the DC. Therefore, in the interests of keeping this * version as close to CreateCompatibleBitmap as possible, I'll * revert to CreateCompatibleBitmap for 8bpp or less. * * History: * 19-Jan-2000 [gilmanw] * Adapted original "fastdib" version for maximum compatibility with * CreateCompatibleBitmap. * * 23-Jan-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ HBITMAP CreateCompatibleDIB2(HDC hdc, int width, int height) { HBITMAP hbmRet = (HBITMAP) NULL; BYTE aj[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 255)]; BITMAPINFO *pbmi = (BITMAPINFO *) aj; // // Redirect 8 bpp or lower to CreateCompatibleBitmap. // if ( (GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES)) <= 8 ) { return CreateCompatibleBitmap(hdc, width, height); } // // Validate hdc. // if ( GetObjectType(hdc) != OBJ_DC ) { return hbmRet; } memset(aj, 0, sizeof(aj)); if ( bFillBitmapInfo(hdc, pbmi) ) { VOID *pvBits; // // Change bitmap size to match specified dimensions. // pbmi->bmiHeader.biWidth = width; pbmi->bmiHeader.biHeight = height; if (pbmi->bmiHeader.biCompression == BI_RGB) { pbmi->bmiHeader.biSizeImage = 0; } else { if ( pbmi->bmiHeader.biBitCount == 16 ) pbmi->bmiHeader.biSizeImage = width * height * 2; else if ( pbmi->bmiHeader.biBitCount == 32 ) pbmi->bmiHeader.biSizeImage = width * height * 4; else pbmi->bmiHeader.biSizeImage = 0; } pbmi->bmiHeader.biClrUsed = 0; pbmi->bmiHeader.biClrImportant = 0; // // Create the DIB section. Let Win32 allocate the memory and return // a pointer to the bitmap surface. // hbmRet = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, &pvBits, NULL, 0); } return hbmRet; } //////////////////////////////////////////////////////////////////////// // // Timer Utility Functions // //////////////////////////////////////////////////////////////////////// LONGLONG StartCounter; // Timer global, to be set by StartTimer() LONGLONG MinimumCount; // Minimum number of performance counter ticks // that must elapse before a test is considered // 'done' LONGLONG CountsPerSecond; // Frequency of the performance counter UINT Iterations; // Timer global, to be set by StartTimer() and // incremented for every call to EndTimer() UINT MinIterations; // Minimum number of iterations of the test to // be done /***************************************************************************\ * StartTimer * * Called by timing routine to start the timer. * \***************************************************************************/ void StartTimer() { if (Icecap && FoundIcecap) { ICStartProfile(PROFILE_GLOBALLEVEL, PROFILE_CURRENTID); ICCommentMarkProfile(CurrentTestIndex, CurrentTestDescription); } // Disable the cursor so that it doesn't interfere with the timing: ShowCursor(FALSE); if (TestRender) { // Rig it so that we do only one iteration of the test. MinIterations = 0; MinimumCount = 0; } else { // Somewhat randomly choose 1 second as the minimum counter time: QueryPerformanceFrequency(reinterpret_cast(&CountsPerSecond)); MinimumCount = CountsPerSecond; // Okay, start timing! Iterations = 0; QueryPerformanceCounter(reinterpret_cast(&StartCounter)); } } /***************************************************************************\ * EndTimer * * Called by timing routine to see if it's okay to stop timing. Timing * can stop if 2 conditions are satisfied: * * 1. We've gone the minimum time duration (to ensure that we good * good accuracy from the timer functions we're using) * 2. We've done the minimum number of iterations (to ensure, if the * routine being timed is very very slow, that we do more than * one iteration) * \***************************************************************************/ BOOL EndTimer() { LONGLONG counter; // Always do at least MIN_ITERATIONS iterations (and only check // the timer that frequently as well): Iterations++; if (Iterations & MinIterations) return(FALSE); // Query the performance counter, and bail if for some bizarre reason // this computer doesn't support a high resolution timer (which I think // all do now-a-days): if (!QueryPerformanceCounter(reinterpret_cast(&counter))) return(TRUE); // Ensure that we get good timer accuracy by going for the minimum // amount of time: if ((counter - StartCounter) <= MinimumCount) return(FALSE); ShowCursor(TRUE); if (Icecap && FoundIcecap) { ICStopProfile(PROFILE_GLOBALLEVEL, PROFILE_CURRENTID); } // Okay, you can stop timing! return(TRUE); } /***************************************************************************\ * GetTimer * * Should only be called after EndTimer() returns TRUE. Returns the * time in seconds, and the number of iterations benchmarked. * \***************************************************************************/ void GetTimer(float* seconds, UINT* iterations) { LONGLONG counter; // Note that we re-check the timer here to account for any // flushes that the caller may have needed to have done after // the EndTimer() call: QueryPerformanceCounter(reinterpret_cast(&counter)); if ((TestRender) || (CountsPerSecond == 0)) { // Either the timer doesn't work, or we're doing a 'test render': *seconds = 1000000.0f; *iterations = 1; } else { // Woo hoo, we're done! *seconds = static_cast(counter - StartCounter) / CountsPerSecond; *iterations = Iterations; } }