vl/dri3: add back buffers support
[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_winsys.h"
45
46 #define BACK_BUFFER_NUM 3
47
48 struct vl_dri3_buffer
49 {
50 struct pipe_resource *texture;
51
52 uint32_t pixmap;
53 uint32_t sync_fence;
54 struct xshmfence *shm_fence;
55
56 bool busy;
57 uint32_t width, height, pitch;
58 };
59
60 struct vl_dri3_screen
61 {
62 struct vl_screen base;
63 xcb_connection_t *conn;
64 xcb_drawable_t drawable;
65
66 uint32_t width, height, depth;
67
68 xcb_special_event_t *special_event;
69
70 struct vl_dri3_buffer *back_buffers[BACK_BUFFER_NUM];
71 int cur_back;
72 };
73
74 static void
75 dri3_free_back_buffer(struct vl_dri3_screen *scrn,
76 struct vl_dri3_buffer *buffer)
77 {
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);
82 FREE(buffer);
83 }
84
85 static void
86 dri3_handle_present_event(struct vl_dri3_screen *scrn,
87 xcb_present_generic_event_t *ge)
88 {
89 switch (ge->evtype) {
90 case XCB_PRESENT_CONFIGURE_NOTIFY: {
91 /* TODO */
92 break;
93 }
94 case XCB_PRESENT_COMPLETE_NOTIFY: {
95 /* TODO */
96 break;
97 }
98 case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
99 /* TODO */
100 break;
101 }
102 }
103 free(ge);
104 }
105
106 static void
107 dri3_flush_present_events(struct vl_dri3_screen *scrn)
108 {
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);
114 }
115 }
116
117 static bool
118 dri3_wait_present_events(struct vl_dri3_screen *scrn)
119 {
120 if (scrn->special_event) {
121 xcb_generic_event_t *ev;
122 ev = xcb_wait_for_special_event(scrn->conn, scrn->special_event);
123 if (!ev)
124 return false;
125 dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
126 return true;
127 }
128 return false;
129 }
130
131 static int
132 dri3_find_back(struct vl_dri3_screen *scrn)
133 {
134 int b;
135
136 for (;;) {
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)
141 return id;
142 }
143 xcb_flush(scrn->conn);
144 if (!dri3_wait_present_events(scrn))
145 return -1;
146 }
147 }
148
149 static struct vl_dri3_buffer *
150 dri3_alloc_back_buffer(struct vl_dri3_screen *scrn)
151 {
152 struct vl_dri3_buffer *buffer;
153 xcb_pixmap_t pixmap;
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;
159 unsigned usage;
160
161 buffer = CALLOC_STRUCT(vl_dri3_buffer);
162 if (!buffer)
163 return NULL;
164
165 fence_fd = xshmfence_alloc_shm();
166 if (fence_fd < 0)
167 goto free_buffer;
168
169 shm_fence = xshmfence_map_shm(fence_fd);
170 if (!shm_fence)
171 goto close_fd;
172
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;
181 templ.depth0 = 1;
182 templ.array_size = 1;
183 buffer->texture = scrn->base.pscreen->resource_create(scrn->base.pscreen,
184 &templ);
185 if (!buffer->texture)
186 goto unmap_shm;
187
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,
193 usage);
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)),
198 scrn->drawable,
199 0,
200 scrn->width, scrn->height, buffer->pitch,
201 scrn->depth, 32,
202 buffer_fd);
203 xcb_dri3_fence_from_fd(scrn->conn,
204 pixmap,
205 (sync_fence = xcb_generate_id(scrn->conn)),
206 false,
207 fence_fd);
208
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;
214
215 xshmfence_trigger(buffer->shm_fence);
216
217 return buffer;
218
219 unmap_shm:
220 xshmfence_unmap_shm(shm_fence);
221 close_fd:
222 close(fence_fd);
223 free_buffer:
224 FREE(buffer);
225 return NULL;
226 }
227
228 static struct vl_dri3_buffer *
229 dri3_get_back_buffer(struct vl_dri3_screen *scrn)
230 {
231 struct vl_dri3_buffer *buffer;
232 struct pipe_resource *texture = NULL;
233
234 assert(scrn);
235
236 scrn->cur_back = dri3_find_back(scrn);
237 if (scrn->cur_back < 0)
238 return NULL;
239 buffer = scrn->back_buffers[scrn->cur_back];
240
241 if (!buffer) {
242 buffer = dri3_alloc_back_buffer(scrn);
243 if (!buffer)
244 return NULL;
245
246 scrn->back_buffers[scrn->cur_back] = buffer;
247 }
248
249 pipe_resource_reference(&texture, buffer->texture);
250 xcb_flush(scrn->conn);
251 xshmfence_await(buffer->shm_fence);
252
253 return buffer;
254 }
255
256 static bool
257 dri3_set_drawable(struct vl_dri3_screen *scrn, Drawable drawable)
258 {
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;
264
265 assert(drawable);
266
267 if (scrn->drawable == drawable)
268 return true;
269
270 scrn->drawable = drawable;
271
272 geom_cookie = xcb_get_geometry(scrn->conn, scrn->drawable);
273 geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
274 if (!geom_reply)
275 return false;
276
277 scrn->width = geom_reply->width;
278 scrn->height = geom_reply->height;
279 scrn->depth = geom_reply->depth;
280 free(geom_reply);
281
282 if (scrn->special_event) {
283 xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
284 scrn->special_event = NULL;
285 }
286
287 peid = xcb_generate_id(scrn->conn);
288 cookie =
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);
293
294 error = xcb_request_check(scrn->conn, cookie);
295 if (error) {
296 free(error);
297 return false;
298 } else
299 scrn->special_event =
300 xcb_register_for_special_xge(scrn->conn, &xcb_present_id, peid, 0);
301
302 dri3_flush_present_events(scrn);
303
304 return true;
305 }
306
307 static void
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)
312 {
313 /* TODO */
314 return;
315 }
316
317 static struct pipe_resource *
318 vl_dri3_screen_texture_from_drawable(struct vl_screen *vscreen, void *drawable)
319 {
320 struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
321 struct vl_dri3_buffer *buffer;
322
323 assert(scrn);
324
325 if (!dri3_set_drawable(scrn, (Drawable)drawable))
326 return NULL;
327
328 buffer = dri3_get_back_buffer(scrn);
329 if (!buffer)
330 return NULL;
331
332 return buffer->texture;
333 }
334
335 static struct u_rect *
336 vl_dri3_screen_get_dirty_area(struct vl_screen *vscreen)
337 {
338 /* TODO */
339 return NULL;
340 }
341
342 static uint64_t
343 vl_dri3_screen_get_timestamp(struct vl_screen *vscreen, void *drawable)
344 {
345 /* TODO */
346 return 0;
347 }
348
349 static void
350 vl_dri3_screen_set_next_timestamp(struct vl_screen *vscreen, uint64_t stamp)
351 {
352 /* TODO */
353 return;
354 }
355
356 static void *
357 vl_dri3_screen_get_private(struct vl_screen *vscreen)
358 {
359 return vscreen;
360 }
361
362 static void
363 vl_dri3_screen_destroy(struct vl_screen *vscreen)
364 {
365 struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
366 int i;
367
368 assert(vscreen);
369
370 dri3_flush_present_events(scrn);
371
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;
376 }
377 }
378
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);
383 FREE(scrn);
384
385 return;
386 }
387
388 struct vl_screen *
389 vl_dri3_screen_create(Display *display, int screen)
390 {
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;
398 int fd;
399
400 assert(display);
401
402 scrn = CALLOC_STRUCT(vl_dri3_screen);
403 if (!scrn)
404 return NULL;
405
406 scrn->conn = XGetXCBConnection(display);
407 if (!scrn->conn)
408 goto free_screen;
409
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))
414 goto free_screen;
415 extension = xcb_get_extension_data(scrn->conn, &xcb_present_id);
416 if (!(extension && extension->present))
417 goto free_screen;
418
419 open_cookie = xcb_dri3_open(scrn->conn, RootWindow(display, screen), None);
420 open_reply = xcb_dri3_open_reply(scrn->conn, open_cookie, NULL);
421 if (!open_reply)
422 goto free_screen;
423 if (open_reply->nfd != 1) {
424 free(open_reply);
425 goto free_screen;
426 }
427
428 fd = xcb_dri3_open_reply_fds(scrn->conn, open_reply)[0];
429 if (fd < 0) {
430 free(open_reply);
431 goto free_screen;
432 }
433 fcntl(fd, F_SETFD, FD_CLOEXEC);
434 free(open_reply);
435
436 fd = loader_get_user_preferred_fd(fd, &is_different_gpu);
437 /* TODO support different GPU */
438 if (is_different_gpu)
439 goto close_fd;
440
441 geom_cookie = xcb_get_geometry(scrn->conn, RootWindow(display, screen));
442 geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
443 if (!geom_reply)
444 goto close_fd;
445 /* TODO support depth other than 24 */
446 if (geom_reply->depth != 24) {
447 free(geom_reply);
448 goto close_fd;
449 }
450 free(geom_reply);
451
452 if (pipe_loader_drm_probe_fd(&scrn->base.dev, fd))
453 scrn->base.pscreen = pipe_loader_create_screen(scrn->base.dev);
454
455 if (!scrn->base.pscreen)
456 goto release_pipe;
457
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;
465
466 return &scrn->base;
467
468 release_pipe:
469 if (scrn->base.dev) {
470 pipe_loader_release(&scrn->base.dev, 1);
471 fd = -1;
472 }
473 close_fd:
474 if (fd != -1)
475 close(fd);
476 free_screen:
477 FREE(scrn);
478 return NULL;
479 }