apple_glx_drawable.c revision ad503c41557606d15b0420c824369456f6d20a8f
1/* 2 Copyright (c) 2008, 2009 Apple Inc. 3 4 Permission is hereby granted, free of charge, to any person 5 obtaining a copy of this software and associated documentation files 6 (the "Software"), to deal in the Software without restriction, 7 including without limitation the rights to use, copy, modify, merge, 8 publish, distribute, sublicense, and/or sell copies of the Software, 9 and to permit persons to whom the Software is furnished to do so, 10 subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be 13 included in all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 19 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 DEALINGS IN THE SOFTWARE. 23 24 Except as contained in this notice, the name(s) of the above 25 copyright holders shall not be used in advertising or otherwise to 26 promote the sale, use or other dealings in this Software without 27 prior written authorization. 28*/ 29 30#include <stdbool.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <assert.h> 34#include <pthread.h> 35#include "apple_glx.h" 36#include "apple_glx_context.h" 37#include "apple_glx_drawable.h" 38#include "appledri.h" 39 40static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER; 41static struct apple_glx_drawable *drawables_list = NULL; 42 43static void 44lock_drawables_list(void) 45{ 46 int err; 47 48 err = pthread_mutex_lock(&drawables_lock); 49 50 if (err) { 51 fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n", 52 __func__, err); 53 abort(); 54 } 55} 56 57static void 58unlock_drawables_list(void) 59{ 60 int err; 61 62 err = pthread_mutex_unlock(&drawables_lock); 63 64 if (err) { 65 fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n", 66 __func__, err); 67 abort(); 68 } 69} 70 71struct apple_glx_drawable * 72apple_glx_find_drawable(Display * dpy, GLXDrawable drawable) 73{ 74 struct apple_glx_drawable *i, *agd = NULL; 75 76 lock_drawables_list(); 77 78 for (i = drawables_list; i; i = i->next) { 79 if (i->drawable == drawable) { 80 agd = i; 81 break; 82 } 83 } 84 85 unlock_drawables_list(); 86 87 return agd; 88} 89 90static void 91drawable_lock(struct apple_glx_drawable *agd) 92{ 93 int err; 94 95 err = pthread_mutex_lock(&agd->mutex); 96 97 if (err) { 98 fprintf(stderr, "pthread_mutex_lock error: %d\n", err); 99 abort(); 100 } 101} 102 103static void 104drawable_unlock(struct apple_glx_drawable *d) 105{ 106 int err; 107 108 err = pthread_mutex_unlock(&d->mutex); 109 110 if (err) { 111 fprintf(stderr, "pthread_mutex_unlock error: %d\n", err); 112 abort(); 113 } 114} 115 116 117static void 118reference_drawable(struct apple_glx_drawable *d) 119{ 120 d->lock(d); 121 d->reference_count++; 122 d->unlock(d); 123} 124 125static void 126release_drawable(struct apple_glx_drawable *d) 127{ 128 d->lock(d); 129 d->reference_count--; 130 d->unlock(d); 131} 132 133/* The drawables list must be locked prior to calling this. */ 134/* Return true if the drawable was destroyed. */ 135static bool 136destroy_drawable(struct apple_glx_drawable *d) 137{ 138 139 d->lock(d); 140 141 if (d->reference_count > 0) { 142 d->unlock(d); 143 return false; 144 } 145 146 d->unlock(d); 147 148 if (d->previous) { 149 d->previous->next = d->next; 150 } 151 else { 152 /* 153 * The item must be at the head of the list, if it 154 * has no previous pointer. 155 */ 156 drawables_list = d->next; 157 } 158 159 if (d->next) 160 d->next->previous = d->previous; 161 162 unlock_drawables_list(); 163 164 if (d->callbacks.destroy) { 165 /* 166 * Warning: this causes other routines to be called (potentially) 167 * from surface_notify_handler. It's probably best to not have 168 * any locks at this point locked. 169 */ 170 d->callbacks.destroy(d->display, d); 171 } 172 173 apple_glx_diagnostic("%s: freeing %p\n", __func__, (void *) d); 174 175 free(d); 176 177 /* So that the locks are balanced and the caller correctly unlocks. */ 178 lock_drawables_list(); 179 180 return true; 181} 182 183/* 184 * This is typically called when a context is destroyed or the current 185 * drawable is made None. 186 */ 187static bool 188destroy_drawable_callback(struct apple_glx_drawable *d) 189{ 190 bool result; 191 192 d->lock(d); 193 194 apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__, 195 (void *) d, d->reference_count); 196 197 d->reference_count--; 198 199 if (d->reference_count > 0) { 200 d->unlock(d); 201 return false; 202 } 203 204 d->unlock(d); 205 206 lock_drawables_list(); 207 208 result = destroy_drawable(d); 209 210 unlock_drawables_list(); 211 212 return result; 213} 214 215static bool 216is_pbuffer(struct apple_glx_drawable *d) 217{ 218 return APPLE_GLX_DRAWABLE_PBUFFER == d->type; 219} 220 221static bool 222is_pixmap(struct apple_glx_drawable *d) 223{ 224 return APPLE_GLX_DRAWABLE_PIXMAP == d->type; 225} 226 227static void 228common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d) 229{ 230 int err; 231 pthread_mutexattr_t attr; 232 233 d->display = dpy; 234 d->reference_count = 0; 235 d->drawable = drawable; 236 d->type = -1; 237 238 err = pthread_mutexattr_init(&attr); 239 240 if (err) { 241 fprintf(stderr, "pthread_mutexattr_init error: %d\n", err); 242 abort(); 243 } 244 245 /* 246 * There are some patterns that require a recursive mutex, 247 * when working with locks that protect the apple_glx_drawable, 248 * and reference functions like ->reference, and ->release. 249 */ 250 err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 251 252 if (err) { 253 fprintf(stderr, "error: setting pthread mutex type: %d\n", err); 254 abort(); 255 } 256 257 err = pthread_mutex_init(&d->mutex, &attr); 258 259 if (err) { 260 fprintf(stderr, "pthread_mutex_init error: %d\n", err); 261 abort(); 262 } 263 264 (void) pthread_mutexattr_destroy(&attr); 265 266 d->lock = drawable_lock; 267 d->unlock = drawable_unlock; 268 269 d->reference = reference_drawable; 270 d->release = release_drawable; 271 272 d->destroy = destroy_drawable_callback; 273 274 d->is_pbuffer = is_pbuffer; 275 d->is_pixmap = is_pixmap; 276 277 d->width = -1; 278 d->height = -1; 279 d->row_bytes = 0; 280 d->path[0] = '\0'; 281 d->fd = -1; 282 d->buffer = NULL; 283 d->buffer_length = 0; 284 285 d->previous = NULL; 286 d->next = NULL; 287} 288 289static void 290link_tail(struct apple_glx_drawable *agd) 291{ 292 lock_drawables_list(); 293 294 /* Link the new drawable into the global list. */ 295 agd->next = drawables_list; 296 297 if (drawables_list) 298 drawables_list->previous = agd; 299 300 drawables_list = agd; 301 302 unlock_drawables_list(); 303} 304 305/*WARNING: this returns a locked and referenced object. */ 306bool 307apple_glx_drawable_create(Display * dpy, 308 int screen, 309 GLXDrawable drawable, 310 struct apple_glx_drawable **agdResult, 311 struct apple_glx_drawable_callbacks *callbacks) 312{ 313 struct apple_glx_drawable *d; 314 315 d = calloc(1, sizeof *d); 316 317 if (NULL == d) { 318 perror("malloc"); 319 return true; 320 } 321 322 common_init(dpy, drawable, d); 323 d->type = callbacks->type; 324 d->callbacks = *callbacks; 325 326 d->reference(d); 327 d->lock(d); 328 329 link_tail(d); 330 331 apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d); 332 333 *agdResult = d; 334 335 return false; 336} 337 338static int error_count = 0; 339 340static int 341error_handler(Display * dpy, XErrorEvent * err) 342{ 343 if (err->error_code == BadWindow) { 344 ++error_count; 345 } 346 347 return 0; 348} 349 350void 351apple_glx_garbage_collect_drawables(Display * dpy) 352{ 353 struct apple_glx_drawable *d, *dnext; 354 Window root; 355 int x, y; 356 unsigned int width, height, bd, depth; 357 int (*old_handler) (Display *, XErrorEvent *); 358 359 360 if (NULL == drawables_list) 361 return; 362 363 old_handler = XSetErrorHandler(error_handler); 364 365 XSync(dpy, False); 366 367 lock_drawables_list(); 368 369 for (d = drawables_list; d;) { 370 dnext = d->next; 371 372 d->lock(d); 373 374 if (d->reference_count > 0) { 375 /* 376 * Skip this, because some context still retains a reference 377 * to the drawable. 378 */ 379 d->unlock(d); 380 d = dnext; 381 continue; 382 } 383 384 d->unlock(d); 385 386 error_count = 0; 387 388 /* 389 * Mesa uses XGetWindowAttributes, but some of these things are 390 * most definitely not Windows, and that's against the rules. 391 * XGetGeometry on the other hand is legal with a Pixmap and Window. 392 */ 393 XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd, 394 &depth); 395 396 if (error_count > 0) { 397 /* 398 * Note: this may not actually destroy the drawable. 399 * If another context retains a reference to the drawable 400 * after the reference count test above. 401 */ 402 (void) destroy_drawable(d); 403 error_count = 0; 404 } 405 406 d = dnext; 407 } 408 409 XSetErrorHandler(old_handler); 410 411 unlock_drawables_list(); 412} 413 414unsigned int 415apple_glx_get_drawable_count(void) 416{ 417 unsigned int result = 0; 418 struct apple_glx_drawable *d; 419 420 lock_drawables_list(); 421 422 for (d = drawables_list; d; d = d->next) 423 ++result; 424 425 unlock_drawables_list(); 426 427 return result; 428} 429 430struct apple_glx_drawable * 431apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags) 432{ 433 struct apple_glx_drawable *d; 434 435 lock_drawables_list(); 436 437 for (d = drawables_list; d; d = d->next) { 438 if (d->type == type && d->drawable == drawable) { 439 if (flags & APPLE_GLX_DRAWABLE_REFERENCE) 440 d->reference(d); 441 442 if (flags & APPLE_GLX_DRAWABLE_LOCK) 443 d->lock(d); 444 445 unlock_drawables_list(); 446 447 return d; 448 } 449 } 450 451 unlock_drawables_list(); 452 453 return NULL; 454} 455 456struct apple_glx_drawable * 457apple_glx_drawable_find(GLXDrawable drawable, int flags) 458{ 459 struct apple_glx_drawable *d; 460 461 lock_drawables_list(); 462 463 for (d = drawables_list; d; d = d->next) { 464 if (d->drawable == drawable) { 465 if (flags & APPLE_GLX_DRAWABLE_REFERENCE) 466 d->reference(d); 467 468 if (flags & APPLE_GLX_DRAWABLE_LOCK) 469 d->lock(d); 470 471 unlock_drawables_list(); 472 473 return d; 474 } 475 } 476 477 unlock_drawables_list(); 478 479 return NULL; 480} 481 482/* Return true if the type is valid for the drawable. */ 483bool 484apple_glx_drawable_destroy_by_type(Display * dpy, 485 GLXDrawable drawable, int type) 486{ 487 struct apple_glx_drawable *d; 488 489 lock_drawables_list(); 490 491 for (d = drawables_list; d; d = d->next) { 492 if (drawable == d->drawable && type == d->type) { 493 /* 494 * The user has requested that we destroy this resource. 495 * However, there may be references in the contexts to it, so 496 * release it, and call destroy_drawable which doesn't destroy 497 * if the reference_count is > 0. 498 */ 499 d->release(d); 500 501 apple_glx_diagnostic("%s d->reference_count %d\n", 502 __func__, d->reference_count); 503 504 destroy_drawable(d); 505 unlock_drawables_list(); 506 return true; 507 } 508 } 509 510 unlock_drawables_list(); 511 512 return false; 513} 514 515struct apple_glx_drawable * 516apple_glx_drawable_find_by_uid(unsigned int uid, int flags) 517{ 518 struct apple_glx_drawable *d; 519 520 lock_drawables_list(); 521 522 for (d = drawables_list; d; d = d->next) { 523 /* Only surfaces have a uid. */ 524 if (APPLE_GLX_DRAWABLE_SURFACE == d->type) { 525 if (d->types.surface.uid == uid) { 526 if (flags & APPLE_GLX_DRAWABLE_REFERENCE) 527 d->reference(d); 528 529 if (flags & APPLE_GLX_DRAWABLE_LOCK) 530 d->lock(d); 531 532 unlock_drawables_list(); 533 534 return d; 535 } 536 } 537 } 538 539 unlock_drawables_list(); 540 541 return NULL; 542} 543