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 41 static struct dri3_egl_surface * 42 loader_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 47 static int 48 egl_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 55 static int 56 egl_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 68 static void 69 egl_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 76 static void 77 egl_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 86 static bool 87 egl_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 95 static __DRIcontext * 96 egl_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 106 static __DRIscreen * 107 egl_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 117 static void 118 egl_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 126 static 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 138 static EGLBoolean 139 dri3_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 152 static EGLBoolean 153 dri3_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 163 static _EGLSurface * 164 dri3_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 219 static int 220 dri3_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 */ 241 static _EGLSurface * 242 dri3_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 257 static _EGLSurface * 258 dri3_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 266 static _EGLSurface * 267 dri3_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 274 static EGLBoolean 275 dri3_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 286 static _EGLImage * 287 dri3_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 346 static _EGLImage * 347 dri3_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 */ 365 static void 366 dri3_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 377 const __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 384 static EGLBoolean 385 dri3_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 398 static EGLBoolean 399 dri3_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 414 static int 415 dri3_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 422 static EGLBoolean 423 dri3_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 441 static __DRIdrawable * 442 dri3_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 449 struct 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 469 EGLBoolean 470 dri3_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