]> git.cworth.org Git - apitrace/blob - glretrace_main.cpp
Split gltrace.cpp code into multiple files.
[apitrace] / glretrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28
29 #include "image.hpp"
30 #include "retrace.hpp"
31 #include "glproc.hpp"
32 #include "glretrace.hpp"
33
34
35 namespace glretrace {
36
37 bool double_buffer = false;
38 bool insideGlBeginEnd = false;
39 Trace::Parser parser;
40 glws::WindowSystem *ws = NULL;
41 glws::Visual *visual = NULL;
42 glws::Drawable *drawable = NULL;
43 glws::Context *context = NULL;
44
45 int window_width = 256, window_height = 256;
46 bool reshape_window = false;
47
48 unsigned frame = 0;
49 long long startTime = 0;
50 bool wait = false;
51
52 bool benchmark = false;
53 const char *compare_prefix = NULL;
54 const char *snapshot_prefix = NULL;
55
56 unsigned dump_state = ~0;
57
58 void
59 checkGlError(void) {
60     if (benchmark || insideGlBeginEnd) {
61         return;
62     }
63
64     GLenum error = glGetError();
65     if (error == GL_NO_ERROR) {
66         return;
67     }
68
69     std::cerr << "warning: glGetError() = ";
70     switch (error) {
71     case GL_INVALID_ENUM:
72         std::cerr << "GL_INVALID_ENUM";
73         break;
74     case GL_INVALID_VALUE:
75         std::cerr << "GL_INVALID_VALUE";
76         break;
77     case GL_INVALID_OPERATION:
78         std::cerr << "GL_INVALID_OPERATION";
79         break;
80     case GL_STACK_OVERFLOW:
81         std::cerr << "GL_STACK_OVERFLOW";
82         break;
83     case GL_STACK_UNDERFLOW:
84         std::cerr << "GL_STACK_UNDERFLOW";
85         break;
86     case GL_OUT_OF_MEMORY:
87         std::cerr << "GL_OUT_OF_MEMORY";
88         break;
89     case GL_INVALID_FRAMEBUFFER_OPERATION:
90         std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION";
91         break;
92     case GL_TABLE_TOO_LARGE:
93         std::cerr << "GL_TABLE_TOO_LARGE";
94         break;
95     default:
96         std::cerr << error;
97         break;
98     }
99     std::cerr << "\n";
100 }
101
102
103 static void snapshot(Image::Image &image) {
104     GLint drawbuffer = double_buffer ? GL_BACK : GL_FRONT;
105     GLint readbuffer = double_buffer ? GL_BACK : GL_FRONT;
106     glGetIntegerv(GL_READ_BUFFER, &drawbuffer);
107     glGetIntegerv(GL_READ_BUFFER, &readbuffer);
108     glReadBuffer(drawbuffer);
109     glReadPixels(0, 0, image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE, image.pixels);
110     checkGlError();
111     glReadBuffer(readbuffer);
112 }
113
114
115 static void frame_complete(void) {
116     ++frame;
117     
118     if (!reshape_window && (snapshot_prefix || compare_prefix)) {
119         Image::Image *ref = NULL;
120         if (compare_prefix) {
121             char filename[PATH_MAX];
122             snprintf(filename, sizeof filename, "%s%04u.png", compare_prefix, frame);
123             ref = Image::readPNG(filename);
124             if (!ref) {
125                 return;
126             }
127             if (retrace::verbosity >= 0)
128                 std::cout << "Read " << filename << "\n";
129         }
130         
131         Image::Image src(window_width, window_height, true);
132         snapshot(src);
133
134         if (snapshot_prefix) {
135             char filename[PATH_MAX];
136             snprintf(filename, sizeof filename, "%s%04u.png", snapshot_prefix, frame);
137             if (src.writePNG(filename) && retrace::verbosity >= 0) {
138                 std::cout << "Wrote " << filename << "\n";
139             }
140         }
141
142         if (ref) {
143             std::cout << "Frame " << frame << " average precision of " << src.compare(*ref) << " bits\n";
144             delete ref;
145         }
146     }
147     
148     if (reshape_window) {
149         // XXX: doesn't quite work
150         drawable->resize(window_width, window_height);
151         reshape_window = false;
152     }
153
154     ws->processEvents();
155 }
156
157
158 static void display(void) {
159     Trace::Call *call;
160
161     while ((call = parser.parse_call())) {
162         const std::string &name = call->name();
163
164         if ((name[0] == 'w' && name[1] == 'g' && name[2] == 'l') ||
165             (name[0] == 'g' && name[1] == 'l' && name[2] == 'X')) {
166             // XXX: We ignore the majority of the OS-specific calls for now
167             if (name == "glXSwapBuffers" ||
168                 name == "wglSwapBuffers") {
169                 if (retrace::verbosity >= 1) {
170                     std::cout << *call;
171                     std::cout.flush();
172                 };
173                 frame_complete();
174                 if (double_buffer)
175                     drawable->swapBuffers();
176                 else
177                     glFlush();
178             } else if (name == "glXMakeCurrent" ||
179                        name == "wglMakeCurrent") {
180                 glFlush();
181                 if (!double_buffer) {
182                     frame_complete();
183                 }
184             } else {
185                 continue;
186             }
187         }
188
189         if (name == "glFlush") {
190             glFlush();
191             if (!double_buffer) {
192                 frame_complete();
193             }
194         }
195         
196         retrace::retrace_call(*call);
197
198         if (!insideGlBeginEnd && call->no >= dump_state) {
199             state_dump(std::cout);
200             exit(0);
201         }
202
203         delete call;
204     }
205
206     // Reached the end of trace
207     glFlush();
208
209     long long endTime = OS::GetTime();
210     float timeInterval = (endTime - startTime) * 1.0E-6;
211
212     if (retrace::verbosity >= -1) { 
213         std::cout << 
214             "Rendered " << frame << " frames"
215             " in " <<  timeInterval << " secs,"
216             " average of " << (frame/timeInterval) << " fps\n";
217     }
218
219     if (wait) {
220         while (ws->processEvents()) {}
221     } else {
222         exit(0);
223     }
224 }
225
226
227 static void usage(void) {
228     std::cout << 
229         "Usage: glretrace [OPTION] TRACE\n"
230         "Replay TRACE.\n"
231         "\n"
232         "  -b           benchmark (no glgeterror; no messages)\n"
233         "  -c PREFIX    compare against snapshots\n"
234         "  -db          use a double buffer visual\n"
235         "  -s PREFIX    take snapshots\n"
236         "  -v           verbose output\n"
237         "  -D CALLNO    dump state at specific call no\n"
238         "  -w           wait on final frame\n";
239 }
240
241 extern "C"
242 int main(int argc, char **argv)
243 {
244
245     int i;
246     for (i = 1; i < argc; ++i) {
247         const char *arg = argv[i];
248
249         if (arg[0] != '-') {
250             break;
251         }
252
253         if (!strcmp(arg, "--")) {
254             break;
255         } else if (!strcmp(arg, "-b")) {
256             benchmark = true;
257             retrace::verbosity = -1;
258         } else if (!strcmp(arg, "-c")) {
259             compare_prefix = argv[++i];
260         } else if (!strcmp(arg, "-D")) {
261             dump_state = atoi(argv[++i]);
262             retrace::verbosity = -2;
263         } else if (!strcmp(arg, "-db")) {
264             double_buffer = true;
265         } else if (!strcmp(arg, "--help")) {
266             usage();
267             return 0;
268         } else if (!strcmp(arg, "-s")) {
269             snapshot_prefix = argv[++i];
270         } else if (!strcmp(arg, "-v")) {
271             ++retrace::verbosity;
272         } else if (!strcmp(arg, "-w")) {
273             wait = true;
274         } else {
275             std::cerr << "error: unknown option " << arg << "\n";
276             usage();
277             return 1;
278         }
279     }
280
281     ws = glws::createNativeWindowSystem();
282     visual = ws->createVisual(double_buffer);
283     drawable = ws->createDrawable(visual);
284     drawable->resize(window_width, window_height);
285     context = ws->createContext(visual);
286     ws->makeCurrent(drawable, context);
287
288     for ( ; i < argc; ++i) {
289         if (parser.open(argv[i])) {
290             startTime = OS::GetTime();
291             display();
292             parser.close();
293         }
294     }
295
296     return 0;
297 }
298
299 } /* namespace glretrace */