add profiler state for clearing the buffer
[gltest.git] / gltest.cpp
1 // stdlib includes
2 #include <time.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <limits.h>
7 #include <unistd.h>
8 #include <assert.h>
9 #include <GL/gl.h>
10 #include <boost/program_options.hpp>
11
12 namespace po = boost::program_options;
13
14 // include proper GL connector
15 #include "glwindow.h"
16 #if defined(USE_GLX)
17 #include "glxbackend.h"
18 static GLBackend *createGLBackend()
19 {
20         return new GLXBackend();
21 }
22 #elif defined(USE_EGL)
23 #include "eglbackend.h"
24 static GLBackend *createGLBackend()
25 {
26         return new EGLBackend();
27 }
28 #else
29 #error "No GL window type selected"
30 #endif
31
32 // configuration
33 static const GLfloat boxWidth = 0.045f;
34 static const GLfloat boxSpeed = 1.25f; // per second
35
36 // profiler
37 enum ProfilerState { statePreRender, stateClear, stateDraw, statePresent, statePostRender, stateOutsideRender, numProfilerStates };
38 static const char *profilerStateNames[numProfilerStates] = { "Pre-Render", "Clearing", "Drawing", "Presenting", "Post-Render", "Outside renderer"};
39
40 // utility functions
41 static double getTime()
42 {
43         struct timespec tp;
44         clock_gettime(CLOCK_MONOTONIC, &tp);
45         return tp.tv_sec + 1e-9 * tp.tv_nsec;
46 }
47
48 static void drawRect(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
49 {
50         glVertex2f(x1, y1); glVertex2f(x2, y1); glVertex2f(x2, y2); glVertex2f(x1, y2);
51 }
52
53 // the window
54 class TearTestWindow : public GLWindow {
55 public:
56         TearTestWindow(bool overdraw, bool copy) : GLWindow(XOpenDisplay(0), createGLBackend()), overdraw(overdraw), copy(copy), boxPos(0), boxDirection(1)
57         {}
58
59         void setSwapInterval(int i) {
60                 getBackend()->setSwapInterval(i);
61         }
62
63 protected:
64         virtual void initGL()
65         {
66                 // initialize GL proper
67                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
68                 glDisable(GL_DEPTH_TEST);
69                 // initialize clocks
70                 lastFrame = getTime();
71                 // initailize profiler
72                 framect = 0;
73                 memset(stateTime, 0, sizeof(stateTime));
74                 curState = -1;
75                 lastDisplay = lastProfile = getTime();
76         }
77         
78         virtual void resizeGL(unsigned int width, unsigned int height)
79         {
80                 /* prevent divide-by-zero */                      
81                 if (height == 0)                                  
82                         height = 1;                                   
83                 glViewport(0, 0, width, height);
84                 glMatrixMode(GL_PROJECTION);                      
85                 glLoadIdentity();                                 
86                 glOrtho (0, 1, 1, 0, 0, 1);
87                 glMatrixMode(GL_MODELVIEW);      
88                 glClear(GL_COLOR_BUFFER_BIT);                                                 
89                 glFlush();
90         }
91         
92         void profilerTick(ProfilerState nextState)
93         {
94                 assert (nextState >= 0 && nextState < numProfilerStates);
95                 double time = getTime();
96                 if (curState >= 0)
97                         stateTime[curState] += time-lastProfile;
98                 curState = nextState;
99                 lastProfile = time;
100                 // display?
101                 const double elapsed = time-lastDisplay;
102                 if (elapsed >= 3) {
103                         printf("%.1f fps, time spent: ", framect/elapsed);
104                         for (int i = 0; i < numProfilerStates; ++i) {
105                                 if (i != 0) printf(", ");
106                                 printf("%s %.1f%%", profilerStateNames[i], stateTime[i]/elapsed*100);
107                         }
108                         printf("\n");
109                         lastDisplay = time;
110                         framect = 0;
111                         memset(stateTime, 0, sizeof(stateTime));
112                 }
113         }
114         
115         void renderGL()
116         {              
117                 //////////////////////////////////////////////
118                 profilerTick(statePreRender);
119                 double time = getTime();
120                 // anim
121                 double passedTime = time-lastFrame;
122                 boxPos += boxSpeed*passedTime*boxDirection;
123                 while (boxPos < 0 || boxPos+boxWidth > 1) { // wrapover
124                         if (boxPos < 0) {
125                                 boxPos = -boxPos;
126                                 boxDirection = -boxDirection;
127                         }
128                         else {
129                                 boxPos = 1.0-boxWidth-(boxPos+boxWidth-1.0);
130                                 boxDirection = -boxDirection;
131                         }
132                 }
133                 lastFrame = time;
134                 //////////////////////////////////////////////
135                 profilerTick(stateClear);
136                 if (overdraw) {
137                         // clear manually
138                         glBegin(GL_QUADS);
139                         glColor3f(0.0f, 0.0f, 0.0f);
140                         drawRect(0, 0, 1, 1);
141                         glEnd();
142                 }
143                 else {
144                         glClear(GL_COLOR_BUFFER_BIT);
145                 }
146                 //////////////////////////////////////////////
147                 profilerTick(stateDraw);
148                 glBegin(GL_QUADS);
149                 glColor3f(0.8f, 1.0f, 0.75f);
150                 drawRect(boxPos, 0, boxPos+boxWidth, 1);
151                 glEnd();
152                 //////////////////////////////////////////////
153                 profilerTick(statePresent);
154                 if (copy) {
155                         glDrawBuffer(GL_FRONT);
156                         glCopyPixels(0, 0, getWidth(), getHeight(), GL_COLOR);
157                         glDrawBuffer(GL_BACK);
158                 }
159                 else {
160                         getBackend()->swapBuffers();
161                 }
162                 //////////////////////////////////////////////
163                 profilerTick(statePostRender);
164                 glFlush();
165                 ++framect;
166                 //////////////////////////////////////////////
167                 profilerTick(stateOutsideRender);
168         }
169         
170         virtual void handleKeyPress(KeySym key)
171         {
172                 switch (key) {
173                         case XK_Escape: close(); break;
174                         case XK_F1: setFullscreen(!getFullscreen()); break;
175                         default: break;
176                 }
177         }
178
179 private:
180         bool overdraw, copy;
181         double lastFrame;
182         GLfloat boxPos, boxDirection;
183         // FPS
184         double lastDisplay, lastProfile;                 
185         int framect, curState;
186         double stateTime[numProfilerStates];
187 };
188
189 int main(int argc, char ** argv)
190 {
191         // program options handling
192         po::options_description desc("Allowed options");
193         desc.add_options()
194                 ("help,h", "produce help message")
195                 ("swap-interval,i", po::value<int>(), "set swap interval")
196                 ("copy,c", "copy to front buffer (instead of performing a buffer swap)")
197                 ("overdraw,o", "overdraw previous image (instead of calling glClear)")
198         ;
199         po::variables_map vm;
200         po::store(po::parse_command_line(argc, argv, desc), vm);
201         po::notify(vm);
202
203         if (vm.count("help")) {
204                 std::cout << desc << "\n";
205                 return 1;
206         }
207
208         // actual program
209         TearTestWindow w(vm.count("overdraw"), vm.count("copy"));
210         w.open(800, 600);
211         if (vm.count("swap-interval"))
212                 w.setSwapInterval(vm["swap-interval"].as<int>());
213         w.exec();
214 }