428261197a
Tested by ajacoutot@, krw@, shadchin@ and jasper@ on various configurations including multihead with both zaphod and xrandr.
539 lines
16 KiB
C
539 lines
16 KiB
C
/*
|
|
* 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>
|
|
*
|
|
*/
|
|
|
|
#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 = 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;
|
|
}
|