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 'glGetCompressedTexImageARB',
134 'glGetCompressedTextureImageEXT',
135 'glGetCompressedMultiTexImageEXT',
136 'glGetConvolutionFilter',
142 'glGetPolygonStipple',
143 'glGetSeparableFilter',
145 'glGetTextureImageEXT',
146 'glGetMultiTexImageEXT',
148 'glGetnCompressedTexImageARB',
149 'glGetnConvolutionFilterARB',
150 'glGetnHistogramARB',
152 'glGetnPixelMapfvARB',
153 'glGetnPixelMapuivARB',
154 'glGetnPixelMapusvARB',
155 'glGetnPolygonStippleARB',
156 'glGetnSeparableFilterARB',
161 map_function_names = set([
166 'glMapNamedBufferEXT',
167 'glMapNamedBufferRangeEXT',
168 'glMapObjectBufferATI',
171 unmap_function_names = set([
175 'glUnmapNamedBufferEXT',
176 'glUnmapObjectBufferATI',
179 def retraceFunctionBody(self, function):
180 is_array_pointer = function.name in self.array_pointer_function_names
181 is_draw_array = function.name in self.draw_array_function_names
182 is_draw_elements = function.name in self.draw_elements_function_names
183 is_misc_draw = function.name in self.misc_draw_function_names
185 if is_array_pointer or is_draw_array or is_draw_elements:
186 print ' if (retrace::parser.version < 1) {'
188 if is_array_pointer or is_draw_array:
189 print ' GLint _array_buffer = 0;'
190 print ' glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &_array_buffer);'
191 print ' if (!_array_buffer) {'
192 self.failFunction(function)
196 print ' GLint _element_array_buffer = 0;'
197 print ' glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &_element_array_buffer);'
198 print ' if (!_element_array_buffer) {'
199 self.failFunction(function)
204 # When no pack buffer object is bound, the pack functions are no-ops.
205 if function.name in self.pack_function_names:
206 print ' GLint _pack_buffer = 0;'
207 print ' glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &_pack_buffer);'
208 print ' if (!_pack_buffer) {'
213 if function.name in self.bind_framebuffer_function_names:
214 print ' assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);'
215 if function.name == 'glFrameTerminatorGREMEDY':
216 print ' glretrace::frame_complete(call);'
219 Retracer.retraceFunctionBody(self, function)
222 if function.name in ('glFlush', 'glFinish'):
223 print ' if (!retrace::doubleBuffer) {'
224 print ' glretrace::frame_complete(call);'
226 if is_draw_array or is_draw_elements or is_misc_draw:
227 print ' assert(call.flags & trace::CALL_FLAG_RENDER);'
230 def invokeFunction(self, function):
231 # Infer the drawable size from GL calls
232 if function.name == "glViewport":
233 print ' glretrace::updateDrawable(x + width, y + height);'
234 if function.name == "glViewportArray":
235 # We are concerned about drawables so only care for the first viewport
236 print ' if (first == 0 && count > 0) {'
237 print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
238 print ' glretrace::updateDrawable(x + w, y + h);'
240 if function.name == "glViewportIndexedf":
241 print ' if (index == 0) {'
242 print ' glretrace::updateDrawable(x + w, y + h);'
244 if function.name == "glViewportIndexedfv":
245 print ' if (index == 0) {'
246 print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
247 print ' glretrace::updateDrawable(x + w, y + h);'
249 if function.name in ('glBlitFramebuffer', 'glBlitFramebufferEXT'):
250 # Some applications do all their rendering in a framebuffer, and
251 # then just blit to the drawable without ever calling glViewport.
252 print ' glretrace::updateDrawable(std::max(dstX0, dstX1), std::max(dstY0, dstY1));'
254 if function.name == "glEnd":
255 print ' glretrace::insideGlBeginEnd = false;'
257 if function.name.startswith('gl') and not function.name.startswith('glX'):
258 print r' if (retrace::debug && !glretrace::getCurrentContext()) {'
259 print r' retrace::warning(call) << "no current context\n";'
262 if function.name == 'memcpy':
263 print ' if (!dest || !src || !n) return;'
265 # Skip glEnable/Disable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) as we don't
266 # faithfully set the CONTEXT_DEBUG_BIT_ARB flags on context creation.
267 if function.name in ('glEnable', 'glDisable'):
268 print ' if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) return;'
270 # Destroy the buffer mapping
271 if function.name in self.unmap_function_names:
272 print r' GLvoid *ptr = NULL;'
273 if function.name == 'glUnmapBuffer':
274 print r' glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &ptr);'
275 elif function.name == 'glUnmapBufferARB':
276 print r' glGetBufferPointervARB(target, GL_BUFFER_MAP_POINTER_ARB, &ptr);'
277 elif function.name == 'glUnmapBufferOES':
278 print r' glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &ptr);'
279 elif function.name == 'glUnmapNamedBufferEXT':
280 print r' glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &ptr);'
281 elif function.name == 'glUnmapObjectBufferATI':
287 print r' retrace::delRegionByPointer(ptr);'
289 print r' retrace::warning(call) << "no current context\n";'
292 if function.name in ('glBindProgramPipeline', 'glBindProgramPipelineEXT'):
293 # Note if glBindProgramPipeline has ever been called
294 print r' if (pipeline) {'
295 print r' _pipelineHasBeenBound = true;'
299 function.name in self.draw_array_function_names or
300 function.name in self.draw_elements_function_names or
301 function.name in self.draw_indirect_function_names or
302 function.name in self.misc_draw_function_names or
303 function.name == 'glBegin'
306 if function.name in ('glUseProgram', 'glUseProgramObjectARB'):
307 print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
308 print r' if (currentContext) {'
309 print r' currentContext->activeProgram = call.arg(0).toUInt();'
312 # Only profile if not inside a list as the queries get inserted into list
313 if function.name == 'glNewList':
314 print r' glretrace::insideList = true;'
316 if function.name == 'glEndList':
317 print r' glretrace::insideList = false;'
319 if function.name != 'glEnd':
320 print r' if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
322 print r' glretrace::beginProfile(call, true);'
324 print r' glretrace::beginProfile(call, false);'
327 if function.name == 'glCreateShaderProgramv':
328 # When dumping state, break down glCreateShaderProgramv so that the
329 # shader source can be recovered.
330 print r' if (retrace::dumpingState) {'
331 print r' GLuint _shader = glCreateShader(type);'
332 print r' if (_shader) {'
333 print r' glShaderSource(_shader, count, strings, NULL);'
334 print r' glCompileShader(_shader);'
335 print r' const GLuint _program = glCreateProgram();'
336 print r' if (_program) {'
337 print r' GLint compiled = GL_FALSE;'
338 print r' glGetShaderiv(_shader, GL_COMPILE_STATUS, &compiled);'
339 print r' glProgramParameteri(_program, GL_PROGRAM_SEPARABLE, GL_TRUE);'
340 print r' if (compiled) {'
341 print r' glAttachShader(_program, _shader);'
342 print r' glLinkProgram(_program);'
343 print r' //glDetachShader(_program, _shader);'
345 print r' //append-shader-info-log-to-program-info-log'
347 print r' //glDeleteShader(_shader);'
348 print r' _result = _program;'
350 print r' _result = 0;'
353 Retracer.invokeFunction(self, function)
356 Retracer.invokeFunction(self, function)
358 if function.name == "glBegin":
359 print ' glretrace::insideGlBeginEnd = true;'
361 print r' if (!glretrace::insideList && !glretrace::insideGlBeginEnd && retrace::profiling) {'
363 print r' glretrace::endProfile(call, true);'
365 print r' glretrace::endProfile(call, false);'
369 if function.name.startswith('gl'):
370 # glGetError is not allowed inside glBegin/glEnd
371 print ' if (retrace::debug && !glretrace::insideGlBeginEnd && glretrace::getCurrentContext()) {'
372 print ' glretrace::checkGlError(call);'
373 if function.name in ('glProgramStringARB', 'glProgramStringNV'):
374 print r' GLint error_position = -1;'
375 print r' glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_position);'
376 print r' if (error_position != -1) {'
377 print r' const char *error_string = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);'
378 print r' retrace::warning(call) << error_string << "\n";'
380 if function.name == 'glCompileShader':
381 print r' GLint compile_status = 0;'
382 print r' glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);'
383 print r' if (!compile_status) {'
384 print r' GLint info_log_length = 0;'
385 print r' glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);'
386 print r' GLchar *infoLog = new GLchar[info_log_length];'
387 print r' glGetShaderInfoLog(shader, info_log_length, NULL, infoLog);'
388 print r' retrace::warning(call) << infoLog << "\n";'
389 print r' delete [] infoLog;'
391 if function.name in ('glLinkProgram', 'glCreateShaderProgramv', 'glCreateShaderProgramEXT'):
392 if function.name != 'glLinkProgram':
393 print r' GLuint program = _result;'
394 print r' GLint link_status = 0;'
395 print r' glGetProgramiv(program, GL_LINK_STATUS, &link_status);'
396 print r' if (!link_status) {'
397 print r' GLint info_log_length = 0;'
398 print r' glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);'
399 print r' GLchar *infoLog = new GLchar[info_log_length];'
400 print r' glGetProgramInfoLog(program, info_log_length, NULL, infoLog);'
401 print r' retrace::warning(call) << infoLog << "\n";'
402 print r' delete [] infoLog;'
404 if function.name == 'glCompileShaderARB':
405 print r' GLint compile_status = 0;'
406 print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);'
407 print r' if (!compile_status) {'
408 print r' GLint info_log_length = 0;'
409 print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
410 print r' GLchar *infoLog = new GLchar[info_log_length];'
411 print r' glGetInfoLogARB(shaderObj, info_log_length, NULL, infoLog);'
412 print r' retrace::warning(call) << infoLog << "\n";'
413 print r' delete [] infoLog;'
415 if function.name == 'glLinkProgramARB':
416 print r' GLint link_status = 0;'
417 print r' glGetObjectParameterivARB(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);'
418 print r' if (!link_status) {'
419 print r' GLint info_log_length = 0;'
420 print r' glGetObjectParameterivARB(programObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);'
421 print r' GLchar *infoLog = new GLchar[info_log_length];'
422 print r' glGetInfoLogARB(programObj, info_log_length, NULL, infoLog);'
423 print r' retrace::warning(call) << infoLog << "\n";'
424 print r' delete [] infoLog;'
426 if function.name in self.map_function_names:
427 print r' if (!_result) {'
428 print r' retrace::warning(call) << "failed to map buffer\n";'
430 if function.name in self.unmap_function_names and function.type is not stdapi.Void:
431 print r' if (!_result) {'
432 print r' retrace::warning(call) << "failed to unmap buffer\n";'
434 if function.name in ('glGetAttribLocation', 'glGetAttribLocationARB'):
435 print r' GLint _origResult = call.ret->toSInt();'
436 print r' if (_result != _origResult) {'
437 print r' retrace::warning(call) << "vertex attrib location mismatch " << _origResult << " -> " << _result << "\n";'
439 if function.name in ('glCheckFramebufferStatus', 'glCheckFramebufferStatusEXT', 'glCheckNamedFramebufferStatusEXT'):
440 print r' GLint _origResult = call.ret->toSInt();'
441 print r' if (_origResult == GL_FRAMEBUFFER_COMPLETE &&'
442 print r' _result != GL_FRAMEBUFFER_COMPLETE) {'
443 print r' retrace::warning(call) << "incomplete framebuffer (" << glstate::enumToString(_result) << ")\n";'
447 # Query the buffer length for whole buffer mappings
448 if function.name in self.map_function_names:
449 if 'length' in function.argNames():
450 assert 'BufferRange' in function.name
452 assert 'BufferRange' not in function.name
453 print r' GLint length = 0;'
454 if function.name in ('glMapBuffer', 'glMapBufferOES'):
455 print r' glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);'
456 elif function.name == 'glMapBufferARB':
457 print r' glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);'
458 elif function.name == 'glMapNamedBufferEXT':
459 print r' glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);'
460 elif function.name == 'glMapObjectBufferATI':
461 print r' glGetObjectBufferivATI(buffer, GL_OBJECT_BUFFER_SIZE_ATI, &length);'
465 def extractArg(self, function, arg, arg_type, lvalue, rvalue):
466 if function.name in self.array_pointer_function_names and arg.name == 'pointer':
467 print ' %s = static_cast<%s>(retrace::toPointer(%s, true));' % (lvalue, arg_type, rvalue)
470 if function.name in self.draw_elements_function_names and arg.name == 'indices' or\
471 function.name in self.draw_indirect_function_names and arg.name == 'indirect':
472 self.extractOpaqueArg(function, arg, arg_type, lvalue, rvalue)
475 # Handle pointer with offsets into the current pack pixel buffer
477 if function.name in self.pack_function_names and arg.output:
478 assert isinstance(arg_type, (stdapi.Pointer, stdapi.Array, stdapi.Blob, stdapi.Opaque))
479 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, arg_type, rvalue)
482 if arg.type is glapi.GLlocation \
483 and 'program' not in function.argNames():
484 # Determine the active program for uniforms swizzling
485 print ' GLint program = -1;'
486 print ' if (glretrace::insideList) {'
487 print ' // glUseProgram & glUseProgramObjectARB are display-list-able'
488 print r' glretrace::Context *currentContext = glretrace::getCurrentContext();'
489 print ' program = _program_map[currentContext->activeProgram];'
491 print ' GLint pipeline = 0;'
492 print ' if (_pipelineHasBeenBound) {'
493 print ' glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &pipeline);'
495 print ' if (pipeline) {'
496 print ' glGetProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, &program);'
498 print ' glGetIntegerv(GL_CURRENT_PROGRAM, &program);'
503 if arg.type is glapi.GLlocationARB \
504 and 'programObj' not in function.argNames():
505 print ' GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);'
507 Retracer.extractArg(self, function, arg, arg_type, lvalue, rvalue)
509 # Don't try to use more samples than the implementation supports
510 if arg.name == 'samples':
511 assert arg.type is glapi.GLsizei
512 print ' GLint max_samples = 0;'
513 print ' glGetIntegerv(GL_MAX_SAMPLES, &max_samples);'
514 print ' if (samples > max_samples) {'
515 print ' samples = max_samples;'
518 # These parameters are referred beyond the call life-time
519 # TODO: Replace ad-hoc solution for bindable parameters with general one
520 if function.name in ('glFeedbackBuffer', 'glSelectBuffer') and arg.output:
521 print ' _allocator.bind(%s);' % arg.name
525 if __name__ == '__main__':
529 #include "glproc.hpp"
530 #include "glretrace.hpp"
531 #include "glstate.hpp"
534 static bool _pipelineHasBeenBound = false;
537 api.addModule(glapi.glapi)
538 api.addModule(glesapi.glesapi)
539 retracer = GlRetracer()
540 retracer.retraceApi(api)