1 ##########################################################################
3 # Copyright 2008-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 tracing generator."""
33 from glxapi import glxapi
34 from trace import Tracer, dump_instance
37 class TypeGetter(stdapi.Visitor):
38 '''Determine which glGet*v function that matches the specified type.'''
40 def __init__(self, prefix = 'glGet', long_suffix = True):
42 self.long_suffix = long_suffix
44 def visit_const(self, const):
45 return self.visit(const.type)
47 def visit_alias(self, alias):
48 if alias.expr == 'GLboolean':
50 return self.prefix + 'Booleanv', alias.expr
52 return self.prefix + 'iv', 'GLint'
53 elif alias.expr == 'GLdouble':
55 return self.prefix + 'Doublev', alias.expr
57 return self.prefix + 'dv', alias.expr
58 elif alias.expr == 'GLfloat':
60 return self.prefix + 'Floatv', alias.expr
62 return self.prefix + 'fv', alias.expr
63 elif alias.expr in ('GLint', 'GLuint', 'GLsizei'):
65 return self.prefix + 'Integerv', 'GLint'
67 return self.prefix + 'iv', 'GLint'
72 def visit_enum(self, enum):
73 return self.visit(glapi.GLint)
75 def visit_bitmask(self, bitmask):
76 return self.visit(glapi.GLint)
78 def visit_opaque(self, pointer):
79 return self.prefix + 'Pointerv', 'GLvoid *'
82 class GlTracer(Tracer):
89 ("TexCoord", "TEXTURE_COORD"),
90 ("EdgeFlag", "EDGE_FLAG"),
91 ("FogCoord", "FOG_COORD"),
92 ("SecondaryColor", "SECONDARY_COLOR"),
96 def header(self, api):
97 Tracer.header(self, api)
99 print '// Whether user arrays were used'
100 print 'static bool __user_arrays = false;'
102 # Whether we need user arrays
103 print 'static inline bool __need_user_arrays(void)'
105 print ' if (!__user_arrays) {'
106 print ' return false;'
110 for camelcase_name, uppercase_name in self.arrays:
111 function_name = 'gl%sPointer' % camelcase_name
112 enable_name = 'GL_%s_ARRAY' % uppercase_name
113 binding_name = 'GL_%s_ARRAY_BUFFER_BINDING' % uppercase_name
114 print ' // %s' % function_name
115 self.array_prolog(api, uppercase_name)
116 print ' if (__glIsEnabled(%s)) {' % enable_name
117 print ' GLint __binding = 0;'
118 print ' __glGetIntegerv(%s, &__binding);' % binding_name
119 print ' if (!__binding) {'
120 self.array_cleanup(api, uppercase_name)
121 print ' return true;'
124 self.array_epilog(api, uppercase_name)
127 print ' // glVertexAttribPointer'
128 print ' GLint __max_vertex_attribs = 0;'
129 print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &__max_vertex_attribs);'
130 print ' for (GLint index = 0; index < __max_vertex_attribs; ++index) {'
131 print ' GLint __enabled = 0;'
132 print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &__enabled);'
133 print ' if (__enabled) {'
134 print ' GLint __binding = 0;'
135 print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &__binding);'
136 print ' if (!__binding) {'
137 print ' return true;'
143 print ' return false;'
147 print 'static void __trace_user_arrays(GLuint maxindex);'
150 print 'struct buffer_mapping {'
152 print ' GLint length;'
156 for target in self.buffer_targets:
157 print 'struct buffer_mapping __%s_mapping;' % target.lower();
159 print 'static inline struct buffer_mapping *'
160 print 'get_buffer_mapping(GLenum target) {'
161 print ' switch(target) {'
162 for target in self.buffer_targets:
163 print ' case GL_%s:' % target
164 print ' return & __%s_mapping;' % target.lower()
166 print ' OS::DebugMessage("warning: unknown buffer target 0x%04X\\n", target);'
167 print ' return NULL;'
172 # Generate memcpy's signature
173 self.trace_function_decl(glapi.memcpy)
175 # Generate a helper function to determine whether a parameter name
176 # refers to a symbolic value or not
178 print 'is_symbolic_pname(GLenum pname) {'
179 print ' switch(pname) {'
180 for function, type, count, name in glparams.parameters:
181 if type is glapi.GLenum:
182 print ' case %s:' % name
183 print ' return true;'
185 print ' return false;'
190 # Generate a helper function to determine whether a parameter value is
191 # potentially symbolic or not; i.e., if the value can be represented in
193 print 'template<class T>'
194 print 'static inline bool'
195 print 'is_symbolic_param(T param) {'
196 print ' return static_cast<T>(static_cast<GLenum>(param)) == param;'
200 # Generate a helper function to know how many elements a parameter has
201 print 'static size_t'
202 print '__gl_param_size(GLenum pname) {'
203 print ' switch(pname) {'
204 for function, type, count, name in glparams.parameters:
206 print ' case %s: return %u;' % (name, count)
207 print ' case GL_COMPRESSED_TEXTURE_FORMATS: {'
208 print ' GLint num_compressed_texture_formats = 0;'
209 print ' __glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &num_compressed_texture_formats);'
210 print ' return num_compressed_texture_formats;'
213 print r' OS::DebugMessage("warning: %s: unknown GLenum 0x%04X\n", __FUNCTION__, pname);'
219 array_pointer_function_names = set((
227 "glSecondaryColorPointer",
229 "glInterleavedArrays",
231 "glVertexPointerEXT",
232 "glNormalPointerEXT",
235 "glTexCoordPointerEXT",
236 "glEdgeFlagPointerEXT",
237 "glFogCoordPointerEXT",
238 "glSecondaryColorPointerEXT",
240 "glVertexAttribPointer",
241 "glVertexAttribPointerARB",
242 "glVertexAttribPointerNV",
243 "glVertexAttribIPointer",
244 "glVertexAttribIPointerEXT",
245 "glVertexAttribLPointer",
246 "glVertexAttribLPointerEXT",
248 #"glMatrixIndexPointerARB",
251 draw_function_names = set((
254 'glDrawRangeElements',
256 'glMultiDrawElements',
257 'glDrawArraysInstanced',
258 'glDrawElementsInstanced',
259 'glDrawArraysInstancedARB',
260 'glDrawElementsInstancedARB',
261 'glDrawElementsBaseVertex',
262 'glDrawRangeElementsBaseVertex',
263 'glDrawElementsInstancedBaseVertex',
264 'glMultiDrawElementsBaseVertex',
265 'glDrawArraysIndirect',
266 'glDrawElementsIndirect',
268 'glDrawRangeElementsEXT',
269 'glDrawRangeElementsEXT_size',
270 'glMultiDrawArraysEXT',
271 'glMultiDrawElementsEXT',
272 'glMultiModeDrawArraysIBM',
273 'glMultiModeDrawElementsIBM',
274 'glDrawArraysInstancedEXT',
275 'glDrawElementsInstancedEXT',
278 interleaved_formats = [
291 'GL_T2F_C4F_N3F_V3F',
292 'GL_T4F_C4F_N3F_V4F',
295 def trace_function_impl_body(self, function):
296 # Defer tracing of user array pointers...
297 if function.name in self.array_pointer_function_names:
298 print ' GLint __array_buffer = 0;'
299 print ' __glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);'
300 print ' if (!__array_buffer) {'
301 print ' __user_arrays = true;'
302 self.dispatch_function(function)
304 # And also break down glInterleavedArrays into the individual calls
305 if function.name == 'glInterleavedArrays':
308 # Initialize the enable flags
309 for camelcase_name, uppercase_name in self.arrays:
310 flag_name = '__' + uppercase_name.lower()
311 print ' GLboolean %s = GL_FALSE;' % flag_name
314 # Switch for the interleaved formats
315 print ' switch (format) {'
316 for format in self.interleaved_formats:
317 print ' case %s:' % format
318 for camelcase_name, uppercase_name in self.arrays:
319 flag_name = '__' + uppercase_name.lower()
320 if format.find('_' + uppercase_name[0]) >= 0:
321 print ' %s = GL_TRUE;' % flag_name
328 # Emit fake glEnableClientState/glDisableClientState flags
329 for camelcase_name, uppercase_name in self.arrays:
330 flag_name = '__' + uppercase_name.lower()
331 enable_name = 'GL_%s_ARRAY' % uppercase_name
333 # Emit a fake function
335 print ' static const Trace::FunctionSig &__sig = %s ? __glEnableClientState_sig : __glDisableClientState_sig;' % flag_name
336 print ' unsigned __call = Trace::BeginEnter(__sig);'
337 print ' Trace::BeginArg(0);'
338 dump_instance(glapi.GLenum, enable_name)
339 print ' Trace::EndArg();'
340 print ' Trace::EndEnter();'
341 print ' Trace::BeginLeave(__call);'
342 print ' Trace::EndLeave();'
348 # ... to the draw calls
349 if function.name in self.draw_function_names:
350 print ' if (__need_user_arrays()) {'
351 arg_names = ', '.join([arg.name for arg in function.args[1:]])
352 print ' GLuint maxindex = __%s_maxindex(%s);' % (function.name, arg_names)
353 print ' __trace_user_arrays(maxindex);'
356 # Emit a fake memcpy on
357 if function.name in ('glUnmapBuffer', 'glUnmapBufferARB'):
358 print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
359 print ' if (mapping && mapping->write) {'
360 print ' unsigned __call = Trace::BeginEnter(__memcpy_sig);'
361 print ' Trace::BeginArg(0);'
362 print ' Trace::LiteralOpaque(mapping->map);'
363 print ' Trace::EndArg();'
364 print ' Trace::BeginArg(1);'
365 print ' Trace::LiteralBlob(mapping->map, mapping->length);'
366 print ' Trace::EndArg();'
367 print ' Trace::BeginArg(2);'
368 print ' Trace::LiteralUInt(mapping->length);'
369 print ' Trace::EndArg();'
370 print ' Trace::EndEnter();'
371 print ' Trace::BeginLeave(__call);'
372 print ' Trace::EndLeave();'
375 Tracer.trace_function_impl_body(self, function)
379 'ELEMENT_ARRAY_BUFFER',
381 'PIXEL_UNPACK_BUFFER',
384 def wrap_ret(self, function, instance):
385 Tracer.wrap_ret(self, function, instance)
387 if function.name in ('glMapBuffer', 'glMapBufferARB'):
388 print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
389 print ' if (mapping) {'
390 print ' mapping->map = %s;' % (instance)
391 print ' mapping->length = 0;'
392 print ' __glGetBufferParameteriv(target, GL_BUFFER_SIZE, &mapping->length);'
393 print ' mapping->write = (access != GL_READ_ONLY);'
396 if function.name == 'glMapBufferRange':
397 print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
398 print ' if (mapping) {'
399 print ' mapping->map = %s;' % (instance)
400 print ' mapping->length = length;'
401 print ' mapping->write = access & GL_MAP_WRITE_BIT;'
409 def gl_boolean(self, value):
410 return self.boolean_names[int(bool(value))]
412 def dump_arg_instance(self, function, arg):
413 if function.name in self.draw_function_names and arg.name == 'indices':
414 print ' GLint __element_array_buffer = 0;'
415 print ' __glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);'
416 print ' if (!__element_array_buffer) {'
417 if isinstance(arg.type, stdapi.Array):
418 Tracer.dump_arg_instance(self, function, arg)
419 print ' Trace::BeginArray(%s);' % arg.type.length
420 print ' for(GLsizei i = 0; i < %s; ++i) {' % arg.type.length
421 print ' Trace::BeginElement();'
422 print ' Trace::LiteralBlob((const void *)%s, count[i]*__gl_type_size(type));' % (arg.name)
423 print ' Trace::EndElement();'
425 print ' Trace::EndArray();'
427 print ' Trace::LiteralBlob((const void *)%s, count*__gl_type_size(type));' % (arg.name)
429 Tracer.dump_arg_instance(self, function, arg)
433 # Several GL state functions take GLenum symbolic names as
434 # integer/floats; so dump the symbolic name whenever possible
435 if arg.type in (glapi.GLint, glapi.GLfloat) and arg.name == 'param':
437 assert function.args[arg.index - 1].name == 'pname'
438 assert function.args[arg.index - 1].type == glapi.GLenum
439 print ' if (is_symbolic_pname(pname) && is_symbolic_param(%s)) {' % arg.name
440 dump_instance(glapi.GLenum, arg.name)
442 Tracer.dump_arg_instance(self, function, arg)
446 Tracer.dump_arg_instance(self, function, arg)
448 def footer(self, api):
449 Tracer.footer(self, api)
451 # A simple state tracker to track the pointer values
453 print 'static void __trace_user_arrays(GLuint maxindex)'
456 for camelcase_name, uppercase_name in self.arrays:
457 function_name = 'gl%sPointer' % camelcase_name
458 enable_name = 'GL_%s_ARRAY' % uppercase_name
459 binding_name = 'GL_%s_ARRAY_BUFFER_BINDING' % uppercase_name
460 function = api.get_function_by_name(function_name)
462 print ' // %s' % function.name
463 self.array_trace_prolog(api, uppercase_name)
464 self.array_prolog(api, uppercase_name)
465 print ' if (__glIsEnabled(%s)) {' % enable_name
466 print ' GLint __binding = 0;'
467 print ' __glGetIntegerv(%s, &__binding);' % binding_name
468 print ' if (!__binding) {'
470 # Get the arguments via glGet*
471 for arg in function.args:
472 arg_get_enum = 'GL_%s_ARRAY_%s' % (uppercase_name, arg.name.upper())
473 arg_get_function, arg_type = TypeGetter().visit(arg.type)
474 print ' %s %s = 0;' % (arg_type, arg.name)
475 print ' __%s(%s, &%s);' % (arg_get_function, arg_get_enum, arg.name)
477 arg_names = ', '.join([arg.name for arg in function.args[:-1]])
478 print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names)
480 # Emit a fake function
481 self.array_trace_intermezzo(api, uppercase_name)
482 print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,)
483 for arg in function.args:
484 assert not arg.output
485 print ' Trace::BeginArg(%u);' % (arg.index,)
486 if arg.name != 'pointer':
487 dump_instance(arg.type, arg.name)
489 print ' Trace::LiteralBlob((const void *)%s, __size);' % (arg.name)
490 print ' Trace::EndArg();'
492 print ' Trace::EndEnter();'
493 print ' Trace::BeginLeave(__call);'
494 print ' Trace::EndLeave();'
497 self.array_epilog(api, uppercase_name)
498 self.array_trace_epilog(api, uppercase_name)
501 # Samething, but for glVertexAttribPointer
502 print ' // glVertexAttribPointer'
503 print ' GLint __max_vertex_attribs = 0;'
504 print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &__max_vertex_attribs);'
505 print ' for (GLint index = 0; index < __max_vertex_attribs; ++index) {'
506 print ' GLint __enabled = 0;'
507 print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &__enabled);'
508 print ' if (__enabled) {'
509 print ' GLint __binding = 0;'
510 print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &__binding);'
511 print ' if (!__binding) {'
513 function = api.get_function_by_name('glVertexAttribPointer')
515 # Get the arguments via glGet*
516 for arg in function.args[1:]:
517 arg_get_enum = 'GL_VERTEX_ATTRIB_ARRAY_%s' % (arg.name.upper(),)
518 arg_get_function, arg_type = TypeGetter('glGetVertexAttrib', False).visit(arg.type)
519 print ' %s %s = 0;' % (arg_type, arg.name)
520 print ' __%s(index, %s, &%s);' % (arg_get_function, arg_get_enum, arg.name)
522 arg_names = ', '.join([arg.name for arg in function.args[1:-1]])
523 print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names)
525 # Emit a fake function
526 print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,)
527 for arg in function.args:
528 assert not arg.output
529 print ' Trace::BeginArg(%u);' % (arg.index,)
530 if arg.name != 'pointer':
531 dump_instance(arg.type, arg.name)
533 print ' Trace::LiteralBlob((const void *)%s, __size);' % (arg.name)
534 print ' Trace::EndArg();'
536 print ' Trace::EndEnter();'
537 print ' Trace::BeginLeave(__call);'
538 print ' Trace::EndLeave();'
548 # Hooks for glTexCoordPointer, which is identical to the other array
549 # pointers except the fact that it is indexed by glClientActiveTexture.
552 def array_prolog(self, api, uppercase_name):
553 if uppercase_name == 'TEXTURE_COORD':
554 print ' GLint client_active_texture = 0;'
555 print ' __glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE, &client_active_texture);'
556 print ' GLint max_texture_coords = 0;'
557 print ' __glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
558 print ' for (GLint unit = 0; unit < max_texture_coords; ++unit) {'
559 print ' GLenum texture = GL_TEXTURE0 + unit;'
560 print ' __glClientActiveTexture(texture);'
562 def array_trace_prolog(self, api, uppercase_name):
563 if uppercase_name == 'TEXTURE_COORD':
564 print ' bool client_active_texture_dirty = false;'
566 def array_epilog(self, api, uppercase_name):
567 if uppercase_name == 'TEXTURE_COORD':
569 self.array_cleanup(api, uppercase_name)
571 def array_cleanup(self, api, uppercase_name):
572 if uppercase_name == 'TEXTURE_COORD':
573 print ' __glClientActiveTexture(client_active_texture);'
575 def array_trace_intermezzo(self, api, uppercase_name):
576 if uppercase_name == 'TEXTURE_COORD':
577 print ' if (texture != client_active_texture || client_active_texture_dirty) {'
578 print ' client_active_texture_dirty = true;'
579 self.fake_glClientActiveTexture_call(api, "texture");
582 def array_trace_epilog(self, api, uppercase_name):
583 if uppercase_name == 'TEXTURE_COORD':
584 print ' if (client_active_texture_dirty) {'
585 self.fake_glClientActiveTexture_call(api, "client_active_texture");
588 def fake_glClientActiveTexture_call(self, api, texture):
589 function = api.get_function_by_name('glClientActiveTexture')
590 self.fake_call(function, [texture])
592 def fake_call(self, function, args):
593 print ' unsigned __fake_call = Trace::BeginEnter(__%s_sig);' % (function.name,)
594 for arg, instance in zip(function.args, args):
595 assert not arg.output
596 print ' Trace::BeginArg(%u);' % (arg.index,)
597 dump_instance(arg.type, instance)
598 print ' Trace::EndArg();'
599 print ' Trace::EndEnter();'
600 print ' Trace::BeginLeave(__fake_call);'
601 print ' Trace::EndLeave();'