Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright © 2007, 2008 Red Hat, Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Soft- | |
6 | * ware"), to deal in the Software without restriction, including without | |
7 | * limitation the rights to use, copy, modify, merge, publish, distribute, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, provided that the above copyright | |
10 | * notice(s) and this permission notice appear in all copies of the Soft- | |
11 | * ware and that both the above copyright notice(s) and this permission | |
12 | * notice appear in supporting documentation. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
16 | * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY | |
17 | * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN | |
18 | * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE- | |
19 | * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
20 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
21 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR- | |
22 | * MANCE OF THIS SOFTWARE. | |
23 | * | |
24 | * Except as contained in this notice, the name of a copyright holder shall | |
25 | * not be used in advertising or otherwise to promote the sale, use or | |
26 | * other dealings in this Software without prior written authorization of | |
27 | * the copyright holder. | |
28 | * | |
29 | * Authors: | |
30 | * Kristian Høgsberg (krh@redhat.com) | |
31 | */ | |
32 | ||
33 | #ifdef HAVE_XORG_CONFIG_H | |
34 | #include <xorg-config.h> | |
35 | #endif | |
36 | ||
37 | #include <errno.h> | |
38 | #ifdef WITH_LIBDRM | |
39 | #include <xf86drm.h> | |
40 | #endif | |
41 | #include "xf86Module.h" | |
42 | #include "list.h" | |
43 | #include "scrnintstr.h" | |
44 | #include "windowstr.h" | |
45 | #include "dixstruct.h" | |
46 | #include "dri2.h" | |
47 | #include "dri2int.h" | |
48 | #include "xf86VGAarbiter.h" | |
49 | #include "damage.h" | |
50 | #include "xf86.h" | |
51 | ||
52 | CARD8 dri2_major; /* version of DRI2 supported by DDX */ | |
53 | CARD8 dri2_minor; | |
54 | ||
55 | uint32_t prime_id_allocate_bitmask; | |
56 | ||
57 | static DevPrivateKeyRec dri2ScreenPrivateKeyRec; | |
58 | ||
59 | #define dri2ScreenPrivateKey (&dri2ScreenPrivateKeyRec) | |
60 | ||
61 | static DevPrivateKeyRec dri2WindowPrivateKeyRec; | |
62 | ||
63 | #define dri2WindowPrivateKey (&dri2WindowPrivateKeyRec) | |
64 | ||
65 | static DevPrivateKeyRec dri2PixmapPrivateKeyRec; | |
66 | ||
67 | #define dri2PixmapPrivateKey (&dri2PixmapPrivateKeyRec) | |
68 | ||
69 | static DevPrivateKeyRec dri2ClientPrivateKeyRec; | |
70 | ||
71 | #define dri2ClientPrivateKey (&dri2ClientPrivateKeyRec) | |
72 | ||
73 | #define dri2ClientPrivate(_pClient) (dixLookupPrivate(&(_pClient)->devPrivates, \ | |
74 | dri2ClientPrivateKey)) | |
75 | ||
76 | typedef struct _DRI2Client { | |
77 | int prime_id; | |
78 | } DRI2ClientRec, *DRI2ClientPtr; | |
79 | ||
80 | static RESTYPE dri2DrawableRes; | |
81 | ||
82 | typedef struct _DRI2Screen *DRI2ScreenPtr; | |
83 | ||
84 | typedef struct _DRI2Drawable { | |
85 | DRI2ScreenPtr dri2_screen; | |
86 | DrawablePtr drawable; | |
87 | struct xorg_list reference_list; | |
88 | int width; | |
89 | int height; | |
90 | DRI2BufferPtr *buffers; | |
91 | int bufferCount; | |
92 | unsigned int swapsPending; | |
93 | ClientPtr blockedClient; | |
94 | Bool blockedOnMsc; | |
95 | int swap_interval; | |
96 | CARD64 swap_count; | |
97 | int64_t target_sbc; /* -1 means no SBC wait outstanding */ | |
98 | CARD64 last_swap_target; /* most recently queued swap target */ | |
99 | CARD64 last_swap_msc; /* msc at completion of most recent swap */ | |
100 | CARD64 last_swap_ust; /* ust at completion of most recent swap */ | |
101 | int swap_limit; /* for N-buffering */ | |
102 | Bool needInvalidate; | |
103 | int prime_id; | |
104 | PixmapPtr prime_slave_pixmap; | |
105 | PixmapPtr redirectpixmap; | |
106 | } DRI2DrawableRec, *DRI2DrawablePtr; | |
107 | ||
108 | typedef struct _DRI2Screen { | |
109 | ScreenPtr screen; | |
110 | int refcnt; | |
111 | unsigned int numDrivers; | |
112 | const char **driverNames; | |
113 | const char *deviceName; | |
114 | int fd; | |
115 | unsigned int lastSequence; | |
116 | int prime_id; | |
117 | ||
118 | DRI2CreateBufferProcPtr CreateBuffer; | |
119 | DRI2DestroyBufferProcPtr DestroyBuffer; | |
120 | DRI2CopyRegionProcPtr CopyRegion; | |
121 | DRI2ScheduleSwapProcPtr ScheduleSwap; | |
122 | DRI2GetMSCProcPtr GetMSC; | |
123 | DRI2ScheduleWaitMSCProcPtr ScheduleWaitMSC; | |
124 | DRI2AuthMagic2ProcPtr AuthMagic; | |
125 | DRI2AuthMagicProcPtr LegacyAuthMagic; | |
126 | DRI2ReuseBufferNotifyProcPtr ReuseBufferNotify; | |
127 | DRI2SwapLimitValidateProcPtr SwapLimitValidate; | |
128 | DRI2GetParamProcPtr GetParam; | |
129 | ||
130 | HandleExposuresProcPtr HandleExposures; | |
131 | ||
132 | ConfigNotifyProcPtr ConfigNotify; | |
133 | DRI2CreateBuffer2ProcPtr CreateBuffer2; | |
134 | DRI2DestroyBuffer2ProcPtr DestroyBuffer2; | |
135 | DRI2CopyRegion2ProcPtr CopyRegion2; | |
136 | } DRI2ScreenRec; | |
137 | ||
138 | static void | |
139 | destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id); | |
140 | ||
141 | static DRI2ScreenPtr | |
142 | DRI2GetScreen(ScreenPtr pScreen) | |
143 | { | |
144 | return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey); | |
145 | } | |
146 | ||
147 | static ScreenPtr | |
148 | GetScreenPrime(ScreenPtr master, int prime_id) | |
149 | { | |
150 | ScreenPtr slave; | |
151 | if (prime_id == 0 || xorg_list_is_empty(&master->offload_slave_list)) { | |
152 | return master; | |
153 | } | |
154 | xorg_list_for_each_entry(slave, &master->offload_slave_list, offload_head) { | |
155 | DRI2ScreenPtr ds; | |
156 | ||
157 | ds = DRI2GetScreen(slave); | |
158 | if (ds->prime_id == prime_id) | |
159 | break; | |
160 | } | |
161 | if (!slave) | |
162 | return master; | |
163 | return slave; | |
164 | } | |
165 | ||
166 | static DRI2ScreenPtr | |
167 | DRI2GetScreenPrime(ScreenPtr master, int prime_id) | |
168 | { | |
169 | ScreenPtr slave = GetScreenPrime(master, prime_id); | |
170 | return DRI2GetScreen(slave); | |
171 | } | |
172 | ||
173 | static DRI2DrawablePtr | |
174 | DRI2GetDrawable(DrawablePtr pDraw) | |
175 | { | |
176 | WindowPtr pWin; | |
177 | PixmapPtr pPixmap; | |
178 | ||
179 | switch (pDraw->type) { | |
180 | case DRAWABLE_WINDOW: | |
181 | pWin = (WindowPtr) pDraw; | |
182 | return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey); | |
183 | case DRAWABLE_PIXMAP: | |
184 | pPixmap = (PixmapPtr) pDraw; | |
185 | return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey); | |
186 | default: | |
187 | return NULL; | |
188 | } | |
189 | } | |
190 | ||
191 | static DRI2DrawablePtr | |
192 | DRI2AllocateDrawable(DrawablePtr pDraw) | |
193 | { | |
194 | DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); | |
195 | DRI2DrawablePtr pPriv; | |
196 | CARD64 ust; | |
197 | WindowPtr pWin; | |
198 | PixmapPtr pPixmap; | |
199 | ||
200 | pPriv = malloc(sizeof *pPriv); | |
201 | if (pPriv == NULL) | |
202 | return NULL; | |
203 | ||
204 | pPriv->dri2_screen = ds; | |
205 | pPriv->drawable = pDraw; | |
206 | pPriv->width = pDraw->width; | |
207 | pPriv->height = pDraw->height; | |
208 | pPriv->buffers = NULL; | |
209 | pPriv->bufferCount = 0; | |
210 | pPriv->swapsPending = 0; | |
211 | pPriv->blockedClient = NULL; | |
212 | pPriv->blockedOnMsc = FALSE; | |
213 | pPriv->swap_count = 0; | |
214 | pPriv->target_sbc = -1; | |
215 | pPriv->swap_interval = 1; | |
216 | /* Initialize last swap target from DDX if possible */ | |
217 | if (!ds->GetMSC || !(*ds->GetMSC) (pDraw, &ust, &pPriv->last_swap_target)) | |
218 | pPriv->last_swap_target = 0; | |
219 | ||
220 | pPriv->swap_limit = 1; /* default to double buffering */ | |
221 | pPriv->last_swap_msc = 0; | |
222 | pPriv->last_swap_ust = 0; | |
223 | xorg_list_init(&pPriv->reference_list); | |
224 | pPriv->needInvalidate = FALSE; | |
225 | pPriv->redirectpixmap = NULL; | |
226 | pPriv->prime_slave_pixmap = NULL; | |
227 | if (pDraw->type == DRAWABLE_WINDOW) { | |
228 | pWin = (WindowPtr) pDraw; | |
229 | dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv); | |
230 | } | |
231 | else { | |
232 | pPixmap = (PixmapPtr) pDraw; | |
233 | dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv); | |
234 | } | |
235 | ||
236 | return pPriv; | |
237 | } | |
238 | ||
239 | Bool | |
240 | DRI2SwapLimit(DrawablePtr pDraw, int swap_limit) | |
241 | { | |
242 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); | |
243 | DRI2ScreenPtr ds; | |
244 | ||
245 | if (!pPriv) | |
246 | return FALSE; | |
247 | ||
248 | ds = pPriv->dri2_screen; | |
249 | ||
250 | if (!ds->SwapLimitValidate || !ds->SwapLimitValidate(pDraw, swap_limit)) | |
251 | return FALSE; | |
252 | ||
253 | pPriv->swap_limit = swap_limit; | |
254 | ||
255 | /* Check throttling */ | |
256 | if (pPriv->swapsPending >= pPriv->swap_limit) | |
257 | return TRUE; | |
258 | ||
259 | if (pPriv->target_sbc == -1 && !pPriv->blockedOnMsc) { | |
260 | if (pPriv->blockedClient) { | |
261 | AttendClient(pPriv->blockedClient); | |
262 | pPriv->blockedClient = NULL; | |
263 | } | |
264 | } | |
265 | ||
266 | return TRUE; | |
267 | } | |
268 | ||
269 | typedef struct DRI2DrawableRefRec { | |
270 | XID id; | |
271 | XID dri2_id; | |
272 | DRI2InvalidateProcPtr invalidate; | |
273 | void *priv; | |
274 | struct xorg_list link; | |
275 | } DRI2DrawableRefRec, *DRI2DrawableRefPtr; | |
276 | ||
277 | static DRI2DrawableRefPtr | |
278 | DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id) | |
279 | { | |
280 | DRI2DrawableRefPtr ref; | |
281 | ||
282 | xorg_list_for_each_entry(ref, &pPriv->reference_list, link) { | |
283 | if (ref->id == id) | |
284 | return ref; | |
285 | } | |
286 | ||
287 | return NULL; | |
288 | } | |
289 | ||
290 | static int | |
291 | DRI2AddDrawableRef(DRI2DrawablePtr pPriv, XID id, XID dri2_id, | |
292 | DRI2InvalidateProcPtr invalidate, void *priv) | |
293 | { | |
294 | DRI2DrawableRefPtr ref; | |
295 | ||
296 | ref = malloc(sizeof *ref); | |
297 | if (ref == NULL) | |
298 | return BadAlloc; | |
299 | ||
300 | if (!AddResource(dri2_id, dri2DrawableRes, pPriv)) { | |
301 | free(ref); | |
302 | return BadAlloc; | |
303 | } | |
304 | if (!DRI2LookupDrawableRef(pPriv, id)) | |
305 | if (!AddResource(id, dri2DrawableRes, pPriv)) { | |
306 | FreeResourceByType(dri2_id, dri2DrawableRes, TRUE); | |
307 | free(ref); | |
308 | return BadAlloc; | |
309 | } | |
310 | ||
311 | ref->id = id; | |
312 | ref->dri2_id = dri2_id; | |
313 | ref->invalidate = invalidate; | |
314 | ref->priv = priv; | |
315 | xorg_list_add(&ref->link, &pPriv->reference_list); | |
316 | ||
317 | return Success; | |
318 | } | |
319 | ||
320 | int | |
321 | DRI2CreateDrawable2(ClientPtr client, DrawablePtr pDraw, XID id, | |
322 | DRI2InvalidateProcPtr invalidate, void *priv, | |
323 | XID *dri2_id_out) | |
324 | { | |
325 | DRI2DrawablePtr pPriv; | |
326 | DRI2ClientPtr dri2_client = dri2ClientPrivate(client); | |
327 | XID dri2_id; | |
328 | int rc; | |
329 | ||
330 | pPriv = DRI2GetDrawable(pDraw); | |
331 | if (pPriv == NULL) | |
332 | pPriv = DRI2AllocateDrawable(pDraw); | |
333 | if (pPriv == NULL) | |
334 | return BadAlloc; | |
335 | ||
336 | pPriv->prime_id = dri2_client->prime_id; | |
337 | ||
338 | dri2_id = FakeClientID(client->index); | |
339 | rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv); | |
340 | if (rc != Success) | |
341 | return rc; | |
342 | ||
343 | if (dri2_id_out) | |
344 | *dri2_id_out = dri2_id; | |
345 | ||
346 | return Success; | |
347 | } | |
348 | ||
349 | int | |
350 | DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id, | |
351 | DRI2InvalidateProcPtr invalidate, void *priv) | |
352 | { | |
353 | return DRI2CreateDrawable2(client, pDraw, id, invalidate, priv, NULL); | |
354 | } | |
355 | ||
356 | static int | |
357 | DRI2DrawableGone(pointer p, XID id) | |
358 | { | |
359 | DRI2DrawablePtr pPriv = p; | |
360 | DRI2DrawableRefPtr ref, next; | |
361 | WindowPtr pWin; | |
362 | PixmapPtr pPixmap; | |
363 | DrawablePtr pDraw; | |
364 | int i; | |
365 | ||
366 | xorg_list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) { | |
367 | if (ref->dri2_id == id) { | |
368 | xorg_list_del(&ref->link); | |
369 | /* If this was the last ref under this X drawable XID, | |
370 | * unregister the X drawable resource. */ | |
371 | if (!DRI2LookupDrawableRef(pPriv, ref->id)) | |
372 | FreeResourceByType(ref->id, dri2DrawableRes, TRUE); | |
373 | free(ref); | |
374 | break; | |
375 | } | |
376 | ||
377 | if (ref->id == id) { | |
378 | xorg_list_del(&ref->link); | |
379 | FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE); | |
380 | free(ref); | |
381 | } | |
382 | } | |
383 | ||
384 | if (!xorg_list_is_empty(&pPriv->reference_list)) | |
385 | return Success; | |
386 | ||
387 | pDraw = pPriv->drawable; | |
388 | if (pDraw->type == DRAWABLE_WINDOW) { | |
389 | pWin = (WindowPtr) pDraw; | |
390 | dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL); | |
391 | } | |
392 | else { | |
393 | pPixmap = (PixmapPtr) pDraw; | |
394 | dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL); | |
395 | } | |
396 | ||
397 | if (pPriv->prime_slave_pixmap) { | |
398 | (*pPriv->prime_slave_pixmap->master_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_slave_pixmap->master_pixmap); | |
399 | (*pPriv->prime_slave_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_slave_pixmap); | |
400 | } | |
401 | ||
402 | if (pPriv->buffers != NULL) { | |
403 | for (i = 0; i < pPriv->bufferCount; i++) | |
404 | destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id); | |
405 | ||
406 | free(pPriv->buffers); | |
407 | } | |
408 | ||
409 | if (pPriv->redirectpixmap) { | |
410 | (*pDraw->pScreen->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE); | |
411 | (*pDraw->pScreen->DestroyPixmap)(pPriv->redirectpixmap); | |
412 | } | |
413 | ||
414 | free(pPriv); | |
415 | ||
416 | return Success; | |
417 | } | |
418 | ||
419 | static DRI2BufferPtr | |
420 | create_buffer(DrawablePtr pDraw, | |
421 | unsigned int attachment, unsigned int format) | |
422 | { | |
423 | ScreenPtr primeScreen; | |
424 | DRI2DrawablePtr pPriv; | |
425 | DRI2ScreenPtr ds; | |
426 | DRI2BufferPtr buffer; | |
427 | pPriv = DRI2GetDrawable(pDraw); | |
428 | primeScreen = GetScreenPrime(pDraw->pScreen, pPriv->prime_id); | |
429 | ds = DRI2GetScreenPrime(pDraw->pScreen, pPriv->prime_id); | |
430 | if (ds->CreateBuffer2) | |
431 | buffer = (*ds->CreateBuffer2)(primeScreen, pDraw, attachment, format); | |
432 | else | |
433 | buffer = (*ds->CreateBuffer)(pDraw, attachment, format); | |
434 | return buffer; | |
435 | } | |
436 | ||
437 | static void | |
438 | destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer, int prime_id) | |
439 | { | |
440 | ScreenPtr primeScreen; | |
441 | DRI2ScreenPtr ds; | |
442 | primeScreen = GetScreenPrime(pDraw->pScreen, prime_id); | |
443 | ds = DRI2GetScreen(primeScreen); | |
444 | if (ds->DestroyBuffer2) | |
445 | (*ds->DestroyBuffer2)(primeScreen, pDraw, buffer); | |
446 | else | |
447 | (*ds->DestroyBuffer)(pDraw, buffer); | |
448 | } | |
449 | ||
450 | static int | |
451 | find_attachment(DRI2DrawablePtr pPriv, unsigned attachment) | |
452 | { | |
453 | int i; | |
454 | ||
455 | if (pPriv->buffers == NULL) { | |
456 | return -1; | |
457 | } | |
458 | ||
459 | for (i = 0; i < pPriv->bufferCount; i++) { | |
460 | if ((pPriv->buffers[i] != NULL) | |
461 | && (pPriv->buffers[i]->attachment == attachment)) { | |
462 | return i; | |
463 | } | |
464 | } | |
465 | ||
466 | return -1; | |
467 | } | |
468 | ||
469 | static Bool | |
470 | allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds, | |
471 | DRI2DrawablePtr pPriv, | |
472 | unsigned int attachment, unsigned int format, | |
473 | int dimensions_match, DRI2BufferPtr * buffer) | |
474 | { | |
475 | int old_buf = find_attachment(pPriv, attachment); | |
476 | ||
477 | if ((old_buf < 0) | |
478 | || attachment == DRI2BufferFrontLeft | |
479 | || !dimensions_match || (pPriv->buffers[old_buf]->format != format)) { | |
480 | *buffer = create_buffer (pDraw, attachment, format); | |
481 | return TRUE; | |
482 | ||
483 | } | |
484 | else { | |
485 | *buffer = pPriv->buffers[old_buf]; | |
486 | ||
487 | if (ds->ReuseBufferNotify) | |
488 | (*ds->ReuseBufferNotify) (pDraw, *buffer); | |
489 | ||
490 | pPriv->buffers[old_buf] = NULL; | |
491 | return FALSE; | |
492 | } | |
493 | } | |
494 | ||
495 | static void | |
496 | update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw, | |
497 | DRI2BufferPtr * buffers, int out_count, int *width, | |
498 | int *height) | |
499 | { | |
500 | int i; | |
501 | ||
502 | if (pPriv->buffers != NULL) { | |
503 | for (i = 0; i < pPriv->bufferCount; i++) { | |
504 | if (pPriv->buffers[i] != NULL) { | |
505 | destroy_buffer(pDraw, pPriv->buffers[i], pPriv->prime_id); | |
506 | } | |
507 | } | |
508 | ||
509 | free(pPriv->buffers); | |
510 | } | |
511 | ||
512 | pPriv->buffers = buffers; | |
513 | pPriv->bufferCount = out_count; | |
514 | pPriv->width = pDraw->width; | |
515 | pPriv->height = pDraw->height; | |
516 | *width = pPriv->width; | |
517 | *height = pPriv->height; | |
518 | } | |
519 | ||
520 | static DRI2BufferPtr * | |
521 | do_get_buffers(DrawablePtr pDraw, int *width, int *height, | |
522 | unsigned int *attachments, int count, int *out_count, | |
523 | int has_format) | |
524 | { | |
525 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); | |
526 | DRI2ScreenPtr ds; | |
527 | DRI2BufferPtr *buffers; | |
528 | int need_real_front = 0; | |
529 | int need_fake_front = 0; | |
530 | int have_fake_front = 0; | |
531 | int front_format = 0; | |
532 | int dimensions_match; | |
533 | int buffers_changed = 0; | |
534 | int i; | |
535 | ||
536 | if (!pPriv) { | |
537 | *width = pDraw->width; | |
538 | *height = pDraw->height; | |
539 | *out_count = 0; | |
540 | return NULL; | |
541 | } | |
542 | ||
543 | ds = DRI2GetScreen(pDraw->pScreen); | |
544 | ||
545 | dimensions_match = (pDraw->width == pPriv->width) | |
546 | && (pDraw->height == pPriv->height); | |
547 | ||
548 | buffers = calloc((count + 1), sizeof(buffers[0])); | |
549 | if (!buffers) | |
550 | goto err_out; | |
551 | ||
552 | for (i = 0; i < count; i++) { | |
553 | const unsigned attachment = *(attachments++); | |
554 | const unsigned format = (has_format) ? *(attachments++) : 0; | |
555 | ||
556 | if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment, | |
557 | format, dimensions_match, &buffers[i])) | |
558 | buffers_changed = 1; | |
559 | ||
560 | if (buffers[i] == NULL) | |
561 | goto err_out; | |
562 | ||
563 | /* If the drawable is a window and the front-buffer is requested, | |
564 | * silently add the fake front-buffer to the list of requested | |
565 | * attachments. The counting logic in the loop accounts for the case | |
566 | * where the client requests both the fake and real front-buffer. | |
567 | */ | |
568 | if (attachment == DRI2BufferBackLeft) { | |
569 | need_real_front++; | |
570 | front_format = format; | |
571 | } | |
572 | ||
573 | if (attachment == DRI2BufferFrontLeft) { | |
574 | need_real_front--; | |
575 | front_format = format; | |
576 | ||
577 | if (pDraw->type == DRAWABLE_WINDOW) { | |
578 | need_fake_front++; | |
579 | } | |
580 | } | |
581 | ||
582 | if (pDraw->type == DRAWABLE_WINDOW) { | |
583 | if (attachment == DRI2BufferFakeFrontLeft) { | |
584 | need_fake_front--; | |
585 | have_fake_front = 1; | |
586 | } | |
587 | } | |
588 | } | |
589 | ||
590 | if (need_real_front > 0) { | |
591 | if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft, | |
592 | front_format, dimensions_match, | |
593 | &buffers[i])) | |
594 | buffers_changed = 1; | |
595 | ||
596 | if (buffers[i] == NULL) | |
597 | goto err_out; | |
598 | i++; | |
599 | } | |
600 | ||
601 | if (need_fake_front > 0) { | |
602 | if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft, | |
603 | front_format, dimensions_match, | |
604 | &buffers[i])) | |
605 | buffers_changed = 1; | |
606 | ||
607 | if (buffers[i] == NULL) | |
608 | goto err_out; | |
609 | ||
610 | i++; | |
611 | have_fake_front = 1; | |
612 | } | |
613 | ||
614 | *out_count = i; | |
615 | ||
616 | update_dri2_drawable_buffers(pPriv, pDraw, buffers, *out_count, width, | |
617 | height); | |
618 | ||
619 | /* If the client is getting a fake front-buffer, pre-fill it with the | |
620 | * contents of the real front-buffer. This ensures correct operation of | |
621 | * applications that call glXWaitX before calling glDrawBuffer. | |
622 | */ | |
623 | if (have_fake_front && buffers_changed) { | |
624 | BoxRec box; | |
625 | RegionRec region; | |
626 | ||
627 | box.x1 = 0; | |
628 | box.y1 = 0; | |
629 | box.x2 = pPriv->width; | |
630 | box.y2 = pPriv->height; | |
631 | RegionInit(®ion, &box, 0); | |
632 | ||
633 | DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft, | |
634 | DRI2BufferFrontLeft); | |
635 | } | |
636 | ||
637 | pPriv->needInvalidate = TRUE; | |
638 | ||
639 | return pPriv->buffers; | |
640 | ||
641 | err_out: | |
642 | ||
643 | *out_count = 0; | |
644 | ||
645 | if (buffers) { | |
646 | for (i = 0; i < count; i++) { | |
647 | if (buffers[i] != NULL) | |
648 | destroy_buffer(pDraw, buffers[i], 0); | |
649 | } | |
650 | ||
651 | free(buffers); | |
652 | buffers = NULL; | |
653 | } | |
654 | ||
655 | update_dri2_drawable_buffers(pPriv, pDraw, buffers, *out_count, width, | |
656 | height); | |
657 | ||
658 | return buffers; | |
659 | } | |
660 | ||
661 | DRI2BufferPtr * | |
662 | DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height, | |
663 | unsigned int *attachments, int count, int *out_count) | |
664 | { | |
665 | return do_get_buffers(pDraw, width, height, attachments, count, | |
666 | out_count, FALSE); | |
667 | } | |
668 | ||
669 | DRI2BufferPtr * | |
670 | DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height, | |
671 | unsigned int *attachments, int count, int *out_count) | |
672 | { | |
673 | return do_get_buffers(pDraw, width, height, attachments, count, | |
674 | out_count, TRUE); | |
675 | } | |
676 | ||
677 | static void | |
678 | DRI2InvalidateDrawable(DrawablePtr pDraw) | |
679 | { | |
680 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); | |
681 | DRI2DrawableRefPtr ref; | |
682 | ||
683 | if (!pPriv || !pPriv->needInvalidate) | |
684 | return; | |
685 | ||
686 | pPriv->needInvalidate = FALSE; | |
687 | ||
688 | xorg_list_for_each_entry(ref, &pPriv->reference_list, link) | |
689 | ref->invalidate(pDraw, ref->priv, ref->id); | |
690 | } | |
691 | ||
692 | /* | |
693 | * In the direct rendered case, we throttle the clients that have more | |
694 | * than their share of outstanding swaps (and thus busy buffers) when a | |
695 | * new GetBuffers request is received. In the AIGLX case, we allow the | |
696 | * client to get the new buffers, but throttle when the next GLX request | |
697 | * comes in (see __glXDRIcontextWait()). | |
698 | */ | |
699 | Bool | |
700 | DRI2ThrottleClient(ClientPtr client, DrawablePtr pDraw) | |
701 | { | |
702 | DRI2DrawablePtr pPriv; | |
703 | ||
704 | pPriv = DRI2GetDrawable(pDraw); | |
705 | if (pPriv == NULL) | |
706 | return FALSE; | |
707 | ||
708 | /* Throttle to swap limit */ | |
709 | if ((pPriv->swapsPending >= pPriv->swap_limit) && !pPriv->blockedClient) { | |
710 | ResetCurrentRequest(client); | |
711 | client->sequence--; | |
712 | IgnoreClient(client); | |
713 | pPriv->blockedClient = client; | |
714 | return TRUE; | |
715 | } | |
716 | ||
717 | return FALSE; | |
718 | } | |
719 | ||
720 | static void | |
721 | __DRI2BlockClient(ClientPtr client, DRI2DrawablePtr pPriv) | |
722 | { | |
723 | if (pPriv->blockedClient == NULL) { | |
724 | IgnoreClient(client); | |
725 | pPriv->blockedClient = client; | |
726 | } | |
727 | } | |
728 | ||
729 | void | |
730 | DRI2BlockClient(ClientPtr client, DrawablePtr pDraw) | |
731 | { | |
732 | DRI2DrawablePtr pPriv; | |
733 | ||
734 | pPriv = DRI2GetDrawable(pDraw); | |
735 | if (pPriv == NULL) | |
736 | return; | |
737 | ||
738 | __DRI2BlockClient(client, pPriv); | |
739 | pPriv->blockedOnMsc = TRUE; | |
740 | } | |
741 | ||
742 | static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable) | |
743 | { | |
744 | if (drawable->type == DRAWABLE_PIXMAP) | |
745 | return (PixmapPtr)drawable; | |
746 | else { | |
747 | struct _Window *pWin = (struct _Window *)drawable; | |
748 | return drawable->pScreen->GetWindowPixmap(pWin); | |
749 | } | |
750 | } | |
751 | ||
752 | /* | |
753 | * A TraverseTree callback to invalidate all windows using the same | |
754 | * pixmap | |
755 | */ | |
756 | static int | |
757 | DRI2InvalidateWalk(WindowPtr pWin, pointer data) | |
758 | { | |
759 | if (pWin->drawable.pScreen->GetWindowPixmap(pWin) != data) | |
760 | return WT_DONTWALKCHILDREN; | |
761 | DRI2InvalidateDrawable(&pWin->drawable); | |
762 | return WT_WALKCHILDREN; | |
763 | } | |
764 | ||
765 | static void | |
766 | DRI2InvalidateDrawableAll(DrawablePtr pDraw) | |
767 | { | |
768 | if (pDraw->type == DRAWABLE_WINDOW) { | |
769 | WindowPtr pWin = (WindowPtr) pDraw; | |
770 | PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin); | |
771 | ||
772 | /* | |
773 | * Find the top-most window using this pixmap | |
774 | */ | |
775 | while (pWin->parent && | |
776 | pDraw->pScreen->GetWindowPixmap(pWin->parent) == pPixmap) | |
777 | pWin = pWin->parent; | |
778 | ||
779 | /* | |
780 | * Walk the sub-tree to invalidate all of the | |
781 | * windows using the same pixmap | |
782 | */ | |
783 | TraverseTree(pWin, DRI2InvalidateWalk, pPixmap); | |
784 | DRI2InvalidateDrawable(&pPixmap->drawable); | |
785 | } | |
786 | else | |
787 | DRI2InvalidateDrawable(pDraw); | |
788 | } | |
789 | ||
790 | DrawablePtr DRI2UpdatePrime(DrawablePtr pDraw, DRI2BufferPtr pDest) | |
791 | { | |
792 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); | |
793 | PixmapPtr spix; | |
794 | PixmapPtr mpix = GetDrawablePixmap(pDraw); | |
795 | ScreenPtr master, slave; | |
796 | Bool ret; | |
797 | ||
798 | master = mpix->drawable.pScreen; | |
799 | ||
800 | if (pDraw->type == DRAWABLE_WINDOW) { | |
801 | WindowPtr pWin = (WindowPtr)pDraw; | |
802 | PixmapPtr pPixmap = pDraw->pScreen->GetWindowPixmap(pWin); | |
803 | ||
804 | if (pDraw->pScreen->GetScreenPixmap(pDraw->pScreen) == pPixmap) { | |
805 | if (pPriv->redirectpixmap && | |
806 | pPriv->redirectpixmap->drawable.width == pDraw->width && | |
807 | pPriv->redirectpixmap->drawable.height == pDraw->height && | |
808 | pPriv->redirectpixmap->drawable.depth == pDraw->depth) { | |
809 | mpix = pPriv->redirectpixmap; | |
810 | } else { | |
811 | if (master->ReplaceScanoutPixmap) { | |
812 | mpix = (*master->CreatePixmap)(master, pDraw->width, pDraw->height, | |
813 | pDraw->depth, CREATE_PIXMAP_USAGE_SHARED); | |
814 | if (!mpix) | |
815 | return NULL; | |
816 | ||
817 | ret = (*master->ReplaceScanoutPixmap)(pDraw, mpix, TRUE); | |
818 | if (ret == FALSE) { | |
819 | (*master->DestroyPixmap)(mpix); | |
820 | return NULL; | |
821 | } | |
822 | pPriv->redirectpixmap = mpix; | |
823 | } else | |
824 | return NULL; | |
825 | } | |
826 | } else if (pPriv->redirectpixmap) { | |
827 | (*master->ReplaceScanoutPixmap)(pDraw, pPriv->redirectpixmap, FALSE); | |
828 | (*master->DestroyPixmap)(pPriv->redirectpixmap); | |
829 | pPriv->redirectpixmap = NULL; | |
830 | } | |
831 | } | |
832 | ||
833 | slave = GetScreenPrime(pDraw->pScreen, pPriv->prime_id); | |
834 | ||
835 | /* check if the pixmap is still fine */ | |
836 | if (pPriv->prime_slave_pixmap) { | |
837 | if (pPriv->prime_slave_pixmap->master_pixmap == mpix) | |
838 | return &pPriv->prime_slave_pixmap->drawable; | |
839 | else { | |
840 | (*pPriv->prime_slave_pixmap->master_pixmap->drawable.pScreen->DestroyPixmap)(pPriv->prime_slave_pixmap->master_pixmap); | |
841 | (*slave->DestroyPixmap)(pPriv->prime_slave_pixmap); | |
842 | pPriv->prime_slave_pixmap = NULL; | |
843 | } | |
844 | } | |
845 | ||
846 | spix = PixmapShareToSlave(mpix, slave); | |
847 | if (!spix) | |
848 | return NULL; | |
849 | ||
850 | pPriv->prime_slave_pixmap = spix; | |
851 | #ifdef COMPOSITE | |
852 | spix->screen_x = mpix->screen_x; | |
853 | spix->screen_y = mpix->screen_y; | |
854 | #endif | |
855 | ||
856 | DRI2InvalidateDrawableAll(pDraw); | |
857 | return &spix->drawable; | |
858 | } | |
859 | ||
860 | static void dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, | |
861 | DRI2BufferPtr pDest, DRI2BufferPtr pSrc) | |
862 | { | |
863 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); | |
864 | DRI2ScreenPtr ds; | |
865 | ScreenPtr primeScreen; | |
866 | ||
867 | primeScreen = GetScreenPrime(pDraw->pScreen, pPriv->prime_id); | |
868 | ds = DRI2GetScreen(primeScreen); | |
869 | ||
870 | if (ds->CopyRegion2) | |
871 | (*ds->CopyRegion2)(primeScreen, pDraw, pRegion, pDest, pSrc); | |
872 | else | |
873 | (*ds->CopyRegion) (pDraw, pRegion, pDest, pSrc); | |
874 | ||
875 | /* cause damage to the box */ | |
876 | if (pPriv->prime_id) { | |
877 | BoxRec box; | |
878 | RegionRec region; | |
879 | box.x1 = 0; | |
880 | box.x2 = box.x1 + pDraw->width; | |
881 | box.y1 = 0; | |
882 | box.y2 = box.y1 + pDraw->height; | |
883 | RegionInit(®ion, &box, 1); | |
884 | RegionTranslate(®ion, pDraw->x, pDraw->y); | |
885 | DamageRegionAppend(pDraw, ®ion); | |
886 | DamageRegionProcessPending(pDraw); | |
887 | RegionUninit(®ion); | |
888 | } | |
889 | } | |
890 | ||
891 | int | |
892 | DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion, | |
893 | unsigned int dest, unsigned int src) | |
894 | { | |
895 | DRI2DrawablePtr pPriv; | |
896 | DRI2BufferPtr pDestBuffer, pSrcBuffer; | |
897 | int i; | |
898 | ||
899 | pPriv = DRI2GetDrawable(pDraw); | |
900 | if (pPriv == NULL) | |
901 | return BadDrawable; | |
902 | ||
903 | pDestBuffer = NULL; | |
904 | pSrcBuffer = NULL; | |
905 | for (i = 0; i < pPriv->bufferCount; i++) { | |
906 | if (pPriv->buffers[i]->attachment == dest) | |
907 | pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i]; | |
908 | if (pPriv->buffers[i]->attachment == src) | |
909 | pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i]; | |
910 | } | |
911 | if (pSrcBuffer == NULL || pDestBuffer == NULL) | |
912 | return BadValue; | |
913 | ||
914 | dri2_copy_region(pDraw, pRegion, pDestBuffer, pSrcBuffer); | |
915 | ||
916 | return Success; | |
917 | } | |
918 | ||
919 | /* Can this drawable be page flipped? */ | |
920 | Bool | |
921 | DRI2CanFlip(DrawablePtr pDraw) | |
922 | { | |
923 | ScreenPtr pScreen = pDraw->pScreen; | |
924 | WindowPtr pWin, pRoot; | |
925 | PixmapPtr pWinPixmap, pRootPixmap; | |
926 | ||
927 | if (pDraw->type == DRAWABLE_PIXMAP) | |
928 | return TRUE; | |
929 | ||
930 | pRoot = pScreen->root; | |
931 | pRootPixmap = pScreen->GetWindowPixmap(pRoot); | |
932 | ||
933 | pWin = (WindowPtr) pDraw; | |
934 | pWinPixmap = pScreen->GetWindowPixmap(pWin); | |
935 | if (pRootPixmap != pWinPixmap) | |
936 | return FALSE; | |
937 | if (!RegionEqual(&pWin->clipList, &pRoot->winSize)) | |
938 | return FALSE; | |
939 | ||
940 | /* Does the window match the pixmap exactly? */ | |
941 | if (pDraw->x != 0 || pDraw->y != 0 || | |
942 | #ifdef COMPOSITE | |
943 | pDraw->x != pWinPixmap->screen_x || pDraw->y != pWinPixmap->screen_y || | |
944 | #endif | |
945 | pDraw->width != pWinPixmap->drawable.width || | |
946 | pDraw->height != pWinPixmap->drawable.height) | |
947 | return FALSE; | |
948 | ||
949 | return TRUE; | |
950 | } | |
951 | ||
952 | /* Can we do a pixmap exchange instead of a blit? */ | |
953 | Bool | |
954 | DRI2CanExchange(DrawablePtr pDraw) | |
955 | { | |
956 | return FALSE; | |
957 | } | |
958 | ||
959 | void | |
960 | DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame, | |
961 | unsigned int tv_sec, unsigned int tv_usec) | |
962 | { | |
963 | DRI2DrawablePtr pPriv; | |
964 | ||
965 | pPriv = DRI2GetDrawable(pDraw); | |
966 | if (pPriv == NULL) | |
967 | return; | |
968 | ||
969 | ProcDRI2WaitMSCReply(client, ((CARD64) tv_sec * 1000000) + tv_usec, | |
970 | frame, pPriv->swap_count); | |
971 | ||
972 | if (pPriv->blockedClient) | |
973 | AttendClient(pPriv->blockedClient); | |
974 | ||
975 | pPriv->blockedClient = NULL; | |
976 | pPriv->blockedOnMsc = FALSE; | |
977 | } | |
978 | ||
979 | static void | |
980 | DRI2WakeClient(ClientPtr client, DrawablePtr pDraw, int frame, | |
981 | unsigned int tv_sec, unsigned int tv_usec) | |
982 | { | |
983 | ScreenPtr pScreen = pDraw->pScreen; | |
984 | DRI2DrawablePtr pPriv; | |
985 | ||
986 | pPriv = DRI2GetDrawable(pDraw); | |
987 | if (pPriv == NULL) { | |
988 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
989 | "[DRI2] %s: bad drawable\n", __func__); | |
990 | return; | |
991 | } | |
992 | ||
993 | /* | |
994 | * Swap completed. | |
995 | * Wake the client iff: | |
996 | * - it was waiting on SBC | |
997 | * - was blocked due to GLX make current | |
998 | * - was blocked due to swap throttling | |
999 | * - is not blocked due to an MSC wait | |
1000 | */ | |
1001 | if (pPriv->target_sbc != -1 && pPriv->target_sbc <= pPriv->swap_count) { | |
1002 | ProcDRI2WaitMSCReply(client, ((CARD64) tv_sec * 1000000) + tv_usec, | |
1003 | frame, pPriv->swap_count); | |
1004 | pPriv->target_sbc = -1; | |
1005 | ||
1006 | AttendClient(pPriv->blockedClient); | |
1007 | pPriv->blockedClient = NULL; | |
1008 | } | |
1009 | else if (pPriv->target_sbc == -1 && !pPriv->blockedOnMsc) { | |
1010 | if (pPriv->blockedClient) { | |
1011 | AttendClient(pPriv->blockedClient); | |
1012 | pPriv->blockedClient = NULL; | |
1013 | } | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | void | |
1018 | DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame, | |
1019 | unsigned int tv_sec, unsigned int tv_usec, int type, | |
1020 | DRI2SwapEventPtr swap_complete, void *swap_data) | |
1021 | { | |
1022 | ScreenPtr pScreen = pDraw->pScreen; | |
1023 | DRI2DrawablePtr pPriv; | |
1024 | CARD64 ust = 0; | |
1025 | BoxRec box; | |
1026 | RegionRec region; | |
1027 | ||
1028 | pPriv = DRI2GetDrawable(pDraw); | |
1029 | if (pPriv == NULL) { | |
1030 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
1031 | "[DRI2] %s: bad drawable\n", __func__); | |
1032 | return; | |
1033 | } | |
1034 | ||
1035 | pPriv->swapsPending--; | |
1036 | pPriv->swap_count++; | |
1037 | ||
1038 | box.x1 = 0; | |
1039 | box.y1 = 0; | |
1040 | box.x2 = pDraw->width; | |
1041 | box.y2 = pDraw->height; | |
1042 | RegionInit(®ion, &box, 0); | |
1043 | DRI2CopyRegion(pDraw, ®ion, DRI2BufferFakeFrontLeft, | |
1044 | DRI2BufferFrontLeft); | |
1045 | ||
1046 | ust = ((CARD64) tv_sec * 1000000) + tv_usec; | |
1047 | if (swap_complete) | |
1048 | swap_complete(client, swap_data, type, ust, frame, pPriv->swap_count); | |
1049 | ||
1050 | pPriv->last_swap_msc = frame; | |
1051 | pPriv->last_swap_ust = ust; | |
1052 | ||
1053 | DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec); | |
1054 | } | |
1055 | ||
1056 | Bool | |
1057 | DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable) | |
1058 | { | |
1059 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable); | |
1060 | ||
1061 | /* If we're currently waiting for a swap on this drawable, reset | |
1062 | * the request and suspend the client. We only support one | |
1063 | * blocked client per drawable. */ | |
1064 | if (pPriv && pPriv->swapsPending && pPriv->blockedClient == NULL) { | |
1065 | ResetCurrentRequest(client); | |
1066 | client->sequence--; | |
1067 | __DRI2BlockClient(client, pPriv); | |
1068 | return TRUE; | |
1069 | } | |
1070 | ||
1071 | return FALSE; | |
1072 | } | |
1073 | ||
1074 | ||
1075 | ||
1076 | int | |
1077 | DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc, | |
1078 | CARD64 divisor, CARD64 remainder, CARD64 * swap_target, | |
1079 | DRI2SwapEventPtr func, void *data) | |
1080 | { | |
1081 | ScreenPtr pScreen = pDraw->pScreen; | |
1082 | DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); | |
1083 | DRI2DrawablePtr pPriv; | |
1084 | DRI2BufferPtr pDestBuffer = NULL, pSrcBuffer = NULL; | |
1085 | int ret, i; | |
1086 | CARD64 ust, current_msc; | |
1087 | ||
1088 | pPriv = DRI2GetDrawable(pDraw); | |
1089 | if (pPriv == NULL) { | |
1090 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
1091 | "[DRI2] %s: bad drawable\n", __func__); | |
1092 | return BadDrawable; | |
1093 | } | |
1094 | ||
1095 | for (i = 0; i < pPriv->bufferCount; i++) { | |
1096 | if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft) | |
1097 | pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i]; | |
1098 | if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft) | |
1099 | pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i]; | |
1100 | } | |
1101 | if (pSrcBuffer == NULL || pDestBuffer == NULL) { | |
1102 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
1103 | "[DRI2] %s: drawable has no back or front?\n", __func__); | |
1104 | return BadDrawable; | |
1105 | } | |
1106 | ||
1107 | /* Old DDX or no swap interval, just blit */ | |
1108 | if (!ds->ScheduleSwap || !pPriv->swap_interval || pPriv->prime_id) { | |
1109 | BoxRec box; | |
1110 | RegionRec region; | |
1111 | ||
1112 | box.x1 = 0; | |
1113 | box.y1 = 0; | |
1114 | box.x2 = pDraw->width; | |
1115 | box.y2 = pDraw->height; | |
1116 | RegionInit(®ion, &box, 0); | |
1117 | ||
1118 | pPriv->swapsPending++; | |
1119 | ||
1120 | dri2_copy_region(pDraw, ®ion, pDestBuffer, pSrcBuffer); | |
1121 | DRI2SwapComplete(client, pDraw, target_msc, 0, 0, DRI2_BLIT_COMPLETE, | |
1122 | func, data); | |
1123 | return Success; | |
1124 | } | |
1125 | ||
1126 | /* | |
1127 | * In the simple glXSwapBuffers case, all params will be 0, and we just | |
1128 | * need to schedule a swap for the last swap target + the swap interval. | |
1129 | */ | |
1130 | if (target_msc == 0 && divisor == 0 && remainder == 0) { | |
1131 | /* If the current vblank count of the drawable's crtc is lower | |
1132 | * than the count stored in last_swap_target from a previous swap | |
1133 | * then reinitialize last_swap_target to the current crtc's msc, | |
1134 | * otherwise the swap will hang. This will happen if the drawable | |
1135 | * is moved to a crtc with a lower refresh rate, or a crtc that just | |
1136 | * got enabled. | |
1137 | */ | |
1138 | if (ds->GetMSC) { | |
1139 | if (!(*ds->GetMSC) (pDraw, &ust, ¤t_msc)) | |
1140 | pPriv->last_swap_target = 0; | |
1141 | ||
1142 | if (current_msc < pPriv->last_swap_target) | |
1143 | pPriv->last_swap_target = current_msc; | |
1144 | ||
1145 | } | |
1146 | ||
1147 | /* | |
1148 | * Swap target for this swap is last swap target + swap interval since | |
1149 | * we have to account for the current swap count, interval, and the | |
1150 | * number of pending swaps. | |
1151 | */ | |
1152 | *swap_target = pPriv->last_swap_target + pPriv->swap_interval; | |
1153 | ||
1154 | } | |
1155 | else { | |
1156 | /* glXSwapBuffersMscOML could have a 0 target_msc, honor it */ | |
1157 | *swap_target = target_msc; | |
1158 | } | |
1159 | ||
1160 | pPriv->swapsPending++; | |
1161 | ret = (*ds->ScheduleSwap) (client, pDraw, pDestBuffer, pSrcBuffer, | |
1162 | swap_target, divisor, remainder, func, data); | |
1163 | if (!ret) { | |
1164 | pPriv->swapsPending--; /* didn't schedule */ | |
1165 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
1166 | "[DRI2] %s: driver failed to schedule swap\n", __func__); | |
1167 | return BadDrawable; | |
1168 | } | |
1169 | ||
1170 | pPriv->last_swap_target = *swap_target; | |
1171 | ||
1172 | /* According to spec, return expected swapbuffers count SBC after this swap | |
1173 | * will complete. | |
1174 | */ | |
1175 | *swap_target = pPriv->swap_count + pPriv->swapsPending; | |
1176 | ||
1177 | DRI2InvalidateDrawableAll(pDraw); | |
1178 | ||
1179 | return Success; | |
1180 | } | |
1181 | ||
1182 | void | |
1183 | DRI2SwapInterval(DrawablePtr pDrawable, int interval) | |
1184 | { | |
1185 | ScreenPtr pScreen = pDrawable->pScreen; | |
1186 | DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable); | |
1187 | ||
1188 | if (pPriv == NULL) { | |
1189 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
1190 | "[DRI2] %s: bad drawable\n", __func__); | |
1191 | return; | |
1192 | } | |
1193 | ||
1194 | /* fixme: check against arbitrary max? */ | |
1195 | pPriv->swap_interval = interval; | |
1196 | } | |
1197 | ||
1198 | int | |
1199 | DRI2GetMSC(DrawablePtr pDraw, CARD64 * ust, CARD64 * msc, CARD64 * sbc) | |
1200 | { | |
1201 | ScreenPtr pScreen = pDraw->pScreen; | |
1202 | DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); | |
1203 | DRI2DrawablePtr pPriv; | |
1204 | Bool ret; | |
1205 | ||
1206 | pPriv = DRI2GetDrawable(pDraw); | |
1207 | if (pPriv == NULL) { | |
1208 | xf86DrvMsg(pScreen->myNum, X_ERROR, | |
1209 | "[DRI2] %s: bad drawable\n", __func__); | |
1210 | return BadDrawable; | |
1211 | } | |
1212 | ||
1213 | if (!ds->GetMSC) { | |
1214 | *ust = 0; | |
1215 | *msc = 0; | |
1216 | *sbc = pPriv->swap_count; | |
1217 | return Success; | |
1218 | } | |
1219 | ||
1220 | /* | |
1221 | * Spec needs to be updated to include unmapped or redirected | |
1222 | * drawables | |
1223 | */ | |
1224 | ||
1225 | ret = (*ds->GetMSC) (pDraw, ust, msc); | |
1226 | if (!ret) | |
1227 | return BadDrawable; | |
1228 | ||
1229 | *sbc = pPriv->swap_count; | |
1230 | ||
1231 | return Success; | |
1232 | } | |
1233 | ||
1234 | int | |
1235 | DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc, | |
1236 | CARD64 divisor, CARD64 remainder) | |
1237 | { | |
1238 | DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen); | |
1239 | DRI2DrawablePtr pPriv; | |
1240 | Bool ret; | |
1241 | ||
1242 | pPriv = DRI2GetDrawable(pDraw); | |
1243 | if (pPriv == NULL) | |
1244 | return BadDrawable; | |
1245 | ||
1246 | /* Old DDX just completes immediately */ | |
1247 | if (!ds->ScheduleWaitMSC) { | |
1248 | DRI2WaitMSCComplete(client, pDraw, target_msc, 0, 0); | |
1249 | ||
1250 | return Success; | |
1251 | } | |
1252 | ||
1253 | ret = | |
1254 | (*ds->ScheduleWaitMSC) (client, pDraw, target_msc, divisor, remainder); | |
1255 | if (!ret) | |
1256 | return BadDrawable; | |
1257 | ||
1258 | return Success; | |
1259 | } | |
1260 | ||
1261 | int | |
1262 | DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc) | |
1263 | { | |
1264 | DRI2DrawablePtr pPriv; | |
1265 | ||
1266 | pPriv = DRI2GetDrawable(pDraw); | |
1267 | if (pPriv == NULL) | |
1268 | return BadDrawable; | |
1269 | ||
1270 | /* target_sbc == 0 means to block until all pending swaps are | |
1271 | * finished. Recalculate target_sbc to get that behaviour. | |
1272 | */ | |
1273 | if (target_sbc == 0) | |
1274 | target_sbc = pPriv->swap_count + pPriv->swapsPending; | |
1275 | ||
1276 | /* If current swap count already >= target_sbc, reply and | |
1277 | * return immediately with (ust, msc, sbc) triplet of | |
1278 | * most recent completed swap. | |
1279 | */ | |
1280 | if (pPriv->swap_count >= target_sbc) { | |
1281 | ProcDRI2WaitMSCReply(client, pPriv->last_swap_ust, | |
1282 | pPriv->last_swap_msc, pPriv->swap_count); | |
1283 | return Success; | |
1284 | } | |
1285 | ||
1286 | pPriv->target_sbc = target_sbc; | |
1287 | __DRI2BlockClient(client, pPriv); | |
1288 | ||
1289 | return Success; | |
1290 | } | |
1291 | ||
1292 | Bool | |
1293 | DRI2HasSwapControl(ScreenPtr pScreen) | |
1294 | { | |
1295 | DRI2ScreenPtr ds = DRI2GetScreen(pScreen); | |
1296 | ||
1297 | return ds->ScheduleSwap && ds->GetMSC; | |
1298 | } | |
1299 | ||
1300 | Bool | |
1301 | DRI2Connect(ClientPtr client, ScreenPtr pScreen, | |
1302 | unsigned int driverType, int *fd, | |
1303 | const char **driverName, const char **deviceName) | |
1304 | { | |
1305 | DRI2ScreenPtr ds; | |
1306 | uint32_t prime_id = DRI2DriverPrimeId(driverType); | |
1307 | uint32_t driver_id = driverType & 0xffff; | |
1308 | ||
1309 | if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey)) | |
1310 | return FALSE; | |
1311 | ||
1312 | ds = DRI2GetScreenPrime(pScreen, prime_id); | |
1313 | if (ds == NULL) | |
1314 | return FALSE; | |
1315 | ||
1316 | if (driver_id >= ds->numDrivers || | |
1317 | !ds->driverNames[driver_id]) | |
1318 | return FALSE; | |
1319 | ||
1320 | *driverName = ds->driverNames[driver_id]; | |
1321 | *deviceName = ds->deviceName; | |
1322 | *fd = ds->fd; | |
1323 | ||
1324 | if (client) { | |
1325 | DRI2ClientPtr dri2_client; | |
1326 | dri2_client = dri2ClientPrivate(client); | |
1327 | dri2_client->prime_id = prime_id; | |
1328 | } | |
1329 | ||
1330 | return TRUE; | |
1331 | } | |
1332 | ||
1333 | static int | |
1334 | DRI2AuthMagic (ScreenPtr pScreen, uint32_t magic) | |
1335 | { | |
1336 | DRI2ScreenPtr ds = DRI2GetScreen(pScreen); | |
1337 | if (ds == NULL) | |
1338 | return -EINVAL; | |
1339 | ||
1340 | return (*ds->LegacyAuthMagic) (ds->fd, magic); | |
1341 | } | |
1342 | ||
1343 | Bool | |
1344 | DRI2Authenticate(ClientPtr client, ScreenPtr pScreen, uint32_t magic) | |
1345 | { | |
1346 | DRI2ScreenPtr ds; | |
1347 | DRI2ClientPtr dri2_client = dri2ClientPrivate(client); | |
1348 | ScreenPtr primescreen; | |
1349 | ||
1350 | ds = DRI2GetScreenPrime(pScreen, dri2_client->prime_id); | |
1351 | if (ds == NULL) | |
1352 | return FALSE; | |
1353 | ||
1354 | primescreen = GetScreenPrime(pScreen, dri2_client->prime_id); | |
1355 | if ((*ds->AuthMagic)(primescreen, magic)) | |
1356 | return FALSE; | |
1357 | return TRUE; | |
1358 | } | |
1359 | ||
1360 | static int | |
1361 | DRI2ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw, | |
1362 | WindowPtr pSib) | |
1363 | { | |
1364 | DrawablePtr pDraw = (DrawablePtr) pWin; | |
1365 | ScreenPtr pScreen = pDraw->pScreen; | |
1366 | DRI2ScreenPtr ds = DRI2GetScreen(pScreen); | |
1367 | DRI2DrawablePtr dd = DRI2GetDrawable(pDraw); | |
1368 | int ret; | |
1369 | ||
1370 | if (ds->ConfigNotify) { | |
1371 | pScreen->ConfigNotify = ds->ConfigNotify; | |
1372 | ||
1373 | ret = (*pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib); | |
1374 | ||
1375 | ds->ConfigNotify = pScreen->ConfigNotify; | |
1376 | pScreen->ConfigNotify = DRI2ConfigNotify; | |
1377 | if (ret) | |
1378 | return ret; | |
1379 | } | |
1380 | ||
1381 | if (!dd || (dd->width == w && dd->height == h)) | |
1382 | return Success; | |
1383 | ||
1384 | DRI2InvalidateDrawable(pDraw); | |
1385 | return Success; | |
1386 | } | |
1387 | ||
1388 | #define MAX_PRIME DRI2DriverPrimeMask | |
1389 | static int | |
1390 | get_prime_id(void) | |
1391 | { | |
1392 | int i; | |
1393 | /* start at 1, prime id 0 is just normal driver */ | |
1394 | for (i = 1; i < MAX_PRIME; i++) { | |
1395 | if (prime_id_allocate_bitmask & (1 << i)) | |
1396 | continue; | |
1397 | ||
1398 | prime_id_allocate_bitmask |= (1 << i); | |
1399 | return i; | |
1400 | } | |
1401 | return -1; | |
1402 | } | |
1403 | ||
1404 | Bool | |
1405 | DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info) | |
1406 | { | |
1407 | DRI2ScreenPtr ds; | |
1408 | ||
1409 | const char *driverTypeNames[] = { | |
1410 | "DRI", /* DRI2DriverDRI */ | |
1411 | "VDPAU", /* DRI2DriverVDPAU */ | |
1412 | }; | |
1413 | unsigned int i; | |
1414 | CARD8 cur_minor; | |
1415 | ||
1416 | if (info->version < 3) | |
1417 | return FALSE; | |
1418 | ||
1419 | if (!xf86VGAarbiterAllowDRI(pScreen)) { | |
1420 | xf86DrvMsg(pScreen->myNum, X_WARNING, | |
1421 | "[DRI2] Direct rendering is not supported when VGA arb is necessary for the device\n"); | |
1422 | return FALSE; | |
1423 | } | |
1424 | ||
1425 | if (!dixRegisterPrivateKey(&dri2ScreenPrivateKeyRec, PRIVATE_SCREEN, 0)) | |
1426 | return FALSE; | |
1427 | ||
1428 | if (!dixRegisterPrivateKey(&dri2WindowPrivateKeyRec, PRIVATE_WINDOW, 0)) | |
1429 | return FALSE; | |
1430 | ||
1431 | if (!dixRegisterPrivateKey(&dri2PixmapPrivateKeyRec, PRIVATE_PIXMAP, 0)) | |
1432 | return FALSE; | |
1433 | ||
1434 | if (!dixRegisterPrivateKey(&dri2ClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(DRI2ClientRec))) | |
1435 | return FALSE; | |
1436 | ||
1437 | ds = calloc(1, sizeof *ds); | |
1438 | if (!ds) | |
1439 | return FALSE; | |
1440 | ||
1441 | ds->screen = pScreen; | |
1442 | ds->fd = info->fd; | |
1443 | ds->deviceName = info->deviceName; | |
1444 | dri2_major = 1; | |
1445 | ||
1446 | ds->CreateBuffer = info->CreateBuffer; | |
1447 | ds->DestroyBuffer = info->DestroyBuffer; | |
1448 | ds->CopyRegion = info->CopyRegion; | |
1449 | ||
1450 | if (info->version >= 4) { | |
1451 | ds->ScheduleSwap = info->ScheduleSwap; | |
1452 | ds->ScheduleWaitMSC = info->ScheduleWaitMSC; | |
1453 | ds->GetMSC = info->GetMSC; | |
1454 | cur_minor = 3; | |
1455 | } | |
1456 | else { | |
1457 | cur_minor = 1; | |
1458 | } | |
1459 | ||
1460 | if (info->version >= 8) { | |
1461 | ds->AuthMagic = info->AuthMagic2; | |
1462 | } | |
1463 | if (info->version >= 5) { | |
1464 | ds->LegacyAuthMagic = info->AuthMagic; | |
1465 | } | |
1466 | ||
1467 | if (info->version >= 6) { | |
1468 | ds->ReuseBufferNotify = info->ReuseBufferNotify; | |
1469 | ds->SwapLimitValidate = info->SwapLimitValidate; | |
1470 | } | |
1471 | ||
1472 | if (info->version >= 7) { | |
1473 | ds->GetParam = info->GetParam; | |
1474 | cur_minor = 4; | |
1475 | } | |
1476 | ||
1477 | if (info->version >= 9) { | |
1478 | ds->CreateBuffer2 = info->CreateBuffer2; | |
1479 | if (info->CreateBuffer2 && pScreen->isGPU) { | |
1480 | ds->prime_id = get_prime_id(); | |
1481 | if (ds->prime_id == -1) { | |
1482 | free(ds); | |
1483 | return FALSE; | |
1484 | } | |
1485 | } | |
1486 | ds->DestroyBuffer2 = info->DestroyBuffer2; | |
1487 | ds->CopyRegion2 = info->CopyRegion2; | |
1488 | } | |
1489 | ||
1490 | /* | |
1491 | * if the driver doesn't provide an AuthMagic function or the info struct | |
1492 | * version is too low, call through LegacyAuthMagic | |
1493 | */ | |
1494 | if (!ds->AuthMagic) { | |
1495 | ds->AuthMagic = DRI2AuthMagic; | |
1496 | /* | |
1497 | * If the driver doesn't provide an AuthMagic function | |
1498 | * it relies on the old method (using libdrm) or fails | |
1499 | */ | |
1500 | if (!ds->LegacyAuthMagic) | |
1501 | #ifdef WITH_LIBDRM | |
1502 | ds->LegacyAuthMagic = drmAuthMagic; | |
1503 | #else | |
1504 | goto err_out; | |
1505 | #endif | |
1506 | } | |
1507 | ||
1508 | /* Initialize minor if needed and set to minimum provied by DDX */ | |
1509 | if (!dri2_minor || dri2_minor > cur_minor) | |
1510 | dri2_minor = cur_minor; | |
1511 | ||
1512 | if (info->version == 3 || info->numDrivers == 0) { | |
1513 | /* Driver too old: use the old-style driverName field */ | |
1514 | ds->numDrivers = 1; | |
1515 | ds->driverNames = malloc(sizeof(*ds->driverNames)); | |
1516 | if (!ds->driverNames) | |
1517 | goto err_out; | |
1518 | ds->driverNames[0] = info->driverName; | |
1519 | } | |
1520 | else { | |
1521 | ds->numDrivers = info->numDrivers; | |
1522 | ds->driverNames = malloc(info->numDrivers * sizeof(*ds->driverNames)); | |
1523 | if (!ds->driverNames) | |
1524 | goto err_out; | |
1525 | memcpy(ds->driverNames, info->driverNames, | |
1526 | info->numDrivers * sizeof(*ds->driverNames)); | |
1527 | } | |
1528 | ||
1529 | dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds); | |
1530 | ||
1531 | ds->ConfigNotify = pScreen->ConfigNotify; | |
1532 | pScreen->ConfigNotify = DRI2ConfigNotify; | |
1533 | ||
1534 | xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n"); | |
1535 | for (i = 0; i < sizeof(driverTypeNames) / sizeof(driverTypeNames[0]); i++) { | |
1536 | if (i < ds->numDrivers && ds->driverNames[i]) { | |
1537 | xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] %s driver: %s\n", | |
1538 | driverTypeNames[i], ds->driverNames[i]); | |
1539 | } | |
1540 | } | |
1541 | ||
1542 | return TRUE; | |
1543 | ||
1544 | err_out: | |
1545 | xf86DrvMsg(pScreen->myNum, X_WARNING, | |
1546 | "[DRI2] Initialization failed for info version %d.\n", | |
1547 | info->version); | |
1548 | free(ds); | |
1549 | return FALSE; | |
1550 | } | |
1551 | ||
1552 | void | |
1553 | DRI2CloseScreen(ScreenPtr pScreen) | |
1554 | { | |
1555 | DRI2ScreenPtr ds = DRI2GetScreen(pScreen); | |
1556 | ||
1557 | pScreen->ConfigNotify = ds->ConfigNotify; | |
1558 | ||
1559 | if (ds->prime_id) | |
1560 | prime_id_allocate_bitmask &= ~(1 << ds->prime_id); | |
1561 | free(ds->driverNames); | |
1562 | free(ds); | |
1563 | dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL); | |
1564 | } | |
1565 | ||
1566 | extern Bool DRI2ModuleSetup(void); | |
1567 | ||
1568 | /* Called by InitExtensions() */ | |
1569 | Bool | |
1570 | DRI2ModuleSetup(void) | |
1571 | { | |
1572 | dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable"); | |
1573 | if (!dri2DrawableRes) | |
1574 | return FALSE; | |
1575 | ||
1576 | return TRUE; | |
1577 | } | |
1578 | ||
1579 | void | |
1580 | DRI2Version(int *major, int *minor) | |
1581 | { | |
1582 | if (major != NULL) | |
1583 | *major = 1; | |
1584 | ||
1585 | if (minor != NULL) | |
1586 | *minor = 2; | |
1587 | } | |
1588 | ||
1589 | int | |
1590 | DRI2GetParam(ClientPtr client, | |
1591 | DrawablePtr drawable, | |
1592 | CARD64 param, | |
1593 | BOOL *is_param_recognized, | |
1594 | CARD64 *value) | |
1595 | { | |
1596 | DRI2ScreenPtr ds = DRI2GetScreen(drawable->pScreen); | |
1597 | char high_byte = (param >> 24); | |
1598 | ||
1599 | switch (high_byte) { | |
1600 | case 0: | |
1601 | /* Parameter names whose high_byte is 0 are reserved for the X | |
1602 | * server. The server currently recognizes no parameters. | |
1603 | */ | |
1604 | goto not_recognized; | |
1605 | case 1: | |
1606 | /* Parameter names whose high byte is 1 are reserved for the DDX. */ | |
1607 | if (ds->GetParam) | |
1608 | return ds->GetParam(client, drawable, param, | |
1609 | is_param_recognized, value); | |
1610 | else | |
1611 | goto not_recognized; | |
1612 | default: | |
1613 | /* Other parameter names are reserved for future use. They are never | |
1614 | * recognized. | |
1615 | */ | |
1616 | goto not_recognized; | |
1617 | } | |
1618 | ||
1619 | not_recognized: | |
1620 | *is_param_recognized = FALSE; | |
1621 | return Success; | |
1622 | } |