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",
89 draw_elements_function_names = set([
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",
108 draw_indirect_function_names = set([
109 "glDrawArraysIndirect",
110 "glDrawElementsIndirect",
113 misc_draw_function_names = set([
120 "glBlitFramebufferEXT",
123 bind_framebuffer_function_names = set([
125 "glBindFramebufferEXT",
126 "glBindFramebufferOES",
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 'glGetCompressedTextureImageEXT',
134 'glGetCompressedMultiTexImageEXT',
135 'glGetConvolutionFilter',
141 'glGetPolygonStipple',
142 'glGetSeparableFilter',
144 'glGetTextureImageEXT',
145 'glGetMultiTexImageEXT',
147 'glGetnCompressedTexImageARB',
148 'glGetnConvolutionFilterARB',
149 'glGetnHistogramARB',
151 'glGetnPixelMapfvARB',
152 'glGetnPixelMapuivARB',
153 'glGetnPixelMapusvARB',
154 'glGetnPolygonStippleARB',
155 'glGetnSeparableFilterARB',
160 map_function_names = set([
165 'glMapNamedBufferEXT',
166 'glMapNamedBufferRangeEXT',
167 'glMapObjectBufferATI',
170 unmap_function_names = set([
174 'glUnmapNamedBufferEXT',
175 'glUnmapObjectBufferATI',
178 def retraceFunctionBody(self, function):
179 is_array_pointer = function.name in self.array_pointer_function_names
180 is_draw_array = function.name in self.draw_array_function_names
181 is_draw_elements = function.name in self.draw_elements_function_names
182 is_misc_draw = function.name in self.misc_draw_function_names
184 if is_array_pointer or is_draw_array or is_draw_elements:
185 print ' if (retrace::parser.version < 1) {'
187 if is_array_pointer or is_draw_array:
188 print ' GLint _array_buffer = 0;'
189 print ' glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &_array_buffer);'
190 print ' if (!_array_buffer) {'
191 self.failFunction(function)
195 print ' GLint _element_array_buffer = 0;'
196 print ' glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &_element_array_buffer);'
197 print ' if (!_element_array_buffer) {'
198 self.failFunction(function)
203 # When no pack buffer object is bound, the pack functions are no-ops.
204 if function.name in self.pack_function_names:
205 print ' GLint _pack_buffer = 0;'
206 print ' glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &_pack_buffer);'
207 print ' if (!_pack_buffer) {'
212 if function.name in self.bind_framebuffer_function_names:
213 print ' assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);'
214 if function.name == 'glFrameTerminatorGREMEDY':
215 print ' glretrace::frame_complete(call);'
218 Retracer.retraceFunctionBody(self, function)
221 if function.name in ('glFlush', 'glFinish'):
222 print ' if (!retrace::doubleBuffer) {'
223 print ' glretrace::frame_complete(call);'
225 if is_draw_array or is_draw_elements or is_misc_draw:
226 print ' assert(call.flags & trace::CALL_FLAG_RENDER);'
229 def invokeFunction(self, function):
230 # Infer the drawable size from GL calls
231 if function.name == "glViewport":
232 print ' glretrace::updateDrawable(x + width, y + height);'
233 if function.name == "glViewportArray":
234 # We are concerned about drawables so only care for the first viewport
235 print ' if (first == 0 && count > 0) {'
236 print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
237 print ' glretrace::updateDrawable(x + w, y + h);'
239 if function.name == "glViewportIndexedf":
240 print ' if (index == 0) {'
241 print ' glretrace::updateDrawable(x + w, y + h);'
243 if function.name == "glViewportIndexedfv":
244 print ' if (index == 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 in ('glBlitFramebuffer', 'glBlitFramebufferEXT'):
249 # Some applications do all their rendering in a framebuffer, and
250 # then just blit to the drawable without ever calling glViewport.
251 print ' glretrace::updateDrawable(std::max(dstX0, dstX1), std::max(dstY0, dstY1));'
253 if function.name == "glEnd":
254 print ' glretrace::insideGlBeginEnd = false;'
256 if function.name.startswith('gl') and not function.name.startswith('glX'):
257 print r' if (retrace::debug && !glretrace::getCurrentContext()) {'
258 print r' retrace::warning(call) << "no current context\n";'
261 if function.name == 'memcpy':
262 print ' if (!dest || !src || !n) return;'
264 # Skip glEnable/Disable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) as we don't
265 # faithfully set the CONTEXT_DEBUG_BIT_ARB flags on context creation.
266 if function.name in ('glEnable', 'glDisable'):
267 print ' if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) return;'
269 # Destroy the buffer mapping
270 if function.name in self.unmap_function_names:
271 print r' GLvoid *ptr = NULL;'
272 if function.name == 'glUnmapBuffer':
273 print r' glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &ptr);'
274 elif function.name == 'glUnmapBufferARB':
275 print r' glGetBufferPointervARB(target, GL_BUFFER_MAP_POINTER_ARB, &ptr);'
276 elif function.name == 'glUnmapBufferOES':
277 print r' glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &ptr);'
278 elif function.name == 'glUnmapNamedBufferEXT':
279 print r' glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &ptr);'
280 elif function.name == 'glUnmapObjectBufferATI':
286 print r' retrace::delRegionByPointer(ptr);'
288 print r' retrace::warning(call) << "no current context\n";'
291 if function.name in ('glBindProgramPipeline', 'glBindProgramPipelineEXT'):
292 # Note if glBindProgramPipeline has ever been called
293 print r' if (pipeline) {'
294 print r' _pipelineHasBeenBound = true;'
298 function.name in self.draw_array_function_names or
299 function.name in self.draw_elements_function_names or
300 function.name in self.draw_indirect_function_names or
301 function.name in self.misc_draw_function_names or
302 function.name == 'glBegin'
305 if function.name in ('glUseProgram', 'glUseProgramObjectARB'):
306 print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
307 print r' if (currentContext) {'
308 print r' currentContext->activeProgram = call.arg(0).toUInt();'
311 # Only profile if not inside a list as the queries get inserted into list
312 if function.name == 'glNewList':
313 print r' glretrace::insideList = true;'
315 if function.name == 'glEndList':
316 print r' glretrace::insideList = false;'
318 if function.name != 'glEnd':
319 print r' if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
321 print r' glretrace::beginProfile(call, true);'
323 print r' glretrace::beginProfile(call, false);'
326 if function.name == 'glCreateShaderProgramv':
327 # When dumping state, break down glCreateShaderProgramv so that the
328 # shader source can be recovered.
329 print r' if (retrace::dumpingState) {'
330 print r' GLuint _shader = glCreateShader(type);'
331 print r' if (_shader) {'
332 print r' glShaderSource(_shader, count, strings, NULL);'
333 print r' glCompileShader(_shader);'
334 print r' const GLuint _program = glCreateProgram();'
335 print r' if (_program) {'
336 print r' GLint compiled = GL_FALSE;'
337 print r' glGetShaderiv(_shader, GL_COMPILE_STATUS, &compiled);'
338 print r' glProgramParameteri(_program, GL_PROGRAM_SEPARABLE, GL_TRUE);'
339 print r' if (compiled) {'
340 print r' glAttachShader(_program, _shader);'
341 print r' glLinkProgram(_program);'
342 print r' //glDetachShader(_program, _shader);'
344 print r' //append-shader-info-log-to-program-info-log'
346 print r' //glDeleteShader(_shader);'
347 print r' _result = _program;'
349 print r' _result = 0;'
352 Retracer.invokeFunction(self, function)
355 Retracer.invokeFunction(self, function)
357 if function.name == "glBegin":
358 print ' glretrace::insideGlBeginEnd = true;'
360 print r' if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
362 print r' glretrace::endProfile(call, true);'
364 print r' glretrace::endProfile(call, false);'
368 if function.name.startswith('gl'):
369 # glGetError is not allowed inside glBegin/glEnd
370 print ' if (retrace::debug && !glretrace::insideGlBeginEnd && glretrace::getCurrentContext()) {'
371 print ' glretrace::checkGlError(call);'
372 if function.name in ('glProgramStringARB', 'glProgramStringNV'):
373 print r' GLint error_position = -1;'
374 print r' glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_position);'
375 print r' if (error_position != -1) {'
376 print r' const char *error_string = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);'
377 print r' retrace::warning(call) << error_string << "\n";'
379 if function.name == 'glCompileShader':
380 print r' GLint compile_status = 0;'
381 print r' glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);'
382 print r' if (!compile_status) {'
383 print r' GLint info_log_length = 0;'
384 print r' glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);'
385 print r' GLchar *infoLog = new GLchar[info_log_length];'
386 print r' glGetShaderInfoLog(shader, info_log_length, NULL, infoLog);'
387 print r' retrace::warning(call) << infoLog << "\n";'
388 print r' delete [] infoLog;'
390 if function.name in ('glLinkProgram', 'glCreateShaderProgramv', 'glCreateShaderProgramEXT'):
391 if function.name != 'glLinkProgram':
392 print r' GLuint program = _result;'
393 print r' GLint link_status = 0;'
394 print r' glGetProgramiv(program, GL_LINK_STATUS, &link_status);'
395 print r' if (!link_status) {'
396 print r' GLint info_log_length = 0;'
397 print r' glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);'
398 print r' GLchar *infoLog = new GLchar[info_log_length];'
399 print r' glGetProgramInfoLog(program, info_log_length, NULL, infoLog);'
400 print r' retrace::warning(call) << infoLog << "\n";'
401 print r' delete [] infoLog;'
403 if function.name == 'glCompileShaderARB':
404 print r' GLint compile_status = 0;'
405 print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);'
406 print r' if (!compile_status) {'
407 print r' GLint info_log_length = 0;'
408 print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
409 print r' GLchar *infoLog = new GLchar[info_log_length];'
410 print r' glGetInfoLogARB(shaderObj, info_log_length, NULL, infoLog);'
411 print r' retrace::warning(call) << infoLog << "\n";'
412 print r' delete [] infoLog;'
414 if function.name == 'glLinkProgramARB':
415 print r' GLint link_status = 0;'
416 print r' glGetObjectParameterivARB(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);'
417 print r' if (!link_status) {'
418 print r' GLint info_log_length = 0;'
419 print r' glGetObjectParameterivARB(programObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
420 print r' GLchar *infoLog = new GLchar[info_log_length];'
421 print r' glGetInfoLogARB(programObj, info_log_length, NULL, infoLog);'
422 print r' retrace::warning(call) << infoLog << "\n";'
423 print r' delete [] infoLog;'
425 if function.name in self.map_function_names:
426 print r' if (!_result) {'
427 print r' retrace::warning(call) << "failed to map buffer\n";'
429 if function.name in self.unmap_function_names and function.type is not stdapi.Void:
430 print r' if (!_result) {'
431 print r' retrace::warning(call) << "failed to unmap buffer\n";'
433 if function.name in ('glGetAttribLocation', 'glGetAttribLocationARB'):
434 print r' GLint _origResult = call.ret->toSInt();'
435 print r' if (_result != _origResult) {'
436 print r' retrace::warning(call) << "vertex attrib location mismatch " << _origResult << " -> " << _result << "\n";'
438 if function.name in ('glCheckFramebufferStatus', 'glCheckFramebufferStatusEXT', 'glCheckNamedFramebufferStatusEXT'):
439 print r' GLint _origResult = call.ret->toSInt();'
440 print r' if (_origResult == GL_FRAMEBUFFER_COMPLETE &&'
441 print r' _result != GL_FRAMEBUFFER_COMPLETE) {'
442 print r' retrace::warning(call) << "incomplete framebuffer (" << glstate::enumToString(_result) << ")\n";'
446 # Query the buffer length for whole buffer mappings
447 if function.name in self.map_function_names:
448 if 'length' in function.argNames():
449 assert 'BufferRange' in function.name
451 assert 'BufferRange' not in function.name
452 print r' GLint length = 0;'
453 if function.name in ('glMapBuffer', 'glMapBufferOES'):
454 print r' glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);'
455 elif function.name == 'glMapBufferARB':
456 print r' glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);'
457 elif function.name == 'glMapNamedBufferEXT':
458 print r' glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);'
459 elif function.name == 'glMapObjectBufferATI':
460 print r' glGetObjectBufferivATI(buffer, GL_OBJECT_BUFFER_SIZE_ATI, &length);'
464 def extractArg(self, function, arg, arg_type, lvalue, rvalue):
465 if function.name in self.array_pointer_function_names and arg.name == 'pointer':
466 print ' %s = static_cast<%s>(retrace::toPointer(%s, true));' % (lvalue, arg_type, rvalue)
469 if function.name in self.draw_elements_function_names and arg.name == 'indices' or\
470 function.name in self.draw_indirect_function_names and arg.name == 'indirect':
471 self.extractOpaqueArg(function, arg, arg_type, lvalue, rvalue)
474 # Handle pointer with offsets into the current pack pixel buffer
476 if function.name in self.pack_function_names and arg.output:
477 assert isinstance(arg_type, (stdapi.Pointer, stdapi.Array, stdapi.Blob, stdapi.Opaque))
478 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, arg_type, rvalue)
481 if arg.type is glapi.GLlocation \
482 and 'program' not in function.argNames():
483 # Determine the active program for uniforms swizzling
484 print ' GLint program = -1;'
485 print ' if (glretrace::insideList) {'
486 print ' // glUseProgram & glUseProgramObjectARB are display-list-able'
487 print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
488 print ' program = _program_map[currentContext->activeProgram];'
490 print ' GLint pipeline = 0;'
491 print ' if (_pipelineHasBeenBound) {'
492 print ' glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &pipeline);'
494 print ' if (pipeline) {'
495 print ' glGetProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, &program);'
497 print ' glGetIntegerv(GL_CURRENT_PROGRAM, &program);'
502 if arg.type is glapi.GLlocationARB \
503 and 'programObj' not in function.argNames():
504 print ' GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);'
506 Retracer.extractArg(self, function, arg, arg_type, lvalue, rvalue)
508 # Don't try to use more samples than the implementation supports
509 if arg.name == 'samples':
510 assert arg.type is glapi.GLsizei
511 print ' GLint max_samples = 0;'
512 print ' glGetIntegerv(GL_MAX_SAMPLES, &max_samples);'
513 print ' if (samples > max_samples) {'
514 print ' samples = max_samples;'
517 # These parameters are referred beyond the call life-time
518 # TODO: Replace ad-hoc solution for bindable parameters with general one
519 if function.name in ('glFeedbackBuffer', 'glSelectBuffer') and arg.output:
520 print ' _allocator.bind(%s);' % arg.name
524 if __name__ == '__main__':
528 #include "glproc.hpp"
529 #include "glretrace.hpp"
530 #include "glstate.hpp"
533 static bool _pipelineHasBeenBound = false;
536 api.addModule(glapi.glapi)
537 api.addModule(glesapi.glesapi)
538 retracer = GlRetracer()
539 retracer.retraceApi(api)