/*
* 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 <[email protected]>
*
*/
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#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, 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;
}
|