blob: b0d26fdcdd921de64e6341fca9f2a960e4001229 [file] [log] [blame]
Brian Paul54f3aab2002-10-02 01:51:44 +00001/* $Id: glapi.c,v 1.64 2002/10/02 01:51:44 brianp Exp $ */
Brian Paulfbd8f211999-11-11 01:22:25 +00002
3/*
4 * Mesa 3-D graphics library
Brian Paul5104b4d2002-03-07 21:50:41 +00005 * Version: 4.1
Brian Paulfbd8f211999-11-11 01:22:25 +00006 *
Brian Paul5104b4d2002-03-07 21:50:41 +00007 * Copyright (C) 1999-2002 Brian Paul All Rights Reserved.
Brian Paulfbd8f211999-11-11 01:22:25 +00008 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27
Brian Paul7e1161b1999-11-25 18:17:04 +000028/*
29 * This file manages the OpenGL API dispatch layer.
30 * The dispatch table (struct _glapi_table) is basically just a list
31 * of function pointers.
32 * There are functions to set/get the current dispatch table for the
33 * current thread and to manage registration/dispatch of dynamically
34 * added extension functions.
Brian Paul9f943992000-01-28 19:03:33 +000035 *
36 * It's intended that this file and the other glapi*.[ch] files are
37 * flexible enough to be reused in several places: XFree86, DRI-
38 * based libGL.so, and perhaps the SGI SI.
39 *
Brian Paul4e9676f2002-06-29 19:48:15 +000040 * NOTE: There are no dependencies on Mesa in this code.
Brian Paulab0c8862001-01-23 23:35:47 +000041 *
42 * Versions (API changes):
43 * 2000/02/23 - original version for Mesa 3.3 and XFree86 4.0
44 * 2001/01/16 - added dispatch override feature for Mesa 3.5
Brian Paul4e9676f2002-06-29 19:48:15 +000045 * 2002/06/28 - added _glapi_set_warning_func(), Mesa 4.1.
Brian Paul54f3aab2002-10-02 01:51:44 +000046 * 2002/10/01 - _glapi_get_proc_address() will now generate new entrypoints
47 * itself (using offset ~0). _glapi_add_entrypoint() can be
48 * called afterward and it'll fill in the correct dispatch
49 * offset. This allows DRI libGL to avoid probing for DRI
50 * drivers! No changes to the public glapi interface.
Brian Paul7e1161b1999-11-25 18:17:04 +000051 */
52
53
54
Brian Paul3c27be32000-02-10 21:27:48 +000055#include "glheader.h"
Brian Paulfbd8f211999-11-11 01:22:25 +000056#include "glapi.h"
Brian Paul0c239fc1999-12-16 12:38:11 +000057#include "glapioffsets.h"
58#include "glapitable.h"
Brian Paul9f943992000-01-28 19:03:33 +000059#include "glthread.h"
Brian Paulbb72d321999-12-16 17:31:59 +000060
Brian Paul3c257e12001-03-28 17:19:58 +000061/***** BEGIN NO-OP DISPATCH *****/
62
63static GLboolean WarnFlag = GL_FALSE;
Brian Paul4e9676f2002-06-29 19:48:15 +000064static _glapi_warning_func warning_func;
Brian Paul3c257e12001-03-28 17:19:58 +000065
Brian Paul4e9676f2002-06-29 19:48:15 +000066
67/*
68 * Enable/disable printing of warning messages.
69 */
Brian Paul3c257e12001-03-28 17:19:58 +000070void
71_glapi_noop_enable_warnings(GLboolean enable)
72{
73 WarnFlag = enable;
74}
75
Brian Paul4e9676f2002-06-29 19:48:15 +000076/*
77 * Register a callback function for reporting errors.
78 */
79void
80_glapi_set_warning_func( _glapi_warning_func func )
81{
82 warning_func = func;
83}
84
Brian Paul3c257e12001-03-28 17:19:58 +000085static GLboolean
86warn(void)
87{
Brian Paul4e9676f2002-06-29 19:48:15 +000088 if ((WarnFlag || getenv("MESA_DEBUG") || getenv("LIBGL_DEBUG"))
89 && warning_func) {
Brian Paul3c257e12001-03-28 17:19:58 +000090 return GL_TRUE;
Brian Paul4e9676f2002-06-29 19:48:15 +000091 }
92 else {
Brian Paul3c257e12001-03-28 17:19:58 +000093 return GL_FALSE;
Brian Paul4e9676f2002-06-29 19:48:15 +000094 }
Brian Paul3c257e12001-03-28 17:19:58 +000095}
96
97
98#define KEYWORD1 static
99#define KEYWORD2
100#define NAME(func) NoOp##func
101
Brian Paul4e9676f2002-06-29 19:48:15 +0000102#define F NULL
Brian Paul3c257e12001-03-28 17:19:58 +0000103
Brian Paul4e9676f2002-06-29 19:48:15 +0000104#define DISPATCH(func, args, msg) \
105 if (warn()) { \
106 warning_func(NULL, "GL User Error: called without context:"); \
107 warning_func msg; \
Brian Paul3c257e12001-03-28 17:19:58 +0000108 }
109
Brian Paul4e9676f2002-06-29 19:48:15 +0000110#define RETURN_DISPATCH(func, args, msg) \
111 if (warn()) { \
112 warning_func(NULL, "GL User Error: called without context:"); \
113 warning_func msg; \
114 } \
Brian Paul3c257e12001-03-28 17:19:58 +0000115 return 0
116
117#define DISPATCH_TABLE_NAME __glapi_noop_table
118#define UNUSED_TABLE_NAME __usused_noop_functions
119
120#define TABLE_ENTRY(name) (void *) NoOp##name
121
122static int NoOpUnused(void)
123{
124 if (warn()) {
Brian Paul4e9676f2002-06-29 19:48:15 +0000125 warning_func(NULL, "GL User Error: calling extension function without a current context\n");
Brian Paul3c257e12001-03-28 17:19:58 +0000126 }
127 return 0;
128}
129
130#include "glapitemp.h"
131
132/***** END NO-OP DISPATCH *****/
133
134
135
136/***** BEGIN THREAD-SAFE DISPATCH *****/
137/* if we support thread-safety, build a special dispatch table for use
138 * in thread-safety mode (ThreadSafe == GL_TRUE). Each entry in the
139 * dispatch table will call _glthread_GetTSD() to get the actual dispatch
140 * table bound to the current thread, then jump through that table.
141 */
142
143#if defined(THREADS)
144
145static GLboolean ThreadSafe = GL_FALSE; /* In thread-safe mode? */
146static _glthread_TSD DispatchTSD; /* Per-thread dispatch pointer */
147static _glthread_TSD RealDispatchTSD; /* only when using override */
148static _glthread_TSD ContextTSD; /* Per-thread context pointer */
149
150
151#define KEYWORD1 static
152#define KEYWORD2 GLAPIENTRY
153#define NAME(func) _ts_##func
154
155#define DISPATCH(FUNC, ARGS, MESSAGE) \
156 struct _glapi_table *dispatch; \
157 dispatch = (struct _glapi_table *) _glthread_GetTSD(&DispatchTSD); \
158 if (!dispatch) \
159 dispatch = (struct _glapi_table *) __glapi_noop_table; \
160 (dispatch->FUNC) ARGS
161
162#define RETURN_DISPATCH(FUNC, ARGS, MESSAGE) \
163 struct _glapi_table *dispatch; \
164 dispatch = (struct _glapi_table *) _glthread_GetTSD(&DispatchTSD); \
165 if (!dispatch) \
166 dispatch = (struct _glapi_table *) __glapi_noop_table; \
167 return (dispatch->FUNC) ARGS
168
169#define DISPATCH_TABLE_NAME __glapi_threadsafe_table
170#define UNUSED_TABLE_NAME __usused_threadsafe_functions
171
172#define TABLE_ENTRY(name) (void *) _ts_##name
173
174static int _ts_Unused(void)
175{
176 return 0;
177}
178
179#include "glapitemp.h"
180
181#endif
182
183/***** END THREAD-SAFE DISPATCH *****/
184
185
186
Brian Paul8ceb5c32000-02-24 22:14:04 +0000187struct _glapi_table *_glapi_Dispatch = (struct _glapi_table *) __glapi_noop_table;
Brian Paulab0c8862001-01-23 23:35:47 +0000188struct _glapi_table *_glapi_RealDispatch = (struct _glapi_table *) __glapi_noop_table;
Brian Paul0f710251999-12-15 15:02:30 +0000189
Brian Paul8f91fb61999-12-17 14:51:28 +0000190/* Used when thread safety disabled */
Brian Paulf9b97d92000-01-28 20:17:42 +0000191void *_glapi_Context = NULL;
Brian Paul8f91fb61999-12-17 14:51:28 +0000192
Brian Paul0f710251999-12-15 15:02:30 +0000193
Brian Paulab0c8862001-01-23 23:35:47 +0000194static GLboolean DispatchOverride = GL_FALSE;
195
196
Brian Paul54f3aab2002-10-02 01:51:44 +0000197
Brian Paul3c257e12001-03-28 17:19:58 +0000198/* strdup() is actually not a standard ANSI C or POSIX routine.
199 * Irix will not define it if ANSI mode is in effect.
200 */
201static char *
202str_dup(const char *str)
Randy Frankd7361e12000-03-27 21:13:58 +0000203{
Brian Paulfffb8092000-03-29 18:46:11 +0000204 char *copy;
205 copy = (char*) malloc(strlen(str) + 1);
206 if (!copy)
207 return NULL;
208 strcpy(copy, str);
209 return copy;
Randy Frankd7361e12000-03-27 21:13:58 +0000210}
211
Brian Paul7fb54ae1999-11-19 22:33:50 +0000212
Brian Paulbb72d321999-12-16 17:31:59 +0000213
214/*
215 * We should call this periodically from a function such as glXMakeCurrent
Brian Paul5104b4d2002-03-07 21:50:41 +0000216 * in order to test if multiple threads are being used.
Brian Paulbb72d321999-12-16 17:31:59 +0000217 */
218void
219_glapi_check_multithread(void)
220{
221#if defined(THREADS)
Brian Paul26e14d22000-01-05 04:36:17 +0000222 if (!ThreadSafe) {
Brian Paulbb72d321999-12-16 17:31:59 +0000223 static unsigned long knownID;
224 static GLboolean firstCall = GL_TRUE;
225 if (firstCall) {
226 knownID = _glthread_GetID();
227 firstCall = GL_FALSE;
228 }
229 else if (knownID != _glthread_GetID()) {
Brian Paul26e14d22000-01-05 04:36:17 +0000230 ThreadSafe = GL_TRUE;
Brian Paulbb72d321999-12-16 17:31:59 +0000231 }
232 }
Brian Paul26e14d22000-01-05 04:36:17 +0000233 if (ThreadSafe) {
Brian Paulbb72d321999-12-16 17:31:59 +0000234 /* make sure that this thread's dispatch pointer isn't null */
235 if (!_glapi_get_dispatch()) {
236 _glapi_set_dispatch(NULL);
237 }
238 }
239#endif
240}
241
242
243
244/*
Brian Paul8f91fb61999-12-17 14:51:28 +0000245 * Set the current context pointer for this thread.
246 * The context pointer is an opaque type which should be cast to
247 * void from the real context pointer type.
248 */
249void
Brian Paulf9b97d92000-01-28 20:17:42 +0000250_glapi_set_context(void *context)
Brian Paul8f91fb61999-12-17 14:51:28 +0000251{
252#if defined(THREADS)
Brian Paul3c27be32000-02-10 21:27:48 +0000253 _glthread_SetTSD(&ContextTSD, context);
Brian Paul26e14d22000-01-05 04:36:17 +0000254 if (ThreadSafe)
Brian Paulf9b97d92000-01-28 20:17:42 +0000255 _glapi_Context = NULL;
Brian Paul8f91fb61999-12-17 14:51:28 +0000256 else
Brian Paulf9b97d92000-01-28 20:17:42 +0000257 _glapi_Context = context;
Brian Paul8f91fb61999-12-17 14:51:28 +0000258#else
Brian Paulf9b97d92000-01-28 20:17:42 +0000259 _glapi_Context = context;
Brian Paul8f91fb61999-12-17 14:51:28 +0000260#endif
261}
262
263
264
265/*
266 * Get the current context pointer for this thread.
267 * The context pointer is an opaque type which should be cast from
268 * void to the real context pointer type.
269 */
270void *
Brian Paulf9b97d92000-01-28 20:17:42 +0000271_glapi_get_context(void)
Brian Paul8f91fb61999-12-17 14:51:28 +0000272{
273#if defined(THREADS)
Brian Paul26e14d22000-01-05 04:36:17 +0000274 if (ThreadSafe) {
Brian Paul8f91fb61999-12-17 14:51:28 +0000275 return _glthread_GetTSD(&ContextTSD);
276 }
277 else {
Brian Paulf9b97d92000-01-28 20:17:42 +0000278 return _glapi_Context;
Brian Paul8f91fb61999-12-17 14:51:28 +0000279 }
280#else
Brian Paulf9b97d92000-01-28 20:17:42 +0000281 return _glapi_Context;
Brian Paul8f91fb61999-12-17 14:51:28 +0000282#endif
283}
284
285
286
287/*
Brian Paul7fb54ae1999-11-19 22:33:50 +0000288 * Set the global or per-thread dispatch table pointer.
289 */
290void
291_glapi_set_dispatch(struct _glapi_table *dispatch)
292{
293 if (!dispatch) {
294 /* use the no-op functions */
Brian Paul8ceb5c32000-02-24 22:14:04 +0000295 dispatch = (struct _glapi_table *) __glapi_noop_table;
Brian Paul7fb54ae1999-11-19 22:33:50 +0000296 }
297#ifdef DEBUG
298 else {
299 _glapi_check_table(dispatch);
300 }
301#endif
302
303#if defined(THREADS)
Brian Paulab0c8862001-01-23 23:35:47 +0000304 if (DispatchOverride) {
305 _glthread_SetTSD(&RealDispatchTSD, (void *) dispatch);
Brian Paul3a71d052000-09-05 20:17:37 +0000306 if (ThreadSafe)
Brian Paul3c257e12001-03-28 17:19:58 +0000307 _glapi_RealDispatch = (struct _glapi_table*) __glapi_threadsafe_table;
Brian Paul3a71d052000-09-05 20:17:37 +0000308 else
Brian Paulab0c8862001-01-23 23:35:47 +0000309 _glapi_RealDispatch = dispatch;
Brian Paul3b18a362000-09-26 15:27:20 +0000310 }
311 else {
Brian Paulab0c8862001-01-23 23:35:47 +0000312 /* normal operation */
Brian Paul3b18a362000-09-26 15:27:20 +0000313 _glthread_SetTSD(&DispatchTSD, (void *) dispatch);
Brian Paul3a71d052000-09-05 20:17:37 +0000314 if (ThreadSafe)
Brian Paul3c257e12001-03-28 17:19:58 +0000315 _glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table;
Brian Paul3a71d052000-09-05 20:17:37 +0000316 else
317 _glapi_Dispatch = dispatch;
318 }
Brian Paul3a71d052000-09-05 20:17:37 +0000319#else /*THREADS*/
Brian Paulab0c8862001-01-23 23:35:47 +0000320 if (DispatchOverride) {
321 _glapi_RealDispatch = dispatch;
322 }
323 else {
324 _glapi_Dispatch = dispatch;
325 }
Brian Paul3a71d052000-09-05 20:17:37 +0000326#endif /*THREADS*/
Brian Paul7fb54ae1999-11-19 22:33:50 +0000327}
328
329
Brian Paulbb72d321999-12-16 17:31:59 +0000330
Brian Paul7fb54ae1999-11-19 22:33:50 +0000331/*
Brian Paulbb72d321999-12-16 17:31:59 +0000332 * Return pointer to current dispatch table for calling thread.
Brian Paul7fb54ae1999-11-19 22:33:50 +0000333 */
334struct _glapi_table *
335_glapi_get_dispatch(void)
336{
337#if defined(THREADS)
Brian Paul26e14d22000-01-05 04:36:17 +0000338 if (ThreadSafe) {
Brian Paulab0c8862001-01-23 23:35:47 +0000339 if (DispatchOverride) {
340 return (struct _glapi_table *) _glthread_GetTSD(&RealDispatchTSD);
341 }
342 else {
343 return (struct _glapi_table *) _glthread_GetTSD(&DispatchTSD);
344 }
Brian Paulbb72d321999-12-16 17:31:59 +0000345 }
Brian Paul590d3471999-12-17 12:20:23 +0000346 else {
Brian Paulab0c8862001-01-23 23:35:47 +0000347 if (DispatchOverride) {
348 assert(_glapi_RealDispatch);
349 return _glapi_RealDispatch;
350 }
351 else {
352 assert(_glapi_Dispatch);
353 return _glapi_Dispatch;
354 }
Brian Paul590d3471999-12-17 12:20:23 +0000355 }
Brian Paul7fb54ae1999-11-19 22:33:50 +0000356#else
Brian Paulc2319b42000-01-17 19:28:31 +0000357 return _glapi_Dispatch;
Brian Paul7fb54ae1999-11-19 22:33:50 +0000358#endif
359}
360
361
Brian Paulab0c8862001-01-23 23:35:47 +0000362/*
363 * Notes on dispatch overrride:
364 *
365 * Dispatch override allows an external agent to hook into the GL dispatch
366 * mechanism before execution goes into the core rendering library. For
367 * example, a trace mechanism would insert itself as an overrider, print
368 * logging info for each GL function, then dispatch to the real GL function.
369 *
370 * libGLS (GL Stream library) is another agent that might use override.
371 *
372 * We don't allow more than one layer of overriding at this time.
373 * In the future we may allow nested/layered override. In that case
374 * _glapi_begin_dispatch_override() will return an override layer,
375 * _glapi_end_dispatch_override(layer) will remove an override layer
376 * and _glapi_get_override_dispatch(layer) will return the dispatch
377 * table for a given override layer. layer = 0 will be the "real"
378 * dispatch table.
379 */
380
381/*
382 * Return: dispatch override layer number.
383 */
384int
385_glapi_begin_dispatch_override(struct _glapi_table *override)
Brian Paul3a71d052000-09-05 20:17:37 +0000386{
Brian Paulab0c8862001-01-23 23:35:47 +0000387 struct _glapi_table *real = _glapi_get_dispatch();
388
389 assert(!DispatchOverride); /* can't nest at this time */
390 DispatchOverride = GL_TRUE;
391
392 _glapi_set_dispatch(real);
Brian Paul3a71d052000-09-05 20:17:37 +0000393
394#if defined(THREADS)
Brian Paulab0c8862001-01-23 23:35:47 +0000395 _glthread_SetTSD(&DispatchTSD, (void *) override);
396 if (ThreadSafe)
Brian Paul3c257e12001-03-28 17:19:58 +0000397 _glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table;
Brian Paulab0c8862001-01-23 23:35:47 +0000398 else
399 _glapi_Dispatch = override;
400#else
401 _glapi_Dispatch = override;
402#endif
403 return 1;
404}
405
406
407void
408_glapi_end_dispatch_override(int layer)
409{
410 struct _glapi_table *real = _glapi_get_dispatch();
411 (void) layer;
412 DispatchOverride = GL_FALSE;
413 _glapi_set_dispatch(real);
414 /* the rest of this isn't needed, just play it safe */
415#if defined(THREADS)
416 _glthread_SetTSD(&RealDispatchTSD, NULL);
417#endif
418 _glapi_RealDispatch = NULL;
419}
420
421
422struct _glapi_table *
423_glapi_get_override_dispatch(int layer)
424{
425 if (layer == 0) {
426 return _glapi_get_dispatch();
Brian Paul3a71d052000-09-05 20:17:37 +0000427 }
428 else {
Brian Paulab0c8862001-01-23 23:35:47 +0000429 if (DispatchOverride) {
430#if defined(THREADS)
431 return (struct _glapi_table *) _glthread_GetTSD(&DispatchTSD);
Brian Paul3a71d052000-09-05 20:17:37 +0000432#else
Brian Paulab0c8862001-01-23 23:35:47 +0000433 return _glapi_Dispatch;
Brian Paul3a71d052000-09-05 20:17:37 +0000434#endif
Brian Paulab0c8862001-01-23 23:35:47 +0000435 }
436 else {
437 return NULL;
438 }
439 }
Brian Paul3a71d052000-09-05 20:17:37 +0000440}
Brian Paulab0c8862001-01-23 23:35:47 +0000441
Brian Paul3a71d052000-09-05 20:17:37 +0000442
Brian Paul959f8022000-03-19 01:10:11 +0000443struct name_address_offset {
444 const char *Name;
445 GLvoid *Address;
446 GLuint Offset;
447};
448
Brian Paul0c239fc1999-12-16 12:38:11 +0000449
Brian Paul54f3aab2002-10-02 01:51:44 +0000450/* The code in this file is auto-generated with Python */
Brian Paulb5fd8862001-11-18 22:48:11 +0000451#include "glprocs.h"
Brian Paul7fb54ae1999-11-19 22:33:50 +0000452
Brian Paul959f8022000-03-19 01:10:11 +0000453
454
455/*
456 * Return dispatch table offset of the named static (built-in) function.
457 * Return -1 if function not found.
458 */
459static GLint
460get_static_proc_offset(const char *funcName)
461{
462 GLuint i;
463 for (i = 0; static_functions[i].Name; i++) {
464 if (strcmp(static_functions[i].Name, funcName) == 0) {
Brian Paul94666c42001-09-14 02:48:53 +0000465 return static_functions[i].Offset;
Brian Paul959f8022000-03-19 01:10:11 +0000466 }
467 }
468 return -1;
469}
470
471
472/*
473 * Return dispatch function address the named static (built-in) function.
474 * Return NULL if function not found.
475 */
476static GLvoid *
477get_static_proc_address(const char *funcName)
478{
Brian Paul9c7ca852000-10-19 20:13:12 +0000479 GLint i;
480 for (i = 0; static_functions[i].Name; i++) {
481 if (strcmp(static_functions[i].Name, funcName) == 0) {
482 return static_functions[i].Address;
483 }
484 }
485 return NULL;
Brian Paul959f8022000-03-19 01:10:11 +0000486}
487
488
489
490/**********************************************************************
491 * Extension function management.
492 */
493
Brian Paul54f3aab2002-10-02 01:51:44 +0000494/*
495 * Number of extension functions which we can dynamically add at runtime.
496 */
497#define MAX_EXTENSION_FUNCS 300
Brian Paul959f8022000-03-19 01:10:11 +0000498
Brian Paul54f3aab2002-10-02 01:51:44 +0000499
500/*
501 * The disptach table size (number of entries) is the sizeof the
502 * _glapi_table struct plus the number of dynamic entries we can add.
503 * The extra slots can be filled in by DRI drivers that register new extension
504 * functions.
505 */
506#define DISPATCH_TABLE_SIZE (sizeof(struct _glapi_table) / sizeof(void *) + MAX_EXTENSION_FUNCS)
507
Brian Paul959f8022000-03-19 01:10:11 +0000508
509static struct name_address_offset ExtEntryTable[MAX_EXTENSION_FUNCS];
510static GLuint NumExtEntryPoints = 0;
511
davem694a497e62001-06-06 22:55:28 +0000512#ifdef USE_SPARC_ASM
513extern void __glapi_sparc_icache_flush(unsigned int *);
514#endif
Brian Paul959f8022000-03-19 01:10:11 +0000515
516/*
517 * Generate a dispatch function (entrypoint) which jumps through
518 * the given slot number (offset) in the current dispatch table.
519 * We need assembly language in order to accomplish this.
520 */
521static void *
522generate_entrypoint(GLuint functionOffset)
523{
524#if defined(USE_X86_ASM)
525 /*
526 * This x86 code contributed by Josh Vanderhoof.
527 *
528 * 0: a1 10 32 54 76 movl __glapi_Dispatch,%eax
529 * 00 01 02 03 04
530 * 5: 85 c0 testl %eax,%eax
531 * 05 06
532 * 7: 74 06 je f <entrypoint+0xf>
533 * 07 08
534 * 9: ff a0 10 32 54 76 jmp *0x76543210(%eax)
535 * 09 0a 0b 0c 0d 0e
536 * f: e8 fc ff ff ff call __glapi_get_dispatch
537 * 0f 10 11 12 13
538 * 14: ff a0 10 32 54 76 jmp *0x76543210(%eax)
539 * 14 15 16 17 18 19
540 */
Brian Paul54f3aab2002-10-02 01:51:44 +0000541 static const unsigned char insn_template[] = {
Brian Paul959f8022000-03-19 01:10:11 +0000542 0xa1, 0x00, 0x00, 0x00, 0x00,
543 0x85, 0xc0,
544 0x74, 0x06,
545 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00,
546 0xe8, 0x00, 0x00, 0x00, 0x00,
547 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00
548 };
Brian Paul54f3aab2002-10-02 01:51:44 +0000549 unsigned char *code = malloc(sizeof(insn_template));
Brian Paul959f8022000-03-19 01:10:11 +0000550 unsigned int next_insn;
551 if (code) {
Brian Paul54f3aab2002-10-02 01:51:44 +0000552 memcpy(code, insn_template, sizeof(insn_template));
Brian Paul959f8022000-03-19 01:10:11 +0000553
554 *(unsigned int *)(code + 0x01) = (unsigned int)&_glapi_Dispatch;
555 *(unsigned int *)(code + 0x0b) = (unsigned int)functionOffset * 4;
556 next_insn = (unsigned int)(code + 0x14);
557 *(unsigned int *)(code + 0x10) = (unsigned int)_glapi_get_dispatch - next_insn;
558 *(unsigned int *)(code + 0x16) = (unsigned int)functionOffset * 4;
559 }
560 return code;
davem69775355a2001-06-05 23:54:00 +0000561#elif defined(USE_SPARC_ASM)
562
563#ifdef __sparc_v9__
564 static const unsigned int insn_template[] = {
565 0x05000000, /* sethi %uhi(_glapi_Dispatch), %g2 */
566 0x03000000, /* sethi %hi(_glapi_Dispatch), %g1 */
567 0x8410a000, /* or %g2, %ulo(_glapi_Dispatch), %g2 */
568 0x82106000, /* or %g1, %lo(_glapi_Dispatch), %g1 */
569 0x8528b020, /* sllx %g2, 32, %g2 */
570 0xc2584002, /* ldx [%g1 + %g2], %g1 */
571 0x05000000, /* sethi %hi(8 * glapioffset), %g2 */
572 0x8410a000, /* or %g2, %lo(8 * glapioffset), %g2 */
573 0xc6584002, /* ldx [%g1 + %g2], %g3 */
574 0x81c0c000, /* jmpl %g3, %g0 */
575 0x01000000 /* nop */
576 };
577#else
578 static const unsigned int insn_template[] = {
579 0x03000000, /* sethi %hi(_glapi_Dispatch), %g1 */
580 0xc2006000, /* ld [%g1 + %lo(_glapi_Dispatch)], %g1 */
581 0xc6006000, /* ld [%g1 + %lo(4*glapioffset)], %g3 */
582 0x81c0c000, /* jmpl %g3, %g0 */
583 0x01000000 /* nop */
584 };
585#endif
586 unsigned int *code = malloc(sizeof(insn_template));
587 unsigned long glapi_addr = (unsigned long) &_glapi_Dispatch;
588 if (code) {
589 memcpy(code, insn_template, sizeof(insn_template));
590
591#ifdef __sparc_v9__
592 code[0] |= (glapi_addr >> (32 + 10));
593 code[1] |= ((glapi_addr & 0xffffffff) >> 10);
davem694a497e62001-06-06 22:55:28 +0000594 __glapi_sparc_icache_flush(&code[0]);
davem69775355a2001-06-05 23:54:00 +0000595 code[2] |= ((glapi_addr >> 32) & ((1 << 10) - 1));
596 code[3] |= (glapi_addr & ((1 << 10) - 1));
davem694a497e62001-06-06 22:55:28 +0000597 __glapi_sparc_icache_flush(&code[2]);
davem69775355a2001-06-05 23:54:00 +0000598 code[6] |= ((functionOffset * 8) >> 10);
599 code[7] |= ((functionOffset * 8) & ((1 << 10) - 1));
davem694a497e62001-06-06 22:55:28 +0000600 __glapi_sparc_icache_flush(&code[6]);
davem69775355a2001-06-05 23:54:00 +0000601#else
602 code[0] |= (glapi_addr >> 10);
603 code[1] |= (glapi_addr & ((1 << 10) - 1));
davem694a497e62001-06-06 22:55:28 +0000604 __glapi_sparc_icache_flush(&code[0]);
davem69775355a2001-06-05 23:54:00 +0000605 code[2] |= (functionOffset * 4);
davem694a497e62001-06-06 22:55:28 +0000606 __glapi_sparc_icache_flush(&code[2]);
davem69775355a2001-06-05 23:54:00 +0000607#endif
608 }
609 return code;
Brian Paul959f8022000-03-19 01:10:11 +0000610#else
611 return NULL;
Brian Paul54f3aab2002-10-02 01:51:44 +0000612#endif /* USE_*_ASM */
Brian Paul959f8022000-03-19 01:10:11 +0000613}
614
615
Brian Paul54f3aab2002-10-02 01:51:44 +0000616/*
617 * This function inserts a new dispatch offset into the assembly language
618 * stub that was generated with the preceeding function.
619 */
620static void
621fill_in_entrypoint_offset(void *entrypoint, GLuint offset)
622{
623#if defined(USE_X86_ASM)
624
625 unsigned char *code = (unsigned char *) entrypoint;
626 *(unsigned int *)(code + 0x0b) = offset * 4;
627 *(unsigned int *)(code + 0x16) = offset * 4;
628
629#elif defined(USE_SPARC_ASM)
630
631 /* XXX this hasn't been tested! */
632 unsigned int *code = (unsigned int *) entrypoint;
633#ifdef __sparc_v9__
634 code[6] = 0x05000000; /* sethi %hi(8 * glapioffset), %g2 */
635 code[7] = 0x8410a000; /* or %g2, %lo(8 * glapioffset), %g2 */
636 code[6] |= ((offset * 8) >> 10);
637 code[7] |= ((offset * 8) & ((1 << 10) - 1));
638 __glapi_sparc_icache_flush(&code[6]);
639#else /* __sparc_v9__ */
640 code[2] = 0xc6006000; /* ld [%g1 + %lo(4*glapioffset)], %g3 */
641 code[2] |= (functionOffset * 4);
642 __glapi_sparc_icache_flush(&code[2]);
643#endif /* __sparc_v9__ */
644
645#endif /* USE_*_ASM */
646}
647
Brian Paul959f8022000-03-19 01:10:11 +0000648
649/*
650 * Add a new extension function entrypoint.
651 * Return: GL_TRUE = success or GL_FALSE = failure
652 */
653GLboolean
654_glapi_add_entrypoint(const char *funcName, GLuint offset)
655{
Brian Paul959f8022000-03-19 01:10:11 +0000656 /* first check if the named function is already statically present */
657 {
658 GLint index = get_static_proc_offset(funcName);
659 if (index >= 0) {
Brian Paulb51b0a82001-03-07 05:06:11 +0000660 return (GLboolean) ((GLuint) index == offset); /* bad offset! */
Brian Paul959f8022000-03-19 01:10:11 +0000661 }
662 }
663
Brian Paul54f3aab2002-10-02 01:51:44 +0000664 /* make sure this offset/name pair is legal (this isn't really needed) */
Brian Paul959f8022000-03-19 01:10:11 +0000665 {
Brian Paul959f8022000-03-19 01:10:11 +0000666 const char *name = _glapi_get_proc_name(offset);
667 if (name && strcmp(name, funcName) != 0)
668 return GL_FALSE; /* bad name! */
669 }
670
Brian Paul54f3aab2002-10-02 01:51:44 +0000671 /* See if this function has already been dynamically added */
Brian Paul959f8022000-03-19 01:10:11 +0000672 {
Brian Paul959f8022000-03-19 01:10:11 +0000673 GLuint i;
674 for (i = 0; i < NumExtEntryPoints; i++) {
675 if (strcmp(ExtEntryTable[i].Name, funcName) == 0) {
Brian Paul54f3aab2002-10-02 01:51:44 +0000676 /* function already registered */
Brian Paul959f8022000-03-19 01:10:11 +0000677 if (ExtEntryTable[i].Offset == offset) {
678 return GL_TRUE; /* offsets match */
679 }
Brian Paul54f3aab2002-10-02 01:51:44 +0000680 else if (ExtEntryTable[i].Offset == ~0
681 && offset < DISPATCH_TABLE_SIZE) {
682 /* need to patch-up the dispatch code */
683 if (offset != ~0) {
684 fill_in_entrypoint_offset(ExtEntryTable[i].Address, offset);
685 ExtEntryTable[i].Offset = offset;
686 }
687 return GL_TRUE;
688 }
Brian Paul959f8022000-03-19 01:10:11 +0000689 else {
690 return GL_FALSE; /* bad offset! */
691 }
692 }
693 }
694
Brian Paul54f3aab2002-10-02 01:51:44 +0000695 /* This is a new function, try to add it. */
696 if (NumExtEntryPoints >= MAX_EXTENSION_FUNCS ||
697 offset >= DISPATCH_TABLE_SIZE) {
698 /* No space left */
Brian Paul959f8022000-03-19 01:10:11 +0000699 return GL_FALSE;
700 }
701 else {
702 void *entrypoint = generate_entrypoint(offset);
703 if (!entrypoint)
Brian Paul54f3aab2002-10-02 01:51:44 +0000704 return GL_FALSE; /* couldn't generate assembly */
Brian Paul959f8022000-03-19 01:10:11 +0000705
Brian Paul54f3aab2002-10-02 01:51:44 +0000706 /* OK! */
Brian Paulfffb8092000-03-29 18:46:11 +0000707 ExtEntryTable[NumExtEntryPoints].Name = str_dup(funcName);
Brian Paul959f8022000-03-19 01:10:11 +0000708 ExtEntryTable[NumExtEntryPoints].Offset = offset;
709 ExtEntryTable[NumExtEntryPoints].Address = entrypoint;
710 NumExtEntryPoints++;
711
Brian Paul959f8022000-03-19 01:10:11 +0000712 return GL_TRUE; /* success */
713 }
714 }
715
716 /* should never get here, but play it safe */
717 return GL_FALSE;
718}
719
720
Brian Paul959f8022000-03-19 01:10:11 +0000721/*
722 * Return offset of entrypoint for named function within dispatch table.
723 */
724GLint
725_glapi_get_proc_offset(const char *funcName)
726{
727 /* search extension functions first */
Brian Paulb51b0a82001-03-07 05:06:11 +0000728 GLuint i;
Brian Paul959f8022000-03-19 01:10:11 +0000729 for (i = 0; i < NumExtEntryPoints; i++) {
730 if (strcmp(ExtEntryTable[i].Name, funcName) == 0) {
731 return ExtEntryTable[i].Offset;
732 }
733 }
734
735 /* search static functions */
736 return get_static_proc_offset(funcName);
737}
738
739
740
741/*
742 * Return entrypoint for named function.
743 */
744const GLvoid *
745_glapi_get_proc_address(const char *funcName)
746{
747 /* search extension functions first */
Brian Paulb51b0a82001-03-07 05:06:11 +0000748 GLuint i;
Brian Paul959f8022000-03-19 01:10:11 +0000749 for (i = 0; i < NumExtEntryPoints; i++) {
750 if (strcmp(ExtEntryTable[i].Name, funcName) == 0) {
751 return ExtEntryTable[i].Address;
752 }
753 }
754
755 /* search static functions */
Brian Paul54f3aab2002-10-02 01:51:44 +0000756 {
757 const GLvoid *func = get_static_proc_address(funcName);
758 if (func)
759 return func;
760 }
Brian Paul959f8022000-03-19 01:10:11 +0000761
Brian Paul54f3aab2002-10-02 01:51:44 +0000762 /* generate new entrypoint - use a temporary dispatch offset of
763 * ~0 (i.e. -1). Later, when the driver calls _glapi_add_entrypoint()
764 * we'll put in the proper offset. If that never happens, and the
765 * user calls this function, he'll segfault. That's what you get
766 * when you try calling a GL function that doesn't really exist.
767 */
768 if (NumExtEntryPoints < MAX_EXTENSION_FUNCS) {
769 GLvoid *entrypoint = generate_entrypoint(~0);
770 if (!entrypoint)
771 return GL_FALSE;
772
773 ExtEntryTable[NumExtEntryPoints].Name = str_dup(funcName);
774 ExtEntryTable[NumExtEntryPoints].Offset = ~0;
775 ExtEntryTable[NumExtEntryPoints].Address = entrypoint;
776 NumExtEntryPoints++;
777
778 return entrypoint;
779 }
780 else {
781 /* no space for new functions! */
782 return NULL;
783 }
784}
Brian Paul959f8022000-03-19 01:10:11 +0000785
786
787
788/*
789 * Return the name of the function at the given dispatch offset.
790 * This is only intended for debugging.
791 */
792const char *
793_glapi_get_proc_name(GLuint offset)
794{
795 const GLuint n = sizeof(static_functions) / sizeof(struct name_address_offset);
796 GLuint i;
Brian Paul54f3aab2002-10-02 01:51:44 +0000797
798 /* search built-in functions */
Brian Paul959f8022000-03-19 01:10:11 +0000799 for (i = 0; i < n; i++) {
800 if (static_functions[i].Offset == offset)
801 return static_functions[i].Name;
802 }
803
804 /* search added extension functions */
805 for (i = 0; i < NumExtEntryPoints; i++) {
806 if (ExtEntryTable[i].Offset == offset) {
807 return ExtEntryTable[i].Name;
808 }
809 }
810 return NULL;
811}
812
813
814
815/*
Brian Paul54f3aab2002-10-02 01:51:44 +0000816 * Return size of dispatch table struct as number of functions (or
817 * slots).
818 */
819GLuint
820_glapi_get_dispatch_table_size(void)
821{
822 return DISPATCH_TABLE_SIZE;
823}
824
825
826
827/*
828 * Get API dispatcher version string.
829 */
830const char *
831_glapi_get_version(void)
832{
833 return "20021001"; /* YYYYMMDD */
834}
835
836
837
838/*
Brian Paul959f8022000-03-19 01:10:11 +0000839 * Make sure there are no NULL pointers in the given dispatch table.
Brian Paul5104b4d2002-03-07 21:50:41 +0000840 * Intended for debugging purposes.
Brian Paul959f8022000-03-19 01:10:11 +0000841 */
842void
843_glapi_check_table(const struct _glapi_table *table)
844{
Brian Paul5104b4d2002-03-07 21:50:41 +0000845#ifdef DEBUG
Brian Paul959f8022000-03-19 01:10:11 +0000846 const GLuint entries = _glapi_get_dispatch_table_size();
847 const void **tab = (const void **) table;
848 GLuint i;
849 for (i = 1; i < entries; i++) {
850 assert(tab[i]);
851 }
852
Brian Paul959f8022000-03-19 01:10:11 +0000853 /* Do some spot checks to be sure that the dispatch table
854 * slots are assigned correctly.
855 */
856 {
857 GLuint BeginOffset = _glapi_get_proc_offset("glBegin");
858 char *BeginFunc = (char*) &table->Begin;
859 GLuint offset = (BeginFunc - (char *) table) / sizeof(void *);
860 assert(BeginOffset == _gloffset_Begin);
861 assert(BeginOffset == offset);
862 }
863 {
864 GLuint viewportOffset = _glapi_get_proc_offset("glViewport");
865 char *viewportFunc = (char*) &table->Viewport;
866 GLuint offset = (viewportFunc - (char *) table) / sizeof(void *);
867 assert(viewportOffset == _gloffset_Viewport);
868 assert(viewportOffset == offset);
869 }
870 {
871 GLuint VertexPointerOffset = _glapi_get_proc_offset("glVertexPointer");
872 char *VertexPointerFunc = (char*) &table->VertexPointer;
873 GLuint offset = (VertexPointerFunc - (char *) table) / sizeof(void *);
874 assert(VertexPointerOffset == _gloffset_VertexPointer);
875 assert(VertexPointerOffset == offset);
876 }
877 {
878 GLuint ResetMinMaxOffset = _glapi_get_proc_offset("glResetMinmax");
879 char *ResetMinMaxFunc = (char*) &table->ResetMinmax;
880 GLuint offset = (ResetMinMaxFunc - (char *) table) / sizeof(void *);
881 assert(ResetMinMaxOffset == _gloffset_ResetMinmax);
882 assert(ResetMinMaxOffset == offset);
883 }
884 {
885 GLuint blendColorOffset = _glapi_get_proc_offset("glBlendColor");
886 char *blendColorFunc = (char*) &table->BlendColor;
887 GLuint offset = (blendColorFunc - (char *) table) / sizeof(void *);
888 assert(blendColorOffset == _gloffset_BlendColor);
889 assert(blendColorOffset == offset);
890 }
891 {
892 GLuint istextureOffset = _glapi_get_proc_offset("glIsTextureEXT");
893 char *istextureFunc = (char*) &table->IsTextureEXT;
894 GLuint offset = (istextureFunc - (char *) table) / sizeof(void *);
895 assert(istextureOffset == _gloffset_IsTextureEXT);
896 assert(istextureOffset == offset);
897 }
Brian Paula14cbff2000-10-27 18:31:21 +0000898 {
899 GLuint secondaryColor3fOffset = _glapi_get_proc_offset("glSecondaryColor3fEXT");
900 char *secondaryColor3fFunc = (char*) &table->SecondaryColor3fEXT;
901 GLuint offset = (secondaryColor3fFunc - (char *) table) / sizeof(void *);
902 assert(secondaryColor3fOffset == _gloffset_SecondaryColor3fEXT);
903 assert(secondaryColor3fOffset == offset);
904 assert(_glapi_get_proc_address("glSecondaryColor3fEXT") == (void *) &glSecondaryColor3fEXT);
905 }
Brian Paul91d6f122002-05-29 15:23:16 +0000906 {
907 GLuint pointParameterivOffset = _glapi_get_proc_offset("glPointParameterivNV");
908 char *pointParameterivFunc = (char*) &table->PointParameterivNV;
909 GLuint offset = (pointParameterivFunc - (char *) table) / sizeof(void *);
910 assert(pointParameterivOffset == _gloffset_PointParameterivNV);
911 assert(pointParameterivOffset == offset);
912 assert(_glapi_get_proc_address("glPointParameterivNV") == (void *) &glPointParameterivNV);
913 }
Brian Paul54f3aab2002-10-02 01:51:44 +0000914 {
915 GLuint setFenceOffset = _glapi_get_proc_offset("glSetFenceNV");
916 char *setFenceFunc = (char*) &table->SetFenceNV;
917 GLuint offset = (setFenceFunc - (char *) table) / sizeof(void *);
918 assert(setFenceOffset == _gloffset_SetFenceNV);
919 assert(setFenceOffset == offset);
920 assert(_glapi_get_proc_address("glSetFenceNV") == (void *) &glSetFenceNV);
921 }
Brian Paul959f8022000-03-19 01:10:11 +0000922#endif
923}