]> git.cworth.org Git - apitrace/blob - glretrace.py
Use glproc.hpp also for both tracing and retracing.
[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 #define RETRACE
155
156 #include "glproc.hpp"
157 #include <GL/glut.h>
158
159 static bool double_buffer = false;
160 static bool insideGlBeginEnd = false;
161
162 static int __window_width = 256, __window_height = 256;
163 bool __reshape_window = false;
164
165 unsigned __frame = 0;
166 long long __startTime = 0;
167 bool __wait = false;
168
169 bool __benchmark = false;
170 const char *__compare_prefix = NULL;
171 const char *__snapshot_prefix = NULL;
172
173
174 static void
175 checkGlError(void) {
176     if (__benchmark || insideGlBeginEnd) {
177         return;
178     }
179
180     GLenum error = glGetError();
181     if (error == GL_NO_ERROR) {
182         return;
183     }
184
185     std::cerr << "warning: glGetError() = ";
186     switch (error) {
187     case GL_INVALID_ENUM:
188         std::cerr << "GL_INVALID_ENUM";
189         break;
190     case GL_INVALID_VALUE:
191         std::cerr << "GL_INVALID_VALUE";
192         break;
193     case GL_INVALID_OPERATION:
194         std::cerr << "GL_INVALID_OPERATION";
195         break;
196     case GL_STACK_OVERFLOW:
197         std::cerr << "GL_STACK_OVERFLOW";
198         break;
199     case GL_STACK_UNDERFLOW:
200         std::cerr << "GL_STACK_UNDERFLOW";
201         break;
202     case GL_OUT_OF_MEMORY:
203         std::cerr << "GL_OUT_OF_MEMORY";
204         break;
205     case GL_INVALID_FRAMEBUFFER_OPERATION:
206         std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION";
207         break;
208     case GL_TABLE_TOO_LARGE:
209         std::cerr << "GL_TABLE_TOO_LARGE";
210         break;
211     default:
212         std::cerr << error;
213         break;
214     }
215     std::cerr << "\n";
216 }
217 '''
218     api = glapi.glapi
219     retracer = GlRetracer()
220     retracer.retrace_api(glapi.glapi)
221     print r'''
222
223 static Trace::Parser parser;
224
225 static void display_noop(void) {
226 }
227
228 #include "image.hpp"
229
230 static void snapshot(Image::Image &image) {
231     GLint drawbuffer = double_buffer ? GL_BACK : GL_FRONT;
232     GLint readbuffer = double_buffer ? GL_BACK : GL_FRONT;
233     glGetIntegerv(GL_READ_BUFFER, &drawbuffer);
234     glGetIntegerv(GL_READ_BUFFER, &readbuffer);
235     glReadBuffer(drawbuffer);
236     glReadPixels(0, 0, image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE, image.pixels);
237     checkGlError();
238     glReadBuffer(readbuffer);
239 }
240
241 static void frame_complete(void) {
242     ++__frame;
243     
244     if (!__reshape_window && (__snapshot_prefix || __compare_prefix)) {
245         Image::Image *ref = NULL;
246         if (__compare_prefix) {
247             char filename[PATH_MAX];
248             snprintf(filename, sizeof filename, "%s%04u.png", __compare_prefix, __frame);
249             ref = Image::readPNG(filename);
250             if (!ref) {
251                 return;
252             }
253             if (verbosity >= 0)
254                 std::cout << "Read " << filename << "\n";
255         }
256         
257         Image::Image src(__window_width, __window_height, true);
258         snapshot(src);
259
260         if (__snapshot_prefix) {
261             char filename[PATH_MAX];
262             snprintf(filename, sizeof filename, "%s%04u.png", __snapshot_prefix, __frame);
263             if (src.writePNG(filename) && verbosity >= 0) {
264                 std::cout << "Wrote " << filename << "\n";
265             }
266         }
267
268         if (ref) {
269             std::cout << "Frame " << __frame << " average precision of " << src.compare(*ref) << " bits\n";
270             delete ref;
271         }
272     }
273
274 }
275
276 static void display(void) {
277     Trace::Call *call;
278
279     while ((call = parser.parse_call())) {
280         const std::string &name = call->name();
281
282         if ((name[0] == 'w' && name[1] == 'g' && name[2] == 'l') ||
283             (name[0] == 'g' && name[1] == 'l' && name[2] == 'X')) {
284             // XXX: We ignore the majority of the OS-specific calls for now
285             if (name == "glXSwapBuffers" ||
286                 name == "wglSwapBuffers") {
287                 frame_complete();
288                 if (double_buffer)
289                     glutSwapBuffers();
290                 else
291                     glFlush();
292                 return;
293             } else {
294                 continue;
295             }
296         }
297
298         if (name == "glFlush") {
299             if (!double_buffer) {
300                 frame_complete();
301             }
302             glFlush();
303         }
304         
305         retrace_call(*call);
306
307         delete call;
308     }
309
310     // Reached the end of trace
311     glFlush();
312
313     long long endTime = OS::GetTime();
314     float timeInterval = (endTime - __startTime) * 1.0E-6;
315
316     std::cout << 
317         "Rendered " << __frame << " frames"
318         " in " <<  timeInterval << " secs,"
319         " average of " << (__frame/timeInterval) << " fps\n";
320
321     if (__wait) {
322         glutDisplayFunc(&display_noop);
323         glutIdleFunc(NULL);
324     } else {
325         exit(0);
326     }
327 }
328
329 static void idle(void) {
330     if (__reshape_window) {
331         // XXX: doesn't quite work
332         glutReshapeWindow(__window_width, __window_height);
333         __reshape_window = false;
334     }
335     glutPostRedisplay();
336 }
337
338 static void usage(void) {
339     std::cout << 
340         "Usage: glretrace [OPTION] TRACE\n"
341         "Replay TRACE.\n"
342         "\n"
343         "  -b           benchmark (no glgeterror; no messages)\n"
344         "  -c PREFIX    compare against snapshots\n"
345         "  -db          use a double buffer visual\n"
346         "  -s PREFIX    take snapshots\n"
347         "  -v           verbose output\n";
348 }
349
350 int main(int argc, char **argv)
351 {
352
353     int i;
354     for (i = 1; i < argc; ++i) {
355         const char *arg = argv[i];
356
357         if (arg[0] != '-') {
358             break;
359         }
360
361         if (!strcmp(arg, "--")) {
362             break;
363         } else if (!strcmp(arg, "-b")) {
364             __benchmark = true;
365             --verbosity;
366         } else if (!strcmp(arg, "-c")) {
367             __compare_prefix = argv[++i];
368         } else if (!strcmp(arg, "-db")) {
369             double_buffer = true;
370         } else if (!strcmp(arg, "--help")) {
371             usage();
372             return 0;
373         } else if (!strcmp(arg, "-s")) {
374             __snapshot_prefix = argv[++i];
375         } else if (!strcmp(arg, "-v")) {
376             ++verbosity;
377         } else if (!strcmp(arg, "-w")) {
378             __wait = true;
379         } else {
380             std::cerr << "error: unknown option " << arg << "\n";
381             usage();
382             return 1;
383         }
384     }
385
386     glutInit(&argc, argv);
387     glutInitWindowPosition(0, 0);
388     glutInitWindowSize(__window_width, __window_height);
389     glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | (double_buffer ? GLUT_DOUBLE : GLUT_SINGLE));
390     glutCreateWindow(argv[0]);
391
392     glutDisplayFunc(&display);
393     glutIdleFunc(&idle);
394
395     for (GLuint h = 0; h < 1024; ++h) {
396         __list_map[h] = h;
397     }
398
399     for ( ; i < argc; ++i) {
400         if (parser.open(argv[i])) {
401             __startTime = OS::GetTime();
402             glutMainLoop();
403             parser.close();
404         }
405     }
406
407     return 0;
408 }
409
410 '''