// Created: 2008-04-13
// RCS-ID: $Id$
// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
-// Licence: wxWindows license
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/app.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
{
width = 800;
height = 600;
- numLines = 10000;
+ numIters = 1000;
+
+ testBitmaps =
+ testImages =
+ testLines =
+ testRawBitmaps =
+ testRectangles = false;
+
+ usePaint =
+ useClient =
+ useMemory = false;
+
+ useDC =
+ useGC =
+ useGL = false;
}
long mapMode,
penWidth,
width,
height,
- numLines;
+ numIters;
+
+ bool testBitmaps,
+ testImages,
+ testLines,
+ testRawBitmaps,
+ testRectangles;
+
+ bool usePaint,
+ useClient,
+ useMemory;
+
+ bool useDC,
+ useGC,
+ useGL;
} opts;
class GraphicsBenchmarkFrame : public wxFrame
GraphicsBenchmarkFrame()
: wxFrame(NULL, wxID_ANY, "wxWidgets Graphics Benchmark")
{
- Connect(wxEVT_PAINT,
- wxPaintEventHandler(GraphicsBenchmarkFrame::OnPaint));
+ SetClientSize(opts.width, opts.height);
+
+#if wxUSE_GLCANVAS
+ m_glCanvas = 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_bitmap.Create(64, 64, 32);
Show();
- SetClientSize(opts.width, opts.height);
- wxClientDC dc(this);
- BenchmarkLines("client", dc);
+ }
+
+#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);
- wxBitmap bmp(opts.width, opts.height);
- wxMemoryDC dc2(bmp);
- BenchmarkLines("memory", dc2);
+ data += 3*opts.width;
+ n++;
+ }
}
-protected:
+#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))
{
- wxPaintDC dc(this);
+ if ( opts.usePaint )
+ {
+ wxPaintDC dc(this);
+ wxGCDC gcdc(dc);
+ BenchmarkDCAndGC("paint", dc, gcdc);
+ }
- BenchmarkLines(" paint", dc);
+ 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("memory", dc, gcdc);
+ }
wxTheApp->ExitMainLoop();
}
- void BenchmarkLines(const char *msg, wxDC& dc)
+ 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(opts.mapMode);
+ dc.SetMapMode((wxMappingMode)opts.mapMode);
if ( opts.penWidth != 0 )
dc.SetPen(wxPen(*wxWHITE, opts.penWidth));
- wxPrintf("Benchmarking %s DC: ", msg);
+ wxPrintf("Benchmarking %s: ", msg);
+ fflush(stdout);
wxStopWatch sw;
int x = 0,
y = 0;
- for ( int n = 0; n < opts.numLines; n++ )
+ for ( int n = 0; n < opts.numIters; n++ )
{
int x1 = rand() % opts.width,
y1 = rand() % opts.height;
const long t = sw.Time();
- wxPrintf("%d lines done in %lums = %gus/line\n",
- opts.numLines, t, (1000. * t)/opts.numLines);
+ 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_bitmap, x, y, true);
+ }
+
+ const long t = sw.Time();
+
+ wxPrintf("%ld bitmaps done in %ldms = %gus/bitmap\n",
+ opts.numIters, t, (1000. * t)/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_bitmap;
+#if wxUSE_GLCANVAS
+ wxGLCanvas* m_glCanvas;
+ wxGLContext* m_glContext;
+#endif // wxUSE_GLCANVAS
};
class GraphicsBenchmarkApp : public wxApp
{
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, "L", "lines", "", 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);
return false;
if ( parser.Found("h", &opts.height) && opts.height < 1 )
return false;
- if ( parser.Found("L", &opts.numLines) && opts.numLines < 1 )
+ 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;
}