/* * Copyright 2003 Red Hat Inc., Raleigh, North Carolina. * * 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 on 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 (including the * next paragraph) 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 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS * 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. */ /* * Authors: * Kevin E. Martin * */ #ifdef HAVE_DMX_CONFIG_H #include #endif #include "dmx.h" #include "dmxwindow.h" #include "glxserver.h" #include "glxswap.h" extern int __glXDoSwapBuffers(__GLXclientState * cl, XID drawId, GLXContextTag tag); typedef struct _SwapGroup *SwapGroupPtr; static Bool SwapBarrierIsReadyToSwap(GLuint barrier); static void SwapSwapBarrier(GLuint barrier); static void UpdateSwapBarrierList(GLuint barrier, SwapGroupPtr pOldSwap, SwapGroupPtr pNewSwap); /************************************************************************ * * Swap Groups * ************************************************************************/ typedef struct _SwapGroup { WindowPtr pWin; SwapGroupPtr pNext; Bool swapping; Bool sleeping; GLuint barrier; XID drawable; GLXContextTag tag; __GLXclientState *clState; } SwapGroupRec; static void SwapSwapGroup(SwapGroupPtr pSwap) { SwapGroupPtr pCur; /* All drawables in swap group are ready to swap, so just swap all * drawables buffers and then wake up those clients that were * previously sleeping */ for (pCur = pSwap; pCur; pCur = pCur->pNext) { if (pCur->swapping) { /* Swap pCur's buffers */ __glXDoSwapBuffers(pCur->clState, pCur->drawable, pCur->tag); pCur->swapping = FALSE; } /* Wakeup client */ if (pCur->sleeping) { ClientWakeup(pCur->clState->client); pCur->sleeping = FALSE; } } } static Bool SwapGroupIsReadyToSwap(SwapGroupPtr pSwap) { Bool isReady = TRUE; /* The swap group is ready to swap when all drawables are ready to * swap. NOTE: A drawable is also ready to swap if it is not * currently mapped */ for (; pSwap; pSwap = pSwap->pNext) { isReady &= (pSwap->swapping || !pSwap->pWin->mapped); /* FIXME: Should we use pSwap->pWin->mapped or ...->realized ??? */ } return isReady; } static Bool SGSwapCleanup(ClientPtr client, void *closure) { /* SwapGroupPtr pSwap = (SwapGroupPtr)closure; */ /* This should not be called unless the client has died in which * case we should remove the buffer from the swap list */ return TRUE; } int SGSwapBuffers(__GLXclientState * cl, XID drawId, GLXContextTag tag, DrawablePtr pDraw) { WindowPtr pWin = (WindowPtr) pDraw; dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); SwapGroupPtr pSwap = pWinPriv->swapGroup; SwapGroupPtr pCur; for (pCur = pSwap; pCur && pCur->pWin != pWin; pCur = pCur->pNext); if (!pCur) return BadDrawable; pCur->clState = cl; pCur->drawable = drawId; pCur->tag = tag; /* We are now in the process of swapping */ pCur->swapping = TRUE; if (pSwap->barrier && SwapBarrierIsReadyToSwap(pSwap->barrier)) { /* The swap group is bound to a barrier and the barrier is ready * to swap, so swap all the swap groups that are bound to this * group's swap barrier */ SwapSwapBarrier(pSwap->barrier); } else if (!pSwap->barrier && SwapGroupIsReadyToSwap(pSwap)) { /* Do the swap if the entire swap group is ready to swap and the * group is not bound to a swap barrier */ SwapSwapGroup(pSwap); } else { /* The swap group/barrier is not yet ready to swap, so put * client to sleep until the rest are ready to swap */ ClientSleep(cl->client, SGSwapCleanup, (void *) pWin); pCur->sleeping = TRUE; } return Success; } static void SGWindowUnmapped(WindowPtr pWin) { dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); SwapGroupPtr pSwap = pWinPriv->swapGroup; /* Now that one of the windows in the swap group has been unmapped, * see if the entire swap group/barrier is ready to swap */ if (pSwap->barrier && SwapBarrierIsReadyToSwap(pSwap->barrier)) { SwapSwapBarrier(pSwap->barrier); } else if (!pSwap->barrier && SwapGroupIsReadyToSwap(pSwap)) { SwapSwapGroup(pSwap); } } static void SGWindowDestroyed(WindowPtr pWin) { JoinSwapGroupSGIX((DrawablePtr) pWin, NULL); } static SwapGroupPtr CreateSwapEntry(WindowPtr pWin) { SwapGroupPtr pEntry; /* Allocate new swap group */ pEntry = malloc(sizeof(*pEntry)); if (!pEntry) return NULL; /* Initialize swap group */ pEntry->pWin = pWin; pEntry->pNext = NULL; pEntry->swapping = FALSE; pEntry->sleeping = FALSE; pEntry->barrier = 0; /* The following are not initialized until SwapBuffers is called: * pEntry->drawable * pEntry->tag * pEntry->clState */ return pEntry; } static void FreeSwapEntry(SwapGroupPtr pEntry) { /* Since we have removed the drawable from its previous swap group * and it won't be added to another swap group, the only thing that * we need to do is to make sure that the drawable's client is not * sleeping. This could happen if one thread is sleeping, while * another thread called glxJoinSwapGroup(). Note that all sleeping * threads should also be swapping, but there is a small window in * the SGSwapBuffer() logic, above, where swapping can be set but * sleeping is not. We check both independently here just to be * pedantic. */ /* Handle swap buffer request */ if (pEntry->swapping) __glXDoSwapBuffers(pEntry->clState, pEntry->drawable, pEntry->tag); /* Wake up client */ if (pEntry->sleeping) ClientWakeup(pEntry->clState->client); /* We can free the pEntry entry since it has already been removed * from the swap group list and it won't be needed any longer */ free(pEntry); } int JoinSwapGroupSGIX(DrawablePtr pDraw, DrawablePtr pMember) { if (pDraw->type == DRAWABLE_WINDOW) { WindowPtr pWin = (WindowPtr) pDraw; dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); SwapGroupPtr pOldSwap = NULL; SwapGroupPtr pEntry; /* If pDraw and pMember are already members of the same swap * group, just return Success since there is nothing to do */ for (pEntry = pWinPriv->swapGroup; pEntry; pEntry = pEntry->pNext) if (pEntry->pWin == (WindowPtr) pMember) return Success; /* Remove pDraw from its current swap group */ if (pWinPriv->swapGroup) { SwapGroupPtr pSwapGroup = pWinPriv->swapGroup; SwapGroupPtr pPrev; /* Find old swap entry in swap group and save in pOldSwap * for later use */ for (pOldSwap = pWinPriv->swapGroup, pPrev = NULL; pOldSwap && pOldSwap->pWin != pWin; pPrev = pOldSwap, pOldSwap = pOldSwap->pNext); if (!pOldSwap) return BadDrawable; /* Remove pDraw's swap group entry from swap group list */ if (pPrev) { pPrev->pNext = pOldSwap->pNext; } else { /* pWin is at the head of the swap group list, so we * need to update all other members of this swap * group */ for (pEntry = pOldSwap->pNext; pEntry; pEntry = pEntry->pNext) DMX_GET_WINDOW_PRIV(pEntry->pWin)->swapGroup = pOldSwap->pNext; /* Update the barrier list as well */ if (pOldSwap->barrier) UpdateSwapBarrierList(pOldSwap->barrier, pOldSwap, pOldSwap->pNext); /* Set pSwapGroup to point to the swap group without * pOldSwap */ pSwapGroup = pOldSwap->pNext; } /* Check to see if current swap group can now swap since we * know at this point that pDraw and pMember are guaranteed * to previously be in different swap groups */ if (pSwapGroup && SwapGroupIsReadyToSwap(pSwapGroup)) { SwapSwapGroup(pSwapGroup); } /* Make the old swap entry a standalone group */ pOldSwap->pNext = NULL; pOldSwap->barrier = 0; /* Reset pWin's swap group */ pWinPriv->swapGroup = NULL; pWinPriv->windowDestroyed = NULL; pWinPriv->windowUnmapped = NULL; } if (!pMember || pMember->type != DRAWABLE_WINDOW) { /* Free old swap group since it is no longer needed */ if (pOldSwap) FreeSwapEntry(pOldSwap); } else if (pDraw == pMember && pOldSwap) { /* Special case where pDraw was previously created and we * are now just putting it to its own swap group */ pWinPriv->swapGroup = pOldSwap; pWinPriv->windowDestroyed = SGWindowDestroyed; pWinPriv->windowUnmapped = SGWindowUnmapped; /* Check to see if pDraw is ready to swap */ if (SwapGroupIsReadyToSwap(pOldSwap)) SwapSwapGroup(pOldSwap); } else if (pMember->type == DRAWABLE_WINDOW) { WindowPtr pMemberWin = (WindowPtr) pMember; dmxWinPrivPtr pMemberPriv = DMX_GET_WINDOW_PRIV(pMemberWin); SwapGroupPtr pMemberSwapGroup = pMemberPriv->swapGroup; /* Finally, how we can add pDraw to pMember's swap group */ /* If pMember is not currently in a swap group, then create * one for it since we are just about to add pDraw to it. */ if (!pMemberSwapGroup) { /* Create new swap group */ pMemberSwapGroup = CreateSwapEntry(pMemberWin); if (!pMemberSwapGroup) { if (pOldSwap) FreeSwapEntry(pOldSwap); return BadAlloc; } /* Set pMember's swap group */ pMemberPriv->swapGroup = pMemberSwapGroup; pMemberPriv->windowDestroyed = SGWindowDestroyed; pMemberPriv->windowUnmapped = SGWindowUnmapped; } /* If pDraw == pMember, that means pDraw was not a member of * a group previously (or it would have been handled by the * special case above), so no additional work is required * since we just created a new swap group for pMember (i.e., * pDraw). */ if (pDraw != pMember) { /* If pDraw was not previously in a swap group, then create * an entry for it */ if (!pOldSwap) { /* Create new swap group */ pOldSwap = CreateSwapEntry(pWin); if (!pOldSwap) { /* If we just created a swap group for pMember, we * need to free it here */ if (pMemberSwapGroup->pNext == NULL) { FreeSwapEntry(pMemberSwapGroup); pMemberPriv->swapGroup = NULL; } return BadAlloc; } } /* Find last entry in pMember's swap group */ for (pEntry = pMemberSwapGroup; pEntry->pNext; pEntry = pEntry->pNext); /* Add pDraw's swap group entry to pMember's swap group list */ pEntry->pNext = pOldSwap; /* Add pDraw to pMember's swap barrier */ pOldSwap->barrier = pEntry->barrier; /* Set pDraw's swap group */ pWinPriv->swapGroup = pMemberSwapGroup; pWinPriv->windowDestroyed = SGWindowDestroyed; pWinPriv->windowUnmapped = SGWindowUnmapped; } } } return Success; } /************************************************************************ * * Swap Barriers * ************************************************************************/ #define GLX_MAX_SWAP_BARRIERS 10 typedef struct _SwapBarrier *SwapBarrierPtr; typedef struct _SwapBarrier { SwapGroupPtr pSwap; SwapBarrierPtr pNext; } SwapBarrierRec; static SwapBarrierPtr SwapBarrierList[GLX_MAX_SWAP_BARRIERS + 1]; void SwapBarrierInit(void) { int i; for (i = 0; i <= GLX_MAX_SWAP_BARRIERS; i++) SwapBarrierList[i] = NULL; } void SwapBarrierReset(void) { int i; for (i = 0; i <= GLX_MAX_SWAP_BARRIERS; i++) { SwapBarrierPtr pBarrier, pNextBarrier; for (pBarrier = SwapBarrierList[i]; pBarrier; pBarrier = pNextBarrier) { pNextBarrier = pBarrier->pNext; free(pBarrier); } SwapBarrierList[i] = NULL; } } int QueryMaxSwapBarriersSGIX(int screen) { return GLX_MAX_SWAP_BARRIERS; } static Bool BindSwapGroupToBarrier(GLuint barrier, SwapGroupPtr pSwapGroup) { SwapBarrierPtr pBarrier; pBarrier = malloc(sizeof(*pBarrier)); if (!pBarrier) return FALSE; /* Add the swap group to barrier's list */ pBarrier->pSwap = pSwapGroup; pBarrier->pNext = SwapBarrierList[barrier]; SwapBarrierList[barrier] = pBarrier; return TRUE; } static Bool UnbindSwapGroupFromBarrier(GLuint barrier, SwapGroupPtr pSwapGroup) { SwapBarrierPtr pBarrier, pPrevBarrier; /* Find the swap group in barrier's list */ for (pBarrier = SwapBarrierList[barrier], pPrevBarrier = NULL; pBarrier && pBarrier->pSwap != pSwapGroup; pPrevBarrier = pBarrier, pBarrier = pBarrier->pNext); if (!pBarrier) return FALSE; /* Remove the swap group from barrier's list */ if (pPrevBarrier) pPrevBarrier->pNext = pBarrier->pNext; else SwapBarrierList[barrier] = pBarrier->pNext; /* Free memory */ free(pBarrier); return TRUE; } static void UpdateSwapBarrierList(GLuint barrier, SwapGroupPtr pOldSwap, SwapGroupPtr pNewSwap) { SwapBarrierPtr pBarrier; /* If the old swap group is being destroyed, then we need to remove * the swap group from the list entirely */ if (!pNewSwap) { UnbindSwapGroupFromBarrier(barrier, pOldSwap); return; } /* Otherwise, find the old swap group in the barrier list and change * it to the new swap group */ for (pBarrier = SwapBarrierList[barrier]; pBarrier; pBarrier = pBarrier->pNext) { if (pBarrier->pSwap == pOldSwap) { pBarrier->pSwap = pNewSwap; return; } } } static Bool SwapBarrierIsReadyToSwap(GLuint barrier) { SwapBarrierPtr pBarrier; Bool isReady = TRUE; /* The swap barier is ready to swap when swap groups that are bound * to barrier are ready to swap */ for (pBarrier = SwapBarrierList[barrier]; pBarrier; pBarrier = pBarrier->pNext) isReady &= SwapGroupIsReadyToSwap(pBarrier->pSwap); return isReady; } static void SwapSwapBarrier(GLuint barrier) { SwapBarrierPtr pBarrier; /* Swap each group that is a member of this barrier */ for (pBarrier = SwapBarrierList[barrier]; pBarrier; pBarrier = pBarrier->pNext) SwapSwapGroup(pBarrier->pSwap); } int BindSwapBarrierSGIX(DrawablePtr pDraw, int barrier) { /* FIXME: Check for errors when pDraw->type != DRAWABLE_WINDOW */ if (barrier < 0 || barrier > GLX_MAX_SWAP_BARRIERS) return BadValue; if (pDraw->type == DRAWABLE_WINDOW) { WindowPtr pWin = (WindowPtr) pDraw; dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin); SwapGroupPtr pSwapGroup = pWinPriv->swapGroup; SwapGroupPtr pCur; if (!pSwapGroup) return BadDrawable; if (barrier && pSwapGroup->barrier) return BadValue; /* Update the swap barrier list */ if (barrier) { if (!BindSwapGroupToBarrier(barrier, pSwapGroup)) return BadAlloc; } else { if (!UnbindSwapGroupFromBarrier(pSwapGroup->barrier, pSwapGroup)) return BadDrawable; } /* Set the barrier for each member of this swap group */ for (pCur = pSwapGroup; pCur; pCur = pCur->pNext) pCur->barrier = barrier; } return Success; }