1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright 2018 Collabora 5 * 6 * Based on platform_surfaceless, which has: 7 * 8 * Copyright (c) 2014 The Chromium OS Authors. 9 * Copyright © 2011 Intel Corporation 10 * 11 * Permission is hereby granted, free of charge, to any person obtaining a 12 * copy of this software and associated documentation files (the "Software"), 13 * to deal in the Software without restriction, including without limitation 14 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 * and/or sell copies of the Software, and to permit persons to whom the 16 * Software is furnished to do so, subject to the following conditions: 17 * 18 * The above copyright notice and this permission notice shall be included 19 * in all copies or substantial portions of the Software. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 * DEALINGS IN THE SOFTWARE. 28 */ 29#ifdef HAVE_LIBDRM 30#include <xf86drm.h> 31#endif 32 33#include <stdlib.h> 34#include <stdio.h> 35#include <string.h> 36#include <dlfcn.h> 37#include <sys/types.h> 38#include <sys/stat.h> 39#include <fcntl.h> 40#include <unistd.h> 41 42#include "egl_dri2.h" 43#include "loader.h" 44#include "util/debug.h" 45 46static __DRIimage* 47device_alloc_image(struct dri2_egl_display *dri2_dpy, 48 struct dri2_egl_surface *dri2_surf) 49{ 50 return dri2_dpy->image->createImage( 51 dri2_dpy->dri_screen, 52 dri2_surf->base.Width, 53 dri2_surf->base.Height, 54 dri2_surf->visual, 55 0, 56 NULL); 57} 58 59static void 60device_free_images(struct dri2_egl_surface *dri2_surf) 61{ 62 struct dri2_egl_display *dri2_dpy = 63 dri2_egl_display(dri2_surf->base.Resource.Display); 64 65 if (dri2_surf->front) { 66 dri2_dpy->image->destroyImage(dri2_surf->front); 67 dri2_surf->front = NULL; 68 } 69 70 free(dri2_surf->swrast_device_buffer); 71 dri2_surf->swrast_device_buffer = NULL; 72} 73 74static int 75device_image_get_buffers(__DRIdrawable *driDrawable, 76 unsigned int format, 77 uint32_t *stamp, 78 void *loaderPrivate, 79 uint32_t buffer_mask, 80 struct __DRIimageList *buffers) 81{ 82 struct dri2_egl_surface *dri2_surf = loaderPrivate; 83 struct dri2_egl_display *dri2_dpy = 84 dri2_egl_display(dri2_surf->base.Resource.Display); 85 86 buffers->image_mask = 0; 87 buffers->front = NULL; 88 buffers->back = NULL; 89 90 /* The EGL 1.5 spec states that pbuffers are single-buffered. Specifically, 91 * the spec states that they have a back buffer but no front buffer, in 92 * contrast to pixmaps, which have a front buffer but no back buffer. 93 * 94 * Single-buffered surfaces with no front buffer confuse Mesa; so we deviate 95 * from the spec, following the precedent of Mesa's EGL X11 platform. The 96 * X11 platform correctly assigns pbuffers to single-buffered configs, but 97 * assigns the pbuffer a front buffer instead of a back buffer. 98 * 99 * Pbuffers in the X11 platform mostly work today, so let's just copy its 100 * behavior instead of trying to fix (and hence potentially breaking) the 101 * world. 102 */ 103 104 if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) { 105 106 if (!dri2_surf->front) 107 dri2_surf->front = 108 device_alloc_image(dri2_dpy, dri2_surf); 109 110 buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT; 111 buffers->front = dri2_surf->front; 112 } 113 114 return 1; 115} 116 117static _EGLSurface * 118dri2_device_create_surface(_EGLDisplay *disp, EGLint type, _EGLConfig *conf, 119 const EGLint *attrib_list) 120{ 121 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 122 struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); 123 struct dri2_egl_surface *dri2_surf; 124 const __DRIconfig *config; 125 126 /* Make sure to calloc so all pointers 127 * are originally NULL. 128 */ 129 dri2_surf = calloc(1, sizeof *dri2_surf); 130 131 if (!dri2_surf) { 132 _eglError(EGL_BAD_ALLOC, "eglCreatePbufferSurface"); 133 return NULL; 134 } 135 136 if (!dri2_init_surface(&dri2_surf->base, disp, type, conf, attrib_list, 137 false, NULL)) 138 goto cleanup_surface; 139 140 config = dri2_get_dri_config(dri2_conf, type, 141 dri2_surf->base.GLColorspace); 142 143 if (!config) { 144 _eglError(EGL_BAD_MATCH, "Unsupported surfacetype/colorspace configuration"); 145 goto cleanup_surface; 146 } 147 148 dri2_surf->visual = dri2_image_format_for_pbuffer_config(dri2_dpy, config); 149 if (dri2_surf->visual == __DRI_IMAGE_FORMAT_NONE) 150 goto cleanup_surface; 151 152 if (!dri2_create_drawable(dri2_dpy, config, dri2_surf, dri2_surf)) 153 goto cleanup_surface; 154 155 return &dri2_surf->base; 156 157 cleanup_surface: 158 free(dri2_surf); 159 return NULL; 160} 161 162static EGLBoolean 163device_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf) 164{ 165 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 166 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); 167 168 device_free_images(dri2_surf); 169 170 dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable); 171 172 dri2_fini_surface(surf); 173 free(dri2_surf); 174 return EGL_TRUE; 175} 176 177static _EGLSurface * 178dri2_device_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf, 179 const EGLint *attrib_list) 180{ 181 return dri2_device_create_surface(disp, EGL_PBUFFER_BIT, conf, attrib_list); 182} 183 184static const struct dri2_egl_display_vtbl dri2_device_display_vtbl = { 185 .create_pbuffer_surface = dri2_device_create_pbuffer_surface, 186 .destroy_surface = device_destroy_surface, 187 .create_image = dri2_create_image_khr, 188 .get_dri_drawable = dri2_surface_get_dri_drawable, 189}; 190 191static void 192device_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) 193{ 194} 195 196static unsigned 197device_get_capability(void *loaderPrivate, enum dri_loader_cap cap) 198{ 199 /* Note: loaderPrivate is _EGLDisplay* */ 200 switch (cap) { 201 case DRI_LOADER_CAP_FP16: 202 return 1; 203 default: 204 return 0; 205 } 206} 207 208static const __DRIimageLoaderExtension image_loader_extension = { 209 .base = { __DRI_IMAGE_LOADER, 2 }, 210 .getBuffers = device_image_get_buffers, 211 .flushFrontBuffer = device_flush_front_buffer, 212 .getCapability = device_get_capability, 213}; 214 215static const __DRIextension *image_loader_extensions[] = { 216 &image_loader_extension.base, 217 &image_lookup_extension.base, 218 &use_invalidate.base, 219 NULL, 220}; 221 222static const __DRIextension *swrast_loader_extensions[] = { 223 &swrast_pbuffer_loader_extension.base, 224 &image_lookup_extension.base, 225 &use_invalidate.base, 226 NULL, 227}; 228 229static int 230device_get_fd(_EGLDisplay *disp, _EGLDevice *dev) 231{ 232#ifdef HAVE_LIBDRM 233 int fd = disp->Options.fd; 234 /* The fcntl() code in _eglGetDeviceDisplay() ensures that valid fd >= 3, 235 * and invalid one is 0. 236 */ 237 if (fd) { 238 /* According to the spec - if the FD does not match the EGLDevice 239 * behaviour is undefined. 240 * 241 * Add a trivial sanity check since it doesn't cost us anything. 242 */ 243 if (dev != _eglAddDevice(fd, false)) 244 return -1; 245 246 /* No EGL_EXT_output* extensions are supported, hence no master perms 247 * are needed. Get the render one - otherwise drivers might error out. 248 */ 249 char *node = drmGetRenderDeviceNameFromFd(fd); 250 251 /* Don't close the internal fd, get render node one based on it. */ 252 fd = loader_open_device(node); 253 free(node); 254 return fd; 255 } 256 const char *node = _eglGetDRMDeviceRenderNode(dev); 257 return loader_open_device(node); 258#else 259 _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet using a HW device"); 260 return -1; 261#endif 262} 263 264static bool 265device_probe_device(_EGLDisplay *disp) 266{ 267 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 268 bool request_software = env_var_as_boolean("LIBGL_ALWAYS_SOFTWARE", false); 269 270 if (request_software) 271 _eglLog(_EGL_WARNING, "Not allowed to force software rendering when " 272 "API explicitly selects a hardware device."); 273 dri2_dpy->fd = device_get_fd(disp, disp->Device); 274 if (dri2_dpy->fd < 0) 275 return false; 276 277 dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd); 278 if (!dri2_dpy->driver_name) 279 goto err_name; 280 281 /* When doing software rendering, some times user still want to explicitly 282 * choose the render node device since cross node import doesn't work between 283 * vgem/virtio_gpu yet. It would be nice to have a new EXTENSION for this. 284 * For now, just fallback to kms_swrast. */ 285 if (disp->Options.ForceSoftware && !request_software && 286 (strcmp(dri2_dpy->driver_name, "vgem") == 0 || 287 strcmp(dri2_dpy->driver_name, "virtio_gpu") == 0)) { 288 free(dri2_dpy->driver_name); 289 _eglLog(_EGL_WARNING, "NEEDS EXTENSION: falling back to kms_swrast"); 290 dri2_dpy->driver_name = strdup("kms_swrast"); 291 } 292 293 if (!dri2_load_driver_dri3(disp)) 294 goto err_load; 295 296 dri2_dpy->loader_extensions = image_loader_extensions; 297 return true; 298 299err_load: 300 free(dri2_dpy->driver_name); 301 dri2_dpy->driver_name = NULL; 302 303err_name: 304 close(dri2_dpy->fd); 305 dri2_dpy->fd = -1; 306 return false; 307 308} 309 310static bool 311device_probe_device_sw(_EGLDisplay *disp) 312{ 313 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 314 315 dri2_dpy->fd = -1; 316 dri2_dpy->driver_name = strdup("swrast"); 317 if (!dri2_dpy->driver_name) 318 return false; 319 320 /* HACK: should be driver_swrast_null */ 321 if (!dri2_load_driver_swrast(disp)) { 322 free(dri2_dpy->driver_name); 323 dri2_dpy->driver_name = NULL; 324 return false; 325 } 326 327 dri2_dpy->loader_extensions = swrast_loader_extensions; 328 return true; 329} 330 331EGLBoolean 332dri2_initialize_device(_EGLDisplay *disp) 333{ 334 _EGLDevice *dev; 335 struct dri2_egl_display *dri2_dpy; 336 const char* err; 337 338 dri2_dpy = calloc(1, sizeof *dri2_dpy); 339 if (!dri2_dpy) 340 return _eglError(EGL_BAD_ALLOC, "eglInitialize"); 341 342 /* Extension requires a PlatformDisplay - the EGLDevice. */ 343 dev = disp->PlatformDisplay; 344 345 dri2_dpy->fd = -1; 346 disp->Device = dev; 347 disp->DriverData = (void *) dri2_dpy; 348 err = "DRI2: failed to load driver"; 349 if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM)) { 350 if (!device_probe_device(disp)) 351 goto cleanup; 352 } else if (_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)) { 353 if (!device_probe_device_sw(disp)) 354 goto cleanup; 355 } else { 356 _eglLog(_EGL_FATAL, "Driver bug: exposed device is neither DRM nor SOFTWARE one"); 357 return EGL_FALSE; 358 } 359 360 if (!dri2_create_screen(disp)) { 361 err = "DRI2: failed to create screen"; 362 goto cleanup; 363 } 364 365 if (!dri2_setup_extensions(disp)) { 366 err = "DRI2: failed to find required DRI extensions"; 367 goto cleanup; 368 } 369 370 dri2_setup_screen(disp); 371#ifdef HAVE_WAYLAND_PLATFORM 372 dri2_dpy->device_name = loader_get_device_name_for_fd(dri2_dpy->fd); 373#endif 374 dri2_set_WL_bind_wayland_display(disp); 375 376 if (!dri2_add_pbuffer_configs_for_visuals(disp)) { 377 err = "DRI2: failed to add configs"; 378 goto cleanup; 379 } 380 381 /* Fill vtbl last to prevent accidentally calling virtual function during 382 * initialization. 383 */ 384 dri2_dpy->vtbl = &dri2_device_display_vtbl; 385 386 return EGL_TRUE; 387 388cleanup: 389 dri2_display_destroy(disp); 390 return _eglError(EGL_NOT_INITIALIZED, err); 391} 392