1/* 2 * GLX implementation that uses Apple's OpenGL.framework 3 * (Indirect rendering path -- it's also used for some direct mode code too) 4 * 5 * Copyright (c) 2007-2011 Apple Inc. 6 * Copyright (c) 2004 Torrey T. Lyons. All Rights Reserved. 7 * Copyright (c) 2002 Greg Parker. All Rights Reserved. 8 * 9 * Portions of this file are copied from Mesa's xf86glx.c, 10 * which contains the following copyright: 11 * 12 * Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. 13 * All Rights Reserved. 14 * 15 * Permission is hereby granted, free of charge, to any person obtaining a 16 * copy of this software and associated documentation files (the "Software"), 17 * to deal in the Software without restriction, including without limitation 18 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 * and/or sell copies of the Software, and to permit persons to whom the 20 * Software is furnished to do so, subject to the following conditions: 21 * 22 * The above copyright notice and this permission notice shall be included in 23 * all copies or substantial portions of the Software. 24 * 25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 28 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 29 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 30 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 31 * DEALINGS IN THE SOFTWARE. 32 */ 33 34#ifdef HAVE_DIX_CONFIG_H 35#include <dix-config.h> 36#endif 37 38#include <AvailabilityMacros.h> 39 40#include <dlfcn.h> 41 42#include <OpenGL/OpenGL.h> 43#include <OpenGL/gl.h> /* Just to prevent glxserver.h from loading mesa's and colliding with OpenGL.h */ 44 45#include <X11/Xproto.h> 46#include <GL/glxproto.h> 47 48#include <glxserver.h> 49#include <glxutil.h> 50 51typedef unsigned long long GLuint64EXT; 52typedef long long GLint64EXT; 53#include <dispatch.h> 54#include <glapi.h> 55 56#include "x-hash.h" 57 58#include "visualConfigs.h" 59#include "dri.h" 60 61// Write debugging output, or not 62#ifdef GLAQUA_DEBUG 63#define GLAQUA_DEBUG_MSG ErrorF 64#else 65#define GLAQUA_DEBUG_MSG(a, ...) 66#endif 67 68__GLXprovider * GlxGetDRISWrastProvider (void); 69 70static void setup_dispatch_table(void); 71GLuint __glFloorLog2(GLuint val); 72void warn_func(void * p1, char *format, ...); 73 74// some prototypes 75static __GLXscreen * __glXAquaScreenProbe(ScreenPtr pScreen); 76static __GLXdrawable * __glXAquaScreenCreateDrawable(ClientPtr client, __GLXscreen *screen, DrawablePtr pDraw, XID drawId, int type, XID glxDrawId, __GLXconfig *conf); 77 78static void __glXAquaContextDestroy(__GLXcontext *baseContext); 79static int __glXAquaContextMakeCurrent(__GLXcontext *baseContext); 80static int __glXAquaContextLoseCurrent(__GLXcontext *baseContext); 81static int __glXAquaContextForceCurrent(__GLXcontext *baseContext); 82static int __glXAquaContextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc, unsigned long mask); 83 84static CGLPixelFormatObj makeFormat(__GLXconfig *conf); 85 86__GLXprovider __glXDRISWRastProvider = { 87 __glXAquaScreenProbe, 88 "Core OpenGL", 89 NULL 90}; 91 92typedef struct __GLXAquaScreen __GLXAquaScreen; 93typedef struct __GLXAquaContext __GLXAquaContext; 94typedef struct __GLXAquaDrawable __GLXAquaDrawable; 95 96/* 97 * The following structs must keep the base as the first member. 98 * It's used to treat the start of the struct as a different struct 99 * in GLX. 100 * 101 * Note: these structs should be initialized with xcalloc or memset 102 * prior to usage, and some of them require initializing 103 * the base with function pointers. 104 */ 105struct __GLXAquaScreen { 106 __GLXscreen base; 107 int index; 108 int num_vis; 109}; 110 111struct __GLXAquaContext { 112 __GLXcontext base; 113 CGLContextObj ctx; 114 CGLPixelFormatObj pixelFormat; 115 xp_surface_id sid; 116 unsigned isAttached :1; 117}; 118 119struct __GLXAquaDrawable { 120 __GLXdrawable base; 121 DrawablePtr pDraw; 122 xp_surface_id sid; 123 __GLXAquaContext *context; 124}; 125 126 127static __GLXcontext * 128__glXAquaScreenCreateContext(__GLXscreen *screen, 129 __GLXconfig *conf, 130 __GLXcontext *baseShareContext) 131{ 132 __GLXAquaContext *context; 133 __GLXAquaContext *shareContext = (__GLXAquaContext *) baseShareContext; 134 CGLError gl_err; 135 136 GLAQUA_DEBUG_MSG("glXAquaScreenCreateContext\n"); 137 138 context = calloc(1, sizeof (__GLXAquaContext)); 139 140 if (context == NULL) 141 return NULL; 142 143 memset(context, 0, sizeof *context); 144 145 context->base.pGlxScreen = screen; 146 147 context->base.destroy = __glXAquaContextDestroy; 148 context->base.makeCurrent = __glXAquaContextMakeCurrent; 149 context->base.loseCurrent = __glXAquaContextLoseCurrent; 150 context->base.copy = __glXAquaContextCopy; 151 context->base.forceCurrent = __glXAquaContextForceCurrent; 152 /*FIXME verify that the context->base is fully initialized. */ 153 154 context->pixelFormat = makeFormat(conf); 155 156 if (!context->pixelFormat) { 157 free(context); 158 return NULL; 159 } 160 161 context->ctx = NULL; 162 gl_err = CGLCreateContext(context->pixelFormat, 163 shareContext ? shareContext->ctx : NULL, 164 &context->ctx); 165 166 if (gl_err != 0) { 167 ErrorF("CGLCreateContext error: %s\n", CGLErrorString(gl_err)); 168 CGLDestroyPixelFormat(context->pixelFormat); 169 free(context); 170 return NULL; 171 } 172 173 setup_dispatch_table(); 174 GLAQUA_DEBUG_MSG("glAquaCreateContext done\n"); 175 176 return &context->base; 177} 178 179/* maps from surface id -> list of __GLcontext */ 180static x_hash_table *surface_hash; 181 182static void __glXAquaContextDestroy(__GLXcontext *baseContext) { 183 x_list *lst; 184 185 __GLXAquaContext *context = (__GLXAquaContext *) baseContext; 186 187 GLAQUA_DEBUG_MSG("glAquaContextDestroy (ctx %p)\n", baseContext); 188 if (context != NULL) { 189 if (context->sid != 0 && surface_hash != NULL) { 190 lst = x_hash_table_lookup(surface_hash, x_cvt_uint_to_vptr(context->sid), NULL); 191 lst = x_list_remove(lst, context); 192 x_hash_table_insert(surface_hash, x_cvt_uint_to_vptr(context->sid), lst); 193 } 194 195 if (context->ctx != NULL) 196 CGLDestroyContext(context->ctx); 197 198 if (context->pixelFormat != NULL) 199 CGLDestroyPixelFormat(context->pixelFormat); 200 201 free(context); 202 } 203} 204 205static int __glXAquaContextLoseCurrent(__GLXcontext *baseContext) { 206 CGLError gl_err; 207 208 GLAQUA_DEBUG_MSG("glAquaLoseCurrent (ctx 0x%p)\n", baseContext); 209 210 gl_err = CGLSetCurrentContext(NULL); 211 if (gl_err != 0) 212 ErrorF("CGLSetCurrentContext error: %s\n", CGLErrorString(gl_err)); 213 214 /* 215 * There should be no need to set __glXLastContext to NULL here, because 216 * glxcmds.c does it as part of the context cache flush after calling 217 * this. 218 */ 219 220 return GL_TRUE; 221} 222 223/* Called when a surface is destroyed as a side effect of destroying 224 the window it's attached to. */ 225static void surface_notify(void *_arg, void *data) { 226 DRISurfaceNotifyArg *arg = (DRISurfaceNotifyArg *)_arg; 227 __GLXAquaDrawable *draw = (__GLXAquaDrawable *)data; 228 __GLXAquaContext *context; 229 x_list *lst; 230 if(_arg == NULL || data == NULL) { 231 ErrorF("surface_notify called with bad params"); 232 return; 233 } 234 235 GLAQUA_DEBUG_MSG("surface_notify(%p, %p)\n", _arg, data); 236 switch (arg->kind) { 237 case AppleDRISurfaceNotifyDestroyed: 238 if (surface_hash != NULL) 239 x_hash_table_remove(surface_hash, x_cvt_uint_to_vptr(arg->id)); 240 draw->pDraw = NULL; 241 draw->sid = 0; 242 break; 243 244 case AppleDRISurfaceNotifyChanged: 245 if (surface_hash != NULL) { 246 lst = x_hash_table_lookup(surface_hash, x_cvt_uint_to_vptr(arg->id), NULL); 247 for (; lst != NULL; lst = lst->next) 248 { 249 context = lst->data; 250 xp_update_gl_context(context->ctx); 251 } 252 } 253 break; 254 default: 255 ErrorF("surface_notify: unknown kind %d\n", arg->kind); 256 break; 257 } 258} 259 260static BOOL attach(__GLXAquaContext *context, __GLXAquaDrawable *draw) { 261 DrawablePtr pDraw; 262 263 GLAQUA_DEBUG_MSG("attach(%p, %p)\n", context, draw); 264 265 if(NULL == context || NULL == draw) 266 return TRUE; 267 268 pDraw = draw->base.pDraw; 269 270 if(NULL == pDraw) { 271 ErrorF("%s:%s() pDraw is NULL!\n", __FILE__, __func__); 272 return TRUE; 273 } 274 275 if (draw->sid == 0) { 276 //if (!quartzProcs->CreateSurface(pDraw->pScreen, pDraw->id, pDraw, 277 if (!DRICreateSurface(pDraw->pScreen, pDraw->id, pDraw, 278 0, &draw->sid, NULL, 279 surface_notify, draw)) 280 return TRUE; 281 draw->pDraw = pDraw; 282 } 283 284 if (!context->isAttached || context->sid != draw->sid) { 285 x_list *lst; 286 287 if (xp_attach_gl_context(context->ctx, draw->sid) != Success) { 288 //quartzProcs->DestroySurface(pDraw->pScreen, pDraw->id, pDraw, 289 DRIDestroySurface(pDraw->pScreen, pDraw->id, pDraw, 290 surface_notify, draw); 291 if (surface_hash != NULL) 292 x_hash_table_remove(surface_hash, x_cvt_uint_to_vptr(draw->sid)); 293 294 draw->sid = 0; 295 return TRUE; 296 } 297 298 context->isAttached = TRUE; 299 context->sid = draw->sid; 300 301 if (surface_hash == NULL) 302 surface_hash = x_hash_table_new(NULL, NULL, NULL, NULL); 303 304 lst = x_hash_table_lookup(surface_hash, x_cvt_uint_to_vptr(context->sid), NULL); 305 if (x_list_find(lst, context) == NULL) { 306 lst = x_list_prepend(lst, context); 307 x_hash_table_insert(surface_hash, x_cvt_uint_to_vptr(context->sid), lst); 308 } 309 310 311 312 GLAQUA_DEBUG_MSG("attached 0x%x to 0x%x\n", (unsigned int) pDraw->id, 313 (unsigned int) draw->sid); 314 } 315 316 draw->context = context; 317 318 return FALSE; 319} 320 321#if 0 // unused 322static void unattach(__GLXAquaContext *context) { 323 x_list *lst; 324 GLAQUA_DEBUG_MSG("unattach\n"); 325 if (context == NULL) { 326 ErrorF("Tried to unattach a null context\n"); 327 return; 328 } 329 if (context->isAttached) { 330 GLAQUA_DEBUG_MSG("unattaching\n"); 331 332 if (surface_hash != NULL) { 333 lst = x_hash_table_lookup(surface_hash, (void *) context->sid, NULL); 334 lst = x_list_remove(lst, context); 335 x_hash_table_insert(surface_hash, (void *) context->sid, lst); 336 } 337 338 CGLClearDrawable(context->ctx); 339 context->isAttached = FALSE; 340 context->sid = 0; 341 } 342} 343#endif 344 345static int __glXAquaContextMakeCurrent(__GLXcontext *baseContext) { 346 CGLError gl_err; 347 __GLXAquaContext *context = (__GLXAquaContext *) baseContext; 348 __GLXAquaDrawable *drawPriv = (__GLXAquaDrawable *) context->base.drawPriv; 349 350 GLAQUA_DEBUG_MSG("glAquaMakeCurrent (ctx 0x%p)\n", baseContext); 351 352 if(attach(context, drawPriv)) 353 return /*error*/ 0; 354 355 gl_err = CGLSetCurrentContext(context->ctx); 356 if (gl_err != 0) 357 ErrorF("CGLSetCurrentContext error: %s\n", CGLErrorString(gl_err)); 358 359 return gl_err == 0; 360} 361 362static int __glXAquaContextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc, unsigned long mask) 363{ 364 CGLError gl_err; 365 366 __GLXAquaContext *dst = (__GLXAquaContext *) baseDst; 367 __GLXAquaContext *src = (__GLXAquaContext *) baseSrc; 368 369 GLAQUA_DEBUG_MSG("GLXAquaContextCopy\n"); 370 371 gl_err = CGLCopyContext(src->ctx, dst->ctx, mask); 372 if (gl_err != 0) 373 ErrorF("CGLCopyContext error: %s\n", CGLErrorString(gl_err)); 374 375 return gl_err == 0; 376} 377 378static int __glXAquaContextForceCurrent(__GLXcontext *baseContext) 379{ 380 CGLError gl_err; 381 __GLXAquaContext *context = (__GLXAquaContext *) baseContext; 382 GLAQUA_DEBUG_MSG("glAquaForceCurrent (ctx %p)\n", context->ctx); 383 384 gl_err = CGLSetCurrentContext(context->ctx); 385 if (gl_err != 0) 386 ErrorF("CGLSetCurrentContext error: %s\n", CGLErrorString(gl_err)); 387 388 return gl_err == 0; 389} 390 391/* Drawing surface notification callbacks */ 392static GLboolean __glXAquaDrawableSwapBuffers(ClientPtr client, __GLXdrawable *base) { 393 CGLError err; 394 __GLXAquaDrawable *drawable; 395 396 // GLAQUA_DEBUG_MSG("glAquaDrawableSwapBuffers(%p)\n",base); 397 398 if(!base) { 399 ErrorF("%s passed NULL\n", __func__); 400 return GL_FALSE; 401 } 402 403 drawable = (__GLXAquaDrawable *)base; 404 405 if(NULL == drawable->context) { 406 ErrorF("%s called with a NULL->context for drawable %p!\n", 407 __func__, (void *)drawable); 408 return GL_FALSE; 409 } 410 411 err = CGLFlushDrawable(drawable->context->ctx); 412 413 if(kCGLNoError != err) { 414 ErrorF("CGLFlushDrawable error: %s in %s\n", CGLErrorString(err), 415 __func__); 416 return GL_FALSE; 417 } 418 419 return GL_TRUE; 420} 421 422 423static CGLPixelFormatObj makeFormat(__GLXconfig *conf) { 424 CGLPixelFormatAttribute attr[64]; 425 CGLPixelFormatObj fobj; 426 GLint formats; 427 CGLError error; 428 int i = 0; 429 430 if(conf->doubleBufferMode) 431 attr[i++] = kCGLPFADoubleBuffer; 432 433 if(conf->stereoMode) 434 attr[i++] = kCGLPFAStereo; 435 436 attr[i++] = kCGLPFAColorSize; 437 attr[i++] = conf->redBits + conf->greenBits + conf->blueBits; 438 attr[i++] = kCGLPFAAlphaSize; 439 attr[i++] = conf->alphaBits; 440 441 if((conf->accumRedBits + conf->accumGreenBits + conf->accumBlueBits + 442 conf->accumAlphaBits) > 0) { 443 444 attr[i++] = kCGLPFAAccumSize; 445 attr[i++] = conf->accumRedBits + conf->accumGreenBits 446 + conf->accumBlueBits + conf->accumAlphaBits; 447 } 448 449 attr[i++] = kCGLPFADepthSize; 450 attr[i++] = conf->depthBits; 451 452 if(conf->stencilBits) { 453 attr[i++] = kCGLPFAStencilSize; 454 attr[i++] = conf->stencilBits; 455 } 456 457 if(conf->numAuxBuffers > 0) { 458 attr[i++] = kCGLPFAAuxBuffers; 459 attr[i++] = conf->numAuxBuffers; 460 } 461 462 if(conf->sampleBuffers > 0) { 463 attr[i++] = kCGLPFASampleBuffers; 464 attr[i++] = conf->sampleBuffers; 465 attr[i++] = kCGLPFASamples; 466 attr[i++] = conf->samples; 467 } 468 469 attr[i] = 0; 470 471 error = CGLChoosePixelFormat(attr, &fobj, &formats); 472 if(error) { 473 ErrorF("error: creating pixel format %s\n", CGLErrorString(error)); 474 return NULL; 475 } 476 477 return fobj; 478} 479 480static void __glXAquaScreenDestroy(__GLXscreen *screen) { 481 482 GLAQUA_DEBUG_MSG("glXAquaScreenDestroy(%p)\n", screen); 483 __glXScreenDestroy(screen); 484 485 free(screen); 486} 487 488/* This is called by __glXInitScreens(). */ 489static __GLXscreen * __glXAquaScreenProbe(ScreenPtr pScreen) { 490 __GLXAquaScreen *screen; 491 492 GLAQUA_DEBUG_MSG("glXAquaScreenProbe\n"); 493 494 if (pScreen == NULL) 495 return NULL; 496 497 screen = calloc(1, sizeof *screen); 498 499 if(NULL == screen) 500 return NULL; 501 502 screen->base.destroy = __glXAquaScreenDestroy; 503 screen->base.createContext = __glXAquaScreenCreateContext; 504 screen->base.createDrawable = __glXAquaScreenCreateDrawable; 505 screen->base.swapInterval = /*FIXME*/ NULL; 506 screen->base.pScreen = pScreen; 507 508 screen->base.fbconfigs = __glXAquaCreateVisualConfigs(&screen->base.numFBConfigs, pScreen->myNum); 509 510 __glXScreenInit(&screen->base, pScreen); 511 512 screen->base.GLXmajor = 1; 513 screen->base.GLXminor = 4; 514 screen->base.GLXextensions = strdup("GLX_SGIX_fbconfig " 515 "GLX_SGIS_multisample " 516 "GLX_ARB_multisample " 517 "GLX_EXT_visual_info " 518 "GLX_EXT_import_context "); 519 520 /*We may be able to add more GLXextensions at a later time. */ 521 522 return &screen->base; 523} 524 525#if 0 // unused 526static void __glXAquaDrawableCopySubBuffer (__GLXdrawable *drawable, 527 int x, int y, int w, int h) { 528 /*TODO finish me*/ 529} 530#endif 531 532static void __glXAquaDrawableDestroy(__GLXdrawable *base) { 533 /* gstaplin: base is the head of the structure, so it's at the same 534 * offset in memory. 535 * Is this safe with strict aliasing? I noticed that the other dri code 536 * does this too... 537 */ 538 __GLXAquaDrawable *glxPriv = (__GLXAquaDrawable *)base; 539 540 GLAQUA_DEBUG_MSG(__func__); 541 542 /* It doesn't work to call DRIDestroySurface here, the drawable's 543 already gone.. But dri.c notices the window destruction and 544 frees the surface itself. */ 545 546 /*gstaplin: verify the statement above. The surface destroy 547 *messages weren't making it through, and may still not be. 548 *We need a good test case for surface creation and destruction. 549 *We also need a good way to enable introspection on the server 550 *to validate the test, beyond using gdb with print. 551 */ 552 553 free(glxPriv); 554} 555 556static __GLXdrawable * 557__glXAquaScreenCreateDrawable(ClientPtr client, 558 __GLXscreen *screen, 559 DrawablePtr pDraw, 560 XID drawId, 561 int type, 562 XID glxDrawId, 563 __GLXconfig *conf) { 564 __GLXAquaDrawable *glxPriv; 565 566 glxPriv = malloc(sizeof *glxPriv); 567 568 if(glxPriv == NULL) 569 return NULL; 570 571 memset(glxPriv, 0, sizeof *glxPriv); 572 573 if(!__glXDrawableInit(&glxPriv->base, screen, pDraw, type, glxDrawId, conf)) { 574 free(glxPriv); 575 return NULL; 576 } 577 578 glxPriv->base.destroy = __glXAquaDrawableDestroy; 579 glxPriv->base.swapBuffers = __glXAquaDrawableSwapBuffers; 580 glxPriv->base.copySubBuffer = NULL; /* __glXAquaDrawableCopySubBuffer; */ 581 582 glxPriv->pDraw = pDraw; 583 glxPriv->sid = 0; 584 glxPriv->context = NULL; 585 586 return &glxPriv->base; 587} 588 589// Extra goodies for glx 590 591GLuint __glFloorLog2(GLuint val) 592{ 593 int c = 0; 594 595 while (val > 1) { 596 c++; 597 val >>= 1; 598 } 599 return c; 600} 601 602#ifndef OPENGL_FRAMEWORK_PATH 603#define OPENGL_FRAMEWORK_PATH "/System/Library/Frameworks/OpenGL.framework/OpenGL" 604#endif 605 606static void setup_dispatch_table(void) { 607 static struct _glapi_table *disp = NULL; 608 static void *handle; 609 const char *opengl_framework_path; 610 611 if(disp) { 612 _glapi_set_dispatch(disp); 613 return; 614 } 615 616 opengl_framework_path = getenv("OPENGL_FRAMEWORK_PATH"); 617 if (!opengl_framework_path) { 618 opengl_framework_path = OPENGL_FRAMEWORK_PATH; 619 } 620 621 (void) dlerror(); /*drain dlerror */ 622 handle = dlopen(opengl_framework_path, RTLD_LOCAL); 623 624 if (!handle) { 625 ErrorF("unable to dlopen %s : %s, using RTLD_DEFAULT\n", 626 opengl_framework_path, dlerror()); 627 handle = RTLD_DEFAULT; 628 } 629 630 disp = _glapi_create_table_from_handle(handle, "gl"); 631 assert(disp); 632 633 _glapi_set_dispatch(disp); 634} 635