]> git.cworth.org Git - apitrace/blob - glretrace.py
Compare screenshots.
[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 static enum {
166     MODE_DISPLAY = 0,
167     MODE_SCREENSHOT,
168     MODE_COMPARE,
169 } __mode = MODE_DISPLAY;
170
171
172 static void
173 checkGlError(void) {
174     if (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 frame_complete(void) {
229     ++__frame;
230     
231     if (!__reshape_window && (__mode == MODE_SCREENSHOT || __mode == MODE_COMPARE)) {
232         char filename[PATH_MAX];
233
234         snprintf(filename, sizeof filename, "screenshot_%04u.png", __frame);
235         
236         Image::Image *ref;
237         if (__mode == MODE_COMPARE) {
238             ref = Image::readPNG(filename);
239             if (!ref) {
240                 return;
241             }
242             if (verbosity)
243                 std::cout << "Read " << filename << "\n";
244         }
245         
246         Image::Image src(__window_width, __window_height, true);
247         glReadPixels(0, 0, __window_width, __window_height, GL_RGBA, GL_UNSIGNED_BYTE, src.pixels);
248
249         if (__mode == MODE_SCREENSHOT) {
250             if (src.writePNG(filename) && verbosity) {
251                 std::cout << "Wrote " << filename << "\n";
252             }
253         }
254
255         if (__mode == MODE_COMPARE) {
256             std::cout << "Frame " << __frame << " average precision of " << src.compare(*ref) << " bits\n";
257             delete ref;
258         }
259     }
260
261 }
262
263 static void display(void) {
264     Trace::Call *call;
265
266     while ((call = parser.parse_call())) {
267         if (call->name() == "glFlush") {
268             glFlush();
269             if (!double_buffer) {
270                 frame_complete();
271             }
272         }
273         
274         if (!retrace_call(*call)) {
275             if (call->name() == "glXSwapBuffers" ||
276                 call->name() == "wglSwapBuffers") {
277                 if (double_buffer)
278                     glutSwapBuffers();
279                 else
280                     glFlush();
281                 frame_complete();
282                 return;
283             }
284         }
285     }
286
287     // Reached the end of trace
288     glFlush();
289
290     long long endTime = OS::GetTime();
291     float timeInterval = (endTime - __startTime) * 1.0E-6;
292
293     std::cout << 
294         "Rendered " << __frame << " frames"
295         " in " <<  timeInterval << " secs,"
296         " average of " << (__frame/timeInterval) << " fps\n";
297
298     if (__mode == MODE_DISPLAY) {
299         glutDisplayFunc(&display_noop);
300         glutIdleFunc(NULL);
301     } else {
302         exit(0);
303     }
304 }
305
306 static void idle(void) {
307     if (__reshape_window) {
308         // XXX: doesn't quite work
309         glutReshapeWindow(__window_width, __window_height);
310         __reshape_window = false;
311     }
312     glutPostRedisplay();
313 }
314
315 static void usage(void) {
316     std::cout << 
317         "Usage: glretrace [OPTION] TRACE\n"
318         "Replay TRACE.\n"
319         "\n"
320         "  -c           compare against screenshots\n"
321         "  -db          use a double buffer visual\n"
322         "  -s           take snapshots\n"
323         "  -v           verbose output\n";
324 }
325
326 int main(int argc, char **argv)
327 {
328
329     int i;
330     for (i = 1; i < argc; ++i) {
331         const char *arg = argv[i];
332
333         if (arg[0] != '-') {
334             break;
335         }
336
337         if (!strcmp(arg, "--")) {
338             break;
339         } else if (!strcmp(arg, "-c")) {
340             __mode = MODE_COMPARE;
341         } else if (!strcmp(arg, "-db")) {
342             double_buffer = true;
343         } else if (!strcmp(arg, "--help")) {
344             usage();
345             return 0;
346         } else if (!strcmp(arg, "-s")) {
347             __mode = MODE_SCREENSHOT;
348         } else if (!strcmp(arg, "-v")) {
349             ++verbosity;
350         } else {
351             std::cerr << "error: unknown option " << arg << "\n";
352             usage();
353             return 1;
354         }
355     }
356
357     glutInit(&argc, argv);
358     glutInitWindowPosition(0, 0);
359     glutInitWindowSize(__window_width, __window_height);
360     glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | (double_buffer ? GLUT_DOUBLE : GLUT_SINGLE));
361     glutCreateWindow(argv[0]);
362
363     glutDisplayFunc(&display);
364     glutIdleFunc(&idle);
365
366     for (GLuint h = 0; h < 1024; ++h) {
367         __list_map[h] = h;
368     }
369
370     for ( ; i < argc; ++i) {
371         if (parser.open(argv[i])) {
372             __startTime = OS::GetTime();
373             glutMainLoop();
374             parser.close();
375         }
376     }
377
378     return 0;
379 }
380
381 '''