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