1 ##########################################################################
3 # Copyright 2010 VMware, Inc.
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:
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
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
24 ##########################################################################/
27 """GL retracer generator."""
30 from retrace import Retracer
31 import specs.stdapi as stdapi
32 import specs.glapi as glapi
33 import specs.glesapi as glesapi
36 class GlRetracer(Retracer):
38 table_name = 'glretrace::gl_callbacks'
40 def retraceFunction(self, function):
41 Retracer.retraceFunction(self, function)
43 array_pointer_function_names = set((
51 "glSecondaryColorPointer",
53 "glInterleavedArrays",
59 "glTexCoordPointerEXT",
60 "glEdgeFlagPointerEXT",
61 "glFogCoordPointerEXT",
62 "glSecondaryColorPointerEXT",
64 "glVertexAttribPointer",
65 "glVertexAttribPointerARB",
66 "glVertexAttribPointerNV",
67 "glVertexAttribIPointer",
68 "glVertexAttribIPointerEXT",
69 "glVertexAttribLPointer",
70 "glVertexAttribLPointerEXT",
72 #"glMatrixIndexPointerARB",
75 draw_array_function_names = set([
78 "glDrawArraysIndirect",
79 "glDrawArraysInstanced",
80 "glDrawArraysInstancedARB",
81 "glDrawArraysInstancedEXT",
82 "glDrawArraysInstancedBaseInstance",
83 "glDrawMeshArraysSUN",
85 "glMultiDrawArraysEXT",
86 "glMultiModeDrawArraysIBM",
87 'glMultiDrawArraysIndirect',
88 'glMultiDrawArraysIndirectAMD',
91 draw_elements_function_names = set([
93 "glDrawElementsBaseVertex",
94 "glDrawElementsIndirect",
95 "glDrawElementsInstanced",
96 "glDrawElementsInstancedARB",
97 "glDrawElementsInstancedEXT",
98 "glDrawElementsInstancedBaseVertex",
99 "glDrawElementsInstancedBaseInstance",
100 "glDrawElementsInstancedBaseVertexBaseInstance",
101 "glDrawRangeElements",
102 "glDrawRangeElementsEXT",
103 "glDrawRangeElementsBaseVertex",
104 "glMultiDrawElements",
105 "glMultiDrawElementsBaseVertex",
106 "glMultiDrawElementsEXT",
107 "glMultiModeDrawElementsIBM",
108 'glMultiDrawElementsIndirect',
109 'glMultiDrawElementsIndirectAMD',
112 draw_indirect_function_names = set([
113 "glDrawArraysIndirect",
114 "glDrawElementsIndirect",
115 'glMultiDrawArraysIndirect',
116 'glMultiDrawArraysIndirectAMD',
117 'glMultiDrawElementsIndirect',
118 'glMultiDrawElementsIndirectAMD',
121 misc_draw_function_names = set([
128 "glBlitFramebufferEXT",
131 bind_framebuffer_function_names = set([
133 "glBindFramebufferEXT",
134 "glBindFramebufferOES",
137 # Names of the functions that can pack into the current pixel buffer
138 # object. See also the ARB_pixel_buffer_object specification.
139 pack_function_names = set([
140 'glGetCompressedTexImage',
141 'glGetCompressedTexImageARB',
142 'glGetCompressedTextureImageEXT',
143 'glGetCompressedMultiTexImageEXT',
144 'glGetConvolutionFilter',
150 'glGetPolygonStipple',
151 'glGetSeparableFilter',
153 'glGetTextureImageEXT',
154 'glGetMultiTexImageEXT',
156 'glGetnCompressedTexImageARB',
157 'glGetnConvolutionFilterARB',
158 'glGetnHistogramARB',
160 'glGetnPixelMapfvARB',
161 'glGetnPixelMapuivARB',
162 'glGetnPixelMapusvARB',
163 'glGetnPolygonStippleARB',
164 'glGetnSeparableFilterARB',
169 map_function_names = set([
174 'glMapNamedBufferEXT',
175 'glMapNamedBufferRangeEXT',
176 'glMapObjectBufferATI',
179 unmap_function_names = set([
183 'glUnmapNamedBufferEXT',
184 'glUnmapObjectBufferATI',
187 def retraceFunctionBody(self, function):
188 is_array_pointer = function.name in self.array_pointer_function_names
189 is_draw_array = function.name in self.draw_array_function_names
190 is_draw_elements = function.name in self.draw_elements_function_names
191 is_misc_draw = function.name in self.misc_draw_function_names
193 if is_array_pointer or is_draw_array or is_draw_elements:
194 print ' if (retrace::parser.version < 1) {'
196 if is_array_pointer or is_draw_array:
197 print ' GLint _array_buffer = 0;'
198 print ' glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &_array_buffer);'
199 print ' if (!_array_buffer) {'
200 self.failFunction(function)
204 print ' GLint _element_array_buffer = 0;'
205 print ' glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &_element_array_buffer);'
206 print ' if (!_element_array_buffer) {'
207 self.failFunction(function)
212 # When no pack buffer object is bound, the pack functions are no-ops.
213 if function.name in self.pack_function_names:
214 print ' GLint _pack_buffer = 0;'
215 print ' glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &_pack_buffer);'
216 print ' if (!_pack_buffer) {'
221 if function.name in self.bind_framebuffer_function_names:
222 print ' assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);'
223 if function.name == 'glFrameTerminatorGREMEDY':
224 print ' glretrace::frame_complete(call);'
227 Retracer.retraceFunctionBody(self, function)
230 if function.name in ('glFlush', 'glFinish'):
231 print ' if (!retrace::doubleBuffer) {'
232 print ' glretrace::frame_complete(call);'
234 if is_draw_array or is_draw_elements or is_misc_draw:
235 print ' assert(call.flags & trace::CALL_FLAG_RENDER);'
238 def invokeFunction(self, function):
239 # Infer the drawable size from GL calls
240 if function.name == "glViewport":
241 print ' glretrace::updateDrawable(x + width, y + height);'
242 if function.name == "glViewportArray":
243 # We are concerned about drawables so only care for the first viewport
244 print ' if (first == 0 && count > 0) {'
245 print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
246 print ' glretrace::updateDrawable(x + w, y + h);'
248 if function.name == "glViewportIndexedf":
249 print ' if (index == 0) {'
250 print ' glretrace::updateDrawable(x + w, y + h);'
252 if function.name == "glViewportIndexedfv":
253 print ' if (index == 0) {'
254 print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
255 print ' glretrace::updateDrawable(x + w, y + h);'
257 if function.name in ('glBlitFramebuffer', 'glBlitFramebufferEXT'):
258 # Some applications do all their rendering in a framebuffer, and
259 # then just blit to the drawable without ever calling glViewport.
260 print ' glretrace::updateDrawable(std::max(dstX0, dstX1), std::max(dstY0, dstY1));'
262 if function.name == "glEnd":
263 print ' glretrace::insideGlBeginEnd = false;'
265 if function.name.startswith('gl') and not function.name.startswith('glX'):
266 print r' if (retrace::debug && !glretrace::getCurrentContext()) {'
267 print r' retrace::warning(call) << "no current context\n";'
270 if function.name == 'memcpy':
271 print ' if (!dest || !src || !n) return;'
273 # Skip glEnable/Disable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) as we don't
274 # faithfully set the CONTEXT_DEBUG_BIT_ARB flags on context creation.
275 if function.name in ('glEnable', 'glDisable'):
276 print ' if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) return;'
278 # Destroy the buffer mapping
279 if function.name in self.unmap_function_names:
280 print r' GLvoid *ptr = NULL;'
281 if function.name == 'glUnmapBuffer':
282 print r' glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &ptr);'
283 elif function.name == 'glUnmapBufferARB':
284 print r' glGetBufferPointervARB(target, GL_BUFFER_MAP_POINTER_ARB, &ptr);'
285 elif function.name == 'glUnmapBufferOES':
286 print r' glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &ptr);'
287 elif function.name == 'glUnmapNamedBufferEXT':
288 print r' glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &ptr);'
289 elif function.name == 'glUnmapObjectBufferATI':
295 print r' retrace::delRegionByPointer(ptr);'
297 print r' retrace::warning(call) << "no current context\n";'
300 if function.name in ('glBindProgramPipeline', 'glBindProgramPipelineEXT'):
301 # Note if glBindProgramPipeline has ever been called
302 print r' if (pipeline) {'
303 print r' _pipelineHasBeenBound = true;'
307 function.name in self.draw_array_function_names or
308 function.name in self.draw_elements_function_names or
309 function.name in self.draw_indirect_function_names or
310 function.name in self.misc_draw_function_names or
311 function.name == 'glBegin'
314 if function.name in ('glUseProgram', 'glUseProgramObjectARB'):
315 print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
316 print r' if (currentContext) {'
317 print r' currentContext->activeProgram = call.arg(0).toUInt();'
320 # Only profile if not inside a list as the queries get inserted into list
321 if function.name == 'glNewList':
322 print r' glretrace::insideList = true;'
324 if function.name == 'glEndList':
325 print r' glretrace::insideList = false;'
327 if function.name != 'glEnd':
328 print r' if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
330 print r' glretrace::beginProfile(call, true);'
332 print r' glretrace::beginProfile(call, false);'
335 if function.name == 'glCreateShaderProgramv':
336 # When dumping state, break down glCreateShaderProgramv so that the
337 # shader source can be recovered.
338 print r' if (retrace::dumpingState) {'
339 print r' GLuint _shader = glCreateShader(type);'
340 print r' if (_shader) {'
341 print r' glShaderSource(_shader, count, strings, NULL);'
342 print r' glCompileShader(_shader);'
343 print r' const GLuint _program = glCreateProgram();'
344 print r' if (_program) {'
345 print r' GLint compiled = GL_FALSE;'
346 print r' glGetShaderiv(_shader, GL_COMPILE_STATUS, &compiled);'
347 print r' glProgramParameteri(_program, GL_PROGRAM_SEPARABLE, GL_TRUE);'
348 print r' if (compiled) {'
349 print r' glAttachShader(_program, _shader);'
350 print r' glLinkProgram(_program);'
351 print r' //glDetachShader(_program, _shader);'
353 print r' //append-shader-info-log-to-program-info-log'
355 print r' //glDeleteShader(_shader);'
356 print r' _result = _program;'
358 print r' _result = 0;'
361 Retracer.invokeFunction(self, function)
364 Retracer.invokeFunction(self, function)
366 if function.name == "glBegin":
367 print ' glretrace::insideGlBeginEnd = true;'
369 print r' if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
371 print r' glretrace::endProfile(call, true);'
373 print r' glretrace::endProfile(call, false);'
377 if function.name.startswith('gl'):
378 # glGetError is not allowed inside glBegin/glEnd
379 print ' if (retrace::debug && !glretrace::insideGlBeginEnd && glretrace::getCurrentContext()) {'
380 print ' glretrace::checkGlError(call);'
381 if function.name in ('glProgramStringARB', 'glProgramStringNV'):
382 print r' GLint error_position = -1;'
383 print r' glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_position);'
384 print r' if (error_position != -1) {'
385 print r' const char *error_string = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);'
386 print r' retrace::warning(call) << error_string << "\n";'
388 if function.name == 'glCompileShader':
389 print r' GLint compile_status = 0;'
390 print r' glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);'
391 print r' if (!compile_status) {'
392 print r' GLint info_log_length = 0;'
393 print r' glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);'
394 print r' GLchar *infoLog = new GLchar[info_log_length];'
395 print r' glGetShaderInfoLog(shader, info_log_length, NULL, infoLog);'
396 print r' retrace::warning(call) << infoLog << "\n";'
397 print r' delete [] infoLog;'
399 if function.name in ('glLinkProgram', 'glCreateShaderProgramv', 'glCreateShaderProgramEXT'):
400 if function.name != 'glLinkProgram':
401 print r' GLuint program = _result;'
402 print r' GLint link_status = 0;'
403 print r' glGetProgramiv(program, GL_LINK_STATUS, &link_status);'
404 print r' if (!link_status) {'
405 print r' GLint info_log_length = 0;'
406 print r' glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);'
407 print r' GLchar *infoLog = new GLchar[info_log_length];'
408 print r' glGetProgramInfoLog(program, info_log_length, NULL, infoLog);'
409 print r' retrace::warning(call) << infoLog << "\n";'
410 print r' delete [] infoLog;'
412 if function.name == 'glCompileShaderARB':
413 print r' GLint compile_status = 0;'
414 print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);'
415 print r' if (!compile_status) {'
416 print r' GLint info_log_length = 0;'
417 print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
418 print r' GLchar *infoLog = new GLchar[info_log_length];'
419 print r' glGetInfoLogARB(shaderObj, info_log_length, NULL, infoLog);'
420 print r' retrace::warning(call) << infoLog << "\n";'
421 print r' delete [] infoLog;'
423 if function.name == 'glLinkProgramARB':
424 print r' GLint link_status = 0;'
425 print r' glGetObjectParameterivARB(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);'
426 print r' if (!link_status) {'
427 print r' GLint info_log_length = 0;'
428 print r' glGetObjectParameterivARB(programObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
429 print r' GLchar *infoLog = new GLchar[info_log_length];'
430 print r' glGetInfoLogARB(programObj, info_log_length, NULL, infoLog);'
431 print r' retrace::warning(call) << infoLog << "\n";'
432 print r' delete [] infoLog;'
434 if function.name in self.map_function_names:
435 print r' if (!_result) {'
436 print r' retrace::warning(call) << "failed to map buffer\n";'
438 if function.name in self.unmap_function_names and function.type is not stdapi.Void:
439 print r' if (!_result) {'
440 print r' retrace::warning(call) << "failed to unmap buffer\n";'
442 if function.name in ('glGetAttribLocation', 'glGetAttribLocationARB'):
443 print r' GLint _origResult = call.ret->toSInt();'
444 print r' if (_result != _origResult) {'
445 print r' retrace::warning(call) << "vertex attrib location mismatch " << _origResult << " -> " << _result << "\n";'
447 if function.name in ('glCheckFramebufferStatus', 'glCheckFramebufferStatusEXT', 'glCheckNamedFramebufferStatusEXT'):
448 print r' GLint _origResult = call.ret->toSInt();'
449 print r' if (_origResult == GL_FRAMEBUFFER_COMPLETE &&'
450 print r' _result != GL_FRAMEBUFFER_COMPLETE) {'
451 print r' retrace::warning(call) << "incomplete framebuffer (" << glstate::enumToString(_result) << ")\n";'
455 # Query the buffer length for whole buffer mappings
456 if function.name in self.map_function_names:
457 if 'length' in function.argNames():
458 assert 'BufferRange' in function.name
460 assert 'BufferRange' not in function.name
461 print r' GLint length = 0;'
462 if function.name in ('glMapBuffer', 'glMapBufferOES'):
463 print r' glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);'
464 elif function.name == 'glMapBufferARB':
465 print r' glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);'
466 elif function.name == 'glMapNamedBufferEXT':
467 print r' glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);'
468 elif function.name == 'glMapObjectBufferATI':
469 print r' glGetObjectBufferivATI(buffer, GL_OBJECT_BUFFER_SIZE_ATI, &length);'
473 def extractArg(self, function, arg, arg_type, lvalue, rvalue):
474 if function.name in self.array_pointer_function_names and arg.name == 'pointer':
475 print ' %s = static_cast<%s>(retrace::toPointer(%s, true));' % (lvalue, arg_type, rvalue)
478 if function.name in self.draw_elements_function_names and arg.name == 'indices' or\
479 function.name in self.draw_indirect_function_names and arg.name == 'indirect':
480 self.extractOpaqueArg(function, arg, arg_type, lvalue, rvalue)
483 # Handle pointer with offsets into the current pack pixel buffer
485 if function.name in self.pack_function_names and arg.output:
486 assert isinstance(arg_type, (stdapi.Pointer, stdapi.Array, stdapi.Blob, stdapi.Opaque))
487 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, arg_type, rvalue)
490 if arg.type is glapi.GLlocation \
491 and 'program' not in function.argNames():
492 # Determine the active program for uniforms swizzling
493 print ' GLint program = -1;'
494 print ' if (glretrace::insideList) {'
495 print ' // glUseProgram & glUseProgramObjectARB are display-list-able'
496 print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
497 print ' program = _program_map[currentContext->activeProgram];'
499 print ' GLint pipeline = 0;'
500 print ' if (_pipelineHasBeenBound) {'
501 print ' glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &pipeline);'
503 print ' if (pipeline) {'
504 print ' glGetProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, &program);'
506 print ' glGetIntegerv(GL_CURRENT_PROGRAM, &program);'
511 if arg.type is glapi.GLlocationARB \
512 and 'programObj' not in function.argNames():
513 print ' GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);'
515 Retracer.extractArg(self, function, arg, arg_type, lvalue, rvalue)
517 # Don't try to use more samples than the implementation supports
518 if arg.name == 'samples':
519 assert arg.type is glapi.GLsizei
520 print ' GLint max_samples = 0;'
521 print ' glGetIntegerv(GL_MAX_SAMPLES, &max_samples);'
522 print ' if (samples > max_samples) {'
523 print ' samples = max_samples;'
526 # These parameters are referred beyond the call life-time
527 # TODO: Replace ad-hoc solution for bindable parameters with general one
528 if function.name in ('glFeedbackBuffer', 'glSelectBuffer') and arg.output:
529 print ' _allocator.bind(%s);' % arg.name
533 if __name__ == '__main__':
537 #include "glproc.hpp"
538 #include "glretrace.hpp"
539 #include "glstate.hpp"
542 static bool _pipelineHasBeenBound = false;
545 api.addModule(glapi.glapi)
546 api.addModule(glesapi.glesapi)
547 retracer = GlRetracer()
548 retracer.retraceApi(api)