+ function_name = 'gl%sPointer' % camelcase_name
+ enable_name = 'GL_%s_ARRAY' % uppercase_name
+ binding_name = 'GL_%s_ARRAY_BUFFER_BINDING' % uppercase_name
+ print ' // %s' % function_name
+ print ' if (%s) {' % profile_check
+ self.array_prolog(api, uppercase_name)
+ print ' if (__glIsEnabled(%s)) {' % enable_name
+ print ' GLint __binding = 0;'
+ print ' __glGetIntegerv(%s, &__binding);' % binding_name
+ print ' if (!__binding) {'
+ self.array_cleanup(api, uppercase_name)
+ print ' return true;'
+ print ' }'
+ print ' }'
+ self.array_epilog(api, uppercase_name)
+ print ' }'
+ print
+
+ print ' // ES1 does not support generic vertex attributes'
+ print ' if (ctx->profile == gltrace::PROFILE_ES1)'
+ print ' return false;'
+ print
+ print ' vertex_attrib __vertex_attrib = __get_vertex_attrib();'
+ print
+ print ' // glVertexAttribPointer'
+ print ' if (__vertex_attrib == VERTEX_ATTRIB) {'
+ print ' GLint __max_vertex_attribs = 0;'
+ print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &__max_vertex_attribs);'
+ print ' for (GLint index = 0; index < __max_vertex_attribs; ++index) {'
+ print ' GLint __enabled = 0;'
+ print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &__enabled);'
+ print ' if (__enabled) {'
+ print ' GLint __binding = 0;'
+ print ' __glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &__binding);'
+ print ' if (!__binding) {'
+ print ' return true;'
+ print ' }'
+ print ' }'
+ print ' }'
+ print ' }'
+ print
+ print ' // glVertexAttribPointerARB'
+ print ' if (__vertex_attrib == VERTEX_ATTRIB_ARB) {'
+ print ' GLint __max_vertex_attribs = 0;'
+ print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &__max_vertex_attribs);'
+ print ' for (GLint index = 0; index < __max_vertex_attribs; ++index) {'
+ print ' GLint __enabled = 0;'
+ print ' __glGetVertexAttribivARB(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB, &__enabled);'
+ print ' if (__enabled) {'
+ print ' GLint __binding = 0;'
+ print ' __glGetVertexAttribivARB(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB, &__binding);'
+ print ' if (!__binding) {'
+ print ' return true;'
+ print ' }'
+ print ' }'
+ print ' }'
+ print ' }'
+ print
+ print ' // glVertexAttribPointerNV'
+ print ' if (__vertex_attrib == VERTEX_ATTRIB_NV) {'
+ print ' for (GLint index = 0; index < 16; ++index) {'
+ print ' GLint __enabled = 0;'
+ print ' __glGetIntegerv(GL_VERTEX_ATTRIB_ARRAY0_NV + index, &__enabled);'
+ print ' if (__enabled) {'
+ print ' return true;'
+ print ' }'
+ print ' }'
+ print ' }'
+ print
+
+ print ' return false;'
+ print '}'
+ print
+
+ print 'static void __trace_user_arrays(GLuint maxindex);'
+ print
+
+ # Buffer mappings
+ print '// whether glMapBufferRange(GL_MAP_WRITE_BIT) has ever been called'
+ print 'static bool __checkBufferMapRange = false;'
+ print
+ print '// whether glBufferParameteriAPPLE(GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE) has ever been called'
+ print 'static bool __checkBufferFlushingUnmapAPPLE = false;'
+ print
+ # Buffer mapping information, necessary for old Mesa 2.1 drivers which
+ # do not support glGetBufferParameteriv(GL_BUFFER_ACCESS_FLAGS/GL_BUFFER_MAP_LENGTH)
+ print 'struct buffer_mapping {'
+ print ' void *map;'
+ print ' GLint length;'
+ print ' bool write;'
+ print ' bool explicit_flush;'
+ print '};'
+ print
+ for target in self.buffer_targets:
+ print 'struct buffer_mapping __%s_mapping;' % target.lower();
+ print
+ print 'static inline struct buffer_mapping *'
+ print 'get_buffer_mapping(GLenum target) {'
+ print ' switch (target) {'
+ for target in self.buffer_targets:
+ print ' case GL_%s:' % target
+ print ' return & __%s_mapping;' % target.lower()
+ print ' default:'
+ print ' os::log("apitrace: warning: unknown buffer target 0x%04X\\n", target);'
+ print ' return NULL;'
+ print ' }'
+ print '}'
+ print
+
+ # Generate a helper function to determine whether a parameter name
+ # refers to a symbolic value or not
+ print 'static bool'
+ print 'is_symbolic_pname(GLenum pname) {'
+ print ' switch (pname) {'
+ for function, type, count, name in glparams.parameters:
+ if type is glapi.GLenum:
+ print ' case %s:' % name
+ print ' return true;'
+ print ' default:'
+ print ' return false;'
+ print ' }'
+ print '}'
+ print
+
+ # Generate a helper function to determine whether a parameter value is
+ # potentially symbolic or not; i.e., if the value can be represented in
+ # an enum or not
+ print 'template<class T>'
+ print 'static inline bool'
+ print 'is_symbolic_param(T param) {'
+ print ' return static_cast<T>(static_cast<GLenum>(param)) == param;'
+ print '}'
+ print
+
+ # Generate a helper function to know how many elements a parameter has
+ print 'static size_t'
+ print '__gl_param_size(GLenum pname) {'
+ print ' switch (pname) {'
+ for function, type, count, name in glparams.parameters:
+ if type is not None:
+ print ' case %s: return %u;' % (name, count)
+ print ' case GL_COMPRESSED_TEXTURE_FORMATS: {'
+ print ' GLint num_compressed_texture_formats = 0;'
+ print ' __glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &num_compressed_texture_formats);'
+ print ' return num_compressed_texture_formats;'
+ print ' }'
+ print ' default:'
+ print r' os::log("apitrace: warning: %s: unknown GLenum 0x%04X\n", __FUNCTION__, pname);'
+ print ' return 1;'
+ print ' }'
+ print '}'
+ print
+
+ # states such as GL_UNPACK_ROW_LENGTH are not available in GLES
+ print 'static inline bool'
+ print 'can_unpack_subimage(void) {'
+ print ' gltrace::Context *ctx = gltrace::getContext();'
+ print ' return (ctx->profile == gltrace::PROFILE_COMPAT);'
+ print '}'
+ print
+
+ array_pointer_function_names = set((
+ "glVertexPointer",
+ "glNormalPointer",
+ "glColorPointer",
+ "glIndexPointer",
+ "glTexCoordPointer",
+ "glEdgeFlagPointer",
+ "glFogCoordPointer",
+ "glSecondaryColorPointer",
+
+ "glInterleavedArrays",
+
+ "glVertexPointerEXT",
+ "glNormalPointerEXT",
+ "glColorPointerEXT",
+ "glIndexPointerEXT",
+ "glTexCoordPointerEXT",
+ "glEdgeFlagPointerEXT",
+ "glFogCoordPointerEXT",
+ "glSecondaryColorPointerEXT",
+
+ "glVertexAttribPointer",
+ "glVertexAttribPointerARB",
+ "glVertexAttribPointerNV",
+ "glVertexAttribIPointer",
+ "glVertexAttribIPointerEXT",
+ "glVertexAttribLPointer",
+ "glVertexAttribLPointerEXT",
+
+ #"glMatrixIndexPointerARB",
+ ))
+
+ draw_function_names = set((
+ 'glDrawArrays',
+ 'glDrawElements',
+ 'glDrawRangeElements',
+ 'glMultiDrawArrays',
+ 'glMultiDrawElements',
+ 'glDrawArraysInstanced',
+ "glDrawArraysInstancedBaseInstance",
+ 'glDrawElementsInstanced',
+ 'glDrawArraysInstancedARB',
+ 'glDrawElementsInstancedARB',
+ 'glDrawElementsBaseVertex',
+ 'glDrawRangeElementsBaseVertex',
+ 'glDrawElementsInstancedBaseVertex',
+ "glDrawElementsInstancedBaseInstance",
+ "glDrawElementsInstancedBaseVertexBaseInstance",
+ 'glMultiDrawElementsBaseVertex',
+ 'glDrawArraysIndirect',
+ 'glDrawElementsIndirect',
+ 'glDrawArraysEXT',
+ 'glDrawRangeElementsEXT',
+ 'glDrawRangeElementsEXT_size',
+ 'glMultiDrawArraysEXT',
+ 'glMultiDrawElementsEXT',
+ 'glMultiModeDrawArraysIBM',
+ 'glMultiModeDrawElementsIBM',
+ 'glDrawArraysInstancedEXT',
+ 'glDrawElementsInstancedEXT',
+ ))
+
+ interleaved_formats = [
+ 'GL_V2F',
+ 'GL_V3F',
+ 'GL_C4UB_V2F',
+ 'GL_C4UB_V3F',
+ 'GL_C3F_V3F',
+ 'GL_N3F_V3F',
+ 'GL_C4F_N3F_V3F',
+ 'GL_T2F_V3F',
+ 'GL_T4F_V4F',
+ 'GL_T2F_C4UB_V3F',
+ 'GL_T2F_C3F_V3F',
+ 'GL_T2F_N3F_V3F',
+ 'GL_T2F_C4F_N3F_V3F',
+ 'GL_T4F_C4F_N3F_V4F',
+ ]
+
+ def traceFunctionImplBody(self, function):
+ # Defer tracing of user array pointers...
+ if function.name in self.array_pointer_function_names:
+ print ' GLint __array_buffer = 0;'
+ print ' __glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);'
+ print ' if (!__array_buffer) {'
+ print ' gltrace::Context *ctx = gltrace::getContext();'
+ print ' ctx->user_arrays = true;'
+ if function.name == "glVertexAttribPointerARB":
+ print ' ctx->user_arrays_arb = true;'
+ if function.name == "glVertexAttribPointerNV":
+ print ' ctx->user_arrays_nv = true;'
+ self.invokeFunction(function)
+
+ # And also break down glInterleavedArrays into the individual calls
+ if function.name == 'glInterleavedArrays':
+ print
+
+ # Initialize the enable flags
+ for camelcase_name, uppercase_name in self.arrays:
+ flag_name = '__' + uppercase_name.lower()
+ print ' GLboolean %s = GL_FALSE;' % flag_name
+ print
+
+ # Switch for the interleaved formats
+ print ' switch (format) {'
+ for format in self.interleaved_formats:
+ print ' case %s:' % format
+ for camelcase_name, uppercase_name in self.arrays:
+ flag_name = '__' + uppercase_name.lower()
+ if format.find('_' + uppercase_name[0]) >= 0:
+ print ' %s = GL_TRUE;' % flag_name
+ print ' break;'
+ print ' default:'
+ print ' return;'
+ print ' }'
+ print
+
+ # Emit fake glEnableClientState/glDisableClientState flags
+ for camelcase_name, uppercase_name in self.arrays:
+ flag_name = '__' + uppercase_name.lower()
+ enable_name = 'GL_%s_ARRAY' % uppercase_name
+
+ # Emit a fake function
+ print ' {'
+ print ' static const trace::FunctionSig &__sig = %s ? __glEnableClientState_sig : __glDisableClientState_sig;' % flag_name
+ print ' unsigned __call = trace::localWriter.beginEnter(&__sig);'
+ print ' trace::localWriter.beginArg(0);'
+ self.serializeValue(glapi.GLenum, enable_name)
+ print ' trace::localWriter.endArg();'
+ print ' trace::localWriter.endEnter();'
+ print ' trace::localWriter.beginLeave(__call);'
+ print ' trace::localWriter.endLeave();'
+ print ' }'
+
+ print ' return;'
+ print ' }'
+
+ # ... to the draw calls
+ if function.name in self.draw_function_names:
+ print ' if (__need_user_arrays()) {'
+ arg_names = ', '.join([arg.name for arg in function.args[1:]])
+ print ' GLuint maxindex = __%s_maxindex(%s);' % (function.name, arg_names)
+ print ' __trace_user_arrays(maxindex);'
+ print ' }'
+
+ # Emit a fake memcpy on buffer uploads
+ if function.name == 'glBufferParameteriAPPLE':
+ print ' if (pname == GL_BUFFER_FLUSHING_UNMAP_APPLE && param == GL_FALSE) {'
+ print ' __checkBufferFlushingUnmapAPPLE = true;'
+ print ' }'
+ if function.name in ('glUnmapBuffer', 'glUnmapBufferARB'):
+ if function.name.endswith('ARB'):
+ suffix = 'ARB'
+ else:
+ suffix = ''
+ print ' GLint access = 0;'
+ print ' __glGetBufferParameteriv%s(target, GL_BUFFER_ACCESS, &access);' % suffix
+ print ' if (access != GL_READ_ONLY) {'
+ print ' GLvoid *map = NULL;'
+ print ' __glGetBufferPointerv%s(target, GL_BUFFER_MAP_POINTER, &map);' % suffix
+ print ' if (map) {'
+ print ' GLint length = -1;'
+ print ' bool flush = true;'
+ print ' if (__checkBufferMapRange) {'
+ print ' __glGetBufferParameteriv%s(target, GL_BUFFER_MAP_LENGTH, &length);' % suffix
+ print ' GLint access_flags = 0;'
+ print ' __glGetBufferParameteriv(target, GL_BUFFER_ACCESS_FLAGS, &access_flags);'
+ print ' flush = flush && !(access_flags & GL_MAP_FLUSH_EXPLICIT_BIT);'
+ print ' if (length == -1) {'
+ print ' // Mesa drivers refuse GL_BUFFER_MAP_LENGTH without GL 3.0'
+ print ' static bool warned = false;'
+ print ' if (!warned) {'
+ print ' os::log("apitrace: warning: glGetBufferParameteriv%s(GL_BUFFER_MAP_LENGTH) failed\\n");' % suffix
+ print ' warned = true;'
+ print ' }'
+ print ' struct buffer_mapping *mapping = get_buffer_mapping(target);'
+ print ' if (mapping) {'
+ print ' length = mapping->length;'
+ print ' flush = flush && !mapping->explicit_flush;'
+ print ' } else {'
+ print ' length = 0;'
+ print ' flush = false;'
+ print ' }'
+ print ' }'
+ print ' } else {'
+ print ' length = 0;'
+ print ' __glGetBufferParameteriv%s(target, GL_BUFFER_SIZE, &length);' % suffix
+ print ' }'
+ print ' if (__checkBufferFlushingUnmapAPPLE) {'
+ print ' GLint flushing_unmap = GL_TRUE;'
+ print ' __glGetBufferParameteriv%s(target, GL_BUFFER_FLUSHING_UNMAP_APPLE, &flushing_unmap);' % suffix
+ print ' flush = flush && flushing_unmap;'
+ print ' }'
+ print ' if (flush && length > 0) {'
+ self.emit_memcpy('map', 'map', 'length')
+ print ' }'
+ print ' }'
+ print ' }'
+ if function.name == 'glUnmapBufferOES':
+ print ' GLint access = 0;'
+ print ' __glGetBufferParameteriv(target, GL_BUFFER_ACCESS_OES, &access);'
+ print ' if (access == GL_WRITE_ONLY_OES) {'
+ print ' GLvoid *map = NULL;'
+ print ' __glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &map);'
+ print ' GLint size = 0;'
+ print ' __glGetBufferParameteriv(target, GL_BUFFER_SIZE, &size);'
+ print ' if (map && size > 0) {'
+ self.emit_memcpy('map', 'map', 'size')
+ print ' }'
+ print ' }'
+ if function.name == 'glUnmapNamedBufferEXT':
+ print ' GLint access_flags = 0;'
+ print ' __glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_ACCESS_FLAGS, &access_flags);'
+ print ' if ((access_flags & GL_MAP_WRITE_BIT) && !(access_flags & GL_MAP_FLUSH_EXPLICIT_BIT)) {'
+ print ' GLvoid *map = NULL;'
+ print ' __glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &map);'
+ print ' GLint length = 0;'
+ print ' __glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_MAP_LENGTH, &length);'
+ print ' if (map && length > 0) {'
+ self.emit_memcpy('map', 'map', 'length')
+ print ' }'
+ print ' }'
+ if function.name == 'glFlushMappedBufferRange':
+ print ' GLvoid *map = NULL;'
+ print ' __glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &map);'
+ print ' if (map && length > 0) {'
+ self.emit_memcpy('(char *)map + offset', '(const char *)map + offset', 'length')
+ print ' }'
+ if function.name == 'glFlushMappedBufferRangeAPPLE':
+ print ' GLvoid *map = NULL;'
+ print ' __glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &map);'
+ print ' if (map && size > 0) {'
+ self.emit_memcpy('(char *)map + offset', '(const char *)map + offset', 'size')
+ print ' }'
+ if function.name == 'glFlushMappedNamedBufferRangeEXT':
+ print ' GLvoid *map = NULL;'
+ print ' __glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &map);'
+ print ' if (map && length > 0) {'
+ self.emit_memcpy('(char *)map + offset', '(const char *)map + offset', 'length')
+ print ' }'
+
+ # Don't leave vertex attrib locations to chance. Instead emit fake
+ # glBindAttribLocation calls to ensure that the same locations will be
+ # used when retracing. Trying to remap locations after the fact would
+ # be an herculian task given that vertex attrib locations appear in
+ # many entry-points, including non-shader related ones.
+ if function.name == 'glLinkProgram':
+ Tracer.invokeFunction(self, function)
+ print ' GLint active_attributes = 0;'
+ print ' __glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &active_attributes);'
+ print ' for (GLint attrib = 0; attrib < active_attributes; ++attrib) {'
+ print ' GLint size = 0;'
+ print ' GLenum type = 0;'
+ print ' GLchar name[256];'
+ # TODO: Use ACTIVE_ATTRIBUTE_MAX_LENGTH instead of 256
+ print ' __glGetActiveAttrib(program, attrib, sizeof name, NULL, &size, &type, name);'
+ print " if (name[0] != 'g' || name[1] != 'l' || name[2] != '_') {"
+ print ' GLint location = __glGetAttribLocation(program, name);'
+ print ' if (location >= 0) {'
+ bind_function = glapi.glapi.get_function_by_name('glBindAttribLocation')
+ self.fake_call(bind_function, ['program', 'location', 'name'])
+ print ' }'
+ print ' }'
+ print ' }'
+ if function.name == 'glLinkProgramARB':
+ Tracer.invokeFunction(self, function)
+ print ' GLint active_attributes = 0;'
+ print ' __glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_ATTRIBUTES_ARB, &active_attributes);'
+ print ' for (GLint attrib = 0; attrib < active_attributes; ++attrib) {'
+ print ' GLint size = 0;'
+ print ' GLenum type = 0;'
+ print ' GLcharARB name[256];'
+ # TODO: Use ACTIVE_ATTRIBUTE_MAX_LENGTH instead of 256
+ print ' __glGetActiveAttribARB(programObj, attrib, sizeof name, NULL, &size, &type, name);'
+ print " if (name[0] != 'g' || name[1] != 'l' || name[2] != '_') {"
+ print ' GLint location = __glGetAttribLocationARB(programObj, name);'
+ print ' if (location >= 0) {'
+ bind_function = glapi.glapi.get_function_by_name('glBindAttribLocationARB')
+ self.fake_call(bind_function, ['programObj', 'location', 'name'])
+ print ' }'
+ print ' }'
+ print ' }'
+
+ Tracer.traceFunctionImplBody(self, function)
+
+ marker_functions = [
+ # GL_GREMEDY_string_marker
+ 'glStringMarkerGREMEDY',
+ # GL_GREMEDY_frame_terminator
+ 'glFrameTerminatorGREMEDY',
+ # GL_EXT_debug_marker
+ 'glInsertEventMarkerEXT',
+ 'glPushGroupMarkerEXT',
+ 'glPopGroupMarkerEXT',
+ ]
+
+ def invokeFunction(self, function):
+ if function.name in ('glLinkProgram', 'glLinkProgramARB'):
+ # These functions have been dispatched already
+ return
+
+ # We implement GL_EXT_debug_marker, GL_GREMEDY_*, etc., and not the
+ # driver
+ if function.name in self.marker_functions:
+ return
+
+ if function.name in ('glXGetProcAddress', 'glXGetProcAddressARB', 'wglGetProcAddress'):
+ else_ = ''
+ for marker_function in self.marker_functions:
+ if self.api.get_function_by_name(marker_function):
+ print ' %sif (strcmp("%s", (const char *)%s) == 0) {' % (else_, marker_function, function.args[0].name)
+ print ' __result = (%s)&%s;' % (function.type, marker_function)
+ print ' }'
+ else_ = 'else '
+ print ' %s{' % else_
+ Tracer.invokeFunction(self, function)
+ print ' }'
+ return
+
+ # Override GL extensions
+ if function.name in ('glGetString', 'glGetIntegerv', 'glGetStringi'):
+ Tracer.invokeFunction(self, function, prefix = 'gltrace::__', suffix = '_override')
+ return
+
+ Tracer.invokeFunction(self, function)
+
+ buffer_targets = [