vl/dri3: implement DRI3 BufferFromPixmap
[mesa.git] / src / gallium / auxiliary / vl / vl_winsys_dri3.c
1 /**************************************************************************
2 *
3 * Copyright 2016 Advanced Micro Devices, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * 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 portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #include <fcntl.h>
29
30 #include <X11/Xlib-xcb.h>
31 #include <X11/xshmfence.h>
32 #include <xcb/dri3.h>
33 #include <xcb/present.h>
34
35 #include "loader.h"
36
37 #include "pipe/p_screen.h"
38 #include "pipe/p_state.h"
39 #include "pipe-loader/pipe_loader.h"
40
41 #include "util/u_memory.h"
42 #include "util/u_inlines.h"
43
44 #include "vl/vl_compositor.h"
45 #include "vl/vl_winsys.h"
46
47 #define BACK_BUFFER_NUM 3
48
49 struct vl_dri3_buffer
50 {
51 struct pipe_resource *texture;
52
53 uint32_t pixmap;
54 uint32_t sync_fence;
55 struct xshmfence *shm_fence;
56
57 bool busy;
58 uint32_t width, height, pitch;
59 };
60
61 struct vl_dri3_screen
62 {
63 struct vl_screen base;
64 xcb_connection_t *conn;
65 xcb_drawable_t drawable;
66
67 uint32_t width, height, depth;
68
69 xcb_special_event_t *special_event;
70
71 struct vl_dri3_buffer *back_buffers[BACK_BUFFER_NUM];
72 int cur_back;
73
74 struct u_rect dirty_areas[BACK_BUFFER_NUM];
75
76 struct vl_dri3_buffer *front_buffer;
77 bool is_pixmap;
78 };
79
80 static void
81 dri3_free_front_buffer(struct vl_dri3_screen *scrn,
82 struct vl_dri3_buffer *buffer)
83 {
84 xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence);
85 xshmfence_unmap_shm(buffer->shm_fence);
86 FREE(buffer);
87 }
88
89 static void
90 dri3_free_back_buffer(struct vl_dri3_screen *scrn,
91 struct vl_dri3_buffer *buffer)
92 {
93 xcb_free_pixmap(scrn->conn, buffer->pixmap);
94 xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence);
95 xshmfence_unmap_shm(buffer->shm_fence);
96 pipe_resource_reference(&buffer->texture, NULL);
97 FREE(buffer);
98 }
99
100 static void
101 dri3_handle_present_event(struct vl_dri3_screen *scrn,
102 xcb_present_generic_event_t *ge)
103 {
104 switch (ge->evtype) {
105 case XCB_PRESENT_CONFIGURE_NOTIFY: {
106 xcb_present_configure_notify_event_t *ce = (void *) ge;
107 scrn->width = ce->width;
108 scrn->height = ce->height;
109 break;
110 }
111 case XCB_PRESENT_COMPLETE_NOTIFY: {
112 /* TODO */
113 break;
114 }
115 case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
116 xcb_present_idle_notify_event_t *ie = (void *) ge;
117 int b;
118 for (b = 0; b < BACK_BUFFER_NUM; b++) {
119 struct vl_dri3_buffer *buf = scrn->back_buffers[b];
120 if (buf && buf->pixmap == ie->pixmap) {
121 buf->busy = false;
122 break;
123 }
124 }
125 break;
126 }
127 }
128 free(ge);
129 }
130
131 static void
132 dri3_flush_present_events(struct vl_dri3_screen *scrn)
133 {
134 if (scrn->special_event) {
135 xcb_generic_event_t *ev;
136 while ((ev = xcb_poll_for_special_event(
137 scrn->conn, scrn->special_event)) != NULL)
138 dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
139 }
140 }
141
142 static bool
143 dri3_wait_present_events(struct vl_dri3_screen *scrn)
144 {
145 if (scrn->special_event) {
146 xcb_generic_event_t *ev;
147 ev = xcb_wait_for_special_event(scrn->conn, scrn->special_event);
148 if (!ev)
149 return false;
150 dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
151 return true;
152 }
153 return false;
154 }
155
156 static int
157 dri3_find_back(struct vl_dri3_screen *scrn)
158 {
159 int b;
160
161 for (;;) {
162 for (b = 0; b < BACK_BUFFER_NUM; b++) {
163 int id = (b + scrn->cur_back) % BACK_BUFFER_NUM;
164 struct vl_dri3_buffer *buffer = scrn->back_buffers[id];
165 if (!buffer || !buffer->busy)
166 return id;
167 }
168 xcb_flush(scrn->conn);
169 if (!dri3_wait_present_events(scrn))
170 return -1;
171 }
172 }
173
174 static struct vl_dri3_buffer *
175 dri3_alloc_back_buffer(struct vl_dri3_screen *scrn)
176 {
177 struct vl_dri3_buffer *buffer;
178 xcb_pixmap_t pixmap;
179 xcb_sync_fence_t sync_fence;
180 struct xshmfence *shm_fence;
181 int buffer_fd, fence_fd;
182 struct pipe_resource templ;
183 struct winsys_handle whandle;
184 unsigned usage;
185
186 buffer = CALLOC_STRUCT(vl_dri3_buffer);
187 if (!buffer)
188 return NULL;
189
190 fence_fd = xshmfence_alloc_shm();
191 if (fence_fd < 0)
192 goto free_buffer;
193
194 shm_fence = xshmfence_map_shm(fence_fd);
195 if (!shm_fence)
196 goto close_fd;
197
198 memset(&templ, 0, sizeof(templ));
199 templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW |
200 PIPE_BIND_SCANOUT | PIPE_BIND_SHARED;
201 templ.format = PIPE_FORMAT_B8G8R8X8_UNORM;
202 templ.target = PIPE_TEXTURE_2D;
203 templ.last_level = 0;
204 templ.width0 = scrn->width;
205 templ.height0 = scrn->height;
206 templ.depth0 = 1;
207 templ.array_size = 1;
208 buffer->texture = scrn->base.pscreen->resource_create(scrn->base.pscreen,
209 &templ);
210 if (!buffer->texture)
211 goto unmap_shm;
212
213 memset(&whandle, 0, sizeof(whandle));
214 whandle.type= DRM_API_HANDLE_TYPE_FD;
215 usage = PIPE_HANDLE_USAGE_EXPLICIT_FLUSH | PIPE_HANDLE_USAGE_READ;
216 scrn->base.pscreen->resource_get_handle(scrn->base.pscreen,
217 buffer->texture, &whandle,
218 usage);
219 buffer_fd = whandle.handle;
220 buffer->pitch = whandle.stride;
221 xcb_dri3_pixmap_from_buffer(scrn->conn,
222 (pixmap = xcb_generate_id(scrn->conn)),
223 scrn->drawable,
224 0,
225 scrn->width, scrn->height, buffer->pitch,
226 scrn->depth, 32,
227 buffer_fd);
228 xcb_dri3_fence_from_fd(scrn->conn,
229 pixmap,
230 (sync_fence = xcb_generate_id(scrn->conn)),
231 false,
232 fence_fd);
233
234 buffer->pixmap = pixmap;
235 buffer->sync_fence = sync_fence;
236 buffer->shm_fence = shm_fence;
237 buffer->width = scrn->width;
238 buffer->height = scrn->height;
239
240 xshmfence_trigger(buffer->shm_fence);
241
242 return buffer;
243
244 unmap_shm:
245 xshmfence_unmap_shm(shm_fence);
246 close_fd:
247 close(fence_fd);
248 free_buffer:
249 FREE(buffer);
250 return NULL;
251 }
252
253 static struct vl_dri3_buffer *
254 dri3_get_back_buffer(struct vl_dri3_screen *scrn)
255 {
256 struct vl_dri3_buffer *buffer;
257 struct pipe_resource *texture = NULL;
258
259 assert(scrn);
260
261 scrn->cur_back = dri3_find_back(scrn);
262 if (scrn->cur_back < 0)
263 return NULL;
264 buffer = scrn->back_buffers[scrn->cur_back];
265
266 if (!buffer || buffer->width != scrn->width ||
267 buffer->height != scrn->height) {
268 struct vl_dri3_buffer *new_buffer;
269
270 new_buffer = dri3_alloc_back_buffer(scrn);
271 if (!new_buffer)
272 return NULL;
273
274 if (buffer)
275 dri3_free_back_buffer(scrn, buffer);
276
277 vl_compositor_reset_dirty_area(&scrn->dirty_areas[scrn->cur_back]);
278 buffer = new_buffer;
279 scrn->back_buffers[scrn->cur_back] = buffer;
280 }
281
282 pipe_resource_reference(&texture, buffer->texture);
283 xcb_flush(scrn->conn);
284 xshmfence_await(buffer->shm_fence);
285
286 return buffer;
287 }
288
289 static bool
290 dri3_set_drawable(struct vl_dri3_screen *scrn, Drawable drawable)
291 {
292 xcb_get_geometry_cookie_t geom_cookie;
293 xcb_get_geometry_reply_t *geom_reply;
294 xcb_void_cookie_t cookie;
295 xcb_generic_error_t *error;
296 xcb_present_event_t peid;
297 bool ret = true;
298
299 assert(drawable);
300
301 if (scrn->drawable == drawable)
302 return true;
303
304 scrn->drawable = drawable;
305
306 geom_cookie = xcb_get_geometry(scrn->conn, scrn->drawable);
307 geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
308 if (!geom_reply)
309 return false;
310
311 scrn->width = geom_reply->width;
312 scrn->height = geom_reply->height;
313 scrn->depth = geom_reply->depth;
314 free(geom_reply);
315
316 if (scrn->special_event) {
317 xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
318 scrn->special_event = NULL;
319 }
320
321 scrn->is_pixmap = false;
322 peid = xcb_generate_id(scrn->conn);
323 cookie =
324 xcb_present_select_input_checked(scrn->conn, peid, scrn->drawable,
325 XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |
326 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
327 XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
328
329 error = xcb_request_check(scrn->conn, cookie);
330 if (error) {
331 if (error->error_code != BadWindow)
332 ret = false;
333 else
334 scrn->is_pixmap = true;
335 free(error);
336 } else
337 scrn->special_event =
338 xcb_register_for_special_xge(scrn->conn, &xcb_present_id, peid, 0);
339
340 dri3_flush_present_events(scrn);
341
342 return ret;
343 }
344
345 static struct vl_dri3_buffer *
346 dri3_get_front_buffer(struct vl_dri3_screen *scrn)
347 {
348 xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
349 xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
350 xcb_sync_fence_t sync_fence;
351 struct xshmfence *shm_fence;
352 int fence_fd, *fds;
353 struct winsys_handle whandle;
354 struct pipe_resource templ, *texture = NULL;
355
356 if (scrn->front_buffer) {
357 pipe_resource_reference(&texture, scrn->front_buffer->texture);
358 return scrn->front_buffer;
359 }
360
361 scrn->front_buffer = CALLOC_STRUCT(vl_dri3_buffer);
362 if (!scrn->front_buffer)
363 return NULL;
364
365 fence_fd = xshmfence_alloc_shm();
366 if (fence_fd < 0)
367 goto free_buffer;
368
369 shm_fence = xshmfence_map_shm(fence_fd);
370 if (!shm_fence)
371 goto close_fd;
372
373 bp_cookie = xcb_dri3_buffer_from_pixmap(scrn->conn, scrn->drawable);
374 bp_reply = xcb_dri3_buffer_from_pixmap_reply(scrn->conn, bp_cookie, NULL);
375 if (!bp_reply)
376 goto unmap_shm;
377
378 fds = xcb_dri3_buffer_from_pixmap_reply_fds(scrn->conn, bp_reply);
379 if (fds[0] < 0)
380 goto free_reply;
381
382 memset(&whandle, 0, sizeof(whandle));
383 whandle.type = DRM_API_HANDLE_TYPE_FD;
384 whandle.handle = (unsigned)fds[0];
385 whandle.stride = bp_reply->stride;
386 memset(&templ, 0, sizeof(templ));
387 templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
388 templ.format = PIPE_FORMAT_B8G8R8X8_UNORM;
389 templ.target = PIPE_TEXTURE_2D;
390 templ.last_level = 0;
391 templ.width0 = bp_reply->width;
392 templ.height0 = bp_reply->height;
393 templ.depth0 = 1;
394 templ.array_size = 1;
395 scrn->front_buffer->texture =
396 scrn->base.pscreen->resource_from_handle(scrn->base.pscreen,
397 &templ, &whandle,
398 PIPE_HANDLE_USAGE_READ_WRITE);
399 close(fds[0]);
400 if (!scrn->front_buffer->texture)
401 goto free_reply;
402
403 xcb_dri3_fence_from_fd(scrn->conn,
404 scrn->drawable,
405 (sync_fence = xcb_generate_id(scrn->conn)),
406 false,
407 fence_fd);
408
409 pipe_resource_reference(&texture, scrn->front_buffer->texture);
410 scrn->front_buffer->pixmap = scrn->drawable;
411 scrn->front_buffer->width = bp_reply->width;
412 scrn->front_buffer->height = bp_reply->height;
413 scrn->front_buffer->shm_fence = shm_fence;
414 scrn->front_buffer->sync_fence = sync_fence;
415 free(bp_reply);
416
417 return scrn->front_buffer;
418
419 free_reply:
420 free(bp_reply);
421 unmap_shm:
422 xshmfence_unmap_shm(shm_fence);
423 close_fd:
424 close(fence_fd);
425 free_buffer:
426 FREE(scrn->front_buffer);
427 return NULL;
428 }
429
430 static void
431 vl_dri3_flush_frontbuffer(struct pipe_screen *screen,
432 struct pipe_resource *resource,
433 unsigned level, unsigned layer,
434 void *context_private, struct pipe_box *sub_box)
435 {
436 struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private;
437 uint32_t options = XCB_PRESENT_OPTION_NONE;
438 struct vl_dri3_buffer *back;
439
440 back = scrn->back_buffers[scrn->cur_back];
441 if (!back)
442 return;
443
444 xshmfence_reset(back->shm_fence);
445 back->busy = true;
446
447 xcb_present_pixmap(scrn->conn,
448 scrn->drawable,
449 back->pixmap,
450 0, 0, 0, 0, 0,
451 None, None,
452 back->sync_fence,
453 options, 0, 0, 0, 0, NULL);
454
455 xcb_flush(scrn->conn);
456
457 return;
458 }
459
460 static struct pipe_resource *
461 vl_dri3_screen_texture_from_drawable(struct vl_screen *vscreen, void *drawable)
462 {
463 struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
464 struct vl_dri3_buffer *buffer;
465
466 assert(scrn);
467
468 if (!dri3_set_drawable(scrn, (Drawable)drawable))
469 return NULL;
470
471 buffer = (scrn->is_pixmap) ?
472 dri3_get_front_buffer(scrn) :
473 dri3_get_back_buffer(scrn);
474 if (!buffer)
475 return NULL;
476
477 return buffer->texture;
478 }
479
480 static struct u_rect *
481 vl_dri3_screen_get_dirty_area(struct vl_screen *vscreen)
482 {
483 struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
484
485 assert(scrn);
486
487 return &scrn->dirty_areas[scrn->cur_back];
488 }
489
490 static uint64_t
491 vl_dri3_screen_get_timestamp(struct vl_screen *vscreen, void *drawable)
492 {
493 /* TODO */
494 return 0;
495 }
496
497 static void
498 vl_dri3_screen_set_next_timestamp(struct vl_screen *vscreen, uint64_t stamp)
499 {
500 /* TODO */
501 return;
502 }
503
504 static void *
505 vl_dri3_screen_get_private(struct vl_screen *vscreen)
506 {
507 return vscreen;
508 }
509
510 static void
511 vl_dri3_screen_destroy(struct vl_screen *vscreen)
512 {
513 struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
514 int i;
515
516 assert(vscreen);
517
518 dri3_flush_present_events(scrn);
519
520 if (scrn->front_buffer) {
521 dri3_free_front_buffer(scrn, scrn->front_buffer);
522 scrn->front_buffer = NULL;
523 return;
524 }
525
526 for (i = 0; i < BACK_BUFFER_NUM; ++i) {
527 if (scrn->back_buffers[i]) {
528 dri3_free_back_buffer(scrn, scrn->back_buffers[i]);
529 scrn->back_buffers[i] = NULL;
530 }
531 }
532
533 if (scrn->special_event)
534 xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
535 scrn->base.pscreen->destroy(scrn->base.pscreen);
536 pipe_loader_release(&scrn->base.dev, 1);
537 FREE(scrn);
538
539 return;
540 }
541
542 struct vl_screen *
543 vl_dri3_screen_create(Display *display, int screen)
544 {
545 struct vl_dri3_screen *scrn;
546 const xcb_query_extension_reply_t *extension;
547 xcb_dri3_open_cookie_t open_cookie;
548 xcb_dri3_open_reply_t *open_reply;
549 xcb_get_geometry_cookie_t geom_cookie;
550 xcb_get_geometry_reply_t *geom_reply;
551 int is_different_gpu;
552 int fd;
553
554 assert(display);
555
556 scrn = CALLOC_STRUCT(vl_dri3_screen);
557 if (!scrn)
558 return NULL;
559
560 scrn->conn = XGetXCBConnection(display);
561 if (!scrn->conn)
562 goto free_screen;
563
564 xcb_prefetch_extension_data(scrn->conn , &xcb_dri3_id);
565 xcb_prefetch_extension_data(scrn->conn, &xcb_present_id);
566 extension = xcb_get_extension_data(scrn->conn, &xcb_dri3_id);
567 if (!(extension && extension->present))
568 goto free_screen;
569 extension = xcb_get_extension_data(scrn->conn, &xcb_present_id);
570 if (!(extension && extension->present))
571 goto free_screen;
572
573 open_cookie = xcb_dri3_open(scrn->conn, RootWindow(display, screen), None);
574 open_reply = xcb_dri3_open_reply(scrn->conn, open_cookie, NULL);
575 if (!open_reply)
576 goto free_screen;
577 if (open_reply->nfd != 1) {
578 free(open_reply);
579 goto free_screen;
580 }
581
582 fd = xcb_dri3_open_reply_fds(scrn->conn, open_reply)[0];
583 if (fd < 0) {
584 free(open_reply);
585 goto free_screen;
586 }
587 fcntl(fd, F_SETFD, FD_CLOEXEC);
588 free(open_reply);
589
590 fd = loader_get_user_preferred_fd(fd, &is_different_gpu);
591 /* TODO support different GPU */
592 if (is_different_gpu)
593 goto close_fd;
594
595 geom_cookie = xcb_get_geometry(scrn->conn, RootWindow(display, screen));
596 geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
597 if (!geom_reply)
598 goto close_fd;
599 /* TODO support depth other than 24 */
600 if (geom_reply->depth != 24) {
601 free(geom_reply);
602 goto close_fd;
603 }
604 free(geom_reply);
605
606 if (pipe_loader_drm_probe_fd(&scrn->base.dev, fd))
607 scrn->base.pscreen = pipe_loader_create_screen(scrn->base.dev);
608
609 if (!scrn->base.pscreen)
610 goto release_pipe;
611
612 scrn->base.destroy = vl_dri3_screen_destroy;
613 scrn->base.texture_from_drawable = vl_dri3_screen_texture_from_drawable;
614 scrn->base.get_dirty_area = vl_dri3_screen_get_dirty_area;
615 scrn->base.get_timestamp = vl_dri3_screen_get_timestamp;
616 scrn->base.set_next_timestamp = vl_dri3_screen_set_next_timestamp;
617 scrn->base.get_private = vl_dri3_screen_get_private;
618 scrn->base.pscreen->flush_frontbuffer = vl_dri3_flush_frontbuffer;
619
620 return &scrn->base;
621
622 release_pipe:
623 if (scrn->base.dev) {
624 pipe_loader_release(&scrn->base.dev, 1);
625 fd = -1;
626 }
627 close_fd:
628 if (fd != -1)
629 close(fd);
630 free_screen:
631 FREE(scrn);
632 return NULL;
633 }