]> git.cworth.org Git - apitrace/blob - retrace/glretrace.py
6fdeffe49eaa8c14ed2cedcb820344679d271216
[apitrace] / retrace / 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 from retrace import Retracer
31 import specs.stdapi as stdapi
32 import specs.glapi as glapi
33 import specs.glesapi as glesapi
34
35
36 class GlRetracer(Retracer):
37
38     table_name = 'glretrace::gl_callbacks'
39
40     def retraceFunction(self, function):
41         Retracer.retraceFunction(self, function)
42
43     array_pointer_function_names = set((
44         "glVertexPointer",
45         "glNormalPointer",
46         "glColorPointer",
47         "glIndexPointer",
48         "glTexCoordPointer",
49         "glEdgeFlagPointer",
50         "glFogCoordPointer",
51         "glSecondaryColorPointer",
52
53         "glInterleavedArrays",
54
55         "glVertexPointerEXT",
56         "glNormalPointerEXT",
57         "glColorPointerEXT",
58         "glIndexPointerEXT",
59         "glTexCoordPointerEXT",
60         "glEdgeFlagPointerEXT",
61         "glFogCoordPointerEXT",
62         "glSecondaryColorPointerEXT",
63
64         "glVertexAttribPointer",
65         "glVertexAttribPointerARB",
66         "glVertexAttribPointerNV",
67         "glVertexAttribIPointer",
68         "glVertexAttribIPointerEXT",
69         "glVertexAttribLPointer",
70         "glVertexAttribLPointerEXT",
71         
72         #"glMatrixIndexPointerARB",
73     ))
74
75     draw_array_function_names = set([
76         "glDrawArrays",
77         "glDrawArraysEXT",
78         "glDrawArraysIndirect",
79         "glDrawArraysInstanced",
80         "glDrawArraysInstancedARB",
81         "glDrawArraysInstancedEXT",
82         "glDrawArraysInstancedBaseInstance",
83         "glDrawMeshArraysSUN",
84         "glMultiDrawArrays",
85         "glMultiDrawArraysEXT",
86         "glMultiModeDrawArraysIBM",
87     ])
88
89     draw_elements_function_names = set([
90         "glDrawElements",
91         "glDrawElementsBaseVertex",
92         "glDrawElementsIndirect",
93         "glDrawElementsInstanced",
94         "glDrawElementsInstancedARB",
95         "glDrawElementsInstancedEXT",
96         "glDrawElementsInstancedBaseVertex",
97         "glDrawElementsInstancedBaseInstance",
98         "glDrawElementsInstancedBaseVertexBaseInstance",
99         "glDrawRangeElements",
100         "glDrawRangeElementsEXT",
101         "glDrawRangeElementsBaseVertex",
102         "glMultiDrawElements",
103         "glMultiDrawElementsBaseVertex",
104         "glMultiDrawElementsEXT",
105         "glMultiModeDrawElementsIBM",
106     ])
107
108     draw_indirect_function_names = set([
109         "glDrawArraysIndirect",
110         "glDrawElementsIndirect",
111     ])
112
113     misc_draw_function_names = set([
114         "glCallList",
115         "glCallLists",
116         "glClear",
117         "glEnd",
118         "glDrawPixels",
119         "glBlitFramebuffer",
120         "glBlitFramebufferEXT",
121     ])
122
123     bind_framebuffer_function_names = set([
124         "glBindFramebuffer",
125         "glBindFramebufferEXT",
126         "glBindFramebufferOES",
127     ])
128
129     # Names of the functions that can pack into the current pixel buffer
130     # object.  See also the ARB_pixel_buffer_object specification.
131     pack_function_names = set([
132         'glGetCompressedTexImage',
133         'glGetConvolutionFilter',
134         'glGetHistogram',
135         'glGetMinmax',
136         'glGetPixelMapfv',
137         'glGetPixelMapuiv',
138         'glGetPixelMapusv',
139         'glGetPolygonStipple',
140         'glGetSeparableFilter',
141         'glGetTexImage',
142         'glReadPixels',
143         'glGetnCompressedTexImageARB',
144         'glGetnConvolutionFilterARB',
145         'glGetnHistogramARB',
146         'glGetnMinmaxARB',
147         'glGetnPixelMapfvARB',
148         'glGetnPixelMapuivARB',
149         'glGetnPixelMapusvARB',
150         'glGetnPolygonStippleARB',
151         'glGetnSeparableFilterARB',
152         'glGetnTexImageARB',
153         'glReadnPixelsARB',
154     ])
155
156     map_function_names = set([
157         'glMapBuffer',
158         'glMapBufferARB',
159         'glMapBufferOES',
160         'glMapBufferRange',
161         'glMapNamedBufferEXT',
162         'glMapNamedBufferRangeEXT',
163         'glMapObjectBufferATI',
164     ])
165
166     unmap_function_names = set([
167         'glUnmapBuffer',
168         'glUnmapBufferARB',
169         'glUnmapBufferOES',
170         'glUnmapNamedBufferEXT',
171         'glUnmapObjectBufferATI',
172     ])
173
174     def retraceFunctionBody(self, function):
175         is_array_pointer = function.name in self.array_pointer_function_names
176         is_draw_array = function.name in self.draw_array_function_names
177         is_draw_elements = function.name in self.draw_elements_function_names
178         is_misc_draw = function.name in self.misc_draw_function_names
179
180         if is_array_pointer or is_draw_array or is_draw_elements:
181             print '    if (retrace::parser.version < 1) {'
182
183             if is_array_pointer or is_draw_array:
184                 print '        GLint _array_buffer = 0;'
185                 print '        glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &_array_buffer);'
186                 print '        if (!_array_buffer) {'
187                 self.failFunction(function)
188                 print '        }'
189
190             if is_draw_elements:
191                 print '        GLint _element_array_buffer = 0;'
192                 print '        glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &_element_array_buffer);'
193                 print '        if (!_element_array_buffer) {'
194                 self.failFunction(function)
195                 print '        }'
196             
197             print '    }'
198
199         # When no pack buffer object is bound, the pack functions are no-ops.
200         if function.name in self.pack_function_names:
201             print '    GLint _pack_buffer = 0;'
202             print '    glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &_pack_buffer);'
203             print '    if (!_pack_buffer) {'
204             print '        return;'
205             print '    }'
206
207         # Pre-snapshots
208         if function.name in self.bind_framebuffer_function_names:
209             print '    assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);'
210         if function.name == 'glFrameTerminatorGREMEDY':
211             print '    glretrace::frame_complete(call);'
212             return
213
214         Retracer.retraceFunctionBody(self, function)
215
216         # Post-snapshots
217         if function.name in ('glFlush', 'glFinish'):
218             print '    if (!retrace::doubleBuffer) {'
219             print '        glretrace::frame_complete(call);'
220             print '    }'
221         if is_draw_array or is_draw_elements or is_misc_draw:
222             print '    assert(call.flags & trace::CALL_FLAG_RENDER);'
223
224
225     def invokeFunction(self, function):
226         # Infer the drawable size from GL calls
227         if function.name == "glViewport":
228             print '    glretrace::updateDrawable(x + width, y + height);'
229         if function.name == "glViewportArray":
230             # We are concerned about drawables so only care for the first viewport
231             print '    if (first == 0 && count > 0) {'
232             print '        GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
233             print '        glretrace::updateDrawable(x + w, y + h);'
234             print '    }'
235         if function.name == "glViewportIndexedf":
236             print '    if (index == 0) {'
237             print '        glretrace::updateDrawable(x + w, y + h);'
238             print '    }'
239         if function.name == "glViewportIndexedfv":
240             print '    if (index == 0) {'
241             print '        GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
242             print '        glretrace::updateDrawable(x + w, y + h);'
243             print '    }'
244         if function.name in ('glBlitFramebuffer', 'glBlitFramebufferEXT'):
245             # Some applications do all their rendering in a framebuffer, and
246             # then just blit to the drawable without ever calling glViewport.
247             print '    glretrace::updateDrawable(std::max(dstX0, dstX1), std::max(dstY0, dstY1));'
248
249         if function.name == "glEnd":
250             print '    glretrace::insideGlBeginEnd = false;'
251
252         if function.name.startswith('gl') and not function.name.startswith('glX'):
253             print r'    if (retrace::debug && !glretrace::getCurrentContext()) {'
254             print r'        retrace::warning(call) << "no current context\n";'
255             print r'    }'
256
257         if function.name == 'memcpy':
258             print '    if (!dest || !src || !n) return;'
259
260         # Skip glEnable/Disable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) as we don't
261         # faithfully set the CONTEXT_DEBUG_BIT_ARB flags on context creation.
262         if function.name in ('glEnable', 'glDisable'):
263             print '    if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) return;'
264
265         # Destroy the buffer mapping
266         if function.name in self.unmap_function_names:
267             print r'        GLvoid *ptr = NULL;'
268             if function.name == 'glUnmapBuffer':
269                 print r'            glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &ptr);'
270             elif function.name == 'glUnmapBufferARB':
271                 print r'            glGetBufferPointervARB(target, GL_BUFFER_MAP_POINTER_ARB, &ptr);'
272             elif function.name == 'glUnmapBufferOES':
273                 print r'            glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &ptr);'
274             elif function.name == 'glUnmapNamedBufferEXT':
275                 print r'            glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &ptr);'
276             elif function.name == 'glUnmapObjectBufferATI':
277                 # TODO
278                 pass
279             else:
280                 assert False
281             print r'        if (ptr) {'
282             print r'            retrace::delRegionByPointer(ptr);'
283             print r'        } else {'
284             print r'            retrace::warning(call) << "no current context\n";'
285             print r'        }'
286
287         if function.name in ('glBindProgramPipeline', 'glBindProgramPipelineEXT'):
288             # Note if glBindProgramPipeline has ever been called
289             print r'    if (pipeline) {'
290             print r'        _pipelineHasBeenBound = true;'
291             print r'    }'
292
293         profileDraw = (
294             function.name in self.draw_array_function_names or
295             function.name in self.draw_elements_function_names or
296             function.name in self.draw_indirect_function_names or
297             function.name in self.misc_draw_function_names or
298             function.name == 'glBegin'
299         )
300
301         if function.name in ('glUseProgram', 'glUseProgramObjectARB'):
302             print r'    glretrace::Context *currentContext = glretrace::getCurrentContext();'
303             print r'    if (currentContext) {'
304             print r'        currentContext->activeProgram = call.arg(0).toUInt();'
305             print r'    }'
306
307         # Only profile if not inside a list as the queries get inserted into list
308         if function.name == 'glNewList':
309             print r'    glretrace::insideList = true;'
310
311         if function.name == 'glEndList':
312             print r'    glretrace::insideList = false;'
313
314         if function.name != 'glEnd':
315             print r'    if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
316             if profileDraw:
317                 print r'        glretrace::beginProfile(call, true);'
318             else:
319                 print r'        glretrace::beginProfile(call, false);'
320             print r'    }'
321
322         if function.name == 'glCreateShaderProgramv':
323             # When dumping state, break down glCreateShaderProgramv so that the
324             # shader source can be recovered.
325             print r'    if (retrace::dumpingState) {'
326             print r'        GLuint _shader = glCreateShader(type);'
327             print r'        if (_shader) {'
328             print r'            glShaderSource(_shader, count, strings, NULL);'
329             print r'            glCompileShader(_shader);'
330             print r'            const GLuint _program = glCreateProgram();'
331             print r'            if (_program) {'
332             print r'                GLint compiled = GL_FALSE;'
333             print r'                glGetShaderiv(_shader, GL_COMPILE_STATUS, &compiled);'
334             print r'                glProgramParameteri(_program, GL_PROGRAM_SEPARABLE, GL_TRUE);'
335             print r'                if (compiled) {'
336             print r'                    glAttachShader(_program, _shader);'
337             print r'                    glLinkProgram(_program);'
338             print r'                    //glDetachShader(_program, _shader);'
339             print r'                }'
340             print r'                //append-shader-info-log-to-program-info-log'
341             print r'            }'
342             print r'            //glDeleteShader(_shader);'
343             print r'            _result = _program;'
344             print r'        } else {'
345             print r'            _result = 0;'
346             print r'        }'
347             print r'    } else {'
348             Retracer.invokeFunction(self, function)
349             print r'    }'
350         else:
351             Retracer.invokeFunction(self, function)
352
353         if function.name == "glBegin":
354             print '    glretrace::insideGlBeginEnd = true;'
355
356         print r'    if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
357         if profileDraw:
358             print r'        glretrace::endProfile(call, true);'
359         else:
360             print r'        glretrace::endProfile(call, false);'
361         print r'    }'
362
363         # Error checking
364         if function.name.startswith('gl'):
365             # glGetError is not allowed inside glBegin/glEnd
366             print '    if (retrace::debug && !glretrace::insideGlBeginEnd && glretrace::getCurrentContext()) {'
367             print '        glretrace::checkGlError(call);'
368             if function.name in ('glProgramStringARB', 'glProgramStringNV'):
369                 print r'        GLint error_position = -1;'
370                 print r'        glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_position);'
371                 print r'        if (error_position != -1) {'
372                 print r'            const char *error_string = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);'
373                 print r'            retrace::warning(call) << error_string << "\n";'
374                 print r'        }'
375             if function.name == 'glCompileShader':
376                 print r'        GLint compile_status = 0;'
377                 print r'        glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);'
378                 print r'        if (!compile_status) {'
379                 print r'             GLint info_log_length = 0;'
380                 print r'             glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);'
381                 print r'             GLchar *infoLog = new GLchar[info_log_length];'
382                 print r'             glGetShaderInfoLog(shader, info_log_length, NULL, infoLog);'
383                 print r'             retrace::warning(call) << infoLog << "\n";'
384                 print r'             delete [] infoLog;'
385                 print r'        }'
386             if function.name in ('glLinkProgram', 'glCreateShaderProgramv', 'glCreateShaderProgramEXT'):
387                 if function.name != 'glLinkProgram':
388                     print r'        GLuint program = _result;'
389                 print r'        GLint link_status = 0;'
390                 print r'        glGetProgramiv(program, GL_LINK_STATUS, &link_status);'
391                 print r'        if (!link_status) {'
392                 print r'             GLint info_log_length = 0;'
393                 print r'             glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);'
394                 print r'             GLchar *infoLog = new GLchar[info_log_length];'
395                 print r'             glGetProgramInfoLog(program, info_log_length, NULL, infoLog);'
396                 print r'             retrace::warning(call) << infoLog << "\n";'
397                 print r'             delete [] infoLog;'
398                 print r'        }'
399             if function.name == 'glCompileShaderARB':
400                 print r'        GLint compile_status = 0;'
401                 print r'        glGetObjectParameterivARB(shaderObj, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);'
402                 print r'        if (!compile_status) {'
403                 print r'             GLint info_log_length = 0;'
404                 print r'             glGetObjectParameterivARB(shaderObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
405                 print r'             GLchar *infoLog = new GLchar[info_log_length];'
406                 print r'             glGetInfoLogARB(shaderObj, info_log_length, NULL, infoLog);'
407                 print r'             retrace::warning(call) << infoLog << "\n";'
408                 print r'             delete [] infoLog;'
409                 print r'        }'
410             if function.name == 'glLinkProgramARB':
411                 print r'        GLint link_status = 0;'
412                 print r'        glGetObjectParameterivARB(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);'
413                 print r'        if (!link_status) {'
414                 print r'             GLint info_log_length = 0;'
415                 print r'             glGetObjectParameterivARB(programObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
416                 print r'             GLchar *infoLog = new GLchar[info_log_length];'
417                 print r'             glGetInfoLogARB(programObj, info_log_length, NULL, infoLog);'
418                 print r'             retrace::warning(call) << infoLog << "\n";'
419                 print r'             delete [] infoLog;'
420                 print r'        }'
421             if function.name in self.map_function_names:
422                 print r'        if (!_result) {'
423                 print r'             retrace::warning(call) << "failed to map buffer\n";'
424                 print r'        }'
425             if function.name in self.unmap_function_names and function.type is not stdapi.Void:
426                 print r'        if (!_result) {'
427                 print r'             retrace::warning(call) << "failed to unmap buffer\n";'
428                 print r'        }'
429             if function.name in ('glGetAttribLocation', 'glGetAttribLocationARB'):
430                 print r'    GLint _origResult = call.ret->toSInt();'
431                 print r'    if (_result != _origResult) {'
432                 print r'        retrace::warning(call) << "vertex attrib location mismatch " << _origResult << " -> " << _result << "\n";'
433                 print r'    }'
434             if function.name in ('glCheckFramebufferStatus', 'glCheckFramebufferStatusEXT', 'glCheckNamedFramebufferStatusEXT'):
435                 print r'    GLint _origResult = call.ret->toSInt();'
436                 print r'    if (_origResult == GL_FRAMEBUFFER_COMPLETE &&'
437                 print r'        _result != GL_FRAMEBUFFER_COMPLETE) {'
438                 print r'        retrace::warning(call) << "incomplete framebuffer (" << glstate::enumToString(_result) << ")\n";'
439                 print r'    }'
440             print '    }'
441
442         # Query the buffer length for whole buffer mappings
443         if function.name in self.map_function_names:
444             if 'length' in function.argNames():
445                 assert 'BufferRange' in function.name
446             else:
447                 assert 'BufferRange' not in function.name
448                 print r'    GLint length = 0;'
449                 if function.name in ('glMapBuffer', 'glMapBufferOES'):
450                     print r'    glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);'
451                 elif function.name == 'glMapBufferARB':
452                     print r'    glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);'
453                 elif function.name == 'glMapNamedBufferEXT':
454                     print r'    glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);'
455                 elif function.name == 'glMapObjectBufferATI':
456                     print r'    glGetObjectBufferivATI(buffer, GL_OBJECT_BUFFER_SIZE_ATI, &length);'
457                 else:
458                     assert False
459
460     def extractArg(self, function, arg, arg_type, lvalue, rvalue):
461         if function.name in self.array_pointer_function_names and arg.name == 'pointer':
462             print '    %s = static_cast<%s>(retrace::toPointer(%s, true));' % (lvalue, arg_type, rvalue)
463             return
464
465         if function.name in self.draw_elements_function_names and arg.name == 'indices' or\
466            function.name in self.draw_indirect_function_names and arg.name == 'indirect':
467             self.extractOpaqueArg(function, arg, arg_type, lvalue, rvalue)
468             return
469
470         # Handle pointer with offsets into the current pack pixel buffer
471         # object.
472         if function.name in self.pack_function_names and arg.output:
473             assert isinstance(arg_type, (stdapi.Pointer, stdapi.Array, stdapi.Blob, stdapi.Opaque))
474             print '    %s = static_cast<%s>((%s).toPointer());' % (lvalue, arg_type, rvalue)
475             return
476
477         if arg.type is glapi.GLlocation \
478            and 'program' not in function.argNames():
479             # Determine the active program for uniforms swizzling
480             print '    GLint program = -1;'
481             print '    if (glretrace::insideList) {'
482             print '        // glUseProgram & glUseProgramObjectARB are display-list-able'
483             print r'    glretrace::Context *currentContext = glretrace::getCurrentContext();'
484             print '        program = _program_map[currentContext->activeProgram];'
485             print '    } else {'
486             print '        GLint pipeline = 0;'
487             print '        if (_pipelineHasBeenBound) {'
488             print '            glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &pipeline);'
489             print '        }'
490             print '        if (pipeline) {'
491             print '            glGetProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, &program);'
492             print '        } else {'
493             print '            glGetIntegerv(GL_CURRENT_PROGRAM, &program);'
494             print '        }'
495             print '    }'
496             print
497
498         if arg.type is glapi.GLlocationARB \
499            and 'programObj' not in function.argNames():
500             print '    GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);'
501
502         Retracer.extractArg(self, function, arg, arg_type, lvalue, rvalue)
503
504         # Don't try to use more samples than the implementation supports
505         if arg.name == 'samples':
506             assert arg.type is glapi.GLsizei
507             print '    GLint max_samples = 0;'
508             print '    glGetIntegerv(GL_MAX_SAMPLES, &max_samples);'
509             print '    if (samples > max_samples) {'
510             print '        samples = max_samples;'
511             print '    }'
512
513         # These parameters are referred beyond the call life-time
514         # TODO: Replace ad-hoc solution for bindable parameters with general one
515         if function.name in ('glFeedbackBuffer', 'glSelectBuffer') and arg.output:
516             print '    _allocator.bind(%s);' % arg.name
517
518
519
520 if __name__ == '__main__':
521     print r'''
522 #include <string.h>
523
524 #include "glproc.hpp"
525 #include "glretrace.hpp"
526 #include "glstate.hpp"
527
528
529 static bool _pipelineHasBeenBound = false;
530 '''
531     api = stdapi.API()
532     api.addModule(glapi.glapi)
533     api.addModule(glesapi.glesapi)
534     retracer = GlRetracer()
535     retracer.retraceApi(api)