X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=gltrace.py;h=6fc456c82ae773cf587e6f702c35535d00fde464;hb=2672a7dbb0e0acd666abfd2498bb19aeca41fe09;hp=2dee18d965ccc82ce7a756e8d6a3a72a6d0893d9;hpb=ac5285bdd7fde00f9d35810ac5cde555dc4fbdf1;p=apitrace diff --git a/gltrace.py b/gltrace.py index 2dee18d..6fc456c 100644 --- a/gltrace.py +++ b/gltrace.py @@ -27,56 +27,67 @@ """GL tracing generator.""" -import stdapi -import glapi -import glparams -from glxapi import glxapi -from trace import Tracer, dump_instance +import specs.stdapi as stdapi +import specs.glapi as glapi +import specs.glparams as glparams +from specs.glxapi import glxapi +from trace import Tracer class TypeGetter(stdapi.Visitor): '''Determine which glGet*v function that matches the specified type.''' - def __init__(self, prefix = 'glGet', long_suffix = True): + def __init__(self, prefix = 'glGet', long_suffix = True, ext_suffix = ''): self.prefix = prefix self.long_suffix = long_suffix + self.ext_suffix = ext_suffix - def visit_const(self, const): + def visitConst(self, const): return self.visit(const.type) - def visit_alias(self, alias): + def visitAlias(self, alias): if alias.expr == 'GLboolean': if self.long_suffix: - return self.prefix + 'Booleanv', alias.expr + suffix = 'Booleanv' + arg_type = alias.expr else: - return self.prefix + 'iv', 'GLint' + suffix = 'iv' + arg_type = 'GLint' elif alias.expr == 'GLdouble': if self.long_suffix: - return self.prefix + 'Doublev', alias.expr + suffix = 'Doublev' + arg_type = alias.expr else: - return self.prefix + 'dv', alias.expr + suffix = 'dv' + arg_type = alias.expr elif alias.expr == 'GLfloat': if self.long_suffix: - return self.prefix + 'Floatv', alias.expr + suffix = 'Floatv' + arg_type = alias.expr else: - return self.prefix + 'fv', alias.expr + suffix = 'fv' + arg_type = alias.expr elif alias.expr in ('GLint', 'GLuint', 'GLsizei'): if self.long_suffix: - return self.prefix + 'Integerv', 'GLint' + suffix = 'Integerv' + arg_type = 'GLint' else: - return self.prefix + 'iv', 'GLint' + suffix = 'iv' + arg_type = 'GLint' else: print alias.expr assert False + function_name = self.prefix + suffix + self.ext_suffix + return function_name, arg_type - def visit_enum(self, enum): + def visitEnum(self, enum): return self.visit(glapi.GLint) - def visit_bitmask(self, bitmask): + def visitBitmask(self, bitmask): return self.visit(glapi.GLint) - def visit_opaque(self, pointer): - return self.prefix + 'Pointerv', 'GLvoid *' + def visitOpaque(self, pointer): + return self.prefix + 'Pointerv' + self.ext_suffix, 'GLvoid *' class GlTracer(Tracer): @@ -93,25 +104,70 @@ class GlTracer(Tracer): ] arrays.reverse() + # arrays available in PROFILE_ES1 + arrays_es1 = ("Vertex", "Normal", "Color", "TexCoord") + def header(self, api): Tracer.header(self, api) - print '// Whether user arrays were used' - print 'static bool __user_arrays = false;' + print '#include "gltrace.hpp"' + print + + # Which glVertexAttrib* variant to use + print 'enum vertex_attrib {' + print ' VERTEX_ATTRIB,' + print ' VERTEX_ATTRIB_ARB,' + print ' VERTEX_ATTRIB_NV,' + print '};' + print + print 'gltrace::Context *' + print 'gltrace::getContext(void)' + print '{' + print ' // TODO return the context set by other APIs (GLX, EGL, and etc.)' + print ' static gltrace::Context __ctx = { gltrace::PROFILE_COMPAT, false, false, false };' + print ' return &__ctx;' + print '}' print + print 'static vertex_attrib __get_vertex_attrib(void) {' + print ' gltrace::Context *ctx = gltrace::getContext();' + print ' if (ctx->user_arrays_arb || ctx->user_arrays_nv) {' + print ' GLboolean __vertex_program = GL_FALSE;' + print ' __glGetBooleanv(GL_VERTEX_PROGRAM_ARB, &__vertex_program);' + print ' if (__vertex_program) {' + print ' if (ctx->user_arrays_nv) {' + print ' GLint __vertex_program_binding_nv = 0;' + print ' __glGetIntegerv(GL_VERTEX_PROGRAM_BINDING_NV, &__vertex_program_binding_nv);' + print ' if (__vertex_program_binding_nv) {' + print ' return VERTEX_ATTRIB_NV;' + print ' }' + print ' }' + print ' return VERTEX_ATTRIB_ARB;' + print ' }' + print ' }' + print ' return VERTEX_ATTRIB;' + print '}' + print + # Whether we need user arrays print 'static inline bool __need_user_arrays(void)' print '{' - print ' if (!__user_arrays) {' + print ' gltrace::Context *ctx = gltrace::getContext();' + print ' if (!ctx->user_arrays) {' print ' return false;' print ' }' print for camelcase_name, uppercase_name in self.arrays: + # in which profile is the array available? + profile_check = 'ctx->profile == gltrace::PROFILE_COMPAT' + if camelcase_name in self.arrays_es1: + profile_check = '(' + profile_check + ' || ctx->profile == gltrace::PROFILE_ES1)'; + 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;' @@ -122,18 +178,55 @@ class GlTracer(Tracer): 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 ' 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 ' 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 ' }' @@ -147,10 +240,20 @@ class GlTracer(Tracer): 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: @@ -158,25 +261,22 @@ class GlTracer(Tracer): print print 'static inline struct buffer_mapping *' print 'get_buffer_mapping(GLenum target) {' - print ' switch(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::DebugMessage("warning: unknown buffer target 0x%04X\\n", target);' + print ' os::log("apitrace: warning: unknown buffer target 0x%04X\\n", target);' print ' return NULL;' print ' }' print '}' print - # Generate memcpy's signature - self.trace_function_decl(glapi.memcpy) - # 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) {' + print ' switch (pname) {' for function, type, count, name in glparams.parameters: if type is glapi.GLenum: print ' case %s:' % name @@ -199,8 +299,8 @@ class GlTracer(Tracer): # Generate a helper function to know how many elements a parameter has print 'static size_t' - print 'pname_size(GLenum pname) {' - print ' switch(pname) {' + 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) @@ -210,12 +310,20 @@ class GlTracer(Tracer): print ' return num_compressed_texture_formats;' print ' }' print ' default:' - print r' OS::DebugMessage("warning: %s: unknown GLenum 0x%04X\n", __FUNCTION__, pname);' + 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", @@ -250,10 +358,32 @@ class GlTracer(Tracer): draw_function_names = set(( 'glDrawArrays', - 'glDrawArraysEXT', '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 = [ @@ -273,14 +403,19 @@ class GlTracer(Tracer): 'GL_T4F_C4F_N3F_V4F', ] - def trace_function_impl_body(self, function): + 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 ' __user_arrays = true;' - self.dispatch_function(function) + 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': @@ -313,14 +448,14 @@ class GlTracer(Tracer): # Emit a fake function print ' {' - print ' static const Trace::FunctionSig &__sig = %s ? __glEnableClientState_sig : __glDisableClientState_sig;' % flag_name - print ' unsigned __call = Trace::BeginEnter(__sig);' - print ' Trace::BeginArg(0);' - dump_instance(glapi.GLenum, enable_name) - print ' Trace::EndArg();' - print ' Trace::EndEnter();' - print ' Trace::BeginLeave(__call);' - print ' Trace::EndLeave();' + 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;' @@ -334,37 +469,205 @@ class GlTracer(Tracer): print ' __trace_user_arrays(maxindex);' print ' }' - # Emit a fake memcpy on + # 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'): - print ' struct buffer_mapping *mapping = get_buffer_mapping(target);' - print ' if (mapping && mapping->write) {' - print ' unsigned __call = Trace::BeginEnter(__memcpy_sig);' - print ' Trace::BeginArg(0);' - print ' Trace::LiteralOpaque(mapping->map);' - print ' Trace::EndArg();' - print ' Trace::BeginArg(1);' - print ' Trace::LiteralBlob(mapping->map, mapping->length);' - print ' Trace::EndArg();' - print ' Trace::BeginArg(2);' - print ' Trace::LiteralUInt(mapping->length);' - print ' Trace::EndArg();' - print ' Trace::EndEnter();' - print ' Trace::BeginLeave(__call);' - print ' Trace::EndLeave();' + 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.trace_function_impl_body(self, function) - + 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 = [ 'ARRAY_BUFFER', 'ELEMENT_ARRAY_BUFFER', 'PIXEL_PACK_BUFFER', 'PIXEL_UNPACK_BUFFER', + 'UNIFORM_BUFFER', + 'TEXTURE_BUFFER', + 'TRANSFORM_FEEDBACK_BUFFER', + 'COPY_READ_BUFFER', + 'COPY_WRITE_BUFFER', + 'DRAW_INDIRECT_BUFFER', + 'ATOMIC_COUNTER_BUFFER', ] - def wrap_ret(self, function, instance): - Tracer.wrap_ret(self, function, instance) - + def wrapRet(self, function, instance): + Tracer.wrapRet(self, function, instance) + + # Keep track of buffer mappings if function.name in ('glMapBuffer', 'glMapBufferARB'): print ' struct buffer_mapping *mapping = get_buffer_mapping(target);' print ' if (mapping) {' @@ -372,14 +675,18 @@ class GlTracer(Tracer): print ' mapping->length = 0;' print ' __glGetBufferParameteriv(target, GL_BUFFER_SIZE, &mapping->length);' print ' mapping->write = (access != GL_READ_ONLY);' + print ' mapping->explicit_flush = false;' print ' }' - if function.name == 'glMapBufferRange': + print ' if (access & GL_MAP_WRITE_BIT) {' + print ' __checkBufferMapRange = true;' + print ' }' print ' struct buffer_mapping *mapping = get_buffer_mapping(target);' print ' if (mapping) {' print ' mapping->map = %s;' % (instance) print ' mapping->length = length;' print ' mapping->write = access & GL_MAP_WRITE_BIT;' + print ' mapping->explicit_flush = access & GL_MAP_FLUSH_EXPLICIT_BIT;' print ' }' boolean_names = [ @@ -390,31 +697,106 @@ class GlTracer(Tracer): def gl_boolean(self, value): return self.boolean_names[int(bool(value))] - def dump_arg_instance(self, function, arg): + # Names of the functions that unpack from a pixel buffer object. See the + # ARB_pixel_buffer_object specification. + unpack_function_names = set([ + 'glBitmap', + 'glColorSubTable', + 'glColorTable', + 'glCompressedTexImage1D', + 'glCompressedTexImage2D', + 'glCompressedTexImage3D', + 'glCompressedTexSubImage1D', + 'glCompressedTexSubImage2D', + 'glCompressedTexSubImage3D', + 'glConvolutionFilter1D', + 'glConvolutionFilter2D', + 'glDrawPixels', + 'glMultiTexImage1DEXT', + 'glMultiTexImage2DEXT', + 'glMultiTexImage3DEXT', + 'glMultiTexSubImage1DEXT', + 'glMultiTexSubImage2DEXT', + 'glMultiTexSubImage3DEXT', + 'glPixelMapfv', + 'glPixelMapuiv', + 'glPixelMapusv', + 'glPolygonStipple', + 'glSeparableFilter2D', + 'glTexImage1D', + 'glTexImage1DEXT', + 'glTexImage2D', + 'glTexImage2DEXT', + 'glTexImage3D', + 'glTexImage3DEXT', + 'glTexSubImage1D', + 'glTexSubImage1DEXT', + 'glTexSubImage2D', + 'glTexSubImage2DEXT', + 'glTexSubImage3D', + 'glTexSubImage3DEXT', + 'glTextureImage1DEXT', + 'glTextureImage2DEXT', + 'glTextureImage3DEXT', + 'glTextureSubImage1DEXT', + 'glTextureSubImage2DEXT', + 'glTextureSubImage3DEXT', + ]) + + def serializeArgValue(self, function, arg): if function.name in self.draw_function_names and arg.name == 'indices': print ' GLint __element_array_buffer = 0;' print ' __glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);' print ' if (!__element_array_buffer) {' - print ' Trace::LiteralBlob((const void *)%s, count*__gl_type_size(type));' % (arg.name) + if isinstance(arg.type, stdapi.Array): + print ' trace::localWriter.beginArray(%s);' % arg.type.length + print ' for(GLsizei i = 0; i < %s; ++i) {' % arg.type.length + print ' trace::localWriter.beginElement();' + print ' trace::localWriter.writeBlob(%s[i], count[i]*__gl_type_size(type));' % (arg.name) + print ' trace::localWriter.endElement();' + print ' }' + print ' trace::localWriter.endArray();' + else: + print ' trace::localWriter.writeBlob(%s, count*__gl_type_size(type));' % (arg.name) print ' } else {' - print ' Trace::LiteralOpaque((const void *)%s);' % (arg.name) + Tracer.serializeArgValue(self, function, arg) + print ' }' + return + + # Recognize offsets instead of blobs when a PBO is bound + if function.name in self.unpack_function_names \ + and (isinstance(arg.type, stdapi.Blob) \ + or (isinstance(arg.type, stdapi.Const) \ + and isinstance(arg.type.type, stdapi.Blob))): + print ' {' + print ' gltrace::Context *ctx = gltrace::getContext();' + print ' GLint __unpack_buffer = 0;' + print ' if (ctx->profile == gltrace::PROFILE_COMPAT)' + print ' __glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &__unpack_buffer);' + print ' if (__unpack_buffer) {' + print ' trace::localWriter.writeOpaque(%s);' % arg.name + print ' } else {' + Tracer.serializeArgValue(self, function, arg) + print ' }' print ' }' return # Several GL state functions take GLenum symbolic names as # integer/floats; so dump the symbolic name whenever possible - if arg.type in (glapi.GLint, glapi.GLfloat) and arg.name == 'param': + if function.name.startswith('gl') \ + and arg.type in (glapi.GLint, glapi.GLfloat, glapi.GLdouble) \ + and arg.name == 'param': assert arg.index > 0 assert function.args[arg.index - 1].name == 'pname' assert function.args[arg.index - 1].type == glapi.GLenum print ' if (is_symbolic_pname(pname) && is_symbolic_param(%s)) {' % arg.name - dump_instance(glapi.GLenum, arg.name) + self.serializeValue(glapi.GLenum, arg.name) print ' } else {' - Tracer.dump_arg_instance(self, function, arg) + Tracer.serializeArgValue(self, function, arg) print ' }' return - Tracer.dump_arg_instance(self, function, arg) + Tracer.serializeArgValue(self, function, arg) def footer(self, api): Tracer.footer(self, api) @@ -423,14 +805,21 @@ class GlTracer(Tracer): # update the state print 'static void __trace_user_arrays(GLuint maxindex)' print '{' + print ' gltrace::Context *ctx = gltrace::getContext();' for camelcase_name, uppercase_name in self.arrays: + # in which profile is the array available? + profile_check = 'ctx->profile == gltrace::PROFILE_COMPAT' + if camelcase_name in self.arrays_es1: + profile_check = '(' + profile_check + ' || ctx->profile == gltrace::PROFILE_ES1)'; + function_name = 'gl%sPointer' % camelcase_name enable_name = 'GL_%s_ARRAY' % uppercase_name binding_name = 'GL_%s_ARRAY_BUFFER_BINDING' % uppercase_name function = api.get_function_by_name(function_name) - print ' // %s' % function.name + print ' // %s' % function.prototype() + print ' if (%s) {' % profile_check self.array_trace_prolog(api, uppercase_name) self.array_prolog(api, uppercase_name) print ' if (__glIsEnabled(%s)) {' % enable_name @@ -450,67 +839,102 @@ class GlTracer(Tracer): # Emit a fake function self.array_trace_intermezzo(api, uppercase_name) - print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,) + print ' unsigned __call = trace::localWriter.beginEnter(&__%s_sig);' % (function.name,) for arg in function.args: assert not arg.output - print ' Trace::BeginArg(%u);' % (arg.index,) + print ' trace::localWriter.beginArg(%u);' % (arg.index,) if arg.name != 'pointer': - dump_instance(arg.type, arg.name) + self.serializeValue(arg.type, arg.name) else: - print ' Trace::LiteralBlob((const void *)%s, __size);' % (arg.name) - print ' Trace::EndArg();' + print ' trace::localWriter.writeBlob((const void *)%s, __size);' % (arg.name) + print ' trace::localWriter.endArg();' - print ' Trace::EndEnter();' - print ' Trace::BeginLeave(__call);' - print ' Trace::EndLeave();' + print ' trace::localWriter.endEnter();' + print ' trace::localWriter.beginLeave(__call);' + print ' trace::localWriter.endLeave();' print ' }' print ' }' self.array_epilog(api, uppercase_name) self.array_trace_epilog(api, uppercase_name) + print ' }' print - # Samething, but for glVertexAttribPointer - print ' // glVertexAttribPointer' - 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) {' - - function = api.get_function_by_name('glVertexAttribPointer') - - # Get the arguments via glGet* - for arg in function.args[1:]: - arg_get_enum = 'GL_VERTEX_ATTRIB_ARRAY_%s' % (arg.name.upper(),) - arg_get_function, arg_type = TypeGetter('glGetVertexAttrib', False).visit(arg.type) - print ' %s %s = 0;' % (arg_type, arg.name) - print ' __%s(index, %s, &%s);' % (arg_get_function, arg_get_enum, arg.name) - - arg_names = ', '.join([arg.name for arg in function.args[1:-1]]) - print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names) + # Samething, but for glVertexAttribPointer* + # + # Some variants of glVertexAttribPointer alias conventional and generic attributes: + # - glVertexAttribPointer: no + # - glVertexAttribPointerARB: implementation dependent + # - glVertexAttribPointerNV: yes + # + # This means that the implementations of these functions do not always + # alias, and they need to be considered independently. + # + print ' // ES1 does not support generic vertex attributes' + print ' if (ctx->profile == gltrace::PROFILE_ES1)' + print ' return;' + print + print ' vertex_attrib __vertex_attrib = __get_vertex_attrib();' + print + for suffix in ['', 'ARB', 'NV']: + if suffix: + SUFFIX = '_' + suffix + else: + SUFFIX = suffix + function_name = 'glVertexAttribPointer' + suffix + function = api.get_function_by_name(function_name) - # Emit a fake function - print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,) - for arg in function.args: - assert not arg.output - print ' Trace::BeginArg(%u);' % (arg.index,) - if arg.name != 'pointer': - dump_instance(arg.type, arg.name) + print ' // %s' % function.prototype() + print ' if (__vertex_attrib == VERTEX_ATTRIB%s) {' % SUFFIX + if suffix == 'NV': + print ' GLint __max_vertex_attribs = 16;' else: - print ' Trace::LiteralBlob((const void *)%s, __size);' % (arg.name) - print ' Trace::EndArg();' - - print ' Trace::EndEnter();' - print ' Trace::BeginLeave(__call);' - print ' Trace::EndLeave();' - print ' }' - print ' }' - print ' }' - print + 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;' + if suffix == 'NV': + print ' __glGetIntegerv(GL_VERTEX_ATTRIB_ARRAY0_NV + index, &__enabled);' + else: + print ' __glGetVertexAttribiv%s(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED%s, &__enabled);' % (suffix, SUFFIX) + print ' if (__enabled) {' + print ' GLint __binding = 0;' + if suffix != 'NV': + # It doesn't seem possible to use VBOs with NV_vertex_program. + print ' __glGetVertexAttribiv%s(index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING%s, &__binding);' % (suffix, SUFFIX) + print ' if (!__binding) {' + + # Get the arguments via glGet* + for arg in function.args[1:]: + if suffix == 'NV': + arg_get_enum = 'GL_ATTRIB_ARRAY_%s%s' % (arg.name.upper(), SUFFIX) + else: + arg_get_enum = 'GL_VERTEX_ATTRIB_ARRAY_%s%s' % (arg.name.upper(), SUFFIX) + arg_get_function, arg_type = TypeGetter('glGetVertexAttrib', False, suffix).visit(arg.type) + print ' %s %s = 0;' % (arg_type, arg.name) + print ' __%s(index, %s, &%s);' % (arg_get_function, arg_get_enum, arg.name) + + arg_names = ', '.join([arg.name for arg in function.args[1:-1]]) + print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names) + + # Emit a fake function + print ' unsigned __call = trace::localWriter.beginEnter(&__%s_sig);' % (function.name,) + for arg in function.args: + assert not arg.output + print ' trace::localWriter.beginArg(%u);' % (arg.index,) + if arg.name != 'pointer': + self.serializeValue(arg.type, arg.name) + else: + print ' trace::localWriter.writeBlob((const void *)%s, __size);' % (arg.name) + print ' trace::localWriter.endArg();' + + print ' trace::localWriter.endEnter();' + print ' trace::localWriter.beginLeave(__call);' + print ' trace::localWriter.endLeave();' + print ' }' + print ' }' + print ' }' + print ' }' + print print '}' print @@ -525,9 +949,12 @@ class GlTracer(Tracer): print ' GLint client_active_texture = 0;' print ' __glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE, &client_active_texture);' print ' GLint max_texture_coords = 0;' - print ' __glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);' + print ' if (ctx->profile == gltrace::PROFILE_COMPAT)' + print ' __glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);' + print ' else' + print ' __glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_coords);' print ' for (GLint unit = 0; unit < max_texture_coords; ++unit) {' - print ' GLenum texture = GL_TEXTURE0 + unit;' + print ' GLint texture = GL_TEXTURE0 + unit;' print ' __glClientActiveTexture(texture);' def array_trace_prolog(self, api, uppercase_name): @@ -561,15 +988,15 @@ class GlTracer(Tracer): self.fake_call(function, [texture]) def fake_call(self, function, args): - print ' unsigned __fake_call = Trace::BeginEnter(__%s_sig);' % (function.name,) + print ' unsigned __fake_call = trace::localWriter.beginEnter(&__%s_sig);' % (function.name,) for arg, instance in zip(function.args, args): assert not arg.output - print ' Trace::BeginArg(%u);' % (arg.index,) - dump_instance(arg.type, instance) - print ' Trace::EndArg();' - print ' Trace::EndEnter();' - print ' Trace::BeginLeave(__fake_call);' - print ' Trace::EndLeave();' + print ' trace::localWriter.beginArg(%u);' % (arg.index,) + self.serializeValue(arg.type, instance) + print ' trace::localWriter.endArg();' + print ' trace::localWriter.endEnter();' + print ' trace::localWriter.beginLeave(__fake_call);' + print ' trace::localWriter.endLeave();'