1 /************************************************************************** 2 * 3 * Copyright (C) 2014 Red Hat Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included 13 * in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 * 23 **************************************************************************/ 24 /* create our own EGL offscreen rendering context via gbm and rendernodes */ 25 26 27 /* if we are using EGL and rendernodes then we talk via file descriptors to the remote 28 node */ 29 #ifdef HAVE_CONFIG_H 30 #include "config.h" 31 #endif 32 33 #define EGL_EGLEXT_PROTOTYPES 34 #include <dirent.h> 35 #include <fcntl.h> 36 #include <unistd.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #include <errno.h> 41 #include <stdbool.h> 42 #include <epoxy/egl.h> 43 #include <gbm.h> 44 #include <xf86drm.h> 45 #include "virglrenderer.h" 46 #include "virgl_egl.h" 47 48 #include "virgl_hw.h" 49 struct virgl_egl { 50 int fd; 51 struct gbm_device *gbm_dev; 52 EGLDisplay egl_display; 53 EGLConfig egl_conf; 54 EGLContext egl_ctx; 55 bool have_mesa_drm_image; 56 bool have_mesa_dma_buf_img_export; 57 }; 58 59 static int egl_rendernode_open(void) 60 { 61 DIR *dir; 62 struct dirent *e; 63 int r, fd; 64 char *p; 65 dir = opendir("/dev/dri"); 66 if (!dir) 67 return -1; 68 69 fd = -1; 70 while ((e = readdir(dir))) { 71 if (e->d_type != DT_CHR) 72 continue; 73 74 if (strncmp(e->d_name, "renderD", 7)) 75 continue; 76 77 r = asprintf(&p, "/dev/dri/%s", e->d_name); 78 if (r < 0) 79 return -1; 80 81 r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); 82 if (r < 0){ 83 free(p); 84 continue; 85 } 86 fd = r; 87 free(p); 88 break; 89 } 90 91 closedir(dir); 92 if (fd < 0) 93 return -1; 94 return fd; 95 } 96 97 static bool virgl_egl_has_extension_in_string(const char *haystack, const char *needle) 98 { 99 const unsigned needle_len = strlen(needle); 100 101 if (needle_len == 0) 102 return false; 103 104 while (true) { 105 const char *const s = strstr(haystack, needle); 106 107 if (s == NULL) 108 return false; 109 110 if (s[needle_len] == ' ' || s[needle_len] == '\0') { 111 return true; 112 } 113 114 /* strstr found an extension whose name begins with 115 * needle, but whose name is not equal to needle. 116 * Restart the search at s + needle_len so that we 117 * don't just find the same extension again and go 118 * into an infinite loop. 119 */ 120 haystack = s + needle_len; 121 } 122 123 return false; 124 } 125 126 struct virgl_egl *virgl_egl_init(int fd, bool surfaceless, bool gles) 127 { 128 static EGLint conf_att[] = { 129 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 130 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, 131 EGL_RED_SIZE, 1, 132 EGL_GREEN_SIZE, 1, 133 EGL_BLUE_SIZE, 1, 134 EGL_ALPHA_SIZE, 0, 135 EGL_NONE, 136 }; 137 static const EGLint ctx_att[] = { 138 EGL_CONTEXT_CLIENT_VERSION, 2, 139 EGL_NONE 140 }; 141 EGLBoolean b; 142 EGLenum api; 143 EGLint major, minor, n; 144 const char *extension_list; 145 struct virgl_egl *d; 146 147 d = malloc(sizeof(struct virgl_egl)); 148 if (!d) 149 return NULL; 150 151 if (gles) 152 conf_att[3] = EGL_OPENGL_ES_BIT; 153 154 if (surfaceless) { 155 conf_att[1] = EGL_PBUFFER_BIT; 156 d->fd = -1; 157 d->gbm_dev = NULL; 158 } else { 159 if (fd >= 0) { 160 d->fd = fd; 161 } else { 162 d->fd = egl_rendernode_open(); 163 } 164 if (d->fd == -1) 165 goto fail; 166 d->gbm_dev = gbm_create_device(d->fd); 167 if (!d->gbm_dev) 168 goto fail; 169 } 170 171 const char *client_extensions = eglQueryString (NULL, EGL_EXTENSIONS); 172 173 if (strstr (client_extensions, "EGL_KHR_platform_base")) { 174 PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = 175 (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress ("eglGetPlatformDisplay"); 176 177 if (!get_platform_display) 178 goto fail; 179 180 if (surfaceless) { 181 d->egl_display = get_platform_display (EGL_PLATFORM_SURFACELESS_MESA, 182 EGL_DEFAULT_DISPLAY, NULL); 183 } else 184 d->egl_display = get_platform_display (EGL_PLATFORM_GBM_KHR, 185 (EGLNativeDisplayType)d->gbm_dev, NULL); 186 } else if (strstr (client_extensions, "EGL_EXT_platform_base")) { 187 PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = 188 (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress ("eglGetPlatformDisplayEXT"); 189 190 if (!get_platform_display) 191 goto fail; 192 193 if (surfaceless) { 194 d->egl_display = get_platform_display (EGL_PLATFORM_SURFACELESS_MESA, 195 EGL_DEFAULT_DISPLAY, NULL); 196 } else 197 d->egl_display = get_platform_display (EGL_PLATFORM_GBM_KHR, 198 (EGLNativeDisplayType)d->gbm_dev, NULL); 199 } else { 200 d->egl_display = eglGetDisplay((EGLNativeDisplayType)d->gbm_dev); 201 } 202 203 if (!d->egl_display) 204 goto fail; 205 206 b = eglInitialize(d->egl_display, &major, &minor); 207 if (!b) 208 goto fail; 209 210 extension_list = eglQueryString(d->egl_display, EGL_EXTENSIONS); 211 #ifdef VIRGL_EGL_DEBUG 212 fprintf(stderr, "EGL major/minor: %d.%d\n", major, minor); 213 fprintf(stderr, "EGL version: %s\n", 214 eglQueryString(d->egl_display, EGL_VERSION)); 215 fprintf(stderr, "EGL vendor: %s\n", 216 eglQueryString(d->egl_display, EGL_VENDOR)); 217 fprintf(stderr, "EGL extensions: %s\n", extension_list); 218 #endif 219 /* require surfaceless context */ 220 if (!virgl_egl_has_extension_in_string(extension_list, "EGL_KHR_surfaceless_context")) 221 goto fail; 222 223 d->have_mesa_drm_image = false; 224 d->have_mesa_dma_buf_img_export = false; 225 if (virgl_egl_has_extension_in_string(extension_list, "EGL_MESA_drm_image")) 226 d->have_mesa_drm_image = true; 227 228 if (virgl_egl_has_extension_in_string(extension_list, "EGL_MESA_image_dma_buf_export")) 229 d->have_mesa_dma_buf_img_export = true; 230 231 if (d->have_mesa_drm_image == false && d->have_mesa_dma_buf_img_export == false) { 232 fprintf(stderr, "failed to find drm image extensions\n"); 233 goto fail; 234 } 235 236 if (gles) 237 api = EGL_OPENGL_ES_API; 238 else 239 api = EGL_OPENGL_API; 240 b = eglBindAPI(api); 241 if (!b) 242 goto fail; 243 244 b = eglChooseConfig(d->egl_display, conf_att, &d->egl_conf, 245 1, &n); 246 247 if (!b || n != 1) 248 goto fail; 249 250 d->egl_ctx = eglCreateContext(d->egl_display, 251 d->egl_conf, 252 EGL_NO_CONTEXT, 253 ctx_att); 254 if (!d->egl_ctx) 255 goto fail; 256 257 258 eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 259 d->egl_ctx); 260 return d; 261 fail: 262 free(d); 263 return NULL; 264 } 265 266 void virgl_egl_destroy(struct virgl_egl *d) 267 { 268 eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 269 EGL_NO_CONTEXT); 270 eglDestroyContext(d->egl_display, d->egl_ctx); 271 eglTerminate(d->egl_display); 272 if (d->gbm_dev) 273 gbm_device_destroy(d->gbm_dev); 274 if (d->fd >= 0) 275 close(d->fd); 276 free(d); 277 } 278 279 virgl_renderer_gl_context virgl_egl_create_context(struct virgl_egl *ve, struct virgl_gl_ctx_param *vparams) 280 { 281 EGLContext eglctx; 282 EGLint ctx_att[] = { 283 EGL_CONTEXT_CLIENT_VERSION, vparams->major_ver, 284 EGL_CONTEXT_MINOR_VERSION_KHR, vparams->minor_ver, 285 EGL_NONE 286 }; 287 eglctx = eglCreateContext(ve->egl_display, 288 ve->egl_conf, 289 vparams->shared ? eglGetCurrentContext() : EGL_NO_CONTEXT, 290 ctx_att); 291 return (virgl_renderer_gl_context)eglctx; 292 } 293 294 void virgl_egl_destroy_context(struct virgl_egl *ve, virgl_renderer_gl_context virglctx) 295 { 296 EGLContext eglctx = (EGLContext)virglctx; 297 eglDestroyContext(ve->egl_display, eglctx); 298 } 299 300 int virgl_egl_make_context_current(struct virgl_egl *ve, virgl_renderer_gl_context virglctx) 301 { 302 EGLContext eglctx = (EGLContext)virglctx; 303 304 return eglMakeCurrent(ve->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 305 eglctx); 306 } 307 308 virgl_renderer_gl_context virgl_egl_get_current_context(UNUSED struct virgl_egl *ve) 309 { 310 EGLContext eglctx = eglGetCurrentContext(); 311 return (virgl_renderer_gl_context)eglctx; 312 } 313 314 int virgl_egl_get_fourcc_for_texture(struct virgl_egl *ve, uint32_t tex_id, uint32_t format, int *fourcc) 315 { 316 int ret = EINVAL; 317 318 #ifndef EGL_MESA_image_dma_buf_export 319 ret = 0; 320 goto fallback; 321 #else 322 EGLImageKHR image; 323 EGLBoolean b; 324 325 if (!ve->have_mesa_dma_buf_img_export) 326 goto fallback; 327 328 image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL); 329 330 if (!image) 331 return EINVAL; 332 333 b = eglExportDMABUFImageQueryMESA(ve->egl_display, image, fourcc, NULL, NULL); 334 if (!b) 335 goto out_destroy; 336 ret = 0; 337 out_destroy: 338 eglDestroyImageKHR(ve->egl_display, image); 339 return ret; 340 341 #endif 342 343 fallback: 344 *fourcc = virgl_egl_get_gbm_format(format); 345 return ret; 346 } 347 348 int virgl_egl_get_fd_for_texture2(struct virgl_egl *ve, uint32_t tex_id, int *fd, 349 int *stride, int *offset) 350 { 351 int ret = EINVAL; 352 EGLImageKHR image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), 353 EGL_GL_TEXTURE_2D_KHR, 354 (EGLClientBuffer)(unsigned long)tex_id, NULL); 355 if (!image) 356 return EINVAL; 357 if (!ve->have_mesa_dma_buf_img_export) 358 goto out_destroy; 359 360 if (!eglExportDMABUFImageMESA(ve->egl_display, image, fd, 361 stride, offset)) 362 goto out_destroy; 363 364 ret = 0; 365 366 out_destroy: 367 eglDestroyImageKHR(ve->egl_display, image); 368 return ret; 369 } 370 371 int virgl_egl_get_fd_for_texture(struct virgl_egl *ve, uint32_t tex_id, int *fd) 372 { 373 EGLImageKHR image; 374 EGLint stride; 375 #ifdef EGL_MESA_image_dma_buf_export 376 EGLint offset; 377 #endif 378 EGLBoolean b; 379 int ret; 380 image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL); 381 382 if (!image) 383 return EINVAL; 384 385 ret = EINVAL; 386 if (ve->have_mesa_dma_buf_img_export) { 387 #ifdef EGL_MESA_image_dma_buf_export 388 b = eglExportDMABUFImageMESA(ve->egl_display, 389 image, 390 fd, 391 &stride, 392 &offset); 393 if (!b) 394 goto out_destroy; 395 #else 396 goto out_destroy; 397 #endif 398 } else { 399 #ifdef EGL_MESA_drm_image 400 EGLint handle; 401 int r; 402 b = eglExportDRMImageMESA(ve->egl_display, 403 image, 404 NULL, &handle, 405 &stride); 406 407 if (!b) 408 goto out_destroy; 409 410 fprintf(stderr,"image exported %d %d\n", handle, stride); 411 412 r = drmPrimeHandleToFD(ve->fd, handle, DRM_CLOEXEC, fd); 413 if (r < 0) 414 goto out_destroy; 415 #else 416 goto out_destroy; 417 #endif 418 } 419 ret = 0; 420 out_destroy: 421 eglDestroyImageKHR(ve->egl_display, image); 422 return ret; 423 } 424 425 uint32_t virgl_egl_get_gbm_format(uint32_t format) 426 { 427 switch (format) { 428 case VIRGL_FORMAT_B8G8R8X8_UNORM: 429 return GBM_FORMAT_XRGB8888; 430 case VIRGL_FORMAT_B8G8R8A8_UNORM: 431 return GBM_FORMAT_ARGB8888; 432 default: 433 fprintf(stderr, "unknown format to convert to GBM %d\n", format); 434 return 0; 435 } 436 } 437