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 #include "glxcmds.h"
43
44 typedef struct _SwapGroup *SwapGroupPtr;
45
46 static Bool SwapBarrierIsReadyToSwap(GLuint barrier);
47 static void SwapSwapBarrier(GLuint barrier);
48 static void UpdateSwapBarrierList(GLuint barrier,
49 SwapGroupPtr pOldSwap, SwapGroupPtr pNewSwap);
50
51 /************************************************************************
52 *
53 * Swap Groups
54 *
55 ************************************************************************/
56
57 typedef struct _SwapGroup {
58 WindowPtr pWin;
59 SwapGroupPtr pNext;
60
61 Bool swapping;
62 Bool sleeping;
63 GLuint barrier;
64
65 XID drawable;
66 GLXContextTag tag;
67 __GLXclientState *clState;
68 } SwapGroupRec;
69
70 static void
SwapSwapGroup(SwapGroupPtr pSwap)71 SwapSwapGroup(SwapGroupPtr pSwap)
72 {
73 SwapGroupPtr pCur;
74
75 /* All drawables in swap group are ready to swap, so just swap all
76 * drawables buffers and then wake up those clients that were
77 * previously sleeping */
78
79 for (pCur = pSwap; pCur; pCur = pCur->pNext) {
80 if (pCur->swapping) {
81 /* Swap pCur's buffers */
82 __glXDoSwapBuffers(pCur->clState, pCur->drawable, pCur->tag);
83 pCur->swapping = FALSE;
84 }
85
86 /* Wakeup client */
87 if (pCur->sleeping) {
88 ClientWakeup(pCur->clState->client);
89 pCur->sleeping = FALSE;
90 }
91 }
92 }
93
94 static Bool
SwapGroupIsReadyToSwap(SwapGroupPtr pSwap)95 SwapGroupIsReadyToSwap(SwapGroupPtr pSwap)
96 {
97 Bool isReady = TRUE;
98
99 /* The swap group is ready to swap when all drawables are ready to
100 * swap. NOTE: A drawable is also ready to swap if it is not
101 * currently mapped */
102 for (; pSwap; pSwap = pSwap->pNext) {
103 isReady &= (pSwap->swapping || !pSwap->pWin->mapped);
104 /* FIXME: Should we use pSwap->pWin->mapped or ...->realized ??? */
105 }
106
107 return isReady;
108 }
109
110 static Bool
SGSwapCleanup(ClientPtr client,void * closure)111 SGSwapCleanup(ClientPtr client, void *closure)
112 {
113 /* SwapGroupPtr pSwap = (SwapGroupPtr)closure; */
114
115 /* This should not be called unless the client has died in which
116 * case we should remove the buffer from the swap list */
117
118 return TRUE;
119 }
120
121 int
SGSwapBuffers(__GLXclientState * cl,XID drawId,GLXContextTag tag,DrawablePtr pDraw)122 SGSwapBuffers(__GLXclientState * cl, XID drawId, GLXContextTag tag,
123 DrawablePtr pDraw)
124 {
125 WindowPtr pWin = (WindowPtr) pDraw;
126 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin);
127 SwapGroupPtr pSwap = pWinPriv->swapGroup;
128 SwapGroupPtr pCur;
129
130 for (pCur = pSwap; pCur && pCur->pWin != pWin; pCur = pCur->pNext);
131 if (!pCur)
132 return BadDrawable;
133
134 pCur->clState = cl;
135 pCur->drawable = drawId;
136 pCur->tag = tag;
137
138 /* We are now in the process of swapping */
139 pCur->swapping = TRUE;
140
141 if (pSwap->barrier && SwapBarrierIsReadyToSwap(pSwap->barrier)) {
142 /* The swap group is bound to a barrier and the barrier is ready
143 * to swap, so swap all the swap groups that are bound to this
144 * group's swap barrier */
145 SwapSwapBarrier(pSwap->barrier);
146 }
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 }
152 else {
153 /* The swap group/barrier is not yet ready to swap, so put
154 * client to sleep until the rest are ready to swap */
155 ClientSleep(cl->client, SGSwapCleanup, (void *) pWin);
156 pCur->sleeping = TRUE;
157 }
158
159 return Success;
160 }
161
162 static void
SGWindowUnmapped(WindowPtr pWin)163 SGWindowUnmapped(WindowPtr pWin)
164 {
165 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin);
166 SwapGroupPtr pSwap = pWinPriv->swapGroup;
167
168 /* Now that one of the windows in the swap group has been unmapped,
169 * see if the entire swap group/barrier is ready to swap */
170
171 if (pSwap->barrier && SwapBarrierIsReadyToSwap(pSwap->barrier)) {
172 SwapSwapBarrier(pSwap->barrier);
173 }
174 else if (!pSwap->barrier && SwapGroupIsReadyToSwap(pSwap)) {
175 SwapSwapGroup(pSwap);
176 }
177 }
178
179 static void
SGWindowDestroyed(WindowPtr pWin)180 SGWindowDestroyed(WindowPtr pWin)
181 {
182 JoinSwapGroupSGIX((DrawablePtr) pWin, NULL);
183 }
184
185 static SwapGroupPtr
CreateSwapEntry(WindowPtr pWin)186 CreateSwapEntry(WindowPtr pWin)
187 {
188 SwapGroupPtr pEntry;
189
190 /* Allocate new swap group */
191 pEntry = malloc(sizeof(*pEntry));
192 if (!pEntry)
193 return NULL;
194
195 /* Initialize swap group */
196 pEntry->pWin = pWin;
197 pEntry->pNext = NULL;
198 pEntry->swapping = FALSE;
199 pEntry->sleeping = FALSE;
200 pEntry->barrier = 0;
201 /* The following are not initialized until SwapBuffers is called:
202 * pEntry->drawable
203 * pEntry->tag
204 * pEntry->clState
205 */
206
207 return pEntry;
208 }
209
210 static void
FreeSwapEntry(SwapGroupPtr pEntry)211 FreeSwapEntry(SwapGroupPtr pEntry)
212 {
213 /* Since we have removed the drawable from its previous swap group
214 * and it won't be added to another swap group, the only thing that
215 * we need to do is to make sure that the drawable's client is not
216 * sleeping. This could happen if one thread is sleeping, while
217 * another thread called glxJoinSwapGroup(). Note that all sleeping
218 * threads should also be swapping, but there is a small window in
219 * the SGSwapBuffer() logic, above, where swapping can be set but
220 * sleeping is not. We check both independently here just to be
221 * pedantic. */
222
223 /* Handle swap buffer request */
224 if (pEntry->swapping)
225 __glXDoSwapBuffers(pEntry->clState, pEntry->drawable, pEntry->tag);
226
227 /* Wake up client */
228 if (pEntry->sleeping)
229 ClientWakeup(pEntry->clState->client);
230
231 /* We can free the pEntry entry since it has already been removed
232 * from the swap group list and it won't be needed any longer */
233 free(pEntry);
234 }
235
236 int
JoinSwapGroupSGIX(DrawablePtr pDraw,DrawablePtr pMember)237 JoinSwapGroupSGIX(DrawablePtr pDraw, DrawablePtr pMember)
238 {
239 if (pDraw->type == DRAWABLE_WINDOW) {
240 WindowPtr pWin = (WindowPtr) pDraw;
241 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin);
242 SwapGroupPtr pOldSwap = NULL;
243 SwapGroupPtr pEntry;
244
245 /* If pDraw and pMember are already members of the same swap
246 * group, just return Success since there is nothing to do */
247 for (pEntry = pWinPriv->swapGroup; pEntry; pEntry = pEntry->pNext)
248 if (pEntry->pWin == (WindowPtr) pMember)
249 return Success;
250
251 /* Remove pDraw from its current swap group */
252 if (pWinPriv->swapGroup) {
253 SwapGroupPtr pSwapGroup = pWinPriv->swapGroup;
254 SwapGroupPtr pPrev;
255
256 /* Find old swap entry in swap group and save in pOldSwap
257 * for later use */
258 for (pOldSwap = pWinPriv->swapGroup, pPrev = NULL;
259 pOldSwap && pOldSwap->pWin != pWin;
260 pPrev = pOldSwap, pOldSwap = pOldSwap->pNext);
261 if (!pOldSwap)
262 return BadDrawable;
263
264 /* Remove pDraw's swap group entry from swap group list */
265 if (pPrev) {
266 pPrev->pNext = pOldSwap->pNext;
267 }
268 else {
269 /* pWin is at the head of the swap group list, so we
270 * need to update all other members of this swap
271 * group */
272 for (pEntry = pOldSwap->pNext; pEntry; pEntry = pEntry->pNext)
273 DMX_GET_WINDOW_PRIV(pEntry->pWin)->swapGroup
274 = pOldSwap->pNext;
275
276 /* Update the barrier list as well */
277 if (pOldSwap->barrier)
278 UpdateSwapBarrierList(pOldSwap->barrier,
279 pOldSwap, pOldSwap->pNext);
280
281 /* Set pSwapGroup to point to the swap group without
282 * pOldSwap */
283 pSwapGroup = pOldSwap->pNext;
284 }
285
286 /* Check to see if current swap group can now swap since we
287 * know at this point that pDraw and pMember are guaranteed
288 * to previously be in different swap groups */
289 if (pSwapGroup && SwapGroupIsReadyToSwap(pSwapGroup)) {
290 SwapSwapGroup(pSwapGroup);
291 }
292
293 /* Make the old swap entry a standalone group */
294 pOldSwap->pNext = NULL;
295 pOldSwap->barrier = 0;
296
297 /* Reset pWin's swap group */
298 pWinPriv->swapGroup = NULL;
299 pWinPriv->windowDestroyed = NULL;
300 pWinPriv->windowUnmapped = NULL;
301 }
302
303 if (!pMember || pMember->type != DRAWABLE_WINDOW) {
304 /* Free old swap group since it is no longer needed */
305 if (pOldSwap)
306 FreeSwapEntry(pOldSwap);
307 }
308 else if (pDraw == pMember && pOldSwap) {
309 /* Special case where pDraw was previously created and we
310 * are now just putting it to its own swap group */
311 pWinPriv->swapGroup = pOldSwap;
312 pWinPriv->windowDestroyed = SGWindowDestroyed;
313 pWinPriv->windowUnmapped = SGWindowUnmapped;
314
315 /* Check to see if pDraw is ready to swap */
316 if (SwapGroupIsReadyToSwap(pOldSwap))
317 SwapSwapGroup(pOldSwap);
318 }
319 else if (pMember->type == DRAWABLE_WINDOW) {
320 WindowPtr pMemberWin = (WindowPtr) pMember;
321 dmxWinPrivPtr pMemberPriv = DMX_GET_WINDOW_PRIV(pMemberWin);
322 SwapGroupPtr pMemberSwapGroup = pMemberPriv->swapGroup;
323
324 /* Finally, how we can add pDraw to pMember's swap group */
325
326 /* If pMember is not currently in a swap group, then create
327 * one for it since we are just about to add pDraw to it. */
328 if (!pMemberSwapGroup) {
329 /* Create new swap group */
330 pMemberSwapGroup = CreateSwapEntry(pMemberWin);
331 if (!pMemberSwapGroup) {
332 if (pOldSwap)
333 FreeSwapEntry(pOldSwap);
334 return BadAlloc;
335 }
336
337 /* Set pMember's swap group */
338 pMemberPriv->swapGroup = pMemberSwapGroup;
339 pMemberPriv->windowDestroyed = SGWindowDestroyed;
340 pMemberPriv->windowUnmapped = SGWindowUnmapped;
341 }
342
343 /* If pDraw == pMember, that means pDraw was not a member of
344 * a group previously (or it would have been handled by the
345 * special case above), so no additional work is required
346 * since we just created a new swap group for pMember (i.e.,
347 * pDraw). */
348
349 if (pDraw != pMember) {
350 /* If pDraw was not previously in a swap group, then create
351 * an entry for it */
352 if (!pOldSwap) {
353 /* Create new swap group */
354 pOldSwap = CreateSwapEntry(pWin);
355 if (!pOldSwap) {
356 /* If we just created a swap group for pMember, we
357 * need to free it here */
358 if (pMemberSwapGroup->pNext == NULL) {
359 FreeSwapEntry(pMemberSwapGroup);
360 pMemberPriv->swapGroup = NULL;
361 }
362 return BadAlloc;
363 }
364 }
365
366 /* Find last entry in pMember's swap group */
367 for (pEntry = pMemberSwapGroup;
368 pEntry->pNext; pEntry = pEntry->pNext);
369
370 /* Add pDraw's swap group entry to pMember's swap group list */
371 pEntry->pNext = pOldSwap;
372
373 /* Add pDraw to pMember's swap barrier */
374 pOldSwap->barrier = pEntry->barrier;
375
376 /* Set pDraw's swap group */
377 pWinPriv->swapGroup = pMemberSwapGroup;
378 pWinPriv->windowDestroyed = SGWindowDestroyed;
379 pWinPriv->windowUnmapped = SGWindowUnmapped;
380 }
381 }
382 }
383
384 return Success;
385 }
386
387 /************************************************************************
388 *
389 * Swap Barriers
390 *
391 ************************************************************************/
392
393 #define GLX_MAX_SWAP_BARRIERS 10
394
395 typedef struct _SwapBarrier *SwapBarrierPtr;
396 typedef struct _SwapBarrier {
397 SwapGroupPtr pSwap;
398 SwapBarrierPtr pNext;
399 } SwapBarrierRec;
400
401 static SwapBarrierPtr SwapBarrierList[GLX_MAX_SWAP_BARRIERS + 1];
402
403 void
SwapBarrierInit(void)404 SwapBarrierInit(void)
405 {
406 int i;
407
408 for (i = 0; i <= GLX_MAX_SWAP_BARRIERS; i++)
409 SwapBarrierList[i] = NULL;
410 }
411
412 void
SwapBarrierReset(void)413 SwapBarrierReset(void)
414 {
415 int i;
416
417 for (i = 0; i <= GLX_MAX_SWAP_BARRIERS; i++) {
418 SwapBarrierPtr pBarrier, pNextBarrier;
419
420 for (pBarrier = SwapBarrierList[i]; pBarrier; pBarrier = pNextBarrier) {
421 pNextBarrier = pBarrier->pNext;
422 free(pBarrier);
423 }
424 SwapBarrierList[i] = NULL;
425 }
426 }
427
428 int
QueryMaxSwapBarriersSGIX(int screen)429 QueryMaxSwapBarriersSGIX(int screen)
430 {
431 return GLX_MAX_SWAP_BARRIERS;
432 }
433
434 static Bool
BindSwapGroupToBarrier(GLuint barrier,SwapGroupPtr pSwapGroup)435 BindSwapGroupToBarrier(GLuint barrier, SwapGroupPtr pSwapGroup)
436 {
437 SwapBarrierPtr pBarrier;
438
439 pBarrier = malloc(sizeof(*pBarrier));
440 if (!pBarrier)
441 return FALSE;
442
443 /* Add the swap group to barrier's list */
444 pBarrier->pSwap = pSwapGroup;
445 pBarrier->pNext = SwapBarrierList[barrier];
446 SwapBarrierList[barrier] = pBarrier;
447
448 return TRUE;
449 }
450
451 static Bool
UnbindSwapGroupFromBarrier(GLuint barrier,SwapGroupPtr pSwapGroup)452 UnbindSwapGroupFromBarrier(GLuint barrier, SwapGroupPtr pSwapGroup)
453 {
454 SwapBarrierPtr pBarrier, pPrevBarrier;
455
456 /* Find the swap group in barrier's list */
457 for (pBarrier = SwapBarrierList[barrier], pPrevBarrier = NULL;
458 pBarrier && pBarrier->pSwap != pSwapGroup;
459 pPrevBarrier = pBarrier, pBarrier = pBarrier->pNext);
460 if (!pBarrier)
461 return FALSE;
462
463 /* Remove the swap group from barrier's list */
464 if (pPrevBarrier)
465 pPrevBarrier->pNext = pBarrier->pNext;
466 else
467 SwapBarrierList[barrier] = pBarrier->pNext;
468
469 /* Free memory */
470 free(pBarrier);
471
472 return TRUE;
473 }
474
475 static void
UpdateSwapBarrierList(GLuint barrier,SwapGroupPtr pOldSwap,SwapGroupPtr pNewSwap)476 UpdateSwapBarrierList(GLuint barrier,
477 SwapGroupPtr pOldSwap, SwapGroupPtr pNewSwap)
478 {
479 SwapBarrierPtr pBarrier;
480
481 /* If the old swap group is being destroyed, then we need to remove
482 * the swap group from the list entirely */
483 if (!pNewSwap) {
484 UnbindSwapGroupFromBarrier(barrier, pOldSwap);
485 return;
486 }
487
488 /* Otherwise, find the old swap group in the barrier list and change
489 * it to the new swap group */
490 for (pBarrier = SwapBarrierList[barrier];
491 pBarrier; pBarrier = pBarrier->pNext) {
492 if (pBarrier->pSwap == pOldSwap) {
493 pBarrier->pSwap = pNewSwap;
494 return;
495 }
496 }
497 }
498
499 static Bool
SwapBarrierIsReadyToSwap(GLuint barrier)500 SwapBarrierIsReadyToSwap(GLuint barrier)
501 {
502 SwapBarrierPtr pBarrier;
503 Bool isReady = TRUE;
504
505 /* The swap barier is ready to swap when swap groups that are bound
506 * to barrier are ready to swap */
507 for (pBarrier = SwapBarrierList[barrier];
508 pBarrier; pBarrier = pBarrier->pNext)
509 isReady &= SwapGroupIsReadyToSwap(pBarrier->pSwap);
510
511 return isReady;
512 }
513
514 static void
SwapSwapBarrier(GLuint barrier)515 SwapSwapBarrier(GLuint barrier)
516 {
517 SwapBarrierPtr pBarrier;
518
519 /* Swap each group that is a member of this barrier */
520 for (pBarrier = SwapBarrierList[barrier];
521 pBarrier; pBarrier = pBarrier->pNext)
522 SwapSwapGroup(pBarrier->pSwap);
523 }
524
525 int
BindSwapBarrierSGIX(DrawablePtr pDraw,int barrier)526 BindSwapBarrierSGIX(DrawablePtr pDraw, int barrier)
527 {
528 /* FIXME: Check for errors when pDraw->type != DRAWABLE_WINDOW */
529
530 if (barrier < 0 || barrier > GLX_MAX_SWAP_BARRIERS)
531 return BadValue;
532
533 if (pDraw->type == DRAWABLE_WINDOW) {
534 WindowPtr pWin = (WindowPtr) pDraw;
535 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin);
536 SwapGroupPtr pSwapGroup = pWinPriv->swapGroup;
537 SwapGroupPtr pCur;
538
539 if (!pSwapGroup)
540 return BadDrawable;
541 if (barrier && pSwapGroup->barrier)
542 return BadValue;
543
544 /* Update the swap barrier list */
545 if (barrier) {
546 if (!BindSwapGroupToBarrier(barrier, pSwapGroup))
547 return BadAlloc;
548 }
549 else {
550 if (!UnbindSwapGroupFromBarrier(pSwapGroup->barrier, pSwapGroup))
551 return BadDrawable;
552 }
553
554 /* Set the barrier for each member of this swap group */
555 for (pCur = pSwapGroup; pCur; pCur = pCur->pNext)
556 pCur->barrier = barrier;
557 }
558
559 return Success;
560 }
561