1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (c) 2014 The Chromium OS Authors. 5 * Copyright © 2011 Intel Corporation 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 */ 25 26#include <stdlib.h> 27#include <stdio.h> 28#include <string.h> 29#include <xf86drm.h> 30#include <dlfcn.h> 31#include <sys/types.h> 32#include <sys/stat.h> 33#include <fcntl.h> 34#include <unistd.h> 35 36#include "egl_dri2.h" 37#include "egl_dri2_fallbacks.h" 38#include "loader.h" 39 40static __DRIimage* 41surfaceless_alloc_image(struct dri2_egl_display *dri2_dpy, 42 struct dri2_egl_surface *dri2_surf) 43{ 44 return dri2_dpy->image->createImage( 45 dri2_dpy->dri_screen, 46 dri2_surf->base.Width, 47 dri2_surf->base.Height, 48 dri2_surf->visual, 49 0, 50 NULL); 51} 52 53static void 54surfaceless_free_images(struct dri2_egl_surface *dri2_surf) 55{ 56 struct dri2_egl_display *dri2_dpy = 57 dri2_egl_display(dri2_surf->base.Resource.Display); 58 59 if (dri2_surf->front) { 60 dri2_dpy->image->destroyImage(dri2_surf->front); 61 dri2_surf->front = NULL; 62 } 63} 64 65static int 66surfaceless_image_get_buffers(__DRIdrawable *driDrawable, 67 unsigned int format, 68 uint32_t *stamp, 69 void *loaderPrivate, 70 uint32_t buffer_mask, 71 struct __DRIimageList *buffers) 72{ 73 struct dri2_egl_surface *dri2_surf = loaderPrivate; 74 struct dri2_egl_display *dri2_dpy = 75 dri2_egl_display(dri2_surf->base.Resource.Display); 76 77 buffers->image_mask = 0; 78 buffers->front = NULL; 79 buffers->back = NULL; 80 81 /* The EGL 1.5 spec states that pbuffers are single-buffered. Specifically, 82 * the spec states that they have a back buffer but no front buffer, in 83 * contrast to pixmaps, which have a front buffer but no back buffer. 84 * 85 * Single-buffered surfaces with no front buffer confuse Mesa; so we deviate 86 * from the spec, following the precedent of Mesa's EGL X11 platform. The 87 * X11 platform correctly assigns pbuffers to single-buffered configs, but 88 * assigns the pbuffer a front buffer instead of a back buffer. 89 * 90 * Pbuffers in the X11 platform mostly work today, so let's just copy its 91 * behavior instead of trying to fix (and hence potentially breaking) the 92 * world. 93 */ 94 95 if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) { 96 97 if (!dri2_surf->front) 98 dri2_surf->front = 99 surfaceless_alloc_image(dri2_dpy, dri2_surf); 100 101 buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT; 102 buffers->front = dri2_surf->front; 103 } 104 105 return 1; 106} 107 108static _EGLSurface * 109dri2_surfaceless_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, 110 _EGLConfig *conf, const EGLint *attrib_list) 111{ 112 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 113 struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); 114 struct dri2_egl_surface *dri2_surf; 115 const __DRIconfig *config; 116 117 /* Make sure to calloc so all pointers 118 * are originally NULL. 119 */ 120 dri2_surf = calloc(1, sizeof *dri2_surf); 121 122 if (!dri2_surf) { 123 _eglError(EGL_BAD_ALLOC, "eglCreatePbufferSurface"); 124 return NULL; 125 } 126 127 if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list)) 128 goto cleanup_surface; 129 130 config = dri2_get_dri_config(dri2_conf, type, 131 dri2_surf->base.GLColorspace); 132 133 if (!config) 134 goto cleanup_surface; 135 136 dri2_surf->dri_drawable = 137 dri2_dpy->dri2->createNewDrawable(dri2_dpy->dri_screen, config, 138 dri2_surf); 139 if (dri2_surf->dri_drawable == NULL) { 140 _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable"); 141 goto cleanup_surface; 142 } 143 144 if (conf->RedSize == 5) 145 dri2_surf->visual = __DRI_IMAGE_FORMAT_RGB565; 146 else if (conf->AlphaSize == 0) 147 dri2_surf->visual = __DRI_IMAGE_FORMAT_XRGB8888; 148 else 149 dri2_surf->visual = __DRI_IMAGE_FORMAT_ARGB8888; 150 151 return &dri2_surf->base; 152 153 cleanup_surface: 154 free(dri2_surf); 155 return NULL; 156} 157 158static EGLBoolean 159surfaceless_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) 160{ 161 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 162 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); 163 164 surfaceless_free_images(dri2_surf); 165 166 dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable); 167 168 free(dri2_surf); 169 return EGL_TRUE; 170} 171 172static _EGLSurface * 173dri2_surfaceless_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp, 174 _EGLConfig *conf, const EGLint *attrib_list) 175{ 176 return dri2_surfaceless_create_surface(drv, disp, EGL_PBUFFER_BIT, conf, 177 attrib_list); 178} 179 180static EGLBoolean 181surfaceless_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) 182{ 183 assert(!surf || surf->Type == EGL_PBUFFER_BIT); 184 185 /* From the EGL 1.5 spec: 186 * If surface is a [...] pbuffer surface, eglSwapBuffers has no effect. 187 */ 188 return EGL_TRUE; 189} 190 191static EGLBoolean 192surfaceless_add_configs_for_visuals(_EGLDriver *drv, _EGLDisplay *dpy) 193{ 194 struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); 195 static const struct { 196 const char *format_name; 197 unsigned int rgba_masks[4]; 198 } visuals[] = { 199 { "ARGB8888", { 0xff0000, 0xff00, 0xff, 0xff000000 } }, 200 { "RGB888", { 0xff0000, 0xff00, 0xff, 0x0 } }, 201 { "RGB565", { 0x00f800, 0x07e0, 0x1f, 0x0 } }, 202 }; 203 unsigned int format_count[ARRAY_SIZE(visuals)] = { 0 }; 204 unsigned int count, i, j; 205 206 count = 0; 207 for (i = 0; dri2_dpy->driver_configs[i] != NULL; i++) { 208 for (j = 0; j < ARRAY_SIZE(visuals); j++) { 209 struct dri2_egl_config *dri2_conf; 210 211 dri2_conf = dri2_add_config(dpy, dri2_dpy->driver_configs[i], 212 count + 1, EGL_PBUFFER_BIT, NULL, visuals[j].rgba_masks); 213 214 if (dri2_conf) { 215 count++; 216 format_count[j]++; 217 } 218 } 219 } 220 221 for (i = 0; i < ARRAY_SIZE(format_count); i++) { 222 if (!format_count[i]) { 223 _eglLog(_EGL_DEBUG, "No DRI config supports native format %s", 224 visuals[i].format_name); 225 } 226 } 227 228 return (count != 0); 229} 230 231static struct dri2_egl_display_vtbl dri2_surfaceless_display_vtbl = { 232 .create_pixmap_surface = dri2_fallback_create_pixmap_surface, 233 .create_pbuffer_surface = dri2_surfaceless_create_pbuffer_surface, 234 .destroy_surface = surfaceless_destroy_surface, 235 .create_image = dri2_create_image_khr, 236 .swap_interval = dri2_fallback_swap_interval, 237 .swap_buffers = surfaceless_swap_buffers, 238 .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage, 239 .swap_buffers_region = dri2_fallback_swap_buffers_region, 240 .post_sub_buffer = dri2_fallback_post_sub_buffer, 241 .copy_buffers = dri2_fallback_copy_buffers, 242 .query_buffer_age = dri2_fallback_query_buffer_age, 243 .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image, 244 .get_sync_values = dri2_fallback_get_sync_values, 245 .get_dri_drawable = dri2_surface_get_dri_drawable, 246}; 247 248static void 249surfaceless_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) 250{ 251} 252 253static const __DRIimageLoaderExtension image_loader_extension = { 254 .base = { __DRI_IMAGE_LOADER, 1 }, 255 .getBuffers = surfaceless_image_get_buffers, 256 .flushFrontBuffer = surfaceless_flush_front_buffer, 257}; 258 259#define DRM_RENDER_DEV_NAME "%s/renderD%d" 260 261static const __DRIextension *image_loader_extensions[] = { 262 &image_loader_extension.base, 263 &image_lookup_extension.base, 264 &use_invalidate.base, 265 NULL, 266}; 267 268EGLBoolean 269dri2_initialize_surfaceless(_EGLDriver *drv, _EGLDisplay *disp) 270{ 271 struct dri2_egl_display *dri2_dpy; 272 const char* err; 273 int i; 274 int driver_loaded = 0; 275 276 loader_set_logger(_eglLog); 277 278 dri2_dpy = calloc(1, sizeof *dri2_dpy); 279 if (!dri2_dpy) 280 return _eglError(EGL_BAD_ALLOC, "eglInitialize"); 281 282 disp->DriverData = (void *) dri2_dpy; 283 284 const int limit = 64; 285 const int base = 128; 286 for (i = 0; i < limit; ++i) { 287 char *card_path; 288 if (asprintf(&card_path, DRM_RENDER_DEV_NAME, DRM_DIR_NAME, base + i) < 0) 289 continue; 290 291 dri2_dpy->fd = loader_open_device(card_path); 292 293 free(card_path); 294 if (dri2_dpy->fd < 0) 295 continue; 296 297 dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd); 298 if (dri2_dpy->driver_name) { 299 if (dri2_load_driver(disp)) { 300 driver_loaded = 1; 301 break; 302 } 303 free(dri2_dpy->driver_name); 304 } 305 close(dri2_dpy->fd); 306 } 307 308 if (!driver_loaded) { 309 err = "DRI2: failed to load driver"; 310 goto cleanup_display; 311 } 312 313 dri2_dpy->loader_extensions = image_loader_extensions; 314 315 if (!dri2_create_screen(disp)) { 316 err = "DRI2: failed to create screen"; 317 goto cleanup_driver; 318 } 319 320 if (!surfaceless_add_configs_for_visuals(drv, disp)) { 321 err = "DRI2: failed to add configs"; 322 goto cleanup_screen; 323 } 324 325 /* Fill vtbl last to prevent accidentally calling virtual function during 326 * initialization. 327 */ 328 dri2_dpy->vtbl = &dri2_surfaceless_display_vtbl; 329 330 return EGL_TRUE; 331 332cleanup_screen: 333 dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen); 334 335cleanup_driver: 336 dlclose(dri2_dpy->driver); 337 free(dri2_dpy->driver_name); 338 close(dri2_dpy->fd); 339cleanup_display: 340 free(dri2_dpy); 341 disp->DriverData = NULL; 342 343 return _eglError(EGL_NOT_INITIALIZED, err); 344} 345