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