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_winsys.h"
46 #define BACK_BUFFER_NUM 3
50 struct pipe_resource
*texture
;
54 struct xshmfence
*shm_fence
;
57 uint32_t width
, height
, pitch
;
62 struct vl_screen base
;
63 xcb_connection_t
*conn
;
64 xcb_drawable_t drawable
;
66 uint32_t width
, height
, depth
;
68 xcb_special_event_t
*special_event
;
70 struct vl_dri3_buffer
*back_buffers
[BACK_BUFFER_NUM
];
75 dri3_free_back_buffer(struct vl_dri3_screen
*scrn
,
76 struct vl_dri3_buffer
*buffer
)
78 xcb_free_pixmap(scrn
->conn
, buffer
->pixmap
);
79 xcb_sync_destroy_fence(scrn
->conn
, buffer
->sync_fence
);
80 xshmfence_unmap_shm(buffer
->shm_fence
);
81 pipe_resource_reference(&buffer
->texture
, NULL
);
86 dri3_handle_present_event(struct vl_dri3_screen
*scrn
,
87 xcb_present_generic_event_t
*ge
)
90 case XCB_PRESENT_CONFIGURE_NOTIFY
: {
94 case XCB_PRESENT_COMPLETE_NOTIFY
: {
98 case XCB_PRESENT_EVENT_IDLE_NOTIFY
: {
107 dri3_flush_present_events(struct vl_dri3_screen
*scrn
)
109 if (scrn
->special_event
) {
110 xcb_generic_event_t
*ev
;
111 while ((ev
= xcb_poll_for_special_event(
112 scrn
->conn
, scrn
->special_event
)) != NULL
)
113 dri3_handle_present_event(scrn
, (xcb_present_generic_event_t
*)ev
);
118 dri3_wait_present_events(struct vl_dri3_screen
*scrn
)
120 if (scrn
->special_event
) {
121 xcb_generic_event_t
*ev
;
122 ev
= xcb_wait_for_special_event(scrn
->conn
, scrn
->special_event
);
125 dri3_handle_present_event(scrn
, (xcb_present_generic_event_t
*)ev
);
132 dri3_find_back(struct vl_dri3_screen
*scrn
)
137 for (b
= 0; b
< BACK_BUFFER_NUM
; b
++) {
138 int id
= (b
+ scrn
->cur_back
) % BACK_BUFFER_NUM
;
139 struct vl_dri3_buffer
*buffer
= scrn
->back_buffers
[id
];
140 if (!buffer
|| !buffer
->busy
)
143 xcb_flush(scrn
->conn
);
144 if (!dri3_wait_present_events(scrn
))
149 static struct vl_dri3_buffer
*
150 dri3_alloc_back_buffer(struct vl_dri3_screen
*scrn
)
152 struct vl_dri3_buffer
*buffer
;
154 xcb_sync_fence_t sync_fence
;
155 struct xshmfence
*shm_fence
;
156 int buffer_fd
, fence_fd
;
157 struct pipe_resource templ
;
158 struct winsys_handle whandle
;
161 buffer
= CALLOC_STRUCT(vl_dri3_buffer
);
165 fence_fd
= xshmfence_alloc_shm();
169 shm_fence
= xshmfence_map_shm(fence_fd
);
173 memset(&templ
, 0, sizeof(templ
));
174 templ
.bind
= PIPE_BIND_RENDER_TARGET
| PIPE_BIND_SAMPLER_VIEW
|
175 PIPE_BIND_SCANOUT
| PIPE_BIND_SHARED
;
176 templ
.format
= PIPE_FORMAT_B8G8R8X8_UNORM
;
177 templ
.target
= PIPE_TEXTURE_2D
;
178 templ
.last_level
= 0;
179 templ
.width0
= scrn
->width
;
180 templ
.height0
= scrn
->height
;
182 templ
.array_size
= 1;
183 buffer
->texture
= scrn
->base
.pscreen
->resource_create(scrn
->base
.pscreen
,
185 if (!buffer
->texture
)
188 memset(&whandle
, 0, sizeof(whandle
));
189 whandle
.type
= DRM_API_HANDLE_TYPE_FD
;
190 usage
= PIPE_HANDLE_USAGE_EXPLICIT_FLUSH
| PIPE_HANDLE_USAGE_READ
;
191 scrn
->base
.pscreen
->resource_get_handle(scrn
->base
.pscreen
,
192 buffer
->texture
, &whandle
,
194 buffer_fd
= whandle
.handle
;
195 buffer
->pitch
= whandle
.stride
;
196 xcb_dri3_pixmap_from_buffer(scrn
->conn
,
197 (pixmap
= xcb_generate_id(scrn
->conn
)),
200 scrn
->width
, scrn
->height
, buffer
->pitch
,
203 xcb_dri3_fence_from_fd(scrn
->conn
,
205 (sync_fence
= xcb_generate_id(scrn
->conn
)),
209 buffer
->pixmap
= pixmap
;
210 buffer
->sync_fence
= sync_fence
;
211 buffer
->shm_fence
= shm_fence
;
212 buffer
->width
= scrn
->width
;
213 buffer
->height
= scrn
->height
;
215 xshmfence_trigger(buffer
->shm_fence
);
220 xshmfence_unmap_shm(shm_fence
);
228 static struct vl_dri3_buffer
*
229 dri3_get_back_buffer(struct vl_dri3_screen
*scrn
)
231 struct vl_dri3_buffer
*buffer
;
232 struct pipe_resource
*texture
= NULL
;
236 scrn
->cur_back
= dri3_find_back(scrn
);
237 if (scrn
->cur_back
< 0)
239 buffer
= scrn
->back_buffers
[scrn
->cur_back
];
242 buffer
= dri3_alloc_back_buffer(scrn
);
246 scrn
->back_buffers
[scrn
->cur_back
] = buffer
;
249 pipe_resource_reference(&texture
, buffer
->texture
);
250 xcb_flush(scrn
->conn
);
251 xshmfence_await(buffer
->shm_fence
);
257 dri3_set_drawable(struct vl_dri3_screen
*scrn
, Drawable drawable
)
259 xcb_get_geometry_cookie_t geom_cookie
;
260 xcb_get_geometry_reply_t
*geom_reply
;
261 xcb_void_cookie_t cookie
;
262 xcb_generic_error_t
*error
;
263 xcb_present_event_t peid
;
267 if (scrn
->drawable
== drawable
)
270 scrn
->drawable
= drawable
;
272 geom_cookie
= xcb_get_geometry(scrn
->conn
, scrn
->drawable
);
273 geom_reply
= xcb_get_geometry_reply(scrn
->conn
, geom_cookie
, NULL
);
277 scrn
->width
= geom_reply
->width
;
278 scrn
->height
= geom_reply
->height
;
279 scrn
->depth
= geom_reply
->depth
;
282 if (scrn
->special_event
) {
283 xcb_unregister_for_special_event(scrn
->conn
, scrn
->special_event
);
284 scrn
->special_event
= NULL
;
287 peid
= xcb_generate_id(scrn
->conn
);
289 xcb_present_select_input_checked(scrn
->conn
, peid
, scrn
->drawable
,
290 XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY
|
291 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY
|
292 XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY
);
294 error
= xcb_request_check(scrn
->conn
, cookie
);
299 scrn
->special_event
=
300 xcb_register_for_special_xge(scrn
->conn
, &xcb_present_id
, peid
, 0);
302 dri3_flush_present_events(scrn
);
308 vl_dri3_flush_frontbuffer(struct pipe_screen
*screen
,
309 struct pipe_resource
*resource
,
310 unsigned level
, unsigned layer
,
311 void *context_private
, struct pipe_box
*sub_box
)
317 static struct pipe_resource
*
318 vl_dri3_screen_texture_from_drawable(struct vl_screen
*vscreen
, void *drawable
)
320 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
321 struct vl_dri3_buffer
*buffer
;
325 if (!dri3_set_drawable(scrn
, (Drawable
)drawable
))
328 buffer
= dri3_get_back_buffer(scrn
);
332 return buffer
->texture
;
335 static struct u_rect
*
336 vl_dri3_screen_get_dirty_area(struct vl_screen
*vscreen
)
343 vl_dri3_screen_get_timestamp(struct vl_screen
*vscreen
, void *drawable
)
350 vl_dri3_screen_set_next_timestamp(struct vl_screen
*vscreen
, uint64_t stamp
)
357 vl_dri3_screen_get_private(struct vl_screen
*vscreen
)
363 vl_dri3_screen_destroy(struct vl_screen
*vscreen
)
365 struct vl_dri3_screen
*scrn
= (struct vl_dri3_screen
*)vscreen
;
370 dri3_flush_present_events(scrn
);
372 for (i
= 0; i
< BACK_BUFFER_NUM
; ++i
) {
373 if (scrn
->back_buffers
[i
]) {
374 dri3_free_back_buffer(scrn
, scrn
->back_buffers
[i
]);
375 scrn
->back_buffers
[i
] = NULL
;
379 if (scrn
->special_event
)
380 xcb_unregister_for_special_event(scrn
->conn
, scrn
->special_event
);
381 scrn
->base
.pscreen
->destroy(scrn
->base
.pscreen
);
382 pipe_loader_release(&scrn
->base
.dev
, 1);
389 vl_dri3_screen_create(Display
*display
, int screen
)
391 struct vl_dri3_screen
*scrn
;
392 const xcb_query_extension_reply_t
*extension
;
393 xcb_dri3_open_cookie_t open_cookie
;
394 xcb_dri3_open_reply_t
*open_reply
;
395 xcb_get_geometry_cookie_t geom_cookie
;
396 xcb_get_geometry_reply_t
*geom_reply
;
397 int is_different_gpu
;
402 scrn
= CALLOC_STRUCT(vl_dri3_screen
);
406 scrn
->conn
= XGetXCBConnection(display
);
410 xcb_prefetch_extension_data(scrn
->conn
, &xcb_dri3_id
);
411 xcb_prefetch_extension_data(scrn
->conn
, &xcb_present_id
);
412 extension
= xcb_get_extension_data(scrn
->conn
, &xcb_dri3_id
);
413 if (!(extension
&& extension
->present
))
415 extension
= xcb_get_extension_data(scrn
->conn
, &xcb_present_id
);
416 if (!(extension
&& extension
->present
))
419 open_cookie
= xcb_dri3_open(scrn
->conn
, RootWindow(display
, screen
), None
);
420 open_reply
= xcb_dri3_open_reply(scrn
->conn
, open_cookie
, NULL
);
423 if (open_reply
->nfd
!= 1) {
428 fd
= xcb_dri3_open_reply_fds(scrn
->conn
, open_reply
)[0];
433 fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
436 fd
= loader_get_user_preferred_fd(fd
, &is_different_gpu
);
437 /* TODO support different GPU */
438 if (is_different_gpu
)
441 geom_cookie
= xcb_get_geometry(scrn
->conn
, RootWindow(display
, screen
));
442 geom_reply
= xcb_get_geometry_reply(scrn
->conn
, geom_cookie
, NULL
);
445 /* TODO support depth other than 24 */
446 if (geom_reply
->depth
!= 24) {
452 if (pipe_loader_drm_probe_fd(&scrn
->base
.dev
, fd
))
453 scrn
->base
.pscreen
= pipe_loader_create_screen(scrn
->base
.dev
);
455 if (!scrn
->base
.pscreen
)
458 scrn
->base
.destroy
= vl_dri3_screen_destroy
;
459 scrn
->base
.texture_from_drawable
= vl_dri3_screen_texture_from_drawable
;
460 scrn
->base
.get_dirty_area
= vl_dri3_screen_get_dirty_area
;
461 scrn
->base
.get_timestamp
= vl_dri3_screen_get_timestamp
;
462 scrn
->base
.set_next_timestamp
= vl_dri3_screen_set_next_timestamp
;
463 scrn
->base
.get_private
= vl_dri3_screen_get_private
;
464 scrn
->base
.pscreen
->flush_frontbuffer
= vl_dri3_flush_frontbuffer
;
469 if (scrn
->base
.dev
) {
470 pipe_loader_release(&scrn
->base
.dev
, 1);