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 'pname_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((
255 'glDrawRangeElements',
256 'glDrawRangeElementsEXT',
259 interleaved_formats = [
272 'GL_T2F_C4F_N3F_V3F',
273 'GL_T4F_C4F_N3F_V4F',
276 def trace_function_impl_body(self, function):
277 # Defer tracing of user array pointers...
278 if function.name in self.array_pointer_function_names:
279 print ' GLint __array_buffer = 0;'
280 print ' __glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);'
281 print ' if (!__array_buffer) {'
282 print ' __user_arrays = true;'
283 self.dispatch_function(function)
285 # And also break down glInterleavedArrays into the individual calls
286 if function.name == 'glInterleavedArrays':
289 # Initialize the enable flags
290 for camelcase_name, uppercase_name in self.arrays:
291 flag_name = '__' + uppercase_name.lower()
292 print ' GLboolean %s = GL_FALSE;' % flag_name
295 # Switch for the interleaved formats
296 print ' switch (format) {'
297 for format in self.interleaved_formats:
298 print ' case %s:' % format
299 for camelcase_name, uppercase_name in self.arrays:
300 flag_name = '__' + uppercase_name.lower()
301 if format.find('_' + uppercase_name[0]) >= 0:
302 print ' %s = GL_TRUE;' % flag_name
309 # Emit fake glEnableClientState/glDisableClientState flags
310 for camelcase_name, uppercase_name in self.arrays:
311 flag_name = '__' + uppercase_name.lower()
312 enable_name = 'GL_%s_ARRAY' % uppercase_name
314 # Emit a fake function
316 print ' static const Trace::FunctionSig &__sig = %s ? __glEnableClientState_sig : __glDisableClientState_sig;' % flag_name
317 print ' unsigned __call = Trace::BeginEnter(__sig);'
318 print ' Trace::BeginArg(0);'
319 dump_instance(glapi.GLenum, enable_name)
320 print ' Trace::EndArg();'
321 print ' Trace::EndEnter();'
322 print ' Trace::BeginLeave(__call);'
323 print ' Trace::EndLeave();'
329 # ... to the draw calls
330 if function.name in self.draw_function_names:
331 print ' if (__need_user_arrays()) {'
332 arg_names = ', '.join([arg.name for arg in function.args[1:]])
333 print ' GLuint maxindex = __%s_maxindex(%s);' % (function.name, arg_names)
334 print ' __trace_user_arrays(maxindex);'
337 # Emit a fake memcpy on
338 if function.name in ('glUnmapBuffer', 'glUnmapBufferARB'):
339 print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
340 print ' if (mapping && mapping->write) {'
341 print ' unsigned __call = Trace::BeginEnter(__memcpy_sig);'
342 print ' Trace::BeginArg(0);'
343 print ' Trace::LiteralOpaque(mapping->map);'
344 print ' Trace::EndArg();'
345 print ' Trace::BeginArg(1);'
346 print ' Trace::LiteralBlob(mapping->map, mapping->length);'
347 print ' Trace::EndArg();'
348 print ' Trace::BeginArg(2);'
349 print ' Trace::LiteralUInt(mapping->length);'
350 print ' Trace::EndArg();'
351 print ' Trace::EndEnter();'
352 print ' Trace::BeginLeave(__call);'
353 print ' Trace::EndLeave();'
356 Tracer.trace_function_impl_body(self, function)
360 'ELEMENT_ARRAY_BUFFER',
362 'PIXEL_UNPACK_BUFFER',
365 def wrap_ret(self, function, instance):
366 Tracer.wrap_ret(self, function, instance)
368 if function.name in ('glMapBuffer', 'glMapBufferARB'):
369 print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
370 print ' if (mapping) {'
371 print ' mapping->map = %s;' % (instance)
372 print ' mapping->length = 0;'
373 print ' __glGetBufferParameteriv(target, GL_BUFFER_SIZE, &mapping->length);'
374 print ' mapping->write = (access != GL_READ_ONLY);'
377 if function.name == 'glMapBufferRange':
378 print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
379 print ' if (mapping) {'
380 print ' mapping->map = %s;' % (instance)
381 print ' mapping->length = length;'
382 print ' mapping->write = access & GL_MAP_WRITE_BIT;'
390 def gl_boolean(self, value):
391 return self.boolean_names[int(bool(value))]
393 def dump_arg_instance(self, function, arg):
394 if function.name in self.draw_function_names and arg.name == 'indices':
395 print ' GLint __element_array_buffer = 0;'
396 print ' __glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);'
397 print ' if (!__element_array_buffer) {'
398 print ' Trace::LiteralBlob((const void *)%s, count*__gl_type_size(type));' % (arg.name)
400 print ' Trace::LiteralOpaque((const void *)%s);' % (arg.name)
404 # Several GL state functions take GLenum symbolic names as
405 # integer/floats; so dump the symbolic name whenever possible
406 if arg.type in (glapi.GLint, glapi.GLfloat) and arg.name == 'param':
408 assert function.args[arg.index - 1].name == 'pname'
409 assert function.args[arg.index - 1].type == glapi.GLenum
410 print ' if (is_symbolic_pname(pname) && is_symbolic_param(%s)) {' % arg.name
411 dump_instance(glapi.GLenum, arg.name)
413 Tracer.dump_arg_instance(self, function, arg)
417 Tracer.dump_arg_instance(self, function, arg)
419 def footer(self, api):
420 Tracer.footer(self, api)
422 # A simple state tracker to track the pointer values
424 print 'static void __trace_user_arrays(GLuint maxindex)'
427 for camelcase_name, uppercase_name in self.arrays:
428 function_name = 'gl%sPointer' % camelcase_name
429 enable_name = 'GL_%s_ARRAY' % uppercase_name
430 binding_name = 'GL_%s_ARRAY_BUFFER_BINDING' % uppercase_name
431 function = api.get_function_by_name(function_name)
433 print ' // %s' % function.name
434 self.array_trace_prolog(api, uppercase_name)
435 self.array_prolog(api, uppercase_name)
436 print ' if (__glIsEnabled(%s)) {' % enable_name
437 print ' GLint __binding = 0;'
438 print ' __glGetIntegerv(%s, &__binding);' % binding_name
439 print ' if (!__binding) {'
441 # Get the arguments via glGet*
442 for arg in function.args:
443 arg_get_enum = 'GL_%s_ARRAY_%s' % (uppercase_name, arg.name.upper())
444 arg_get_function, arg_type = TypeGetter().visit(arg.type)
445 print ' %s %s = 0;' % (arg_type, arg.name)
446 print ' __%s(%s, &%s);' % (arg_get_function, arg_get_enum, arg.name)
448 arg_names = ', '.join([arg.name for arg in function.args[:-1]])
449 print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names)
451 # Emit a fake function
452 self.array_trace_intermezzo(api, uppercase_name)
453 print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,)
454 for arg in function.args:
455 assert not arg.output
456 print ' Trace::BeginArg(%u);' % (arg.index,)
457 if arg.name != 'pointer':
458 dump_instance(arg.type, arg.name)
460 print ' Trace::LiteralBlob((const void *)%s, __size);' % (arg.name)
461 print ' Trace::EndArg();'
463 print ' Trace::EndEnter();'
464 print ' Trace::BeginLeave(__call);'
465 print ' Trace::EndLeave();'
468 self.array_epilog(api, uppercase_name)
469 self.array_trace_epilog(api, uppercase_name)
472 # Samething, but for glVertexAttribPointer
473 print ' // glVertexAttribPointer'
474 print ' GLint __max_vertex_attribs = 0;'
475 print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &__max_vertex_attribs);'
476 print ' for (GLint index = 0; index < __max_vertex_attribs; ++index) {'
477 print ' GLint __enabled = 0;'
478 print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &__enabled);'
479 print ' if (__enabled) {'
480 print ' GLint __binding = 0;'
481 print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &__binding);'
482 print ' if (!__binding) {'
484 function = api.get_function_by_name('glVertexAttribPointer')
486 # Get the arguments via glGet*
487 for arg in function.args[1:]:
488 arg_get_enum = 'GL_VERTEX_ATTRIB_ARRAY_%s' % (arg.name.upper(),)
489 arg_get_function, arg_type = TypeGetter('glGetVertexAttrib', False).visit(arg.type)
490 print ' %s %s = 0;' % (arg_type, arg.name)
491 print ' __%s(index, %s, &%s);' % (arg_get_function, arg_get_enum, arg.name)
493 arg_names = ', '.join([arg.name for arg in function.args[1:-1]])
494 print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names)
496 # Emit a fake function
497 print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,)
498 for arg in function.args:
499 assert not arg.output
500 print ' Trace::BeginArg(%u);' % (arg.index,)
501 if arg.name != 'pointer':
502 dump_instance(arg.type, arg.name)
504 print ' Trace::LiteralBlob((const void *)%s, __size);' % (arg.name)
505 print ' Trace::EndArg();'
507 print ' Trace::EndEnter();'
508 print ' Trace::BeginLeave(__call);'
509 print ' Trace::EndLeave();'
519 # Hooks for glTexCoordPointer, which is identical to the other array
520 # pointers except the fact that it is indexed by glClientActiveTexture.
523 def array_prolog(self, api, uppercase_name):
524 if uppercase_name == 'TEXTURE_COORD':
525 print ' GLint client_active_texture = 0;'
526 print ' __glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE, &client_active_texture);'
527 print ' GLint max_texture_coords = 0;'
528 print ' __glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
529 print ' for (GLint unit = 0; unit < max_texture_coords; ++unit) {'
530 print ' GLenum texture = GL_TEXTURE0 + unit;'
531 print ' __glClientActiveTexture(texture);'
533 def array_trace_prolog(self, api, uppercase_name):
534 if uppercase_name == 'TEXTURE_COORD':
535 print ' bool client_active_texture_dirty = false;'
537 def array_epilog(self, api, uppercase_name):
538 if uppercase_name == 'TEXTURE_COORD':
540 self.array_cleanup(api, uppercase_name)
542 def array_cleanup(self, api, uppercase_name):
543 if uppercase_name == 'TEXTURE_COORD':
544 print ' __glClientActiveTexture(client_active_texture);'
546 def array_trace_intermezzo(self, api, uppercase_name):
547 if uppercase_name == 'TEXTURE_COORD':
548 print ' if (texture != client_active_texture || client_active_texture_dirty) {'
549 print ' client_active_texture_dirty = true;'
550 self.fake_glClientActiveTexture_call(api, "texture");
553 def array_trace_epilog(self, api, uppercase_name):
554 if uppercase_name == 'TEXTURE_COORD':
555 print ' if (client_active_texture_dirty) {'
556 self.fake_glClientActiveTexture_call(api, "client_active_texture");
559 def fake_glClientActiveTexture_call(self, api, texture):
560 function = api.get_function_by_name('glClientActiveTexture')
561 self.fake_call(function, [texture])
563 def fake_call(self, function, args):
564 print ' unsigned __fake_call = Trace::BeginEnter(__%s_sig);' % (function.name,)
565 for arg, instance in zip(function.args, args):
566 assert not arg.output
567 print ' Trace::BeginArg(%u);' % (arg.index,)
568 dump_instance(arg.type, instance)
569 print ' Trace::EndArg();'
570 print ' Trace::EndEnter();'
571 print ' Trace::BeginLeave(__fake_call);'
572 print ' Trace::EndLeave();'