Files
wxWidgets/tests/benchmarks/graphics.cpp
Vadim Zeitlin 530276ec6a Fix crash in graphics benchmark
Set the GL context pointer deleted in the frame dtor to NULL to ensure we
don't crash on exit when not using OpenGL.
2015-12-06 17:31:24 +01:00

585 lines
16 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: graphics.cpp
// Purpose: Some benchmarks for measuring graphics operations performance
// Author: Vadim Zeitlin
// Created: 2008-04-13
// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/app.h"
#include "wx/frame.h"
#include "wx/cmdline.h"
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/dcgraph.h"
#include "wx/image.h"
#include "wx/rawbmp.h"
#include "wx/stopwatch.h"
#include "wx/crt.h"
#if wxUSE_GLCANVAS
#include "wx/glcanvas.h"
#ifdef _MSC_VER
#pragma comment(lib, "opengl32")
#endif
#endif // wxUSE_GLCANVAS
#if wxUSE_GLCANVAS
GLuint g_texture;
wxImage g_image;
void InitializeTexture(int w, int h)
{
glGenTextures(1, &g_texture);
glBindTexture(GL_TEXTURE_2D, g_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
g_image.Create(w, h, false /* don't clear */);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0,
GL_RGB, g_image.GetWidth(), g_image.GetHeight(), 0,
GL_RGB, GL_UNSIGNED_BYTE, g_image.GetData());
}
#endif // wxUSE_GLCANVAS
struct GraphicsBenchmarkOptions
{
GraphicsBenchmarkOptions()
{
mapMode = 0;
penWidth = 0;
width = 800;
height = 600;
numIters = 1000;
testBitmaps =
testImages =
testLines =
testRawBitmaps =
testRectangles = false;
usePaint =
useClient =
useMemory = false;
useDC =
useGC =
useGL = false;
}
long mapMode,
penWidth,
width,
height,
numIters;
bool testBitmaps,
testImages,
testLines,
testRawBitmaps,
testRectangles;
bool usePaint,
useClient,
useMemory;
bool useDC,
useGC,
useGL;
} opts;
class GraphicsBenchmarkFrame : public wxFrame
{
public:
GraphicsBenchmarkFrame()
: wxFrame(NULL, wxID_ANY, "wxWidgets Graphics Benchmark")
{
SetClientSize(opts.width, opts.height);
#if wxUSE_GLCANVAS
m_glCanvas = NULL;
m_glContext = NULL;
if ( opts.useGL )
{
m_glCanvas = new wxGLCanvas(this, wxID_ANY, NULL,
wxPoint(0, 0),
wxSize(opts.width, opts.height));
m_glContext = new wxGLContext(m_glCanvas);
m_glContext->SetCurrent(*m_glCanvas);
glViewport(0, 0, opts.width, opts.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1, 1, -1, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
InitializeTexture(opts.width, opts.height);
m_glCanvas->Connect(
wxEVT_PAINT,
wxPaintEventHandler(GraphicsBenchmarkFrame::OnGLRender),
NULL,
this
);
}
else // Not using OpenGL
#endif // wxUSE_GLCANVAS
{
Connect(wxEVT_PAINT,
wxPaintEventHandler(GraphicsBenchmarkFrame::OnPaint));
}
Connect(wxEVT_SIZE, wxSizeEventHandler(GraphicsBenchmarkFrame::OnSize));
m_bitmapARGB.Create(64, 64, 32);
m_bitmapARGB.UseAlpha(true);
m_bitmapRGB.Create(64, 64, 24);
Show();
}
#if wxUSE_GLCANVAS
virtual ~GraphicsBenchmarkFrame()
{
delete m_glContext;
}
#endif // wxUSE_GLCANVAS
private:
// Just change the image in some (quick) way to show that it's really being
// updated on screen.
void UpdateRGB(unsigned char* data, int n)
{
for ( int y = 0; y < opts.height; ++y )
{
memset(data, n % 256, 3*opts.width);
data += 3*opts.width;
n++;
}
}
#if wxUSE_GLCANVAS
void OnGLRender(wxPaintEvent& WXUNUSED(event))
{
m_glContext->SetCurrent(*m_glCanvas);
glEnable(GL_TEXTURE_2D);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
wxPrintf("Benchmarking %s: ", "OpenGL images");
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
UpdateRGB(g_image.GetData(), n);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0, opts.width, opts.height,
GL_RGB, GL_UNSIGNED_BYTE, g_image.GetData());
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(-1.0, -1.0);
glTexCoord2f(0, 1);
glVertex2f(-1.0, 1.0);
glTexCoord2f(1, 1);
glVertex2f(1.0, 1.0);
glTexCoord2f(1, 0);
glVertex2f(1.0, -1.0);
glEnd();
m_glCanvas->SwapBuffers();
}
const long t = sw.Time();
wxPrintf("%ld images done in %ldms = %gus/image or %ld FPS\n",
opts.numIters, t, (1000. * t)/opts.numIters,
(1000*opts.numIters + t - 1)/t);
wxTheApp->ExitMainLoop();
}
#endif // wxUSE_GLCANVAS
void OnPaint(wxPaintEvent& WXUNUSED(event))
{
if ( opts.usePaint )
{
wxPaintDC dc(this);
wxGCDC gcdc(dc);
BenchmarkDCAndGC("paint", dc, gcdc);
}
if ( opts.useClient )
{
wxClientDC dc(this);
wxGCDC gcdc(dc);
BenchmarkDCAndGC("client", dc, gcdc);
}
if ( opts.useMemory )
{
{
wxBitmap bmp(opts.width, opts.height);
wxMemoryDC dc(bmp);
wxGCDC gcdc(dc);
BenchmarkDCAndGC("default memory", dc, gcdc);
}
{
wxBitmap bmp(opts.width, opts.height, 24);
wxMemoryDC dc(bmp);
wxGCDC gcdc(dc);
BenchmarkDCAndGC("RGB memory", dc, gcdc);
}
{
wxBitmap bmp(opts.width, opts.height, 32);
bmp.UseAlpha(false);
wxMemoryDC dc(bmp);
wxGCDC gcdc(dc);
BenchmarkDCAndGC("0RGB memory", dc, gcdc);
}
{
wxBitmap bmp(opts.width, opts.height, 32);
bmp.UseAlpha(true);
wxMemoryDC dc(bmp);
wxGCDC gcdc(dc);
BenchmarkDCAndGC("ARGB memory", dc, gcdc);
}
}
wxTheApp->ExitMainLoop();
}
void BenchmarkDCAndGC(const char* dckind, wxDC& dc, wxGCDC& gcdc)
{
if ( opts.useDC )
BenchmarkAll(wxString::Format("%6s DC", dckind), dc);
if ( opts.useGC )
BenchmarkAll(wxString::Format("%6s GC", dckind), gcdc);
}
void BenchmarkAll(const wxString& msg, wxDC& dc)
{
BenchmarkBitmaps(msg, dc);
BenchmarkImages(msg, dc);
BenchmarkLines(msg, dc);
BenchmarkRawBitmaps(msg, dc);
BenchmarkRectangles(msg, dc);
}
void BenchmarkLines(const wxString& msg, wxDC& dc)
{
if ( !opts.testLines )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
if ( opts.penWidth != 0 )
dc.SetPen(wxPen(*wxWHITE, opts.penWidth));
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
int x = 0,
y = 0;
for ( int n = 0; n < opts.numIters; n++ )
{
int x1 = rand() % opts.width,
y1 = rand() % opts.height;
dc.DrawLine(x, y, x1, y1);
x = x1;
y = y1;
}
const long t = sw.Time();
wxPrintf("%ld lines done in %ldms = %gus/line\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkRectangles(const wxString& msg, wxDC& dc)
{
if ( !opts.testRectangles )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
if ( opts.penWidth != 0 )
dc.SetPen(wxPen(*wxWHITE, opts.penWidth));
dc.SetBrush( *wxRED_BRUSH );
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawRectangle(x, y, 32, 32);
}
const long t = sw.Time();
wxPrintf("%ld rects done in %ldms = %gus/rect\n",
opts.numIters, t, (1000. * t)/opts.numIters);
}
void BenchmarkBitmaps(const wxString& msg, wxDC& dc)
{
if ( !opts.testBitmaps )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
if ( opts.penWidth != 0 )
dc.SetPen(wxPen(*wxWHITE, opts.penWidth));
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawBitmap(m_bitmapARGB, x, y, true);
}
const long t = sw.Time();
wxPrintf("%ld ARGB bitmaps done in %ldms = %gus/bitmap\n",
opts.numIters, t, (1000. * t)/opts.numIters);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
sw.Start();
for ( int n = 0; n < opts.numIters; n++ )
{
int x = rand() % opts.width,
y = rand() % opts.height;
dc.DrawBitmap(m_bitmapRGB, x, y, true);
}
const long t2 = sw.Time();
wxPrintf("%ld RGB bitmaps done in %ldms = %gus/bitmap\n",
opts.numIters, t2, (1000. * t2)/opts.numIters);
}
void BenchmarkImages(const wxString& msg, wxDC& dc)
{
if ( !opts.testImages )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxImage image(wxSize(opts.width, opts.height), false /* don't clear */);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
UpdateRGB(image.GetData(), n);
dc.DrawBitmap(image, 0, 0);
}
const long t = sw.Time();
wxPrintf("%ld images done in %ldms = %gus/image or %ld FPS\n",
opts.numIters, t, (1000. * t)/opts.numIters,
(1000*opts.numIters + t - 1)/t);
}
void BenchmarkRawBitmaps(const wxString& msg, wxDC& dc)
{
if ( !opts.testRawBitmaps )
return;
if ( opts.mapMode != 0 )
dc.SetMapMode((wxMappingMode)opts.mapMode);
wxPrintf("Benchmarking %s: ", msg);
fflush(stdout);
wxBitmap bitmap(opts.width, opts.height, 24);
wxNativePixelData data(bitmap);
wxStopWatch sw;
for ( int n = 0; n < opts.numIters; n++ )
{
unsigned char c = n % 256;
{
wxNativePixelData::Iterator p(data);
for ( int y = 0; y < opts.height; ++y )
{
wxNativePixelData::Iterator rowStart = p;
for ( int x = 0; x < opts.width; ++x )
{
p.Red() =
p.Green() =
p.Blue() = c;
++p;
}
p = rowStart;
p.OffsetY(data, 1);
c++;
}
}
dc.DrawBitmap(bitmap, 0, 0);
}
const long t = sw.Time();
wxPrintf("%ld raw bitmaps done in %ldms = %gus/bitmap or %ld FPS\n",
opts.numIters, t, (1000. * t)/opts.numIters,
(1000*opts.numIters + t - 1)/t);
}
wxBitmap m_bitmapARGB;
wxBitmap m_bitmapRGB;
#if wxUSE_GLCANVAS
wxGLCanvas* m_glCanvas;
wxGLContext* m_glContext;
#endif // wxUSE_GLCANVAS
};
class GraphicsBenchmarkApp : public wxApp
{
public:
virtual void OnInitCmdLine(wxCmdLineParser& parser)
{
static const wxCmdLineEntryDesc desc[] =
{
{ wxCMD_LINE_SWITCH, "", "bitmaps" },
{ wxCMD_LINE_SWITCH, "", "images" },
{ wxCMD_LINE_SWITCH, "", "lines" },
{ wxCMD_LINE_SWITCH, "", "rawbmp" },
{ wxCMD_LINE_SWITCH, "", "rectangles" },
{ wxCMD_LINE_SWITCH, "", "paint" },
{ wxCMD_LINE_SWITCH, "", "client" },
{ wxCMD_LINE_SWITCH, "", "memory" },
{ wxCMD_LINE_SWITCH, "", "dc" },
{ wxCMD_LINE_SWITCH, "", "gc" },
#if wxUSE_GLCANVAS
{ wxCMD_LINE_SWITCH, "", "gl" },
#endif // wxUSE_GLCANVAS
{ wxCMD_LINE_OPTION, "m", "map-mode", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "p", "pen-width", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "w", "width", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "h", "height", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "I", "images", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_OPTION, "N", "number-of-iterations", "", wxCMD_LINE_VAL_NUMBER },
{ wxCMD_LINE_NONE },
};
parser.SetDesc(desc);
}
virtual bool OnCmdLineParsed(wxCmdLineParser& parser)
{
if ( parser.Found("m", &opts.mapMode) &&
(opts.mapMode < 1 || opts.mapMode > wxMM_METRIC) )
return false;
if ( parser.Found("p", &opts.penWidth) && opts.penWidth < 1 )
return false;
if ( parser.Found("w", &opts.width) && opts.width < 1 )
return false;
if ( parser.Found("h", &opts.height) && opts.height < 1 )
return false;
if ( parser.Found("N", &opts.numIters) && opts.numIters < 1 )
return false;
opts.testBitmaps = parser.Found("bitmaps");
opts.testImages = parser.Found("images");
opts.testLines = parser.Found("lines");
opts.testRawBitmaps = parser.Found("rawbmp");
opts.testRectangles = parser.Found("rectangles");
if ( !(opts.testBitmaps || opts.testImages || opts.testLines
|| opts.testRawBitmaps || opts.testRectangles) )
{
// Do everything by default.
opts.testBitmaps =
opts.testImages =
opts.testLines =
opts.testRawBitmaps =
opts.testRectangles = true;
}
opts.usePaint = parser.Found("paint");
opts.useClient = parser.Found("client");
opts.useMemory = parser.Found("memory");
if ( !(opts.usePaint || opts.useClient || opts.useMemory) )
{
opts.usePaint =
opts.useClient =
opts.useMemory = true;
}
opts.useDC = parser.Found("dc");
opts.useGC = parser.Found("gc");
#if wxUSE_GLCANVAS
opts.useGL = parser.Found("gl");
if ( opts.useGL )
{
if ( opts.useDC || opts.useGC )
{
wxLogError("Can't use both OpenGL and normal graphics.");
return false;
}
}
else // Not using OpenGL
#endif // wxUSE_GLCANVAS
{
if ( !(opts.useDC || opts.useGC) )
{
opts.useDC =
opts.useGC = true;
}
}
return true;
}
virtual bool OnInit()
{
if ( !wxApp::OnInit() )
return false;
new GraphicsBenchmarkFrame;
return true;
}
};
wxIMPLEMENT_APP_CONSOLE(GraphicsBenchmarkApp);