/* * Mesa 3-D graphics library * Version: 6.3 * * Copyright (C) 1999-2003 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * This file manages the OpenGL API dispatch layer. * The dispatch table (struct _glapi_table) is basically just a list * of function pointers. * There are functions to set/get the current dispatch table for the * current thread and to manage registration/dispatch of dynamically * added extension functions. * * It's intended that this file and the other glapi*.[ch] files are * flexible enough to be reused in several places: XFree86, DRI- * based libGL.so, and perhaps the SGI SI. * * NOTE: There are no dependencies on Mesa in this code. * * Versions (API changes): * 2000/02/23 - original version for Mesa 3.3 and XFree86 4.0 * 2001/01/16 - added dispatch override feature for Mesa 3.5 * 2002/06/28 - added _glapi_set_warning_func(), Mesa 4.1. * 2002/10/01 - _glapi_get_proc_address() will now generate new entrypoints * itself (using offset ~0). _glapi_add_entrypoint() can be * called afterward and it'll fill in the correct dispatch * offset. This allows DRI libGL to avoid probing for DRI * drivers! No changes to the public glapi interface. */ #include "glheader.h" #include "glapi.h" #include "glapioffsets.h" #include "glapitable.h" #include "glthread.h" /***** BEGIN NO-OP DISPATCH *****/ static GLboolean WarnFlag = GL_FALSE; static _glapi_warning_func warning_func; static void init_glapi_relocs(void); static _glapi_proc generate_entrypoint(GLuint functionOffset); static void fill_in_entrypoint_offset(_glapi_proc entrypoint, GLuint offset); /* * Enable/disable printing of warning messages. */ PUBLIC void _glapi_noop_enable_warnings(GLboolean enable) { WarnFlag = enable; } /* * Register a callback function for reporting errors. */ PUBLIC void _glapi_set_warning_func( _glapi_warning_func func ) { warning_func = func; } static GLboolean warn(void) { if ((WarnFlag || getenv("MESA_DEBUG") || getenv("LIBGL_DEBUG")) && warning_func) { return GL_TRUE; } else { return GL_FALSE; } } #define KEYWORD1 static #define KEYWORD2 GLAPIENTRY #define NAME(func) NoOp##func #define F NULL #define DISPATCH(func, args, msg) \ if (warn()) { \ warning_func(NULL, "GL User Error: called without context: %s", #func); \ } #define RETURN_DISPATCH(func, args, msg) \ if (warn()) { \ warning_func(NULL, "GL User Error: called without context: %s", #func); \ } \ return 0 #define DISPATCH_TABLE_NAME __glapi_noop_table #define UNUSED_TABLE_NAME __unused_noop_functions #define TABLE_ENTRY(name) (_glapi_proc) NoOp##name static GLint NoOpUnused(void) { if (warn()) { warning_func(NULL, "GL User Error: calling extension function without a current context\n"); } return 0; } #include "glapitemp.h" /***** END NO-OP DISPATCH *****/ /***** BEGIN THREAD-SAFE DISPATCH *****/ #if defined(THREADS) #if defined(GLX_USE_TLS) __thread struct _glapi_table * _glapi_tls_Dispatch __attribute__((tls_model("initial-exec"))) = (struct _glapi_table *) __glapi_noop_table; static __thread struct _glapi_table * _glapi_tls_RealDispatch __attribute__((tls_model("initial-exec"))) = (struct _glapi_table *) __glapi_noop_table; __thread void * _glapi_tls_Context __attribute__((tls_model("initial-exec"))); /** * Legacy per-thread dispatch pointer. This is only needed to support * non-TLS DRI drivers. */ _glthread_TSD _gl_DispatchTSD; #else /** * \name Multi-threaded control support variables * * If thread-safety is supported, there are two potential mechanisms that can * be used. The old-style mechanism would set \c _glapi_Dispatch to a special * thread-safe dispatch table. These dispatch routines would call * \c _glapi_get_dispatch to get the actual dispatch pointer. In this * setup \c _glapi_Dispatch could never be \c NULL. This dual layered * dispatch setup performed great for single-threaded apps, but didn't * perform well for multithreaded apps. * * In the new mechansim, there are two variables. The first is * \c _glapi_DispatchTSD. In the single-threaded case, this variable points * to the dispatch table. In the multi-threaded case, this variable is * \c NULL, and thread-specific variable \c _gl_DispatchTSD points to the * actual dispatch table. \c _glapi_DispatchTSD is used to signal to the * static dispatch functions to call \c _glapi_get_dispatch to get the real * dispatch table. * * There is a race condition in setting \c _glapi_DispatchTSD to \c NULL. * It is possible for the original thread to be setting it at the same instant * a new thread, perhaps running on a different processor, is clearing it. * Because of that, \c ThreadSafe, which can only ever be changed to * \c GL_TRUE, is used to determine whether or not the application is * multithreaded. */ /*@{*/ static GLboolean ThreadSafe = GL_FALSE; /**< In thread-safe mode? */ _glthread_TSD _gl_DispatchTSD; /**< Per-thread dispatch pointer */ static _glthread_TSD RealDispatchTSD; /**< only when using override */ static _glthread_TSD ContextTSD; /**< Per-thread context pointer */ /*@}*/ #endif /* defined(GLX_USE_TLS) */ #define DISPATCH_TABLE_NAME __glapi_threadsafe_table #define UNUSED_TABLE_NAME __unused_threadsafe_functions #define TABLE_ENTRY(name) (_glapi_proc) gl##name static GLint glUnused(void) { return 0; } #include "glapitemp.h" #endif /***** END THREAD-SAFE DISPATCH *****/ #if defined(GLX_USE_TLS) /** * \name Old dispatch pointers * * Very old DRI based drivers assume that \c _glapi_Dispatch will never be * \c NULL. Becuase of that, special "thread-safe" dispatch functions are * needed here. Slightly more recent drivers detect the multi-threaded case * by \c _glapi_DispatchTSD being \c NULL. * * \deprecated * * \warning * \c _glapi_RealDispatch does not exist in TLS builds. I don't think it was * ever used outside libGL.so, so this should be safe. */ /*@{*/ PUBLIC const struct _glapi_table *_glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table; PUBLIC const struct _glapi_table *_glapi_DispatchTSD = NULL; PUBLIC const void *_glapi_Context = NULL; /*@}*/ #else PUBLIC struct _glapi_table *_glapi_Dispatch = (struct _glapi_table *) __glapi_noop_table; #if defined( THREADS ) PUBLIC struct _glapi_table *_glapi_DispatchTSD = (struct _glapi_table *) __glapi_noop_table; #endif PUBLIC struct _glapi_table *_glapi_RealDispatch = (struct _glapi_table *) __glapi_noop_table; /* Used when thread safety disabled */ PUBLIC void *_glapi_Context = NULL; #endif /* defined(GLX_USE_TLS) */ static GLboolean DispatchOverride = GL_FALSE; /** * strdup() is actually not a standard ANSI C or POSIX routine. * Irix will not define it if ANSI mode is in effect. */ static char * str_dup(const char *str) { char *copy; copy = (char*) malloc(strlen(str) + 1); if (!copy) return NULL; strcpy(copy, str); return copy; } /** * We should call this periodically from a function such as glXMakeCurrent * in order to test if multiple threads are being used. */ PUBLIC void _glapi_check_multithread(void) { #if defined(THREADS) && !defined(GLX_USE_TLS) if (!ThreadSafe) { static unsigned long knownID; static GLboolean firstCall = GL_TRUE; if (firstCall) { knownID = _glthread_GetID(); firstCall = GL_FALSE; } else if (knownID != _glthread_GetID()) { ThreadSafe = GL_TRUE; _glapi_set_dispatch(NULL); } } else if (!_glapi_get_dispatch()) { /* make sure that this thread's dispatch pointer isn't null */ _glapi_set_dispatch(NULL); } #endif } /** * Set the current context pointer for this thread. * The context pointer is an opaque type which should be cast to * void from the real context pointer type. */ PUBLIC void _glapi_set_context(void *context) { (void) __unused_noop_functions; /* silence a warning */ #if defined(GLX_USE_TLS) _glapi_tls_Context = context; #elif defined(THREADS) (void) __unused_threadsafe_functions; /* silence a warning */ _glthread_SetTSD(&ContextTSD, context); _glapi_Context = (ThreadSafe) ? NULL : context; #else _glapi_Context = context; #endif } /** * Get the current context pointer for this thread. * The context pointer is an opaque type which should be cast from * void to the real context pointer type. */ PUBLIC void * _glapi_get_context(void) { #if defined(GLX_USE_TLS) return _glapi_tls_Context; #elif defined(THREADS) if (ThreadSafe) { return _glthread_GetTSD(&ContextTSD); } else { return _glapi_Context; } #else return _glapi_Context; #endif } /** * Set the global or per-thread dispatch table pointer. */ PUBLIC void _glapi_set_dispatch(struct _glapi_table *dispatch) { #if defined(PTHREADS) || defined(GLX_USE_TLS) static pthread_once_t once_control = PTHREAD_ONCE_INIT; pthread_once( & once_control, init_glapi_relocs ); #endif if (!dispatch) { /* use the no-op functions */ dispatch = (struct _glapi_table *) __glapi_noop_table; } #ifdef DEBUG else { _glapi_check_table(dispatch); } #endif #if defined(GLX_USE_TLS) if (DispatchOverride) { _glapi_tls_RealDispatch = dispatch; } else { _glthread_SetTSD(&_gl_DispatchTSD, (void *) dispatch); _glapi_tls_Dispatch = dispatch; } #elif defined(THREADS) if (DispatchOverride) { _glthread_SetTSD(&RealDispatchTSD, (void *) dispatch); if (ThreadSafe) _glapi_RealDispatch = (struct _glapi_table*) __glapi_threadsafe_table; else _glapi_RealDispatch = dispatch; } else { /* normal operation */ _glthread_SetTSD(&_gl_DispatchTSD, (void *) dispatch); if (ThreadSafe) { _glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table; _glapi_DispatchTSD = NULL; } else { _glapi_Dispatch = dispatch; _glapi_DispatchTSD = dispatch; } } #else /*THREADS*/ if (DispatchOverride) { _glapi_RealDispatch = dispatch; } else { _glapi_Dispatch = dispatch; } #endif /*THREADS*/ } /** * Return pointer to current dispatch table for calling thread. */ PUBLIC struct _glapi_table * _glapi_get_dispatch(void) { #if defined(GLX_USE_TLS) struct _glapi_table * api = (DispatchOverride) ? _glapi_tls_RealDispatch : _glapi_tls_Dispatch; assert( api != NULL ); return api; #elif defined(THREADS) if (ThreadSafe) { if (DispatchOverride) { return (struct _glapi_table *) _glthread_GetTSD(&RealDispatchTSD); } else { return (struct _glapi_table *) _glthread_GetTSD(&_gl_DispatchTSD); } } else { if (DispatchOverride) { assert(_glapi_RealDispatch); return _glapi_RealDispatch; } else { assert(_glapi_DispatchTSD); return _glapi_DispatchTSD; } } #else return _glapi_Dispatch; #endif } /* * Notes on dispatch overrride: * * Dispatch override allows an external agent to hook into the GL dispatch * mechanism before execution goes into the core rendering library. For * example, a trace mechanism would insert itself as an overrider, print * logging info for each GL function, then dispatch to the real GL function. * * libGLS (GL Stream library) is another agent that might use override. * * We don't allow more than one layer of overriding at this time. * In the future we may allow nested/layered override. In that case * _glapi_begin_dispatch_override() will return an override layer, * _glapi_end_dispatch_override(layer) will remove an override layer * and _glapi_get_override_dispatch(layer) will return the dispatch * table for a given override layer. layer = 0 will be the "real" * dispatch table. */ /* * Return: dispatch override layer number. */ PUBLIC int _glapi_begin_dispatch_override(struct _glapi_table *override) { struct _glapi_table *real = _glapi_get_dispatch(); assert(!DispatchOverride); /* can't nest at this time */ DispatchOverride = GL_TRUE; _glapi_set_dispatch(real); #if defined(GLX_USE_TLS) _glthread_SetTSD(&_gl_DispatchTSD, (void *) override); _glapi_tls_Dispatch = override; #elif defined(THREADS) _glthread_SetTSD(&_gl_DispatchTSD, (void *) override); if ( ThreadSafe ) { _glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table; _glapi_DispatchTSD = NULL; } else { _glapi_Dispatch = override; _glapi_DispatchTSD = override; } #else _glapi_Dispatch = override; #endif return 1; } PUBLIC void _glapi_end_dispatch_override(int layer) { struct _glapi_table *real = _glapi_get_dispatch(); (void) layer; DispatchOverride = GL_FALSE; _glapi_set_dispatch(real); /* the rest of this isn't needed, just play it safe */ #if defined(GLX_USE_TLS) _glapi_tls_RealDispatch = NULL; #else # if defined(THREADS) _glthread_SetTSD(&RealDispatchTSD, NULL); # endif _glapi_RealDispatch = NULL; #endif } PUBLIC struct _glapi_table * _glapi_get_override_dispatch(int layer) { if (layer == 0) { return _glapi_get_dispatch(); } else { if (DispatchOverride) { #if defined(GLX_USE_TLS) return (struct _glapi_table *) _glapi_tls_Dispatch; #elif defined(THREADS) return (struct _glapi_table *) _glthread_GetTSD(&_gl_DispatchTSD); #else return _glapi_Dispatch; #endif } else { return NULL; } } } #if !defined( USE_X86_ASM ) #define NEED_FUNCTION_POINTER #endif /* The code in this file is auto-generated with Python */ #include "glprocs.h" /** * Search the table of static entrypoint functions for the named function * and return the corresponding glprocs_table_t entry. */ static const glprocs_table_t * find_entry( const char * n ) { GLuint i; for (i = 0; static_functions[i].Name_offset >= 0; i++) { const char * test_name; test_name = gl_string_table + static_functions[i].Name_offset; if (strcmp(test_name, n) == 0) { return & static_functions[i]; } } return NULL; } /** * Return dispatch table offset of the named static (built-in) function. * Return -1 if function not found. */ static GLint get_static_proc_offset(const char *funcName) { const glprocs_table_t * const f = find_entry( funcName ); if ( f != NULL ) { return f->Offset; } return -1; } #ifdef USE_X86_ASM #if defined( GLX_USE_TLS ) extern GLubyte gl_dispatch_functions_start[]; extern GLubyte gl_dispatch_functions_end[]; #else extern const GLubyte gl_dispatch_functions_start[]; #endif # if defined(THREADS) && !defined(GLX_USE_TLS) # define X86_DISPATCH_FUNCTION_SIZE 32 # else # define X86_DISPATCH_FUNCTION_SIZE 16 # endif /** * Return dispatch function address the named static (built-in) function. * Return NULL if function not found. */ static const _glapi_proc get_static_proc_address(const char *funcName) { const glprocs_table_t * const f = find_entry( funcName ); if ( f != NULL ) { return (_glapi_proc) (gl_dispatch_functions_start + (X86_DISPATCH_FUNCTION_SIZE * f->Offset)); } else { return NULL; } } #else /** * Return pointer to the named static (built-in) function. * \return NULL if function not found. */ static const _glapi_proc get_static_proc_address(const char *funcName) { const glprocs_table_t * const f = find_entry( funcName ); return ( f != NULL ) ? f->Address : NULL; } #endif /* USE_X86_ASM */ /** * Return the name of the function at the given offset in the dispatch * table. For debugging only. */ static const char * get_static_proc_name( GLuint offset ) { GLuint i; for (i = 0; static_functions[i].Name_offset >= 0; i++) { if (static_functions[i].Offset == offset) { return gl_string_table + static_functions[i].Name_offset; } } return NULL; } /********************************************************************** * Extension function management. */ /* * Number of extension functions which we can dynamically add at runtime. */ #define MAX_EXTENSION_FUNCS 300 /* * The dispatch table size (number of entries) is the size of the * _glapi_table struct plus the number of dynamic entries we can add. * The extra slots can be filled in by DRI drivers that register new extension * functions. */ #define DISPATCH_TABLE_SIZE (sizeof(struct _glapi_table) / sizeof(void *) + MAX_EXTENSION_FUNCS) struct name_address_offset { const char *Name; _glapi_proc Address; GLuint Offset; }; static struct name_address_offset ExtEntryTable[MAX_EXTENSION_FUNCS]; static GLuint NumExtEntryPoints = 0; #ifdef USE_SPARC_ASM extern void __glapi_sparc_icache_flush(unsigned int *); #endif /** * Generate a dispatch function (entrypoint) which jumps through * the given slot number (offset) in the current dispatch table. * We need assembly language in order to accomplish this. */ static _glapi_proc generate_entrypoint(GLuint functionOffset) { #if defined(USE_X86_ASM) /* 32 is chosen as something of a magic offset. For x86, the dispatch * at offset 32 is the first one where the offset in the * "jmp OFFSET*4(%eax)" can't be encoded in a single byte. */ const GLubyte * const template_func = gl_dispatch_functions_start + (X86_DISPATCH_FUNCTION_SIZE * 32); GLubyte * const code = (GLubyte *) malloc( X86_DISPATCH_FUNCTION_SIZE ); if ( code != NULL ) { (void) memcpy( code, template_func, X86_DISPATCH_FUNCTION_SIZE ); fill_in_entrypoint_offset( (_glapi_proc) code, functionOffset ); } return (_glapi_proc) code; #elif defined(USE_SPARC_ASM) #if (defined(__sparc_v9__) && (!defined(__linux__) || defined(__linux_sparc_64__))) static const unsigned int insn_template[] = { 0x05000000, /* sethi %uhi(_glapi_Dispatch), %g2 */ 0x03000000, /* sethi %hi(_glapi_Dispatch), %g1 */ 0x8410a000, /* or %g2, %ulo(_glapi_Dispatch), %g2 */ 0x82106000, /* or %g1, %lo(_glapi_Dispatch), %g1 */ 0x8528b020, /* sllx %g2, 32, %g2 */ 0xc2584002, /* ldx [%g1 + %g2], %g1 */ 0x05000000, /* sethi %hi(8 * glapioffset), %g2 */ 0x8410a000, /* or %g2, %lo(8 * glapioffset), %g2 */ 0xc6584002, /* ldx [%g1 + %g2], %g3 */ 0x81c0c000, /* jmpl %g3, %g0 */ 0x01000000 /* nop */ }; #else static const unsigned int insn_template[] = { 0x03000000, /* sethi %hi(_glapi_Dispatch), %g1 */ 0xc2006000, /* ld [%g1 + %lo(_glapi_Dispatch)], %g1 */ 0xc6006000, /* ld [%g1 + %lo(4*glapioffset)], %g3 */ 0x81c0c000, /* jmpl %g3, %g0 */ 0x01000000 /* nop */ }; #endif unsigned int *code = (unsigned int *) malloc(sizeof(insn_template)); unsigned long glapi_addr = (unsigned long) &_glapi_Dispatch; if (code) { memcpy(code, insn_template, sizeof(insn_template)); #if (defined(__sparc_v9__) && (!defined(__linux__) || defined(__linux_sparc_64__))) code[0] |= (glapi_addr >> (32 + 10)); code[1] |= ((glapi_addr & 0xffffffff) >> 10); __glapi_sparc_icache_flush(&code[0]); code[2] |= ((glapi_addr >> 32) & ((1 << 10) - 1)); code[3] |= (glapi_addr & ((1 << 10) - 1)); __glapi_sparc_icache_flush(&code[2]); code[6] |= ((functionOffset * 8) >> 10); code[7] |= ((functionOffset * 8) & ((1 << 10) - 1)); __glapi_sparc_icache_flush(&code[6]); #else code[0] |= (glapi_addr >> 10); code[1] |= (glapi_addr & ((1 << 10) - 1)); __glapi_sparc_icache_flush(&code[0]); code[2] |= (functionOffset * 4); __glapi_sparc_icache_flush(&code[2]); #endif } return (_glapi_proc) code; #else (void) functionOffset; return NULL; #endif /* USE_*_ASM */ } /** * This function inserts a new dispatch offset into the assembly language * stub that was generated with the preceeding function. */ static void fill_in_entrypoint_offset(_glapi_proc entrypoint, GLuint offset) { #if defined(USE_X86_ASM) GLubyte * const code = (GLubyte *) entrypoint; #if X86_DISPATCH_FUNCTION_SIZE == 32 *((unsigned int *)(code + 11)) = 4 * offset; *((unsigned int *)(code + 22)) = 4 * offset; #elif X86_DISPATCH_FUNCTION_SIZE == 16 && defined( GLX_USE_TLS ) *((unsigned int *)(code + 8)) = 4 * offset; #elif X86_DISPATCH_FUNCTION_SIZE == 16 *((unsigned int *)(code + 7)) = 4 * offset; #else # error Invalid X86_DISPATCH_FUNCTION_SIZE! #endif #elif defined(USE_SPARC_ASM) /* XXX this hasn't been tested! */ unsigned int *code = (unsigned int *) entrypoint; #if (defined(__sparc_v9__) && (!defined(__linux__) || defined(__linux_sparc_64__))) code[6] = 0x05000000; /* sethi %hi(8 * glapioffset), %g2 */ code[7] = 0x8410a000; /* or %g2, %lo(8 * glapioffset), %g2 */ code[6] |= ((offset * 8) >> 10); code[7] |= ((offset * 8) & ((1 << 10) - 1)); __glapi_sparc_icache_flush(&code[6]); #else /* __sparc_v9__ && !linux */ code[2] = 0xc6006000; /* ld [%g1 + %lo(4*glapioffset)], %g3 */ code[2] |= (offset * 4); __glapi_sparc_icache_flush(&code[2]); #endif /* __sparc_v9__ && !linux */ #else /* an unimplemented architecture */ (void) entrypoint; (void) offset; #endif /* USE_*_ASM */ } /** * Add a new extension function entrypoint. * Return: GL_TRUE = success or GL_FALSE = failure */ PUBLIC GLboolean _glapi_add_entrypoint(const char *funcName, GLuint offset) { /* trivial rejection test */ #ifdef MANGLE if (!funcName || funcName[0] != 'm' || funcName[1] != 'g' || funcName[2] != 'l') return GL_FALSE; #else if (!funcName || funcName[0] != 'g' || funcName[1] != 'l') return GL_FALSE; #endif /* first check if the named function is already statically present */ { GLint index = get_static_proc_offset(funcName); if (index >= 0) { return (GLboolean) ((GLuint) index == offset); /* bad offset! */ } } /* See if this function has already been dynamically added */ { GLuint i; for (i = 0; i < NumExtEntryPoints; i++) { if (strcmp(ExtEntryTable[i].Name, funcName) == 0) { /* function already registered */ if (ExtEntryTable[i].Offset == offset) { return GL_TRUE; /* offsets match */ } else if (ExtEntryTable[i].Offset == (GLuint) ~0 && offset < DISPATCH_TABLE_SIZE) { /* need to patch-up the dispatch code */ if (offset != (GLuint) ~0) { fill_in_entrypoint_offset(ExtEntryTable[i].Address, offset); ExtEntryTable[i].Offset = offset; } return GL_TRUE; } else { return GL_FALSE; /* bad offset! */ } } } } /* This is a new function, try to add it. */ if (NumExtEntryPoints >= MAX_EXTENSION_FUNCS || offset >= DISPATCH_TABLE_SIZE) { /* No space left */ return GL_FALSE; } else { _glapi_proc entrypoint = generate_entrypoint(offset); if (!entrypoint) return GL_FALSE; /* couldn't generate assembly */ /* OK! */ ExtEntryTable[NumExtEntryPoints].Name = str_dup(funcName); ExtEntryTable[NumExtEntryPoints].Offset = offset; ExtEntryTable[NumExtEntryPoints].Address = entrypoint; NumExtEntryPoints++; return GL_TRUE; /* success */ } /* should never get here, silence compiler warnings */ return GL_FALSE; } /** * Return offset of entrypoint for named function within dispatch table. */ PUBLIC GLint _glapi_get_proc_offset(const char *funcName) { /* search extension functions first */ GLuint i; for (i = 0; i < NumExtEntryPoints; i++) { if (strcmp(ExtEntryTable[i].Name, funcName) == 0) { return ExtEntryTable[i].Offset; } } /* search static functions */ return get_static_proc_offset(funcName); } /** * Return pointer to the named function. If the function name isn't found * in the name of static functions, try generating a new API entrypoint on * the fly with assembly language. */ PUBLIC _glapi_proc _glapi_get_proc_address(const char *funcName) { GLuint i; #ifdef MANGLE if (funcName[0] != 'm' || funcName[1] != 'g' || funcName[2] != 'l') return NULL; #else if (funcName[0] != 'g' || funcName[1] != 'l') return NULL; #endif /* search extension functions first */ for (i = 0; i < NumExtEntryPoints; i++) { if (strcmp(ExtEntryTable[i].Name, funcName) == 0) { return ExtEntryTable[i].Address; } } /* search static functions */ { const _glapi_proc func = get_static_proc_address(funcName); if (func) return func; } /* generate new entrypoint - use a temporary dispatch offset of * ~0 (i.e. -1). Later, when the driver calls _glapi_add_entrypoint() * we'll put in the proper offset. If that never happens, and the * user calls this function, he'll segfault. That's what you get * when you try calling a GL function that doesn't really exist. */ if (NumExtEntryPoints < MAX_EXTENSION_FUNCS) { _glapi_proc entrypoint = generate_entrypoint(~0); if (!entrypoint) return GL_FALSE; ExtEntryTable[NumExtEntryPoints].Name = str_dup(funcName); ExtEntryTable[NumExtEntryPoints].Offset = ~0; ExtEntryTable[NumExtEntryPoints].Address = entrypoint; NumExtEntryPoints++; return entrypoint; } else { /* no space for new functions! */ return NULL; } } /** * Return the name of the function at the given dispatch offset. * This is only intended for debugging. */ PUBLIC const char * _glapi_get_proc_name(GLuint offset) { GLuint i; const char * n; /* search built-in functions */ n = get_static_proc_name(offset); if ( n != NULL ) { return n; } /* search added extension functions */ for (i = 0; i < NumExtEntryPoints; i++) { if (ExtEntryTable[i].Offset == offset) { return ExtEntryTable[i].Name; } } return NULL; } /** * Return size of dispatch table struct as number of functions (or * slots). */ PUBLIC GLuint _glapi_get_dispatch_table_size(void) { return DISPATCH_TABLE_SIZE; } /** * Get API dispatcher version string. */ PUBLIC const char * _glapi_get_version(void) { return "20021001"; /* YYYYMMDD */ } /** * Make sure there are no NULL pointers in the given dispatch table. * Intended for debugging purposes. */ PUBLIC void _glapi_check_table(const struct _glapi_table *table) { #ifdef DEBUG const GLuint entries = _glapi_get_dispatch_table_size(); const void **tab = (const void **) table; GLuint i; for (i = 1; i < entries; i++) { assert(tab[i]); } /* Do some spot checks to be sure that the dispatch table * slots are assigned correctly. */ { GLuint BeginOffset = _glapi_get_proc_offset("glBegin"); char *BeginFunc = (char*) &table->Begin; GLuint offset = (BeginFunc - (char *) table) / sizeof(void *); assert(BeginOffset == _gloffset_Begin); assert(BeginOffset == offset); } { GLuint viewportOffset = _glapi_get_proc_offset("glViewport"); char *viewportFunc = (char*) &table->Viewport; GLuint offset = (viewportFunc - (char *) table) / sizeof(void *); assert(viewportOffset == _gloffset_Viewport); assert(viewportOffset == offset); } { GLuint VertexPointerOffset = _glapi_get_proc_offset("glVertexPointer"); char *VertexPointerFunc = (char*) &table->VertexPointer; GLuint offset = (VertexPointerFunc - (char *) table) / sizeof(void *); assert(VertexPointerOffset == _gloffset_VertexPointer); assert(VertexPointerOffset == offset); } { GLuint ResetMinMaxOffset = _glapi_get_proc_offset("glResetMinmax"); char *ResetMinMaxFunc = (char*) &table->ResetMinmax; GLuint offset = (ResetMinMaxFunc - (char *) table) / sizeof(void *); assert(ResetMinMaxOffset == _gloffset_ResetMinmax); assert(ResetMinMaxOffset == offset); } { GLuint blendColorOffset = _glapi_get_proc_offset("glBlendColor"); char *blendColorFunc = (char*) &table->BlendColor; GLuint offset = (blendColorFunc - (char *) table) / sizeof(void *); assert(blendColorOffset == _gloffset_BlendColor); assert(blendColorOffset == offset); } { GLuint istextureOffset = _glapi_get_proc_offset("glIsTextureEXT"); char *istextureFunc = (char*) &table->IsTextureEXT; GLuint offset = (istextureFunc - (char *) table) / sizeof(void *); assert(istextureOffset == _gloffset_IsTextureEXT); assert(istextureOffset == offset); } { GLuint secondaryColor3fOffset = _glapi_get_proc_offset("glSecondaryColor3fEXT"); char *secondaryColor3fFunc = (char*) &table->SecondaryColor3fEXT; GLuint offset = (secondaryColor3fFunc - (char *) table) / sizeof(void *); assert(secondaryColor3fOffset == _gloffset_SecondaryColor3fEXT); assert(secondaryColor3fOffset == offset); assert(_glapi_get_proc_address("glSecondaryColor3fEXT") == (_glapi_proc) &glSecondaryColor3fEXT); } { GLuint pointParameterivOffset = _glapi_get_proc_offset("glPointParameterivNV"); char *pointParameterivFunc = (char*) &table->PointParameterivNV; GLuint offset = (pointParameterivFunc - (char *) table) / sizeof(void *); assert(pointParameterivOffset == _gloffset_PointParameterivNV); assert(pointParameterivOffset == offset); assert(_glapi_get_proc_address("glPointParameterivNV") == (_glapi_proc) &glPointParameterivNV); } { GLuint setFenceOffset = _glapi_get_proc_offset("glSetFenceNV"); char *setFenceFunc = (char*) &table->SetFenceNV; GLuint offset = (setFenceFunc - (char *) table) / sizeof(void *); assert(setFenceOffset == _gloffset_SetFenceNV); assert(setFenceOffset == offset); assert(_glapi_get_proc_address("glSetFenceNV") == (_glapi_proc) &glSetFenceNV); } #else (void) table; #endif } /** * Perform platform-specific GL API entry-point fixups. * * */ static void init_glapi_relocs( void ) { #if defined( USE_X86_ASM ) && defined( GLX_USE_TLS ) extern void * _x86_get_dispatch(void); const GLubyte * const get_disp = (const GLubyte *) _x86_get_dispatch; GLubyte * curr_func = (GLubyte *) gl_dispatch_functions_start; while ( curr_func != (GLubyte *) gl_dispatch_functions_end ) { (void) memcpy( curr_func, get_disp, 6 ); curr_func += X86_DISPATCH_FUNCTION_SIZE; } #endif /* defined( USE_X86_ASM ) && defined( GLX_USE_TLS ) */ }