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