]> git.cworth.org Git - apitrace/blob - glretrace.py
Take the screenshots from the current draw buffer, and before swapping buffers.
[apitrace] / glretrace.py
1 ##########################################################################
2 #
3 # Copyright 2010 VMware, Inc.
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 """GL retracer generator."""
28
29
30 import stdapi
31 import glapi
32 from retrace import Retracer
33
34
35 class GlRetracer(Retracer):
36
37     def retrace_function(self, function):
38         Retracer.retrace_function(self, function)
39
40     draw_array_function_names = set([
41         "glDrawArrays",
42         "glDrawArraysEXT",
43         "glDrawArraysIndirect",
44         "glDrawArraysInstanced",
45         "glDrawArraysInstancedARB",
46         "glDrawArraysInstancedEXT",
47         "glDrawMeshArraysSUN",
48         "glMultiDrawArrays",
49         "glMultiDrawArraysEXT",
50         "glMultiModeDrawArraysIBM",
51     ])
52
53     draw_elements_function_names = set([
54         "glDrawElements",
55         "glDrawElementsBaseVertex",
56         "glDrawElementsIndirect",
57         "glDrawElementsInstanced",
58         "glDrawElementsInstancedARB",
59         "glDrawElementsInstancedBaseVertex",
60         "glDrawElementsInstancedEXT",
61         "glDrawRangeElements",
62         "glDrawRangeElementsBaseVertex",
63         "glDrawRangeElementsEXT",
64         "glMultiDrawElements",
65         "glMultiDrawElementsBaseVertex",
66         "glMultiDrawElementsEXT",
67         "glMultiModeDrawElementsIBM",
68     ])
69
70     def call_function(self, function):
71         if (function.name in self.draw_array_function_names or 
72             function.name in self.draw_elements_function_names):
73             print '    GLint __array_buffer = 0;'
74             print '    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);'
75             print '    if (!__array_buffer) {'
76             self.fail_function(function)
77             print '    }'
78
79         if function.name in self.draw_elements_function_names:
80             print '    GLint __element_array_buffer = 0;'
81             print '    glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);'
82             print '    if (!__element_array_buffer) {'
83             self.fail_function(function)
84             print '    }'
85
86         if function.name == "glViewport":
87             print '    if (x + width > __window_width) {'
88             print '        __window_width = x + width;'
89             print '        __reshape_window = true;'
90             print '    }'
91             print '    if (y + height > __window_height) {'
92             print '        __window_height = y + height;'
93             print '        __reshape_window = true;'
94             print '    }'
95
96         if function.name == "glEnd":
97             print '    insideGlBeginEnd = false;'
98         
99         Retracer.call_function(self, function)
100
101         if function.name == "glBegin":
102             print '    insideGlBeginEnd = true;'
103         else:
104             # glGetError is not allowed inside glBegin/glEnd
105             print '    checkGlError();'
106
107     pointer_function_names = set([
108         "glColorPointer",
109         "glColorPointerEXT",
110         "glEdgeFlagPointer",
111         "glEdgeFlagPointerEXT",
112         "glFogCoordPointer",
113         "glFogCoordPointerEXT",
114         "glIndexPointer",
115         "glIndexPointerEXT",
116         "glMatrixIndexPointerARB",
117         "glNormalPointer",
118         "glNormalPointerEXT",
119         "glSecondaryColorPointer",
120         "glSecondaryColorPointerEXT",
121         "glTexCoordPointer",
122         "glTexCoordPointerEXT",
123         "glVertexAttribLPointer",
124         "glVertexAttribPointer",
125         "glVertexAttribPointerARB",
126         "glVertexAttribPointerNV",
127         "glVertexPointer",
128         "glVertexPointerEXT",
129     ])
130
131     def extract_arg(self, function, arg, arg_type, lvalue, rvalue):
132         if (function.name in self.pointer_function_names and arg.name == 'pointer' or
133             function.name in self.draw_elements_function_names and arg.name == 'indices'):
134             print '    if (dynamic_cast<Trace::Null *>(&%s)) {' % rvalue
135             print '        %s = 0;' % (lvalue)
136             print '    } else {'
137             print '        %s = (%s)(uintptr_t)(%s);' % (lvalue, arg_type, rvalue)
138             print '    }'
139             return
140
141         if function.name.startswith('glUniform') and function.args[0].name == arg.name == 'location':
142             print '    GLint program = -1;'
143             print '    glGetIntegerv(GL_CURRENT_PROGRAM, &program);'
144
145         Retracer.extract_arg(self, function, arg, arg_type, lvalue, rvalue)
146
147
148 if __name__ == '__main__':
149     print r'''
150 #include <string.h>
151 #include <stdio.h>
152 #include <iostream>
153
154 #include "glproc.hpp"
155 #include <GL/glut.h>
156
157 static bool double_buffer = false;
158 static bool insideGlBeginEnd = false;
159
160 static int __window_width = 256, __window_height = 256;
161 bool __reshape_window = false;
162
163 unsigned __frame = 0;
164 long long __startTime = 0;
165 bool __wait = false;
166
167 bool __benchmark = false;
168 const char *__compare_prefix = NULL;
169 const char *__snapshot_prefix = NULL;
170
171
172 static void
173 checkGlError(void) {
174     if (__benchmark || insideGlBeginEnd) {
175         return;
176     }
177
178     GLenum error = glGetError();
179     if (error == GL_NO_ERROR) {
180         return;
181     }
182
183     std::cerr << "warning: glGetError() = ";
184     switch (error) {
185     case GL_INVALID_ENUM:
186         std::cerr << "GL_INVALID_ENUM";
187         break;
188     case GL_INVALID_VALUE:
189         std::cerr << "GL_INVALID_VALUE";
190         break;
191     case GL_INVALID_OPERATION:
192         std::cerr << "GL_INVALID_OPERATION";
193         break;
194     case GL_STACK_OVERFLOW:
195         std::cerr << "GL_STACK_OVERFLOW";
196         break;
197     case GL_STACK_UNDERFLOW:
198         std::cerr << "GL_STACK_UNDERFLOW";
199         break;
200     case GL_OUT_OF_MEMORY:
201         std::cerr << "GL_OUT_OF_MEMORY";
202         break;
203     case GL_INVALID_FRAMEBUFFER_OPERATION:
204         std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION";
205         break;
206     case GL_TABLE_TOO_LARGE:
207         std::cerr << "GL_TABLE_TOO_LARGE";
208         break;
209     default:
210         std::cerr << error;
211         break;
212     }
213     std::cerr << "\n";
214 }
215 '''
216     api = glapi.glapi
217     retracer = GlRetracer()
218     retracer.retrace_api(glapi.glapi)
219     print r'''
220
221 static Trace::Parser parser;
222
223 static void display_noop(void) {
224 }
225
226 #include "image.hpp"
227
228 static void snapshot(Image::Image &image) {
229     GLint drawbuffer = double_buffer ? GL_BACK : GL_FRONT;
230     GLint readbuffer = double_buffer ? GL_BACK : GL_FRONT;
231     glGetIntegerv(GL_READ_BUFFER, &drawbuffer);
232     glGetIntegerv(GL_READ_BUFFER, &readbuffer);
233     glReadBuffer(drawbuffer);
234     glReadPixels(0, 0, image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE, image.pixels);
235     checkGlError();
236     glReadBuffer(readbuffer);
237 }
238
239 static void frame_complete(void) {
240     ++__frame;
241     
242     if (!__reshape_window && (__snapshot_prefix || __compare_prefix)) {
243         Image::Image *ref = NULL;
244         if (__compare_prefix) {
245             char filename[PATH_MAX];
246             snprintf(filename, sizeof filename, "%s%04u.png", __compare_prefix, __frame);
247             ref = Image::readPNG(filename);
248             if (!ref) {
249                 return;
250             }
251             if (verbosity >= 0)
252                 std::cout << "Read " << filename << "\n";
253         }
254         
255         Image::Image src(__window_width, __window_height, true);
256         snapshot(src);
257
258         if (__snapshot_prefix) {
259             char filename[PATH_MAX];
260             snprintf(filename, sizeof filename, "%s%04u.png", __snapshot_prefix, __frame);
261             if (src.writePNG(filename) && verbosity >= 0) {
262                 std::cout << "Wrote " << filename << "\n";
263             }
264         }
265
266         if (ref) {
267             std::cout << "Frame " << __frame << " average precision of " << src.compare(*ref) << " bits\n";
268             delete ref;
269         }
270     }
271
272 }
273
274 static void display(void) {
275     Trace::Call *call;
276
277     while ((call = parser.parse_call())) {
278         const std::string &name = call->name();
279
280         if ((name[0] == 'w' && name[1] == 'g' && name[2] == 'l') ||
281             (name[0] == 'g' && name[1] == 'l' && name[2] == 'X')) {
282             // XXX: We ignore the majority of the OS-specific calls for now
283             if (name == "glXSwapBuffers" ||
284                 name == "wglSwapBuffers") {
285                 frame_complete();
286                 if (double_buffer)
287                     glutSwapBuffers();
288                 else
289                     glFlush();
290                 return;
291             } else {
292                 continue;
293             }
294         }
295
296         if (name == "glFlush") {
297             if (!double_buffer) {
298                 frame_complete();
299             }
300             glFlush();
301         }
302         
303         retrace_call(*call);
304
305         delete call;
306     }
307
308     // Reached the end of trace
309     glFlush();
310
311     long long endTime = OS::GetTime();
312     float timeInterval = (endTime - __startTime) * 1.0E-6;
313
314     std::cout << 
315         "Rendered " << __frame << " frames"
316         " in " <<  timeInterval << " secs,"
317         " average of " << (__frame/timeInterval) << " fps\n";
318
319     if (__wait) {
320         glutDisplayFunc(&display_noop);
321         glutIdleFunc(NULL);
322     } else {
323         exit(0);
324     }
325 }
326
327 static void idle(void) {
328     if (__reshape_window) {
329         // XXX: doesn't quite work
330         glutReshapeWindow(__window_width, __window_height);
331         __reshape_window = false;
332     }
333     glutPostRedisplay();
334 }
335
336 static void usage(void) {
337     std::cout << 
338         "Usage: glretrace [OPTION] TRACE\n"
339         "Replay TRACE.\n"
340         "\n"
341         "  -b           benchmark (no glgeterror; no messages)\n"
342         "  -c PREFIX    compare against snapshots\n"
343         "  -db          use a double buffer visual\n"
344         "  -s PREFIX    take snapshots\n"
345         "  -v           verbose output\n";
346 }
347
348 int main(int argc, char **argv)
349 {
350
351     int i;
352     for (i = 1; i < argc; ++i) {
353         const char *arg = argv[i];
354
355         if (arg[0] != '-') {
356             break;
357         }
358
359         if (!strcmp(arg, "--")) {
360             break;
361         } else if (!strcmp(arg, "-b")) {
362             __benchmark = true;
363             --verbosity;
364         } else if (!strcmp(arg, "-c")) {
365             __compare_prefix = argv[++i];
366         } else if (!strcmp(arg, "-db")) {
367             double_buffer = true;
368         } else if (!strcmp(arg, "--help")) {
369             usage();
370             return 0;
371         } else if (!strcmp(arg, "-s")) {
372             __snapshot_prefix = argv[++i];
373         } else if (!strcmp(arg, "-v")) {
374             ++verbosity;
375         } else if (!strcmp(arg, "-w")) {
376             __wait = true;
377         } else {
378             std::cerr << "error: unknown option " << arg << "\n";
379             usage();
380             return 1;
381         }
382     }
383
384     glutInit(&argc, argv);
385     glutInitWindowPosition(0, 0);
386     glutInitWindowSize(__window_width, __window_height);
387     glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | (double_buffer ? GLUT_DOUBLE : GLUT_SINGLE));
388     glutCreateWindow(argv[0]);
389
390     glutDisplayFunc(&display);
391     glutIdleFunc(&idle);
392
393     for (GLuint h = 0; h < 1024; ++h) {
394         __list_map[h] = h;
395     }
396
397     for ( ; i < argc; ++i) {
398         if (parser.open(argv[i])) {
399             __startTime = OS::GetTime();
400             glutMainLoop();
401             parser.close();
402         }
403     }
404
405     return 0;
406 }
407
408 '''