1/* 2 * Copyright 2003 Red Hat Inc., Raleigh, North Carolina. 3 * 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation on the rights to use, copy, modify, merge, 10 * publish, distribute, sublicense, and/or sell copies of the Software, 11 * and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial 16 * portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS 22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 * 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 25 * SOFTWARE. 26 */ 27 28/* 29 * Authors: 30 * Kevin E. Martin <kem@redhat.com> 31 * 32 */ 33 34#ifdef HAVE_DMX_CONFIG_H 35#include <dmx-config.h> 36#endif 37 38#include "dmx.h" 39#include "dmxwindow.h" 40#include "glxserver.h" 41#include "glxswap.h" 42 43extern int __glXDoSwapBuffers(__GLXclientState *cl, XID drawId, 44 GLXContextTag tag); 45 46typedef struct _SwapGroup *SwapGroupPtr; 47 48static Bool SwapBarrierIsReadyToSwap(GLuint barrier); 49static void SwapSwapBarrier(GLuint barrier); 50static void UpdateSwapBarrierList(GLuint barrier, 51 SwapGroupPtr pOldSwap, 52 SwapGroupPtr pNewSwap); 53 54 55/************************************************************************ 56 * 57 * Swap Groups 58 * 59 ************************************************************************/ 60 61typedef struct _SwapGroup { 62 WindowPtr pWin; 63 SwapGroupPtr pNext; 64 65 Bool swapping; 66 Bool sleeping; 67 GLuint barrier; 68 69 XID drawable; 70 GLXContextTag tag; 71 __GLXclientState *clState; 72} SwapGroupRec; 73 74 75static void SwapSwapGroup(SwapGroupPtr pSwap) 76{ 77 SwapGroupPtr pCur; 78 79 /* All drawables in swap group are ready to swap, so just swap all 80 * drawables buffers and then wake up those clients that were 81 * previously sleeping */ 82 83 for (pCur = pSwap; pCur; pCur = pCur->pNext) { 84 if (pCur->swapping) { 85 /* Swap pCur's buffers */ 86 __glXDoSwapBuffers(pCur->clState, pCur->drawable, pCur->tag); 87 pCur->swapping = FALSE; 88 } 89 90 /* Wakeup client */ 91 if (pCur->sleeping) { 92 ClientWakeup(pCur->clState->client); 93 pCur->sleeping = FALSE; 94 } 95 } 96} 97 98static Bool SwapGroupIsReadyToSwap(SwapGroupPtr pSwap) 99{ 100 Bool isReady = TRUE; 101 102 /* The swap group is ready to swap when all drawables are ready to 103 * swap. NOTE: A drawable is also ready to swap if it is not 104 * currently mapped */ 105 for (; pSwap; pSwap = pSwap->pNext) { 106 isReady &= (pSwap->swapping || !pSwap->pWin->mapped); 107 /* FIXME: Should we use pSwap->pWin->mapped or ...->realized ??? */ 108 } 109 110 return isReady; 111} 112 113static Bool SGSwapCleanup(ClientPtr client, pointer closure) 114{ 115 /* SwapGroupPtr pSwap = (SwapGroupPtr)closure; */ 116 117 /* This should not be called unless the client has died in which 118 * case we should remove the buffer from the swap list */ 119 120 return TRUE; 121} 122 123int SGSwapBuffers(__GLXclientState *cl, XID drawId, GLXContextTag tag, 124 DrawablePtr pDraw) 125{ 126 WindowPtr pWin = (WindowPtr)pDraw; 127 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); 128 SwapGroupPtr pSwap = pWinPriv->swapGroup; 129 SwapGroupPtr pCur; 130 131 for (pCur = pSwap; pCur && pCur->pWin != pWin; pCur = pCur->pNext); 132 if (!pCur) 133 return BadDrawable; 134 135 pCur->clState = cl; 136 pCur->drawable = drawId; 137 pCur->tag = tag; 138 139 /* We are now in the process of swapping */ 140 pCur->swapping = TRUE; 141 142 if (pSwap->barrier && SwapBarrierIsReadyToSwap(pSwap->barrier)) { 143 /* The swap group is bound to a barrier and the barrier is ready 144 * to swap, so swap all the swap groups that are bound to this 145 * group's swap barrier */ 146 SwapSwapBarrier(pSwap->barrier); 147 } else if (!pSwap->barrier && SwapGroupIsReadyToSwap(pSwap)) { 148 /* Do the swap if the entire swap group is ready to swap and the 149 * group is not bound to a swap barrier */ 150 SwapSwapGroup(pSwap); 151 } else { 152 /* The swap group/barrier is not yet ready to swap, so put 153 * client to sleep until the rest are ready to swap */ 154 ClientSleep(cl->client, SGSwapCleanup, (pointer)pWin); 155 pCur->sleeping = TRUE; 156 } 157 158 return Success; 159} 160 161static void SGWindowUnmapped(WindowPtr pWin) 162{ 163 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); 164 SwapGroupPtr pSwap = pWinPriv->swapGroup; 165 166 /* Now that one of the windows in the swap group has been unmapped, 167 * see if the entire swap group/barrier is ready to swap */ 168 169 if (pSwap->barrier && SwapBarrierIsReadyToSwap(pSwap->barrier)) { 170 SwapSwapBarrier(pSwap->barrier); 171 } else if (!pSwap->barrier && SwapGroupIsReadyToSwap(pSwap)) { 172 SwapSwapGroup(pSwap); 173 } 174} 175 176static void SGWindowDestroyed(WindowPtr pWin) 177{ 178 JoinSwapGroupSGIX((DrawablePtr)pWin, NULL); 179} 180 181static SwapGroupPtr CreateSwapEntry(WindowPtr pWin) 182{ 183 SwapGroupPtr pEntry; 184 185 /* Allocate new swap group */ 186 pEntry = malloc(sizeof(*pEntry)); 187 if (!pEntry) return NULL; 188 189 /* Initialize swap group */ 190 pEntry->pWin = pWin; 191 pEntry->pNext = NULL; 192 pEntry->swapping = FALSE; 193 pEntry->sleeping = FALSE; 194 pEntry->barrier = 0; 195 /* The following are not initialized until SwapBuffers is called: 196 * pEntry->drawable 197 * pEntry->tag 198 * pEntry->clState 199 */ 200 201 return pEntry; 202} 203 204static void FreeSwapEntry(SwapGroupPtr pEntry) 205{ 206 /* Since we have removed the drawable from its previous swap group 207 * and it won't be added to another swap group, the only thing that 208 * we need to do is to make sure that the drawable's client is not 209 * sleeping. This could happen if one thread is sleeping, while 210 * another thread called glxJoinSwapGroup(). Note that all sleeping 211 * threads should also be swapping, but there is a small window in 212 * the SGSwapBuffer() logic, above, where swapping can be set but 213 * sleeping is not. We check both independently here just to be 214 * pedantic. */ 215 216 /* Handle swap buffer request */ 217 if (pEntry->swapping) 218 __glXDoSwapBuffers(pEntry->clState, pEntry->drawable, pEntry->tag); 219 220 /* Wake up client */ 221 if (pEntry->sleeping) 222 ClientWakeup(pEntry->clState->client); 223 224 /* We can free the pEntry entry since it has already been removed 225 * from the swap group list and it won't be needed any longer */ 226 free(pEntry); 227} 228 229int JoinSwapGroupSGIX(DrawablePtr pDraw, DrawablePtr pMember) 230{ 231 if (pDraw->type == DRAWABLE_WINDOW) { 232 WindowPtr pWin = (WindowPtr)pDraw; 233 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); 234 SwapGroupPtr pOldSwap = NULL; 235 SwapGroupPtr pEntry; 236 237 /* If pDraw and pMember are already members of the same swap 238 * group, just return Success since there is nothing to do */ 239 for (pEntry = pWinPriv->swapGroup; pEntry; pEntry = pEntry->pNext) 240 if (pEntry->pWin == (WindowPtr)pMember) 241 return Success; 242 243 /* Remove pDraw from its current swap group */ 244 if (pWinPriv->swapGroup) { 245 SwapGroupPtr pSwapGroup = pWinPriv->swapGroup; 246 SwapGroupPtr pPrev; 247 248 /* Find old swap entry in swap group and save in pOldSwap 249 * for later use */ 250 for (pOldSwap = pWinPriv->swapGroup, pPrev = NULL; 251 pOldSwap && pOldSwap->pWin != pWin; 252 pPrev = pOldSwap, pOldSwap = pOldSwap->pNext); 253 if (!pOldSwap) 254 return BadDrawable; 255 256 /* Remove pDraw's swap group entry from swap group list */ 257 if (pPrev) { 258 pPrev->pNext = pOldSwap->pNext; 259 } else { 260 /* pWin is at the head of the swap group list, so we 261 * need to update all other members of this swap 262 * group */ 263 for (pEntry = pOldSwap->pNext; pEntry; pEntry = pEntry->pNext) 264 DMX_GET_WINDOW_PRIV(pEntry->pWin)->swapGroup 265 = pOldSwap->pNext; 266 267 /* Update the barrier list as well */ 268 if (pOldSwap->barrier) 269 UpdateSwapBarrierList(pOldSwap->barrier, 270 pOldSwap, pOldSwap->pNext); 271 272 /* Set pSwapGroup to point to the swap group without 273 * pOldSwap */ 274 pSwapGroup = pOldSwap->pNext; 275 } 276 277 /* Check to see if current swap group can now swap since we 278 * know at this point that pDraw and pMember are guaranteed 279 * to previously be in different swap groups */ 280 if (pSwapGroup && SwapGroupIsReadyToSwap(pSwapGroup)) { 281 SwapSwapGroup(pSwapGroup); 282 } 283 284 /* Make the old swap entry a standalone group */ 285 pOldSwap->pNext = NULL; 286 pOldSwap->barrier = 0; 287 288 /* Reset pWin's swap group */ 289 pWinPriv->swapGroup = NULL; 290 pWinPriv->windowDestroyed = NULL; 291 pWinPriv->windowUnmapped = NULL; 292 } 293 294 if (!pMember || pMember->type != DRAWABLE_WINDOW) { 295 /* Free old swap group since it is no longer needed */ 296 if (pOldSwap) FreeSwapEntry(pOldSwap); 297 } else if (pDraw == pMember && pOldSwap) { 298 /* Special case where pDraw was previously created and we 299 * are now just putting it to its own swap group */ 300 pWinPriv->swapGroup = pOldSwap; 301 pWinPriv->windowDestroyed = SGWindowDestroyed; 302 pWinPriv->windowUnmapped = SGWindowUnmapped; 303 304 /* Check to see if pDraw is ready to swap */ 305 if (SwapGroupIsReadyToSwap(pOldSwap)) 306 SwapSwapGroup(pOldSwap); 307 } else if (pMember->type == DRAWABLE_WINDOW) { 308 WindowPtr pMemberWin = (WindowPtr)pMember; 309 dmxWinPrivPtr pMemberPriv = DMX_GET_WINDOW_PRIV(pMemberWin); 310 SwapGroupPtr pMemberSwapGroup = pMemberPriv->swapGroup; 311 312 /* Finally, how we can add pDraw to pMember's swap group */ 313 314 /* If pMember is not currently in a swap group, then create 315 * one for it since we are just about to add pDraw to it. */ 316 if (!pMemberSwapGroup) { 317 /* Create new swap group */ 318 pMemberSwapGroup = CreateSwapEntry(pMemberWin); 319 if (!pMemberSwapGroup) { 320 if (pOldSwap) FreeSwapEntry(pOldSwap); 321 return BadAlloc; 322 } 323 324 /* Set pMember's swap group */ 325 pMemberPriv->swapGroup = pMemberSwapGroup; 326 pMemberPriv->windowDestroyed = SGWindowDestroyed; 327 pMemberPriv->windowUnmapped = SGWindowUnmapped; 328 } 329 330 /* If pDraw == pMember, that means pDraw was not a member of 331 * a group previously (or it would have been handled by the 332 * special case above), so no additional work is required 333 * since we just created a new swap group for pMember (i.e., 334 * pDraw). */ 335 336 if (pDraw != pMember) { 337 /* If pDraw was not previously in a swap group, then create 338 * an entry for it */ 339 if (!pOldSwap) { 340 /* Create new swap group */ 341 pOldSwap = CreateSwapEntry(pWin); 342 if (!pOldSwap) { 343 /* If we just created a swap group for pMember, we 344 * need to free it here */ 345 if (pMemberSwapGroup->pNext == NULL) { 346 FreeSwapEntry(pMemberSwapGroup); 347 pMemberPriv->swapGroup = NULL; 348 } 349 return BadAlloc; 350 } 351 } 352 353 /* Find last entry in pMember's swap group */ 354 for (pEntry = pMemberSwapGroup; 355 pEntry->pNext; 356 pEntry = pEntry->pNext); 357 358 /* Add pDraw's swap group entry to pMember's swap group list */ 359 pEntry->pNext = pOldSwap; 360 361 /* Add pDraw to pMember's swap barrier */ 362 pOldSwap->barrier = pEntry->barrier; 363 364 /* Set pDraw's swap group */ 365 pWinPriv->swapGroup = pMemberSwapGroup; 366 pWinPriv->windowDestroyed = SGWindowDestroyed; 367 pWinPriv->windowUnmapped = SGWindowUnmapped; 368 } 369 } 370 } 371 372 return Success; 373} 374 375 376/************************************************************************ 377 * 378 * Swap Barriers 379 * 380 ************************************************************************/ 381 382#define GLX_MAX_SWAP_BARRIERS 10 383 384typedef struct _SwapBarrier *SwapBarrierPtr; 385typedef struct _SwapBarrier { 386 SwapGroupPtr pSwap; 387 SwapBarrierPtr pNext; 388} SwapBarrierRec; 389 390static SwapBarrierPtr SwapBarrierList[GLX_MAX_SWAP_BARRIERS+1]; 391 392void SwapBarrierInit(void) 393{ 394 int i; 395 396 for (i = 0; i <= GLX_MAX_SWAP_BARRIERS; i++) 397 SwapBarrierList[i] = NULL; 398} 399 400void SwapBarrierReset(void) 401{ 402 int i; 403 404 for (i = 0; i <= GLX_MAX_SWAP_BARRIERS; i++) { 405 SwapBarrierPtr pBarrier, pNextBarrier; 406 for (pBarrier = SwapBarrierList[i]; 407 pBarrier; 408 pBarrier = pNextBarrier) { 409 pNextBarrier = pBarrier->pNext; 410 free(pBarrier); 411 } 412 SwapBarrierList[i] = NULL; 413 } 414} 415 416int QueryMaxSwapBarriersSGIX(int screen) 417{ 418 return GLX_MAX_SWAP_BARRIERS; 419} 420 421static Bool BindSwapGroupToBarrier(GLuint barrier, SwapGroupPtr pSwapGroup) 422{ 423 SwapBarrierPtr pBarrier; 424 425 pBarrier = malloc(sizeof(*pBarrier)); 426 if (!pBarrier) return FALSE; 427 428 /* Add the swap group to barrier's list */ 429 pBarrier->pSwap = pSwapGroup; 430 pBarrier->pNext = SwapBarrierList[barrier]; 431 SwapBarrierList[barrier] = pBarrier; 432 433 return TRUE; 434} 435 436static Bool UnbindSwapGroupFromBarrier(GLuint barrier, SwapGroupPtr pSwapGroup) 437{ 438 SwapBarrierPtr pBarrier, pPrevBarrier; 439 440 /* Find the swap group in barrier's list */ 441 for (pBarrier = SwapBarrierList[barrier], pPrevBarrier = NULL; 442 pBarrier && pBarrier->pSwap != pSwapGroup; 443 pPrevBarrier = pBarrier, pBarrier = pBarrier->pNext); 444 if (!pBarrier) return FALSE; 445 446 /* Remove the swap group from barrier's list */ 447 if (pPrevBarrier) pPrevBarrier->pNext = pBarrier->pNext; 448 else SwapBarrierList[barrier] = pBarrier->pNext; 449 450 /* Free memory */ 451 free(pBarrier); 452 453 return TRUE; 454} 455 456static void UpdateSwapBarrierList(GLuint barrier, 457 SwapGroupPtr pOldSwap, 458 SwapGroupPtr pNewSwap) 459{ 460 SwapBarrierPtr pBarrier; 461 462 /* If the old swap group is being destroyed, then we need to remove 463 * the swap group from the list entirely */ 464 if (!pNewSwap) { 465 UnbindSwapGroupFromBarrier(barrier, pOldSwap); 466 return; 467 } 468 469 /* Otherwise, find the old swap group in the barrier list and change 470 * it to the new swap group */ 471 for (pBarrier = SwapBarrierList[barrier]; 472 pBarrier; 473 pBarrier = pBarrier->pNext) { 474 if (pBarrier->pSwap == pOldSwap) { 475 pBarrier->pSwap = pNewSwap; 476 return; 477 } 478 } 479} 480 481static Bool SwapBarrierIsReadyToSwap(GLuint barrier) 482{ 483 SwapBarrierPtr pBarrier; 484 Bool isReady = TRUE; 485 486 /* The swap barier is ready to swap when swap groups that are bound 487 * to barrier are ready to swap */ 488 for (pBarrier = SwapBarrierList[barrier]; 489 pBarrier; 490 pBarrier = pBarrier->pNext) 491 isReady &= SwapGroupIsReadyToSwap(pBarrier->pSwap); 492 493 return isReady; 494} 495 496static void SwapSwapBarrier(GLuint barrier) 497{ 498 SwapBarrierPtr pBarrier; 499 500 /* Swap each group that is a member of this barrier */ 501 for (pBarrier = SwapBarrierList[barrier]; 502 pBarrier; 503 pBarrier = pBarrier->pNext) 504 SwapSwapGroup(pBarrier->pSwap); 505} 506 507int BindSwapBarrierSGIX(DrawablePtr pDraw, int barrier) 508{ 509 /* FIXME: Check for errors when pDraw->type != DRAWABLE_WINDOW */ 510 511 if (barrier < 0 || barrier > GLX_MAX_SWAP_BARRIERS) 512 return BadValue; 513 514 if (pDraw->type == DRAWABLE_WINDOW) { 515 WindowPtr pWin = (WindowPtr)pDraw; 516 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); 517 SwapGroupPtr pSwapGroup = pWinPriv->swapGroup; 518 SwapGroupPtr pCur; 519 520 if (!pSwapGroup) return BadDrawable; 521 if (barrier && pSwapGroup->barrier) return BadValue; 522 523 /* Update the swap barrier list */ 524 if (barrier) { 525 if (!BindSwapGroupToBarrier(barrier, pSwapGroup)) 526 return BadAlloc; 527 } else { 528 if (!UnbindSwapGroupFromBarrier(pSwapGroup->barrier, pSwapGroup)) 529 return BadDrawable; 530 } 531 532 /* Set the barrier for each member of this swap group */ 533 for (pCur = pSwapGroup; pCur; pCur = pCur->pNext) 534 pCur->barrier = barrier; 535 } 536 537 return Success; 538} 539