1/*
2 * Copyright © 2015 Boyan Ding
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <stdbool.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include <xcb/xcb.h>
29#include <xcb/dri3.h>
30#include <xcb/present.h>
31
32#include <xf86drm.h>
33
34#include "egl_dri2.h"
35#include "egl_dri2_fallbacks.h"
36#include "platform_x11_dri3.h"
37
38#include "loader.h"
39#include "loader_dri3_helper.h"
40
41static struct dri3_egl_surface *
42loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) {
43   size_t offset = offsetof(struct dri3_egl_surface, loader_drawable);
44   return (struct dri3_egl_surface *)(((void*) draw) - offset);
45}
46
47static int
48egl_dri3_get_swap_interval(struct loader_dri3_drawable *draw)
49{
50   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
51
52   return dri3_surf->base.SwapInterval;
53}
54
55static int
56egl_dri3_clamp_swap_interval(struct loader_dri3_drawable *draw, int interval)
57{
58   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
59
60   if (interval > dri3_surf->base.Config->MaxSwapInterval)
61      interval = dri3_surf->base.Config->MaxSwapInterval;
62   else if (interval < dri3_surf->base.Config->MinSwapInterval)
63      interval = dri3_surf->base.Config->MinSwapInterval;
64
65   return interval;
66}
67
68static void
69egl_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
70{
71   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
72
73   dri3_surf->base.SwapInterval = interval;
74}
75
76static void
77egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw,
78                           int width, int height)
79{
80   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
81
82   dri3_surf->base.Width = width;
83   dri3_surf->base.Height = height;
84}
85
86static bool
87egl_dri3_in_current_context(struct loader_dri3_drawable *draw)
88{
89   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
90   _EGLContext *ctx = _eglGetCurrentContext();
91
92   return ctx->Resource.Display == dri3_surf->base.Resource.Display;
93}
94
95static __DRIcontext *
96egl_dri3_get_dri_context(struct loader_dri3_drawable *draw)
97{
98   _EGLContext *ctx = _eglGetCurrentContext();
99   struct dri2_egl_context *dri2_ctx;
100   if (!ctx)
101      return NULL;
102   dri2_ctx = dri2_egl_context(ctx);
103   return dri2_ctx->dri_context;
104}
105
106static __DRIscreen *
107egl_dri3_get_dri_screen(struct loader_dri3_drawable *draw)
108{
109   _EGLContext *ctx = _eglGetCurrentContext();
110   struct dri2_egl_context *dri2_ctx;
111   if (!ctx)
112      return NULL;
113   dri2_ctx = dri2_egl_context(ctx);
114   return dri2_egl_display(dri2_ctx->base.Resource.Display)->dri_screen;
115}
116
117static void
118egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
119{
120   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
121   _EGLDisplay *disp = dri3_surf->base.Resource.Display;
122
123   dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->base);
124}
125
126static const struct loader_dri3_vtable egl_dri3_vtable = {
127   .get_swap_interval = egl_dri3_get_swap_interval,
128   .clamp_swap_interval = egl_dri3_clamp_swap_interval,
129   .set_swap_interval = egl_dri3_set_swap_interval,
130   .set_drawable_size = egl_dri3_set_drawable_size,
131   .in_current_context = egl_dri3_in_current_context,
132   .get_dri_context = egl_dri3_get_dri_context,
133   .get_dri_screen = egl_dri3_get_dri_screen,
134   .flush_drawable = egl_dri3_flush_drawable,
135   .show_fps = NULL,
136};
137
138static EGLBoolean
139dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
140{
141   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
142
143   (void) drv;
144
145   loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
146
147   free(surf);
148
149   return EGL_TRUE;
150}
151
152static EGLBoolean
153dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
154                       EGLint interval)
155{
156   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
157
158   loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
159
160   return EGL_TRUE;
161}
162
163static _EGLSurface *
164dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
165                    _EGLConfig *conf, void *native_surface,
166                    const EGLint *attrib_list)
167{
168   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
169   struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
170   struct dri3_egl_surface *dri3_surf;
171   const __DRIconfig *dri_config;
172   xcb_drawable_t drawable;
173
174   STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
175   drawable = (uintptr_t) native_surface;
176
177   (void) drv;
178
179   dri3_surf = calloc(1, sizeof *dri3_surf);
180   if (!dri3_surf) {
181      _eglError(EGL_BAD_ALLOC, "dri3_create_surface");
182      return NULL;
183   }
184
185   if (!_eglInitSurface(&dri3_surf->base, disp, type, conf, attrib_list))
186      goto cleanup_surf;
187
188   if (type == EGL_PBUFFER_BIT) {
189      drawable = xcb_generate_id(dri2_dpy->conn);
190      xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
191                        drawable, dri2_dpy->screen->root,
192                        dri3_surf->base.Width, dri3_surf->base.Height);
193   }
194
195   dri_config = dri2_get_dri_config(dri2_conf, type,
196                                    dri3_surf->base.GLColorspace);
197
198   if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
199                                 dri2_dpy->dri_screen,
200                                 dri2_dpy->is_different_gpu, dri_config,
201                                 &dri2_dpy->loader_dri3_ext,
202                                 &egl_dri3_vtable,
203                                 &dri3_surf->loader_drawable)) {
204      _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
205      goto cleanup_pixmap;
206   }
207
208   return &dri3_surf->base;
209
210 cleanup_pixmap:
211   if (type == EGL_PBUFFER_BIT)
212      xcb_free_pixmap(dri2_dpy->conn, drawable);
213 cleanup_surf:
214   free(dri3_surf);
215
216   return NULL;
217}
218
219static int
220dri3_authenticate(_EGLDisplay *disp, uint32_t id)
221{
222#ifdef HAVE_WAYLAND_PLATFORM
223   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
224
225   if (dri2_dpy->device_name) {
226      _eglLog(_EGL_WARNING,
227              "Wayland client render node authentication is unnecessary");
228      return 0;
229   }
230
231   _eglLog(_EGL_WARNING,
232           "Wayland client primary node authentication isn't supported");
233#endif
234
235   return -1;
236}
237
238/**
239 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
240 */
241static _EGLSurface *
242dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
243                           _EGLConfig *conf, void *native_window,
244                           const EGLint *attrib_list)
245{
246   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
247   _EGLSurface *surf;
248
249   surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
250                              native_window, attrib_list);
251   if (surf != NULL)
252      dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval);
253
254   return surf;
255}
256
257static _EGLSurface *
258dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
259                           _EGLConfig *conf, void *native_pixmap,
260                           const EGLint *attrib_list)
261{
262   return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
263                              native_pixmap, attrib_list);
264}
265
266static _EGLSurface *
267dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp,
268                                _EGLConfig *conf, const EGLint *attrib_list)
269{
270   return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf,
271                              XCB_WINDOW_NONE, attrib_list);
272}
273
274static EGLBoolean
275dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
276                     EGLuint64KHR *ust, EGLuint64KHR *msc,
277                     EGLuint64KHR *sbc)
278{
279   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
280
281   return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0,
282                                   (int64_t *) ust, (int64_t *) msc,
283                                   (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE;
284}
285
286static _EGLImage *
287dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
288                             EGLClientBuffer buffer, const EGLint *attr_list)
289{
290   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
291   struct dri2_egl_image *dri2_img;
292   xcb_drawable_t drawable;
293   xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
294   xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
295   unsigned int format;
296
297   drawable = (xcb_drawable_t) (uintptr_t) buffer;
298   bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, drawable);
299   bp_reply = xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn,
300                                                bp_cookie, NULL);
301   if (!bp_reply) {
302      _eglError(EGL_BAD_ALLOC, "xcb_dri3_buffer_from_pixmap");
303      return NULL;
304   }
305
306   switch (bp_reply->depth) {
307   case 16:
308      format = __DRI_IMAGE_FORMAT_RGB565;
309      break;
310   case 24:
311      format = __DRI_IMAGE_FORMAT_XRGB8888;
312      break;
313   case 32:
314      format = __DRI_IMAGE_FORMAT_ARGB8888;
315      break;
316   default:
317      _eglError(EGL_BAD_PARAMETER,
318                "dri3_create_image_khr: unsupported pixmap depth");
319      free(bp_reply);
320      return EGL_NO_IMAGE_KHR;
321   }
322
323   dri2_img = malloc(sizeof *dri2_img);
324   if (!dri2_img) {
325      _eglError(EGL_BAD_ALLOC, "dri3_create_image_khr");
326      return EGL_NO_IMAGE_KHR;
327   }
328
329   if (!_eglInitImage(&dri2_img->base, disp)) {
330      free(dri2_img);
331      return EGL_NO_IMAGE_KHR;
332   }
333
334   dri2_img->dri_image = loader_dri3_create_image(dri2_dpy->conn,
335                                                  bp_reply,
336                                                  format,
337                                                  dri2_dpy->dri_screen,
338                                                  dri2_dpy->image,
339                                                  dri2_img);
340
341   free(bp_reply);
342
343   return &dri2_img->base;
344}
345
346static _EGLImage *
347dri3_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
348                      _EGLContext *ctx, EGLenum target,
349                      EGLClientBuffer buffer, const EGLint *attr_list)
350{
351   (void) drv;
352
353   switch (target) {
354   case EGL_NATIVE_PIXMAP_KHR:
355      return dri3_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
356   default:
357      return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
358   }
359}
360
361/**
362 * Called by the driver when it needs to update the real front buffer with the
363 * contents of its fake front buffer.
364 */
365static void
366dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
367{
368   /* There does not seem to be any kind of consensus on whether we should
369    * support front-buffer rendering or not:
370    * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html
371    */
372   _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
373   (void) driDrawable;
374   (void) loaderPrivate;
375}
376
377const __DRIimageLoaderExtension dri3_image_loader_extension = {
378   .base = { __DRI_IMAGE_LOADER, 1 },
379
380   .getBuffers          = loader_dri3_get_buffers,
381   .flushFrontBuffer    = dri3_flush_front_buffer,
382};
383
384static EGLBoolean
385dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
386{
387   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
388
389   /* No-op for a pixmap or pbuffer surface */
390   if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT)
391      return EGL_FALSE;
392
393   return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable,
394                                       0, 0, 0, 0,
395                                       draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1;
396}
397
398static EGLBoolean
399dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
400                  void *native_pixmap_target)
401{
402   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
403   xcb_pixmap_t target;
404
405   STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target));
406   target = (uintptr_t) native_pixmap_target;
407
408   loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target,
409                             dri3_surf->loader_drawable.drawable);
410
411   return EGL_TRUE;
412}
413
414static int
415dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
416{
417   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
418
419   return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable);
420}
421
422static EGLBoolean
423dri3_query_surface(_EGLDriver *drv, _EGLDisplay *dpy,
424                   _EGLSurface *surf, EGLint attribute,
425                   EGLint *value)
426{
427   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
428
429   switch (attribute) {
430   case EGL_WIDTH:
431   case EGL_HEIGHT:
432      loader_dri3_update_drawable_geometry(&dri3_surf->loader_drawable);
433      break;
434   default:
435      break;
436   }
437
438   return _eglQuerySurface(drv, dpy, surf, attribute, value);
439}
440
441static __DRIdrawable *
442dri3_get_dri_drawable(_EGLSurface *surf)
443{
444   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
445
446   return dri3_surf->loader_drawable.dri_drawable;
447}
448
449struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
450   .authenticate = dri3_authenticate,
451   .create_window_surface = dri3_create_window_surface,
452   .create_pixmap_surface = dri3_create_pixmap_surface,
453   .create_pbuffer_surface = dri3_create_pbuffer_surface,
454   .destroy_surface = dri3_destroy_surface,
455   .create_image = dri3_create_image_khr,
456   .swap_interval = dri3_set_swap_interval,
457   .swap_buffers = dri3_swap_buffers,
458   .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
459   .swap_buffers_region = dri2_fallback_swap_buffers_region,
460   .post_sub_buffer = dri2_fallback_post_sub_buffer,
461   .copy_buffers = dri3_copy_buffers,
462   .query_buffer_age = dri3_query_buffer_age,
463   .query_surface = dri3_query_surface,
464   .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,
465   .get_sync_values = dri3_get_sync_values,
466   .get_dri_drawable = dri3_get_dri_drawable,
467};
468
469EGLBoolean
470dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
471{
472   xcb_dri3_query_version_reply_t *dri3_query;
473   xcb_dri3_query_version_cookie_t dri3_query_cookie;
474   xcb_present_query_version_reply_t *present_query;
475   xcb_present_query_version_cookie_t present_query_cookie;
476   xcb_generic_error_t *error;
477   const xcb_query_extension_reply_t *extension;
478
479   xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
480   xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
481
482   extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id);
483   if (!(extension && extension->present))
484      return EGL_FALSE;
485
486   extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id);
487   if (!(extension && extension->present))
488      return EGL_FALSE;
489
490   dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn,
491                                              XCB_DRI3_MAJOR_VERSION,
492                                              XCB_DRI3_MINOR_VERSION);
493
494   present_query_cookie = xcb_present_query_version(dri2_dpy->conn,
495                                                    XCB_PRESENT_MAJOR_VERSION,
496                                                    XCB_PRESENT_MINOR_VERSION);
497
498   dri3_query =
499      xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error);
500   if (dri3_query == NULL || error != NULL) {
501      _eglLog(_EGL_WARNING, "DRI3: failed to query the version");
502      free(dri3_query);
503      free(error);
504      return EGL_FALSE;
505   }
506   free(dri3_query);
507
508   present_query =
509      xcb_present_query_version_reply(dri2_dpy->conn,
510                                      present_query_cookie, &error);
511   if (present_query == NULL || error != NULL) {
512      _eglLog(_EGL_WARNING, "DRI3: failed to query Present version");
513      free(present_query);
514      free(error);
515      return EGL_FALSE;
516   }
517   free(present_query);
518
519   dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, dri2_dpy->screen->root, 0);
520   if (dri2_dpy->fd < 0) {
521      int conn_error = xcb_connection_has_error(dri2_dpy->conn);
522      _eglLog(_EGL_WARNING, "DRI3: Screen seems not DRI3 capable");
523
524      if (conn_error)
525         _eglLog(_EGL_WARNING, "DRI3: Failed to initialize");
526
527      return EGL_FALSE;
528   }
529
530   dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
531
532   dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
533   if (!dri2_dpy->driver_name) {
534      _eglLog(_EGL_WARNING, "DRI3: No driver found");
535      close(dri2_dpy->fd);
536      return EGL_FALSE;
537   }
538
539#ifdef HAVE_WAYLAND_PLATFORM
540   /* Only try to get a render device name since dri3 doesn't provide a
541    * mechanism for authenticating client opened device node fds. If this
542    * fails then don't advertise the extension. */
543   dri2_dpy->device_name = drmGetRenderDeviceNameFromFd(dri2_dpy->fd);
544#endif
545
546   return EGL_TRUE;
547}
548