diff options
Diffstat (limited to 'hw/dmx/glxProxy/glxswap.c')
-rw-r--r-- | hw/dmx/glxProxy/glxswap.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/hw/dmx/glxProxy/glxswap.c b/hw/dmx/glxProxy/glxswap.c new file mode 100644 index 000000000..3e36833b0 --- /dev/null +++ b/hw/dmx/glxProxy/glxswap.c @@ -0,0 +1,535 @@ +/* $XFree86$ */ +/* + * 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 <kem@redhat.com> + * + */ + +#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, pointer 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, (pointer)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 = xalloc(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 */ + xfree(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; + xfree(pBarrier); + } + SwapBarrierList[i] = NULL; + } +} + +int QueryMaxSwapBarriersSGIX(int screen) +{ + return GLX_MAX_SWAP_BARRIERS; +} + +static Bool BindSwapGroupToBarrier(GLuint barrier, SwapGroupPtr pSwapGroup) +{ + SwapBarrierPtr pBarrier; + + pBarrier = xalloc(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 */ + xfree(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; +} |