Windows2003-3790/windows/advcore/gdiplus/test/perftest/perfsuite.cpp
2020-09-30 16:53:55 +02:00

947 lines
26 KiB
C++

/******************************Module*Header*******************************\
* Module Name: perfsuite.cpp
*
* Copyright (c) 1991-1999 Microsoft Corporation
*
* Contains the test prototypes and includes
*
\**************************************************************************/
#include "perftest.h"
#include <winuser.h>
/***************************************************************************\
* 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<ULONG*>(&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<LARGE_INTEGER*>(&CountsPerSecond));
MinimumCount = CountsPerSecond;
// Okay, start timing!
Iterations = 0;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&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<LARGE_INTEGER*>(&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<LARGE_INTEGER*>(&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<float>(counter - StartCounter) / CountsPerSecond;
*iterations = Iterations;
}
}