1 /**************************************************************************
3 * Copyright 2016 Advanced Micro Devices, Inc.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
30 #include <X11/Xlib-xcb.h>
31 #include <X11/xshmfence.h>
33 #include <xcb/present.h>
37 #include "pipe/p_screen.h"
38 #include "pipe/p_state.h"
39 #include "pipe-loader/pipe_loader.h"
41 #include "util/u_memory.h"
42 #include "util/u_inlines.h"
44 #include "vl/vl_compositor.h"
45 #include "vl/vl_winsys.h"
47 #define BACK_BUFFER_NUM 3
51 struct pipe_resource
*texture
;
52 struct pipe_resource
*linear_texture
;
56 struct xshmfence
*shm_fence
;
59 uint32_t width
, height
, pitch
;
64 struct vl_screen base
;
65 xcb_connection_t
*conn
;
66 xcb_drawable_t drawable
;
68 uint32_t width
, height
, depth
;
70 xcb_present_event_t eid
;
71 xcb_special_event_t
*special_event
;
73 struct pipe_context
*pipe
;
75 struct vl_dri3_buffer
*back_buffers
[BACK_BUFFER_NUM
];
78 struct u_rect dirty_areas
[BACK_BUFFER_NUM
];
80 struct vl_dri3_buffer
*front_buffer
;
83 uint32_t send_msc_serial
, recv_msc_serial
;
84 uint64_t send_sbc
, recv_sbc
;
85 int64_t last_ust
, ns_frame
, last_msc
, next_msc
;
88 bool is_different_gpu
;
92 dri3_free_front_buffer(struct vl_dri3_screen
*scrn
,
93 struct vl_dri3_buffer
*buffer
)
95 xcb_sync_destroy_fence(scrn
->conn
, buffer
->sync_fence
);
96 xshmfence_unmap_shm(buffer
->shm_fence
);
97 pipe_resource_reference(&buffer
->texture
, NULL
);
102 dri3_free_back_buffer(struct vl_dri3_screen
*scrn
,
103 struct vl_dri3_buffer
*buffer
)
105 xcb_free_pixmap(scrn
->conn
, buffer
->pixmap
);
106 xcb_sync_destroy_fence(scrn
->conn
, buffer
->sync_fence
);
107 xshmfence_unmap_shm(buffer
->shm_fence
);
108 pipe_resource_reference(&buffer
->texture
, NULL
);
109 if (buffer
->linear_texture
)
110 pipe_resource_reference(&buffer
->linear_texture
, NULL
);
115 dri3_handle_stamps(struct vl_dri3_screen
*scrn
, uint64_t ust
, uint64_t msc
)
117 int64_t ust_ns
= ust
* 1000;
119 if (scrn
->last_ust
&& (ust_ns
> scrn
->last_ust
) &&
120 scrn
->last_msc
&& (msc
> scrn
->last_msc
))
121 scrn
->ns_frame
= (ust_ns
- scrn
->last_ust
) / (msc
- scrn
->last_msc
);
123 scrn
->last_ust
= ust_ns
;
124 scrn
->last_msc
= msc
;
128 dri3_handle_present_event(struct vl_dri3_screen
*scrn
,
129 xcb_present_generic_event_t
*ge
)
131 switch (ge
->evtype
) {
132 case XCB_PRESENT_CONFIGURE_NOTIFY
: {
133 xcb_present_configure_notify_event_t
*ce
= (void *) ge
;
134 scrn
->width
= ce
->width
;
135 scrn
->height
= ce
->height
;
138 case XCB_PRESENT_COMPLETE_NOTIFY
: {
139 xcb_present_complete_notify_event_t
*ce
= (void *) ge
;
140 if (ce
->kind
== XCB_PRESENT_COMPLETE_KIND_PIXMAP
) {
141 scrn
->recv_sbc
= (scrn
->send_sbc
& 0xffffffff00000000LL
) | ce
->serial
;
142 if (scrn
->recv_sbc
> scrn
->send_sbc
)
143 scrn
->recv_sbc
-= 0x100000000;
144 dri3_handle_stamps(scrn
, ce
->ust
, ce
->msc
);
145 } else if (ce
->kind
== XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC
) {
146 scrn
->recv_msc_serial
= ce
->serial
;
147 dri3_handle_stamps(scrn
, ce
->ust
, ce
->msc
);
151 case XCB_PRESENT_EVENT_IDLE_NOTIFY
: {
152 xcb_present_idle_notify_event_t
*ie
= (void *) ge
;
154 for (b
= 0; b
< BACK_BUFFER_NUM
; b
++) {
155 struct vl_dri3_buffer
*buf
= scrn
->back_buffers
[b
];
156 if (buf
&& buf
->pixmap
== ie
->pixmap
) {
168 dri3_flush_present_events(struct vl_dri3_screen
*scrn
)
170 if (scrn
->special_event
) {
171 xcb_generic_event_t
*ev
;
172 while ((ev
= xcb_poll_for_special_event(
173 scrn
->conn
, scrn
->special_event
)) != NULL
)
174 dri3_handle_present_event(scrn
, (xcb_present_generic_event_t
*)ev
);
179 dri3_wait_present_events(struct vl_dri3_screen
*scrn
)
181 if (scrn
->special_event
) {
182 xcb_generic_event_t
*ev
;
183 ev
= xcb_wait_for_special_event(scrn
->conn
, scrn
->special_event
);
186 dri3_handle_present_event(scrn
, (xcb_present_generic_event_t
*)ev
);
193 dri3_find_back(struct vl_dri3_screen
*scrn
)
198 for (b
= 0; b
< BACK_BUFFER_NUM
; b
++) {
199 int id
= (b
+ scrn
->cur_back
) % BACK_BUFFER_NUM
;
200 struct vl_dri3_buffer
*buffer
= scrn
->back_buffers
[id
];
201 if (!buffer
|| !buffer
->busy
)
204 xcb_flush(scrn
->conn
);
205 if (!dri3_wait_present_events(scrn
))
210 static struct vl_dri3_buffer
*
211 dri3_alloc_back_buffer(struct vl_dri3_screen
*scrn
)
213 struct vl_dri3_buffer
*buffer
;
215 xcb_sync_fence_t sync_fence
;
216 struct xshmfence
*shm_fence
;
217 int buffer_fd
, fence_fd
;
218 struct pipe_resource templ
, *pixmap_buffer_texture
;
219 struct winsys_handle whandle
;
222 buffer
= CALLOC_STRUCT(vl_dri3_buffer
);
226 fence_fd
= xshmfence_alloc_shm();
230 shm_fence
= xshmfence_map_shm(fence_fd
);
234 memset(&templ
, 0, sizeof(templ
));
235 templ
.bind
= PIPE_BIND_RENDER_TARGET
| PIPE_BIND_SAMPLER_VIEW
;
236 templ
.format
= PIPE_FORMAT_B8G8R8X8_UNORM
;
237 templ
.target
= PIPE_TEXTURE_2D
;
238 templ
.last_level
= 0;
239 templ
.width0
= scrn
->width
;
240 templ
.height0
= scrn
->height
;
242 templ
.array_size
= 1;
244 if (scrn
->is_different_gpu
) {
245 buffer
->texture
= scrn
->base
.pscreen
->resource_create(scrn
->base
.pscreen
,
247 if (!buffer
->texture
)
250 templ
.bind
|= PIPE_BIND_SCANOUT
| PIPE_BIND_SHARED
|
252 buffer
->linear_texture
= scrn
->base
.pscreen
->resource_create(scrn
->base
.pscreen
,
254 pixmap_buffer_texture
= buffer
->linear_texture
;
256 if (!buffer
->linear_texture
)
257 goto no_linear_texture
;
259 templ
.bind
|= PIPE_BIND_SCANOUT
| PIPE_BIND_SHARED
;
260 buffer
->texture
= scrn
->base
.pscreen
->resource_create(scrn
->base
.pscreen
,
262 if (!buffer
->texture
)
264 pixmap_buffer_texture
= buffer
->texture
;
266 memset(&whandle
, 0, sizeof(whandle
));
267 whandle
.type
= DRM_API_HANDLE_TYPE_FD
;
268 usage
= PIPE_HANDLE_USAGE_EXPLICIT_FLUSH
| PIPE_HANDLE_USAGE_READ
;
269 scrn
->base
.pscreen
->resource_get_handle(scrn
->base
.pscreen
, NULL
,
270 pixmap_buffer_texture
, &whandle
,
272 buffer_fd
= whandle
.handle
;
273 buffer
->pitch
= whandle
.stride
;
274 xcb_dri3_pixmap_from_buffer(scrn
->conn
,
275 (pixmap
= xcb_generate_id(scrn
->conn
)),
278 scrn
->width
, scrn
->height
, buffer
->pitch
,
281 xcb_dri3_fence_from_fd(scrn
->conn
,
283 (sync_fence
= xcb_generate_id(scrn
->conn
)),
287 buffer
->pixmap
= pixmap
;
288 buffer
->sync_fence
= sync_fence
;
289 buffer
->shm_fence
= shm_fence
;
290 buffer
->width
= scrn
->width
;
291 buffer
->height
= scrn
->height
;
293 xshmfence_trigger(buffer
->shm_fence
);
298 pipe_resource_reference(&buffer
->texture
, NULL
);
300 xshmfence_unmap_shm(shm_fence
);
308 static struct vl_dri3_buffer
*
309 dri3_get_back_buffer(struct vl_dri3_screen
*scrn
)
311 struct vl_dri3_buffer
*buffer
;
312 struct pipe_resource
*texture
= NULL
;
316 scrn
->cur_back
= dri3_find_back(scrn
);
317 if (scrn
->cur_back
< 0)
319 buffer
= scrn
->back_buffers
[scrn
->cur_back
];
321 if (!buffer
|| buffer
->width
!= scrn
->width
||
322 buffer
->height
!= scrn
->height
) {
323 struct vl_dri3_buffer
*new_buffer
;
325 new_buffer
= dri3_alloc_back_buffer(scrn
);
330 dri3_free_back_buffer(scrn
, buffer
);
332 vl_compositor_reset_dirty_area(&scrn
->dirty_areas
[scrn
->cur_back
]);
334 scrn
->back_buffers
[scrn
->cur_back
] = buffer
;
337 pipe_resource_reference(&texture
, buffer
->texture
);
338 xcb_flush(scrn
->conn
);
339 xshmfence_await(buffer
->shm_fence
);
345 dri3_set_drawable(struct vl_dri3_screen
*scrn
, Drawable drawable
)
347 xcb_get_geometry_cookie_t geom_cookie
;
348 xcb_get_geometry_reply_t
*geom_reply
;
349 xcb_void_cookie_t cookie
;
350 xcb_generic_error_t
*error
;
355 if (scrn
->drawable
== drawable
)
358 scrn
->drawable
= drawable
;
360 geom_cookie
= xcb_get_geometry(scrn
->conn
, scrn
->drawable
);
361 geom_reply
= xcb_get_geometry_reply(scrn
->conn
, geom_cookie
, NULL
);
365 scrn
->width
= geom_reply
->width
;
366 scrn
->height
= geom_reply
->height
;
367 scrn
->depth
= geom_reply
->depth
;
370 if (scrn
->special_event
) {
371 xcb_unregister_for_special_event(scrn
->conn
, scrn
->special_event
);
372 scrn
->special_event
= NULL
;
373 cookie
= xcb_present_select_input_checked(scrn
->conn
, scrn
->eid
,
375 XCB_PRESENT_EVENT_MASK_NO_EVENT
);
376 xcb_discard_reply(scrn
->conn
, cookie
.sequence
);
379 scrn
->is_pixmap
= false;
380 scrn
->eid
= xcb_generate_id(scrn
->conn
);
382 xcb_present_select_input_checked(scrn
->conn
, scrn
->eid
, scrn
->drawable
,
383 XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY
|
384 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY
|
385 XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY
);
387 error
= xcb_request_check(scrn
->conn
, cookie
);
389 if (error
->error_code
!= BadWindow
)
392 scrn
->is_pixmap
= true;
393 if (scrn
->front_buffer
) {
394 dri3_free_front_buffer(scrn
, scrn
->front_buffer
);
395 scrn
->front_buffer
= NULL
;
400 scrn
->special_event
=
401 xcb_register_for_special_xge(scrn
->conn
, &xcb_present_id
, scrn
->eid
, 0);
403 dri3_flush_present_events(scrn
);
408 static struct vl_dri3_buffer
*
409 dri3_get_front_buffer(struct vl_dri3_screen
*scrn
)
411 xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie
;
412 xcb_dri3_buffer_from_pixmap_reply_t
*bp_reply
;
413 xcb_sync_fence_t sync_fence
;
414 struct xshmfence
*shm_fence
;
416 struct winsys_handle whandle
;
417 struct pipe_resource templ
, *texture
= NULL
;
419 if (scrn
->front_buffer
) {
420 pipe_resource_reference(&texture
, scrn
->front_buffer
->texture
);
421 return scrn
->front_buffer
;
424 scrn
->front_buffer
= CALLOC_STRUCT(vl_dri3_buffer
);
425 if (!scrn
->front_buffer
)
428 fence_fd
= xshmfence_alloc_shm();
432 shm_fence
= xshmfence_map_shm(fence_fd
);
436 bp_cookie
= xcb_dri3_buffer_from_pixmap(scrn
->conn
, scrn
->drawable
);
437 bp_reply
= xcb_dri3_buffer_from_pixmap_reply(scrn
->conn
, bp_cookie
, NULL
);
441 fds
= xcb_dri3_buffer_from_pixmap_reply_fds(scrn
->conn
, bp_reply
);
445 memset(&whandle
, 0, sizeof(whandle
));
446 whandle
.type
= DRM_API_HANDLE_TYPE_FD
;
447 whandle
.handle
= (unsigned)fds
[0];
448 whandle
.stride
= bp_reply
->stride
;
449 memset(&templ
, 0, sizeof(templ
));
450 templ
.bind
= PIPE_BIND_RENDER_TARGET
| PIPE_BIND_SAMPLER_VIEW
;
451 templ
.format
= PIPE_FORMAT_B8G8R8X8_UNORM
;
452 templ
.target
= PIPE_TEXTURE_2D
;
453 templ
.last_level
= 0;
454 templ
.width0
= bp_reply
->width
;
455 templ
.height0
= bp_reply
->height
;
457 templ
.array_size
= 1;
458 scrn
->front_buffer
->texture
=
459 scrn
->base
.pscreen
->resource_from_handle(scrn
->base
.pscreen
,
461 PIPE_HANDLE_USAGE_READ_WRITE
);
463 if (!scrn
->front_buffer
->texture
)
466 xcb_dri3_fence_from_fd(scrn
->conn
,
468 (sync_fence
= xcb_generate_id(scrn
->conn
)),
472 pipe_resource_reference(&texture
, scrn
->front_buffer
->texture
);
473 scrn
->front_buffer
->pixmap
= scrn
->drawable
;
474 scrn
->front_buffer
->width
= bp_reply
->width
;
475 scrn
->front_buffer
->height
= bp_reply
->height
;
476 scrn
->front_buffer
->shm_fence
= shm_fence
;
477 scrn
->front_buffer
->sync_fence
= sync_fence
;
480 return scrn
->front_buffer
;
485 xshmfence_unmap_shm(shm_fence
);
489 FREE(scrn
->front_buffer
);
494 vl_dri3_flush_frontbuffer(struct pipe_screen
*screen
,
495 struct pipe_resource
*resource
,
496 unsigned level
, unsigned layer
,
497 void *context_private
, struct pipe_box
*sub_box
)
499 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)context_private
;
500 uint32_t options
= XCB_PRESENT_OPTION_NONE
;
501 struct vl_dri3_buffer
*back
;
502 struct pipe_box src_box
;
504 back
= scrn
->back_buffers
[scrn
->cur_back
];
509 while (scrn
->special_event
&& scrn
->recv_sbc
< scrn
->send_sbc
)
510 if (!dri3_wait_present_events(scrn
))
514 if (scrn
->is_different_gpu
) {
515 u_box_origin_2d(scrn
->width
, scrn
->height
, &src_box
);
516 scrn
->pipe
->resource_copy_region(scrn
->pipe
,
517 back
->linear_texture
,
522 scrn
->pipe
->flush(scrn
->pipe
, NULL
, 0);
524 xshmfence_reset(back
->shm_fence
);
527 xcb_present_pixmap(scrn
->conn
,
530 (uint32_t)(++scrn
->send_sbc
),
538 xcb_flush(scrn
->conn
);
540 scrn
->flushed
= true;
545 static struct pipe_resource
*
546 vl_dri3_screen_texture_from_drawable(struct vl_screen
*vscreen
, void *drawable
)
548 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
549 struct vl_dri3_buffer
*buffer
;
553 if (!dri3_set_drawable(scrn
, (Drawable
)drawable
))
557 while (scrn
->special_event
&& scrn
->recv_sbc
< scrn
->send_sbc
)
558 if (!dri3_wait_present_events(scrn
))
561 scrn
->flushed
= false;
563 buffer
= (scrn
->is_pixmap
) ?
564 dri3_get_front_buffer(scrn
) :
565 dri3_get_back_buffer(scrn
);
569 return buffer
->texture
;
572 static struct u_rect
*
573 vl_dri3_screen_get_dirty_area(struct vl_screen
*vscreen
)
575 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
579 return &scrn
->dirty_areas
[scrn
->cur_back
];
583 vl_dri3_screen_get_timestamp(struct vl_screen
*vscreen
, void *drawable
)
585 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
589 if (!dri3_set_drawable(scrn
, (Drawable
)drawable
))
592 if (!scrn
->last_ust
) {
593 xcb_present_notify_msc(scrn
->conn
,
595 ++scrn
->send_msc_serial
,
597 xcb_flush(scrn
->conn
);
599 while (scrn
->special_event
&&
600 scrn
->send_msc_serial
> scrn
->recv_msc_serial
) {
601 if (!dri3_wait_present_events(scrn
))
606 return scrn
->last_ust
;
610 vl_dri3_screen_set_next_timestamp(struct vl_screen
*vscreen
, uint64_t stamp
)
612 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
616 if (stamp
&& scrn
->last_ust
&& scrn
->ns_frame
&& scrn
->last_msc
)
617 scrn
->next_msc
= ((int64_t)stamp
- scrn
->last_ust
+ scrn
->ns_frame
/2) /
618 scrn
->ns_frame
+ scrn
->last_msc
;
624 vl_dri3_screen_get_private(struct vl_screen
*vscreen
)
630 vl_dri3_screen_destroy(struct vl_screen
*vscreen
)
632 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
637 dri3_flush_present_events(scrn
);
639 if (scrn
->front_buffer
) {
640 dri3_free_front_buffer(scrn
, scrn
->front_buffer
);
641 scrn
->front_buffer
= NULL
;
645 for (i
= 0; i
< BACK_BUFFER_NUM
; ++i
) {
646 if (scrn
->back_buffers
[i
]) {
647 dri3_free_back_buffer(scrn
, scrn
->back_buffers
[i
]);
648 scrn
->back_buffers
[i
] = NULL
;
652 if (scrn
->special_event
) {
653 xcb_void_cookie_t cookie
=
654 xcb_present_select_input_checked(scrn
->conn
, scrn
->eid
,
656 XCB_PRESENT_EVENT_MASK_NO_EVENT
);
658 xcb_discard_reply(scrn
->conn
, cookie
.sequence
);
659 xcb_unregister_for_special_event(scrn
->conn
, scrn
->special_event
);
661 scrn
->pipe
->destroy(scrn
->pipe
);
662 scrn
->base
.pscreen
->destroy(scrn
->base
.pscreen
);
663 pipe_loader_release(&scrn
->base
.dev
, 1);
670 vl_dri3_screen_create(Display
*display
, int screen
)
672 struct vl_dri3_screen
*scrn
;
673 const xcb_query_extension_reply_t
*extension
;
674 xcb_dri3_open_cookie_t open_cookie
;
675 xcb_dri3_open_reply_t
*open_reply
;
676 xcb_get_geometry_cookie_t geom_cookie
;
677 xcb_get_geometry_reply_t
*geom_reply
;
682 scrn
= CALLOC_STRUCT(vl_dri3_screen
);
686 scrn
->conn
= XGetXCBConnection(display
);
690 xcb_prefetch_extension_data(scrn
->conn
, &xcb_dri3_id
);
691 xcb_prefetch_extension_data(scrn
->conn
, &xcb_present_id
);
692 extension
= xcb_get_extension_data(scrn
->conn
, &xcb_dri3_id
);
693 if (!(extension
&& extension
->present
))
695 extension
= xcb_get_extension_data(scrn
->conn
, &xcb_present_id
);
696 if (!(extension
&& extension
->present
))
699 open_cookie
= xcb_dri3_open(scrn
->conn
, RootWindow(display
, screen
), None
);
700 open_reply
= xcb_dri3_open_reply(scrn
->conn
, open_cookie
, NULL
);
703 if (open_reply
->nfd
!= 1) {
708 fd
= xcb_dri3_open_reply_fds(scrn
->conn
, open_reply
)[0];
713 fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
716 fd
= loader_get_user_preferred_fd(fd
, &scrn
->is_different_gpu
);
718 geom_cookie
= xcb_get_geometry(scrn
->conn
, RootWindow(display
, screen
));
719 geom_reply
= xcb_get_geometry_reply(scrn
->conn
, geom_cookie
, NULL
);
722 /* TODO support depth other than 24 */
723 if (geom_reply
->depth
!= 24) {
729 if (pipe_loader_drm_probe_fd(&scrn
->base
.dev
, fd
))
730 scrn
->base
.pscreen
= pipe_loader_create_screen(scrn
->base
.dev
);
732 if (!scrn
->base
.pscreen
)
735 scrn
->pipe
= scrn
->base
.pscreen
->context_create(scrn
->base
.pscreen
,
740 scrn
->base
.destroy
= vl_dri3_screen_destroy
;
741 scrn
->base
.texture_from_drawable
= vl_dri3_screen_texture_from_drawable
;
742 scrn
->base
.get_dirty_area
= vl_dri3_screen_get_dirty_area
;
743 scrn
->base
.get_timestamp
= vl_dri3_screen_get_timestamp
;
744 scrn
->base
.set_next_timestamp
= vl_dri3_screen_set_next_timestamp
;
745 scrn
->base
.get_private
= vl_dri3_screen_get_private
;
746 scrn
->base
.pscreen
->flush_frontbuffer
= vl_dri3_flush_frontbuffer
;
751 scrn
->base
.pscreen
->destroy(scrn
->base
.pscreen
);
753 if (scrn
->base
.dev
) {
754 pipe_loader_release(&scrn
->base
.dev
, 1);