blob: fb7cee68fcd0c8eac03f310085b09b7592d2a541 [file] [log] [blame]
Chia-I Wu1db76e02014-09-15 14:21:14 +08001/*
2 * XGL
3 *
4 * Copyright (C) 2014 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 * Chia-I Wu <olv@lunarg.com>
26 */
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h>
31#include <fcntl.h>
32#include <xcb/xcb.h>
33#include <xcb/dri3.h>
34#include <xcb/present.h>
35
36#include "kmd/winsys.h"
37#include "dev.h"
38#include "fence.h"
39#include "gpu.h"
40#include "img.h"
41#include "mem.h"
42#include "queue.h"
43#include "wsi_x11.h"
44
45struct intel_wsi_x11 {
46 xcb_connection_t *c;
47 xcb_window_t root;
48 xcb_randr_provider_t provider;
49 int root_depth;
50
51 int dri3_major, dri3_minor;
52 int present_major, present_minor;
53
54 int fd;
55
56 xcb_present_event_t present_special_event_id;
57 xcb_special_event_t *present_special_event;
58
59 struct {
60 uint32_t serial;
61 } local;
62
63 struct {
64 uint32_t serial;
65 XGL_UINT64 msc;
66 } remote;
67};
68
69/**
70 * Return true if DRI3 and Present are supported by the server.
71 */
72static bool wsi_x11_has_dri3_and_present(xcb_connection_t *c)
73{
74 const xcb_query_extension_reply_t *ext;
75
76 xcb_prefetch_extension_data(c, &xcb_dri3_id);
77 xcb_prefetch_extension_data(c, &xcb_present_id);
78
79 ext = xcb_get_extension_data(c, &xcb_dri3_id);
80 if (!ext || !ext->present)
81 return false;
82
83 ext = xcb_get_extension_data(c, &xcb_present_id);
84 if (!ext || !ext->present)
85 return false;
86
87 return true;
88}
89
90/**
91 * Return the depth of the root window.
92 */
93static int wsi_x11_get_root_depth(struct intel_wsi_x11 *x11)
94{
95 const xcb_setup_t *setup;
96 xcb_screen_iterator_t iter;
97
98 setup = xcb_get_setup(x11->c);
99
100 iter = xcb_setup_roots_iterator(setup);
101 for (; iter.rem; xcb_screen_next(&iter)) {
102 if (iter.data->root == x11->root)
103 return iter.data->root_depth;
104 }
105
106 return 0;
107}
108
109/**
110 * Query DRI3 and Present versions and return an intel_wsi_x11.
111 */
112static struct intel_wsi_x11 *wsi_x11_create(xcb_connection_t *c,
113 xcb_window_t root,
114 xcb_randr_provider_t provider)
115{
116 xcb_dri3_query_version_cookie_t dri3_cookie;
117 xcb_dri3_query_version_reply_t *dri3_reply;
118 xcb_present_query_version_cookie_t present_cookie;
119 xcb_present_query_version_reply_t *present_reply;
120 struct intel_wsi_x11 *x11;
121
122 dri3_cookie = xcb_dri3_query_version(c,
123 XCB_DRI3_MAJOR_VERSION, XCB_DRI3_MINOR_VERSION);
124 present_cookie = xcb_present_query_version(c,
125 XCB_PRESENT_MAJOR_VERSION, XCB_PRESENT_MINOR_VERSION);
126
127 x11 = icd_alloc(sizeof(*x11), 0, XGL_SYSTEM_ALLOC_INTERNAL);
128 if (!x11)
129 return NULL;
130 memset(x11, 0, sizeof(*x11));
131
132 x11->c = c;
133 x11->root = root;
134 x11->provider = provider;
135
136 x11->root_depth = wsi_x11_get_root_depth(x11);
137
138 dri3_reply = xcb_dri3_query_version_reply(c, dri3_cookie, NULL);
139 if (!dri3_reply) {
140 icd_free(x11);
141 return NULL;
142 }
143
144 x11->dri3_major = dri3_reply->major_version;
145 x11->dri3_minor = dri3_reply->minor_version;
146 free(dri3_reply);
147
148 present_reply = xcb_present_query_version_reply(c, present_cookie, NULL);
149 if (!present_reply) {
150 icd_free(x11);
151 return NULL;
152 }
153
154 x11->present_major = present_reply->major_version;
155 x11->present_minor = present_reply->minor_version;
156 free(present_reply);
157
158 x11->fd = -1;
159
160 return x11;
161}
162
163/**
164 * Return true if x11->fd points to the primary or render node of the GPU.
165 */
166static bool wsi_x11_uses_gpu(const struct intel_wsi_x11 *x11,
167 const struct intel_gpu *gpu)
168{
169 struct stat x11_stat, gpu_stat;
170
171 if (fstat(x11->fd, &x11_stat))
172 return false;
173
174 /* is it the primary node? */
175 if (!stat(gpu->primary_node, &gpu_stat) &&
176 !memcmp(&x11_stat, &gpu_stat, sizeof(x11_stat)))
177 return true;
178
179 /* is it the render node? */
180 if (gpu->render_node && !stat(gpu->render_node, &gpu_stat) &&
181 !memcmp(&x11_stat, &gpu_stat, sizeof(x11_stat)))
182 return true;
183
184 return false;
185}
186
187/**
188 * Send a DRI3Open to get the server GPU fd.
189 */
190static XGL_RESULT wsi_x11_dri3_open(struct intel_wsi_x11 *x11)
191{
192 xcb_dri3_open_cookie_t cookie;
193 xcb_dri3_open_reply_t *reply;
194 int fd;
195
196 cookie = xcb_dri3_open(x11->c, x11->root, x11->provider);
197 reply = xcb_dri3_open_reply(x11->c, cookie, NULL);
198 if (!reply)
199 return XGL_ERROR_UNKNOWN;
200
201 fd = (reply->nfd == 1) ? xcb_dri3_open_reply_fds(x11->c, reply)[0] : -1;
202 free(reply);
203
204 if (fd < 0)
205 return XGL_ERROR_UNKNOWN;
206
207 fcntl(fd, F_SETFD, FD_CLOEXEC);
208 x11->fd = fd;
209
210 return XGL_SUCCESS;
211}
212
213/**
214 * Send a DRI3PixmapFromBuffer to create a Pixmap from \p mem for \p img.
215 */
216static XGL_RESULT wsi_x11_dri3_pixmap_from_buffer(struct intel_wsi_x11 *x11,
217 struct intel_dev *dev,
218 struct intel_img *img,
219 struct intel_mem *mem)
220{
221 struct intel_winsys_handle export;
222 xcb_pixmap_t pixmap;
223
224 /* get prime fd of the bo first */
225 export.type = INTEL_WINSYS_HANDLE_FD;
226 if (intel_winsys_export_handle(dev->winsys, mem->bo, img->layout.tiling,
227 img->layout.bo_stride, img->layout.bo_height, &export))
228 return XGL_ERROR_UNKNOWN;
229
230 pixmap = xcb_generate_id(x11->c);
231
232 /* create a pixmap from the prime fd */
233 xcb_dri3_pixmap_from_buffer(x11->c, pixmap,
234 x11->root, img->total_size,
235 img->layout.width0, img->layout.height0,
236 img->layout.bo_stride, x11->root_depth,
237 img->layout.block_size * 8, export.handle);
238
239 img->x11_prime_fd = export.handle;
240 img->x11_pixmap = pixmap;
241
242 return XGL_SUCCESS;
243}
244
245/**
246 * Send a PresentSelectInput to select interested events.
247 */
248static XGL_RESULT wsi_x11_present_select_input(struct intel_wsi_x11 *x11)
249{
250 xcb_void_cookie_t cookie;
251 xcb_generic_error_t *error;
252
253 /* create the event queue */
254 x11->present_special_event_id = xcb_generate_id(x11->c);
255 x11->present_special_event = xcb_register_for_special_xge(x11->c,
256 &xcb_present_id, x11->present_special_event_id, NULL);
257
258 cookie = xcb_present_select_input_checked(x11->c,
259 x11->present_special_event_id, x11->root,
260 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
261
262 error = xcb_request_check(x11->c, cookie);
263 if (error) {
264 free(error);
265 return XGL_ERROR_UNKNOWN;
266 }
267
268 return XGL_SUCCESS;
269}
270
271/**
272 * Send a PresentPixmap.
273 */
274static XGL_RESULT wsi_x11_present_pixmap(struct intel_wsi_x11 *x11,
275 const XGL_WSI_X11_PRESENT_INFO *info)
276{
277 struct intel_img *img = intel_img(info->srcImage);
278 uint32_t options = XCB_PRESENT_OPTION_NONE;
279 xcb_void_cookie_t cookie;
280 xcb_generic_error_t *err;
281
282 if (info->async)
283 options |= XCB_PRESENT_OPTION_ASYNC;
284 if (!info->flip)
285 options |= XCB_PRESENT_OPTION_COPY;
286
287 cookie = xcb_present_pixmap(x11->c,
288 info->destWindow,
289 img->x11_pixmap,
290 ++x11->local.serial,
291 0, /* valid-area */
292 0, /* update-area */
293 0, /* x-off */
294 0, /* y-off */
295 info->crtc,
296 0, /* wait-fence */
297 0, /* idle-fence */
298 options,
299 info->target_msc,
300 info->divisor,
301 info->remainder,
302 0, NULL);
303
304 err = xcb_request_check(x11->c, cookie);
305 if (err) {
306 free(err);
307 return XGL_ERROR_UNKNOWN;
308 }
309
310 return XGL_SUCCESS;
311}
312
313/**
314 * Send a PresentNotifyMSC for the current MSC.
315 */
316static void wsi_x11_present_notify_msc(struct intel_wsi_x11 *x11,
317 xcb_randr_crtc_t crtc)
318{
319 /* cannot specify CRTC? */
320 xcb_present_notify_msc(x11->c, x11->root, ++x11->local.serial,
321 0, 0, 0);
322
323 xcb_flush(x11->c);
324}
325
326/**
327 * Handle a Present event.
328 */
329static void wsi_x11_present_event(struct intel_wsi_x11 *x11,
330 const xcb_present_generic_event_t *ev)
331{
332 union {
333 const xcb_present_generic_event_t *ev;
334 const xcb_present_complete_notify_event_t *complete;
335 } u = { .ev = ev };
336
337 switch (u.ev->evtype) {
338 case XCB_PRESENT_COMPLETE_NOTIFY:
339 x11->remote.serial = u.complete->serial;
340 x11->remote.msc = u.complete->msc;
341 break;
342 default:
343 break;
344 }
345}
346
347void intel_wsi_x11_destroy(struct intel_wsi_x11 *x11)
348{
349 if (x11->present_special_event)
350 xcb_unregister_for_special_event(x11->c, x11->present_special_event);
351
352 if (x11->fd >= 0)
353 close(x11->fd);
354
355 icd_free(x11);
356}
357
358XGL_RESULT intel_wsi_x11_wait(struct intel_wsi_x11 *x11,
359 uint32_t serial, bool wait)
360{
361 while (x11->remote.serial < serial) {
362 xcb_present_generic_event_t *ev;
363
364 if (wait) {
365 ev = (xcb_present_generic_event_t *)
366 xcb_wait_for_special_event(x11->c,
367 x11->present_special_event);
368 if (!ev)
369 return XGL_ERROR_UNKNOWN;
370 } else {
371 ev = (xcb_present_generic_event_t *)
372 xcb_poll_for_special_event(x11->c,
373 x11->present_special_event);
374 if (!ev)
375 return XGL_NOT_READY;
376 }
377
378 wsi_x11_present_event(x11, ev);
379
380 free(ev);
381 }
382
383 return XGL_SUCCESS;
384}
385
386/**
387 * Create a presentable image.
388 */
389static XGL_RESULT wsi_x11_img_create(struct intel_wsi_x11 *x11,
390 struct intel_dev *dev,
391 const XGL_WSI_X11_PRESENTABLE_IMAGE_CREATE_INFO *info,
392 struct intel_img **img_ret)
393{
394 XGL_IMAGE_CREATE_INFO img_info;
395 XGL_MEMORY_ALLOC_INFO mem_info;
396 struct intel_img *img;
397 struct intel_mem *mem;
398 XGL_RESULT ret;
399
400 /* create image */
401 memset(&img_info, 0, sizeof(img_info));
402 img_info.sType = XGL_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
403 img_info.imageType = XGL_IMAGE_2D;
404 img_info.format = info->format;
405 img_info.extent.width = info->extent.width;
406 img_info.extent.height = info->extent.height;
407 img_info.extent.depth = 1;
408 img_info.mipLevels = 1;
409 img_info.arraySize = 1;
410 img_info.samples = 1;
411 img_info.tiling = XGL_OPTIMAL_TILING;
412 img_info.usage = info->usage;
413 img_info.flags = 0;
414
415 ret = intel_img_create(dev, &img_info, true, &img);
416 if (ret != XGL_SUCCESS)
417 return ret;
418
419 /* allocate memory */
420 memset(&mem_info, 0, sizeof(mem_info));
421 mem_info.sType = XGL_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
422 mem_info.allocationSize = img->total_size;
423 mem_info.alignment = 4096;
424 mem_info.flags = 0;
425 mem_info.heapCount = 1;
426 mem_info.memPriority = XGL_MEMORY_PRIORITY_HIGH;
427
428 ret = intel_mem_alloc(dev, &mem_info, &mem);
429 if (ret != XGL_SUCCESS) {
430 intel_img_destroy(img);
431 return ret;
432 }
433
434 ret = wsi_x11_dri3_pixmap_from_buffer(x11, dev, img, mem);
435 if (ret != XGL_SUCCESS) {
436 intel_mem_free(mem);
437 intel_img_destroy(img);
438 return ret;
439 }
440
441 intel_obj_bind_mem(&img->obj, mem, 0);
442
443 *img_ret = img;
444
445 return XGL_SUCCESS;
446}
447
448XGL_RESULT XGLAPI intelWsiX11AssociateConnection(
449 XGL_PHYSICAL_GPU gpu_,
450 const XGL_WSI_X11_CONNECTION_INFO* pConnectionInfo)
451{
452 struct intel_gpu *gpu = intel_gpu(gpu_);
453 struct intel_wsi_x11 *x11;
454 XGL_RESULT ret;
455
456 if (gpu->x11)
457 return XGL_SUCCESS;
458
459 if (gpu->device_fd >= 0)
460 return XGL_ERROR_DEVICE_ALREADY_CREATED;
461
462 if (!wsi_x11_has_dri3_and_present(pConnectionInfo->pConnection))
463 return XGL_ERROR_UNKNOWN;
464
465 x11 = wsi_x11_create(pConnectionInfo->pConnection,
466 pConnectionInfo->root, pConnectionInfo->provider);
467 if (!x11)
468 return XGL_ERROR_UNKNOWN;
469
470 ret = wsi_x11_dri3_open(x11);
471 if (ret != XGL_SUCCESS) {
472 intel_wsi_x11_destroy(x11);
473 return ret;
474 }
475
476 if (!wsi_x11_uses_gpu(x11, gpu)) {
477 intel_wsi_x11_destroy(x11);
478 return XGL_ERROR_UNKNOWN;
479 }
480
481 ret = wsi_x11_present_select_input(x11);
482 if (ret != XGL_SUCCESS) {
483 intel_wsi_x11_destroy(x11);
484 return ret;
485 }
486
487 intel_gpu_associate_x11(gpu, x11, x11->fd);
488
489 return XGL_SUCCESS;
490}
491
492XGL_RESULT XGLAPI intelWsiX11GetMSC(
493 XGL_DEVICE device,
494 xcb_randr_crtc_t crtc,
495 XGL_UINT64* pMsc)
496{
497 struct intel_dev *dev = intel_dev(device);
498 struct intel_wsi_x11 *x11 = dev->gpu->x11;
499 XGL_RESULT ret;
500
501 wsi_x11_present_notify_msc(x11, crtc);
502
503 /* wait for the event */
504 ret = intel_wsi_x11_wait(x11, x11->local.serial, -1);
505 if (ret != XGL_SUCCESS)
506 return ret;
507
508 *pMsc = x11->remote.msc;
509
510 return XGL_SUCCESS;
511}
512
513XGL_RESULT XGLAPI intelWsiX11CreatePresentableImage(
514 XGL_DEVICE device,
515 const XGL_WSI_X11_PRESENTABLE_IMAGE_CREATE_INFO* pCreateInfo,
516 XGL_IMAGE* pImage,
517 XGL_GPU_MEMORY* pMem)
518{
519 struct intel_dev *dev = intel_dev(device);
520 struct intel_wsi_x11 *x11 = dev->gpu->x11;
521 struct intel_img *img;
522 XGL_RESULT ret;
523
524 ret = wsi_x11_img_create(x11, dev, pCreateInfo, &img);
525 if (ret == XGL_SUCCESS) {
526 *pImage = (XGL_IMAGE) img;
527 *pMem = (XGL_GPU_MEMORY) img->obj.mem;
528 }
529
530 return ret;
531}
532
533XGL_RESULT XGLAPI intelWsiX11QueuePresent(
534 XGL_QUEUE queue_,
535 const XGL_WSI_X11_PRESENT_INFO* pPresentInfo,
536 XGL_FENCE fence_)
537{
538 struct intel_queue *queue = intel_queue(queue_);
539 struct intel_fence *fence = intel_fence(fence_);
540 struct intel_wsi_x11 *x11 = queue->dev->gpu->x11;
541 XGL_RESULT ret;
542
543 ret = wsi_x11_present_pixmap(x11, pPresentInfo);
544 if (ret != XGL_SUCCESS)
545 return ret;
546
547 if (fence)
548 intel_fence_set_x11(fence, x11, x11->local.serial);
549
550 return XGL_SUCCESS;
551}