1/* 2 * Copyright © 2011 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Kristian Høgsberg <krh@bitplanet.net> 26 */ 27 28#include <stdlib.h> 29#include <stdio.h> 30#include <string.h> 31#include <xf86drm.h> 32#include <dlfcn.h> 33#include <sys/types.h> 34#include <sys/stat.h> 35#include <fcntl.h> 36#include <unistd.h> 37 38#include "egl_dri2.h" 39 40static struct gbm_bo * 41lock_front_buffer(struct gbm_surface *_surf) 42{ 43 struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf; 44 struct dri2_egl_surface *dri2_surf = surf->dri_private; 45 struct gbm_bo *bo; 46 47 if (dri2_surf->current == NULL) { 48 _eglError(EGL_BAD_SURFACE, "no front buffer"); 49 return NULL; 50 } 51 52 bo = dri2_surf->current->bo; 53 dri2_surf->current->locked = 1; 54 dri2_surf->current = NULL; 55 56 return bo; 57} 58 59static void 60release_buffer(struct gbm_surface *_surf, struct gbm_bo *bo) 61{ 62 struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf; 63 struct dri2_egl_surface *dri2_surf = surf->dri_private; 64 int i; 65 66 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) { 67 if (dri2_surf->color_buffers[i].bo == bo) { 68 dri2_surf->color_buffers[i].locked = 0; 69 } 70 } 71} 72 73static int 74has_free_buffers(struct gbm_surface *_surf) 75{ 76 struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf; 77 struct dri2_egl_surface *dri2_surf = surf->dri_private; 78 int i; 79 80 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) 81 if (!dri2_surf->color_buffers[i].locked) 82 return 1; 83 84 return 0; 85} 86 87static _EGLSurface * 88dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, 89 _EGLConfig *conf, EGLNativeWindowType window, 90 const EGLint *attrib_list) 91{ 92 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 93 struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); 94 struct dri2_egl_surface *dri2_surf; 95 struct gbm_dri_surface *surf; 96 97 (void) drv; 98 99 dri2_surf = malloc(sizeof *dri2_surf); 100 if (!dri2_surf) { 101 _eglError(EGL_BAD_ALLOC, "dri2_create_surface"); 102 return NULL; 103 } 104 105 memset(dri2_surf, 0, sizeof *dri2_surf); 106 if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list)) 107 goto cleanup_surf; 108 109 switch (type) { 110 case EGL_WINDOW_BIT: 111 if (!window) 112 return NULL; 113 surf = gbm_dri_surface((struct gbm_surface *) window); 114 dri2_surf->gbm_surf = surf; 115 dri2_surf->base.Width = surf->base.width; 116 dri2_surf->base.Height = surf->base.height; 117 surf->dri_private = dri2_surf; 118 break; 119 default: 120 goto cleanup_surf; 121 } 122 123 dri2_surf->dri_drawable = 124 (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen, 125 dri2_conf->dri_double_config, 126 dri2_surf->gbm_surf); 127 128 if (dri2_surf->dri_drawable == NULL) { 129 _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable"); 130 goto cleanup_surf; 131 } 132 133 return &dri2_surf->base; 134 135 cleanup_surf: 136 free(dri2_surf); 137 138 return NULL; 139} 140 141static _EGLSurface * 142dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp, 143 _EGLConfig *conf, EGLNativeWindowType window, 144 const EGLint *attrib_list) 145{ 146 return dri2_create_surface(drv, disp, EGL_WINDOW_BIT, conf, 147 window, attrib_list); 148} 149 150static EGLBoolean 151dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) 152{ 153 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 154 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); 155 int i; 156 157 if (!_eglPutSurface(surf)) 158 return EGL_TRUE; 159 160 (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable); 161 162 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) { 163 if (dri2_surf->color_buffers[i].bo) 164 gbm_bo_destroy(dri2_surf->color_buffers[i].bo); 165 } 166 167 for (i = 0; i < __DRI_BUFFER_COUNT; i++) { 168 if (dri2_surf->dri_buffers[i]) 169 dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen, 170 dri2_surf->dri_buffers[i]); 171 } 172 173 free(surf); 174 175 return EGL_TRUE; 176} 177 178static int 179get_back_bo(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer) 180{ 181 struct dri2_egl_display *dri2_dpy = 182 dri2_egl_display(dri2_surf->base.Resource.Display); 183 struct gbm_dri_bo *bo; 184 struct gbm_dri_surface *surf = dri2_surf->gbm_surf; 185 int i, name, pitch; 186 187 if (dri2_surf->back == NULL) { 188 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) { 189 if (!dri2_surf->color_buffers[i].locked) { 190 dri2_surf->back = &dri2_surf->color_buffers[i]; 191 break; 192 } 193 } 194 } 195 196 if (dri2_surf->back == NULL) 197 return -1; 198 if (dri2_surf->back->bo == NULL) 199 dri2_surf->back->bo = gbm_bo_create(&dri2_dpy->gbm_dri->base.base, 200 surf->base.width, surf->base.height, 201 surf->base.format, surf->base.flags); 202 if (dri2_surf->back->bo == NULL) 203 return -1; 204 205 bo = (struct gbm_dri_bo *) dri2_surf->back->bo; 206 207 dri2_dpy->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_NAME, &name); 208 dri2_dpy->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch); 209 210 buffer->attachment = __DRI_BUFFER_BACK_LEFT; 211 buffer->name = name; 212 buffer->pitch = pitch; 213 buffer->cpp = 4; 214 buffer->flags = 0; 215 216 return 0; 217} 218 219static int 220get_aux_bo(struct dri2_egl_surface *dri2_surf, 221 unsigned int attachment, unsigned int format, __DRIbuffer *buffer) 222{ 223 struct dri2_egl_display *dri2_dpy = 224 dri2_egl_display(dri2_surf->base.Resource.Display); 225 __DRIbuffer *b = dri2_surf->dri_buffers[attachment]; 226 227 if (b == NULL) { 228 b = dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen, 229 attachment, format, 230 dri2_surf->base.Width, 231 dri2_surf->base.Height); 232 dri2_surf->dri_buffers[attachment] = b; 233 } 234 if (b == NULL) 235 return -1; 236 237 memcpy(buffer, b, sizeof *buffer); 238 239 return 0; 240} 241 242static __DRIbuffer * 243dri2_get_buffers_with_format(__DRIdrawable *driDrawable, 244 int *width, int *height, 245 unsigned int *attachments, int count, 246 int *out_count, void *loaderPrivate) 247{ 248 struct dri2_egl_surface *dri2_surf = loaderPrivate; 249 int i, j; 250 251 dri2_surf->buffer_count = 0; 252 for (i = 0, j = 0; i < 2 * count; i += 2, j++) { 253 assert(attachments[i] < __DRI_BUFFER_COUNT); 254 assert(dri2_surf->buffer_count < 5); 255 256 switch (attachments[i]) { 257 case __DRI_BUFFER_BACK_LEFT: 258 if (get_back_bo(dri2_surf, &dri2_surf->buffers[j]) < 0) { 259 _eglError(EGL_BAD_ALLOC, "failed to allocate color buffer"); 260 return NULL; 261 } 262 break; 263 default: 264 if (get_aux_bo(dri2_surf, attachments[i], attachments[i + 1], 265 &dri2_surf->buffers[j]) < 0) { 266 _eglError(EGL_BAD_ALLOC, "failed to allocate aux buffer"); 267 return NULL; 268 } 269 break; 270 } 271 } 272 273 *out_count = j; 274 if (j == 0) 275 return NULL; 276 277 *width = dri2_surf->base.Width; 278 *height = dri2_surf->base.Height; 279 280 return dri2_surf->buffers; 281} 282 283static __DRIbuffer * 284dri2_get_buffers(__DRIdrawable * driDrawable, 285 int *width, int *height, 286 unsigned int *attachments, int count, 287 int *out_count, void *loaderPrivate) 288{ 289 unsigned int *attachments_with_format; 290 __DRIbuffer *buffer; 291 const unsigned int format = 32; 292 int i; 293 294 attachments_with_format = calloc(count * 2, sizeof(unsigned int)); 295 if (!attachments_with_format) { 296 *out_count = 0; 297 return NULL; 298 } 299 300 for (i = 0; i < count; ++i) { 301 attachments_with_format[2*i] = attachments[i]; 302 attachments_with_format[2*i + 1] = format; 303 } 304 305 buffer = 306 dri2_get_buffers_with_format(driDrawable, 307 width, height, 308 attachments_with_format, count, 309 out_count, loaderPrivate); 310 311 free(attachments_with_format); 312 313 return buffer; 314} 315 316static void 317dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate) 318{ 319 (void) driDrawable; 320 (void) loaderPrivate; 321} 322 323static EGLBoolean 324dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) 325{ 326 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 327 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw); 328 329 if (dri2_surf->base.Type == EGL_WINDOW_BIT) { 330 if (dri2_surf->current) 331 _eglError(EGL_BAD_SURFACE, "dri2_swap_buffers"); 332 dri2_surf->current = dri2_surf->back; 333 dri2_surf->back = NULL; 334 } 335 336 (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable); 337 (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable); 338 339 return EGL_TRUE; 340} 341 342static _EGLImage * 343dri2_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx, 344 EGLClientBuffer buffer, const EGLint *attr_list) 345{ 346 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 347 struct gbm_dri_bo *dri_bo = gbm_dri_bo((struct gbm_bo *) buffer); 348 struct dri2_egl_image *dri2_img; 349 350 dri2_img = malloc(sizeof *dri2_img); 351 if (!dri2_img) { 352 _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr_pixmap"); 353 return NULL; 354 } 355 356 if (!_eglInitImage(&dri2_img->base, disp)) { 357 free(dri2_img); 358 return NULL; 359 } 360 361 dri2_img->dri_image = dri2_dpy->image->dupImage(dri_bo->image, dri2_img); 362 if (dri2_img->dri_image == NULL) { 363 free(dri2_img); 364 _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr_pixmap"); 365 return NULL; 366 } 367 368 return &dri2_img->base; 369} 370 371static _EGLImage * 372dri2_drm_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, 373 _EGLContext *ctx, EGLenum target, 374 EGLClientBuffer buffer, const EGLint *attr_list) 375{ 376 (void) drv; 377 378 switch (target) { 379 case EGL_NATIVE_PIXMAP_KHR: 380 return dri2_create_image_khr_pixmap(disp, ctx, buffer, attr_list); 381 default: 382 return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list); 383 } 384} 385 386static int 387dri2_drm_authenticate(_EGLDisplay *disp, uint32_t id) 388{ 389 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); 390 391 return drmAuthMagic(dri2_dpy->fd, id); 392} 393 394EGLBoolean 395dri2_initialize_drm(_EGLDriver *drv, _EGLDisplay *disp) 396{ 397 struct dri2_egl_display *dri2_dpy; 398 struct gbm_device *gbm; 399 int fd = -1; 400 int i; 401 402 dri2_dpy = malloc(sizeof *dri2_dpy); 403 if (!dri2_dpy) 404 return _eglError(EGL_BAD_ALLOC, "eglInitialize"); 405 406 memset(dri2_dpy, 0, sizeof *dri2_dpy); 407 408 disp->DriverData = (void *) dri2_dpy; 409 410 gbm = disp->PlatformDisplay; 411 if (gbm == NULL) { 412 fd = open("/dev/dri/card0", O_RDWR); 413 dri2_dpy->own_device = 1; 414 gbm = gbm_create_device(fd); 415 if (gbm == NULL) 416 return EGL_FALSE; 417 } 418 419 if (strcmp(gbm_device_get_backend_name(gbm), "drm") != 0) { 420 free(dri2_dpy); 421 return EGL_FALSE; 422 } 423 424 dri2_dpy->gbm_dri = gbm_dri_device(gbm); 425 if (dri2_dpy->gbm_dri->base.type != GBM_DRM_DRIVER_TYPE_DRI) { 426 free(dri2_dpy); 427 return EGL_FALSE; 428 } 429 430 if (fd < 0) { 431 fd = dup(gbm_device_get_fd(gbm)); 432 if (fd < 0) { 433 free(dri2_dpy); 434 return EGL_FALSE; 435 } 436 } 437 438 dri2_dpy->fd = fd; 439 dri2_dpy->device_name = dri2_get_device_name_for_fd(dri2_dpy->fd); 440 dri2_dpy->driver_name = dri2_dpy->gbm_dri->base.driver_name; 441 442 dri2_dpy->dri_screen = dri2_dpy->gbm_dri->screen; 443 dri2_dpy->core = dri2_dpy->gbm_dri->core; 444 dri2_dpy->dri2 = dri2_dpy->gbm_dri->dri2; 445 dri2_dpy->image = dri2_dpy->gbm_dri->image; 446 dri2_dpy->flush = dri2_dpy->gbm_dri->flush; 447 dri2_dpy->driver_configs = dri2_dpy->gbm_dri->driver_configs; 448 449 dri2_dpy->gbm_dri->lookup_image = dri2_lookup_egl_image; 450 dri2_dpy->gbm_dri->lookup_user_data = disp; 451 452 dri2_dpy->gbm_dri->get_buffers = dri2_get_buffers; 453 dri2_dpy->gbm_dri->flush_front_buffer = dri2_flush_front_buffer; 454 dri2_dpy->gbm_dri->get_buffers_with_format = dri2_get_buffers_with_format; 455 456 dri2_dpy->gbm_dri->base.base.surface_lock_front_buffer = lock_front_buffer; 457 dri2_dpy->gbm_dri->base.base.surface_release_buffer = release_buffer; 458 dri2_dpy->gbm_dri->base.base.surface_has_free_buffers = has_free_buffers; 459 460 dri2_setup_screen(disp); 461 462 for (i = 0; dri2_dpy->driver_configs[i]; i++) 463 dri2_add_config(disp, dri2_dpy->driver_configs[i], 464 i + 1, 0, EGL_WINDOW_BIT, NULL, NULL); 465 466 drv->API.CreateWindowSurface = dri2_create_window_surface; 467 drv->API.DestroySurface = dri2_destroy_surface; 468 drv->API.SwapBuffers = dri2_swap_buffers; 469 drv->API.CreateImageKHR = dri2_drm_create_image_khr; 470 471#ifdef HAVE_WAYLAND_PLATFORM 472 disp->Extensions.WL_bind_wayland_display = EGL_TRUE; 473#endif 474 dri2_dpy->authenticate = dri2_drm_authenticate; 475 476 /* we're supporting EGL 1.4 */ 477 disp->VersionMajor = 1; 478 disp->VersionMinor = 4; 479 480 return EGL_TRUE; 481} 482