From 4826d36ce42478221eabfe21e96548a1e10581d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20H=C4=83loiu?= Date: Sun, 26 Mar 2017 21:33:16 +0300 Subject: [PATCH] Add initial EGLStream implementation --- src/compositor/output.h | 1 + src/platform/backend/backend.h | 2 + src/platform/backend/drm.c | 155 +++++++++++++++++------ src/platform/context/egl.c | 217 ++++++++++++++++++++++++++++++--- src/platform/context/egl.h | 5 + 5 files changed, 328 insertions(+), 52 deletions(-) diff --git a/src/compositor/output.h b/src/compositor/output.h index d546ffbc..0116fe88 100644 --- a/src/compositor/output.h +++ b/src/compositor/output.h @@ -34,6 +34,7 @@ struct wlc_output_information { int32_t physical_width, physical_height; int32_t subpixel; uint32_t connector_id; + uint32_t crtc_id; enum wl_output_transform transform; enum wlc_connector_type connector; }; diff --git a/src/platform/backend/backend.h b/src/platform/backend/backend.h index 5522d860..5db25069 100644 --- a/src/platform/backend/backend.h +++ b/src/platform/backend/backend.h @@ -13,6 +13,8 @@ struct wlc_backend_surface { EGLNativeDisplayType display; EGLNativeWindowType window; EGLint display_type; + int drm_fd; + bool use_egldevice; struct { WLC_NONULL void (*terminate)(struct wlc_backend_surface *surface); diff --git a/src/platform/backend/drm.c b/src/platform/backend/drm.c index 6f4e56bb..c6a434f2 100644 --- a/src/platform/backend/drm.c +++ b/src/platform/backend/drm.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include "backend.h" #include "compositor/compositor.h" #include "compositor/output.h" +#include "platform/context/egl.h" #include "session/fd.h" // FIXME: Contains global state (event_source && fd) @@ -27,13 +29,14 @@ struct drm_output_information { drmModeConnector *connector; drmModeEncoder *encoder; drmModeCrtc *crtc; + drmModeModeInfo mode; struct wlc_output_information info; uint32_t width, height; }; struct drm_surface { - struct gbm_device *device; - struct gbm_surface *surface; + void *device; + struct gbm_surface *gbm_surface; drmModeConnector *connector; drmModeEncoder *encoder; drmModeCrtc *crtc; @@ -50,10 +53,8 @@ struct drm_surface { }; static struct { - struct gbm_device *device; -} gbm; - -static struct { + bool use_egldevice; + void *device; int fd; struct wl_event_source *event_source; } drm; @@ -61,12 +62,12 @@ static struct { static void release_fb(struct gbm_surface *surface, struct drm_fb *fb) { - assert(surface && fb); + assert(fb); if (fb->fd > 0) drmModeRmFB(drm.fd, fb->fd); - if (fb->bo) + if (surface && fb->bo) gbm_surface_release_buffer(surface, fb->bo); fb->bo = NULL; @@ -81,9 +82,11 @@ page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int use struct wlc_backend_surface *bsurface = data; struct drm_surface *dsurface = bsurface->internal; - uint8_t next = (dsurface->index + 1) % NUM_FBS; - release_fb(dsurface->surface, &dsurface->fb[next]); - dsurface->index = next; + if (!drm.use_egldevice) { + uint8_t next = (dsurface->index + 1) % NUM_FBS; + release_fb(dsurface->gbm_surface, &dsurface->fb[next]); + dsurface->index = next; + } struct timespec ts; ts.tv_sec = sec; @@ -107,7 +110,7 @@ drm_event(int fd, uint32_t mask, void *data) } static bool -create_fb(struct gbm_surface *surface, struct drm_fb *fb) +create_gbm_fb(struct gbm_surface *surface, struct drm_fb *fb) { assert(surface && fb); @@ -141,6 +144,56 @@ create_fb(struct gbm_surface *surface, struct drm_fb *fb) return false; } +static uint32_t +create_dumb_fb(uint32_t width, uint32_t height) +{ + struct drm_mode_destroy_dumb destroy_request = { 0 }; + struct drm_mode_create_dumb create_request = { 0 }; + create_request.width = width; + create_request.height = height; + create_request.bpp = 32; /* RGBX8888 */ + + if (drmIoctl(drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_request) < 0) { + goto failed_ioctl; + } + + uint32_t fd; + if (drmModeAddFB(drm.fd, width, height, 24, 32, create_request.pitch, create_request.handle, &fd)) { + goto fail_add_fb; + } + + struct drm_mode_map_dumb map_request = { 0 }; + map_request.handle = create_request.handle; + if (drmIoctl(drm.fd, DRM_IOCTL_MODE_MAP_DUMB, &map_request)) { + goto fail_map; + } + + uint8_t *map; + map = mmap(0, create_request.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm.fd, map_request.offset); + if (map == MAP_FAILED) { + goto fail_map; + } + + memset(map, 0, create_request.size); + + return fd; + +failed_ioctl: + wlc_log(WLC_LOG_WARN, "Failed ioctl to create dumb fb"); + goto fail; +fail_add_fb: + wlc_log(WLC_LOG_WARN, "Failed to add dumb fb"); + goto fail_destroy_dumb; +fail_map: + wlc_log(WLC_LOG_WARN, "Failed ioctl to map dumb fb"); + drmModeRmFB(drm.fd, fd); +fail_destroy_dumb: + destroy_request.handle = create_request.handle; + drmIoctl(drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request); +fail: + return 0; +} + static bool page_flip(struct wlc_backend_surface *bsurface) { @@ -148,12 +201,12 @@ page_flip(struct wlc_backend_surface *bsurface) struct drm_surface *dsurface = bsurface->internal; assert(!dsurface->flipping); struct drm_fb *fb = &dsurface->fb[dsurface->index]; - release_fb(dsurface->surface, fb); + release_fb(dsurface->gbm_surface, fb); struct wlc_output *o; except((o = wl_container_of(bsurface, o, bsurface))); - if (!create_fb(dsurface->surface, fb)) + if (!create_gbm_fb(dsurface->gbm_surface, fb)) return false; if (fb->stride != dsurface->stride) { @@ -175,7 +228,7 @@ page_flip(struct wlc_backend_surface *bsurface) failed_to_page_flip: wlc_log(WLC_LOG_WARN, "Failed to page flip: %m"); fail: - release_fb(dsurface->surface, fb); + release_fb(dsurface->gbm_surface, fb); return false; } @@ -209,15 +262,15 @@ surface_release(struct wlc_backend_surface *bsurface) { struct drm_surface *dsurface = bsurface->internal; struct drm_fb *fb = &dsurface->fb[dsurface->index]; - release_fb(dsurface->surface, fb); + release_fb(dsurface->gbm_surface, fb); drmModeSetCrtc(drm.fd, dsurface->crtc->crtc_id, dsurface->crtc->buffer_id, dsurface->crtc->x, dsurface->crtc->y, &dsurface->connector->connector_id, 1, &dsurface->crtc->mode); if (dsurface->crtc) drmModeFreeCrtc(dsurface->crtc); - if (dsurface->surface) - gbm_surface_destroy(dsurface->surface); + if (dsurface->gbm_surface) + gbm_surface_destroy(dsurface->gbm_surface); if (dsurface->encoder) drmModeFreeEncoder(dsurface->encoder); @@ -229,7 +282,16 @@ surface_release(struct wlc_backend_surface *bsurface) } static bool -add_output(struct gbm_device *device, struct gbm_surface *surface, struct drm_output_information *info) +set_crtc_default_mode(struct drm_output_information *info) +{ + int fb = 0; + if (!(fb = create_dumb_fb(info->width, info->height))) + return false; + return drmModeSetCrtc(drm.fd, info->crtc->crtc_id, fb, 0, 0, &info->connector->connector_id, 1, &info->mode) == 0; +} + +static bool +add_output(void *device, struct gbm_surface *surface, struct drm_output_information *info) { struct wlc_backend_surface bsurface; if (!wlc_backend_surface(&bsurface, surface_release, sizeof(struct drm_surface))) @@ -239,11 +301,13 @@ add_output(struct gbm_device *device, struct gbm_surface *surface, struct drm_ou dsurface->connector = info->connector; dsurface->encoder = info->encoder; dsurface->crtc = info->crtc; - dsurface->surface = surface; + dsurface->gbm_surface = surface; dsurface->device = device; + bsurface.use_egldevice = drm.use_egldevice; + bsurface.drm_fd = drm.fd; bsurface.display = (EGLNativeDisplayType)device; - bsurface.display_type = EGL_PLATFORM_GBM_MESA; + bsurface.display_type = (drm.use_egldevice ? EGL_PLATFORM_DEVICE_EXT : EGL_PLATFORM_GBM_KHR); bsurface.window = (EGLNativeWindowType)surface; bsurface.api.sleep = surface_sleep; bsurface.api.page_flip = page_flip; @@ -367,6 +431,7 @@ query_drm(int fd, struct chck_iter_pool *out_infos) info->info.physical_height = connector->mmHeight; info->info.subpixel = connector->subpixel; info->info.connector_id = connector->connector_type_id; + info->info.crtc_id = crtc_id; info->info.connector = wlc_connector_for_drm_connector(connector->connector_type); for (int i = 0; i < connector->count_modes; ++i) { @@ -380,6 +445,7 @@ query_drm(int fd, struct chck_iter_pool *out_infos) if (!info->width && !info->height) { info->width = connector->modes[i].hdisplay; info->height = connector->modes[i].vdisplay; + info->mode = connector->modes[i]; } } @@ -387,12 +453,22 @@ query_drm(int fd, struct chck_iter_pool *out_infos) mode.flags |= WL_OUTPUT_MODE_CURRENT; info->width = connector->modes[i].hdisplay; info->height = connector->modes[i].vdisplay; + info->mode = connector->modes[i]; } wlc_log(WLC_LOG_INFO, "MODE: (%d) %ux%u@%u %s", c, mode.width, mode.height, mode.refresh, (mode.flags & WL_OUTPUT_MODE_CURRENT ? "*" : (mode.flags & WL_OUTPUT_MODE_PREFERRED ? "!" : ""))); wlc_output_information_add_mode(&info->info, &mode); } + if (!info->width && !info->height && connector->count_modes) { + struct wlc_output_mode *mode; + mode = chck_iter_pool_get(&info->info.modes, 0); + mode->flags |= WL_OUTPUT_MODE_PREFERRED; + info->width = mode->width; + info->height = mode->height; + info->mode = connector->modes[0]; + } + info->crtc = crtc; info->encoder = encoder; info->connector = connector; @@ -412,13 +488,12 @@ terminate(void) if (drm.event_source) wl_event_source_remove(drm.event_source); - if (gbm.device) - gbm_device_destroy(gbm.device); + if (!drm.use_egldevice && drm.device) + gbm_device_destroy(drm.device); wlc_fd_close(drm.fd); memset(&drm, 0, sizeof(drm)); - memset(&gbm, 0, sizeof(gbm)); wlc_log(WLC_LOG_INFO, "Closed drm"); } @@ -473,11 +548,14 @@ update_outputs(struct chck_pool *outputs) if (outputs && output_exists_for_connector(outputs, info->connector)) continue; + if (drm.use_egldevice && !set_crtc_default_mode(info)) + continue; + struct gbm_surface *surface; - if (!(surface = gbm_surface_create(gbm.device, info->width, info->height, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING))) + if (!drm.use_egldevice && !(surface = gbm_surface_create(drm.device, info->width, info->height, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING))) continue; - count += (add_output(gbm.device, surface, info) ? 1 : 0); + count += (add_output(drm.device, surface, info) ? 1 : 0); } chck_iter_pool_release(&infos); @@ -487,6 +565,7 @@ update_outputs(struct chck_pool *outputs) bool wlc_drm(struct wlc_backend *backend) { + chck_cstr_to_bool(getenv("WLC_USE_EGLDEVICE"), &drm.use_egldevice); drm.fd = -1; const char *device = getenv("WLC_DRM_DEVICE"); @@ -503,15 +582,20 @@ wlc_drm(struct wlc_backend *backend) if (drm.fd < 0) goto card_open_fail; - /* GBM will load a dri driver, but even though they need symbols from - * libglapi, in some version of Mesa they are not linked to it. Since - * only the gl-renderer module links to it, the call above won't make - * these symbols globally available, and loading the DRI driver fails. - * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */ - dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); + if (!drm.use_egldevice) { + /* GBM will load a dri driver, but even though they need symbols from + * libglapi, in some version of Mesa they are not linked to it. Since + * only the gl-renderer module links to it, the call above won't make + * these symbols globally available, and loading the DRI driver fails. + * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */ + dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); - if (!(gbm.device = gbm_create_device(drm.fd))) - goto gbm_device_fail; + if (!(drm.device = gbm_create_device(drm.fd))) + goto gbm_device_fail; + } else { + if (!(drm.device = get_egl_device())) + goto egl_device_fail; + } if (!(drm.event_source = wl_event_loop_add_fd(wlc_event_loop(), drm.fd, WL_EVENT_READABLE, drm_event, NULL))) goto fail; @@ -525,6 +609,9 @@ wlc_drm(struct wlc_backend *backend) goto fail; gbm_device_fail: wlc_log(WLC_LOG_WARN, "gbm_create_device failed"); + goto fail; +egl_device_fail: + wlc_log(WLC_LOG_WARN, "Failed to get EGL device"); fail: terminate(); return false; diff --git a/src/platform/context/egl.c b/src/platform/context/egl.c index 323304ed..fee9f8a6 100644 --- a/src/platform/context/egl.c +++ b/src/platform/context/egl.c @@ -14,12 +14,49 @@ #include "compositor/output.h" #include "platform/backend/backend.h" +#ifndef EGL_NV_stream_attrib +#define EGL_NV_stream_attrib 1 +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamAttribNV(EGLDisplay dpy, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglSetStreamAttribNV(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value); +EGLAPI EGLBoolean EGLAPIENTRY eglQueryStreamAttribNV(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib *value); +EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerAcquireAttribNV(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerReleaseAttribNV(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +#endif +typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMATTRIBNVPROC)(EGLDisplay dpy, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSTREAMATTRIBNVPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSTREAMATTRIBNVPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib *value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERRELEASEATTRIBNVPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +#endif /* EGL_NV_stream_attrib */ + +#ifndef EGL_EXT_stream_acquire_mode +#define EGL_EXT_stream_acquire_mode 1 +#define EGL_CONSUMER_AUTO_ACQUIRE_EXT 0x332B +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERACQUIREATTRIBEXTPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerAcquireAttribEXT(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +#endif +#endif /* EGL_EXT_stream_acquire_mode */ + +#ifndef EGL_NV_output_drm_flip_event +#define EGL_NV_output_drm_flip_event 1 +#define EGL_DRM_FLIP_EVENT_DATA_NV 0x333E +#endif /* EGL_NV_output_drm_flip_event */ + +/* XXX khronos eglext.h does not yet have EGL_DRM_MASTER_FD_EXT */ +#ifndef EGL_DRM_MASTER_FD_EXT +#define EGL_DRM_MASTER_FD_EXT 0x333C +#endif + struct ctx { const char *extensions; + const char *device_extensions; struct wl_display *wl_display; EGLDisplay display; EGLContext context; EGLSurface surface; + EGLStreamKHR stream; EGLConfig config; bool flip_failed; @@ -31,6 +68,13 @@ struct ctx { PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplayWL; PFNEGLUNBINDWAYLANDDISPLAYWL eglUnbindWaylandDisplayWL; PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC eglSwapBuffersWithDamage; + // Needed for EGL streams + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; + PFNEGLGETOUTPUTLAYERSEXTPROC eglGetOutputLayersEXT; + PFNEGLCREATESTREAMKHRPROC eglCreateStreamKHR; + PFNEGLSTREAMCONSUMEROUTPUTEXTPROC eglStreamConsumerOutputEXT; + PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC eglCreateStreamProducerSurfaceKHR; + PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC eglStreamConsumerAcquireAttribNV; } api; }; @@ -98,15 +142,15 @@ egl_call(const char *func, uint32_t line, const char *eglfunc) #define EGL_CALL(x) x; egl_call(__PRETTY_FUNCTION__, __LINE__, __STRING(x)) WLC_PURE static bool -has_extension(const struct ctx *context, const char *extension) +is_extension_supported(const char *extensions, const char *extension) { - assert(context && extension); + assert(extension); - if (!context->extensions) + if (!extensions) return false; size_t len = strlen(extension), pos; - const char *s = context->extensions; + const char *s = extensions; while ((pos = strcspn(s, " ")) != 0) { size_t next = pos + (s[pos] != 0); @@ -119,6 +163,50 @@ has_extension(const struct ctx *context, const char *extension) return false; } +WLC_PURE static bool +has_extension(const struct ctx *context, const char *extension) +{ + assert(context && extension); + return is_extension_supported(context->extensions, extension); +} + +WLC_PURE static bool +has_device_extension(const struct ctx *context, const char *extension) +{ + assert(context && extension); + return is_extension_supported(context->device_extensions, extension); +} + +EGLDeviceEXT +get_egl_device() +{ + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (void*)eglGetProcAddress("eglQueryDevicesEXT"); + PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (void*)eglGetProcAddress("eglQueryDeviceStringEXT"); + + const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!is_extension_supported(extensions, "EGL_EXT_device_base") && + (!is_extension_supported(extensions, "EGL_EXT_device_enumeration") || + !is_extension_supported(extensions, "EGL_EXT_device_query"))) + return NULL; + + EGLint num_devices; + if (!eglQueryDevicesEXT(0, NULL, &num_devices) || num_devices < 1) + return NULL; + + EGLDeviceEXT devices[255]; + if (!eglQueryDevicesEXT(num_devices, devices, &num_devices)) + return NULL; + + for (int i = 0; i < num_devices; i++) { + EGLDeviceEXT device = devices[i]; + const char *device_extensions = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS); + if (is_extension_supported(device_extensions, "EGL_EXT_device_drm") && device != EGL_NO_DEVICE_EXT) + return device; + } + + return NULL; +} + static void terminate(struct ctx *context) { @@ -170,25 +258,71 @@ terminate(struct ctx *context) * like mesa will be able to adverise these (even though it can do EGL 1.5). */ static EGLDisplay -get_display(struct ctx *context, EGLint type, void *native) +get_display(struct ctx *context, EGLint type, void *native, int drm_fd) { - PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT; + /* In practice any EGL 1.5 implementation would support the EXT extension */ + if (context->api.eglGetPlatformDisplayEXT) { + if (has_extension(context, "EGL_EXT_platform_device") && has_device_extension(context, "EGL_EXT_device_drm")) { + /* + * Provide the DRM fd when creating the EGLDisplay, so that the + * EGL implementation can make any necessary DRM calls using the + * same fd as the application. + */ + EGLint attribs[] = { + EGL_DRM_MASTER_FD_EXT, drm_fd, + EGL_NONE + }; - /* Initialize extensions to those of the NULL display, for has_extension */ - context->extensions = EGL_CALL(eglQueryString(NULL, EGL_EXTENSIONS)); + return context->api.eglGetPlatformDisplayEXT(type, native, attribs); + } - /* In practise any EGL 1.5 implementation would support the EXT extension */ - if (has_extension(context, "EGL_EXT_platform_base")) { - PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT = - (void *) eglGetProcAddress("eglGetPlatformDisplayEXT"); - if (getPlatformDisplayEXT) - return getPlatformDisplayEXT(type, native, NULL); + return context->api.eglGetPlatformDisplayEXT(type, native, NULL); } /* Welp, everything is awful. */ return eglGetDisplay(native); } +static EGLSurface +create_surface_egl_device(struct ctx *context, struct wlc_backend_surface *bsurface) +{ + struct wlc_output *output; + output = wl_container_of(bsurface, output, bsurface); + + EGLAttrib layer_attribs[] = { + EGL_DRM_CRTC_EXT, output->information.crtc_id, + EGL_NONE, + }; + + EGLint surface_attribs[] = { + EGL_WIDTH, output->mode.w, + EGL_HEIGHT, output->mode.h, + EGL_NONE + }; + + EGLint n = 0; + EGLBoolean ret; + EGLOutputLayerEXT egl_layer; + + ret = context->api.eglGetOutputLayersEXT(context->display, layer_attribs, &egl_layer, 1, &n); + if (!ret || !n) { + return EGL_NO_SURFACE; + } + + ret = context->api.eglStreamConsumerOutputEXT(context->display, context->stream, egl_layer); + if (!ret) { + return EGL_NO_SURFACE; + } + + return context->api.eglCreateStreamProducerSurfaceKHR(context->display, context->config, context->stream, surface_attribs); +} + +static EGLSurface +create_surface_gbm(EGLDisplay display, EGLConfig config, EGLNativeWindowType window) +{ + return eglCreateWindowSurface(display, config, window, NULL); +} + static struct ctx* create_context(struct wlc_backend_surface *bsurface) { @@ -198,7 +332,24 @@ create_context(struct wlc_backend_surface *bsurface) if (!(context = calloc(1, sizeof(struct ctx)))) return NULL; - context->display = get_display(context, bsurface->display_type, bsurface->display); + /* Initialize extensions to those of the NULL display, for has_extension */ + context->extensions = EGL_CALL(eglQueryString(NULL, EGL_EXTENSIONS)); + if (has_extension(context, "EGL_EXT_platform_base")) { + context->api.eglGetPlatformDisplayEXT = (void*)eglGetProcAddress("eglGetPlatformDisplayEXT"); + } + + if (bsurface->use_egldevice) { + PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (void*)eglGetProcAddress("eglQueryDeviceStringEXT"); + context->device_extensions = EGL_CALL(eglQueryDeviceStringEXT(bsurface->display, EGL_EXTENSIONS)); + + context->api.eglGetOutputLayersEXT = (void*)eglGetProcAddress("eglGetOutputLayersEXT"); + context->api.eglCreateStreamKHR = (void*)eglGetProcAddress("eglCreateStreamKHR"); + context->api.eglStreamConsumerOutputEXT = (void*)eglGetProcAddress("eglStreamConsumerOutputEXT"); + context->api.eglCreateStreamProducerSurfaceKHR = (void*)eglGetProcAddress("eglCreateStreamProducerSurfaceKHR"); + context->api.eglStreamConsumerAcquireAttribNV = (void*)eglGetProcAddress("eglStreamConsumerAcquireAttribNV"); + } + + context->display = get_display(context, bsurface->display_type, bsurface->display, bsurface->drm_fd); if (!context->display) goto egl_fail; @@ -214,7 +365,7 @@ create_context(struct wlc_backend_surface *bsurface) } configs[] = { { (const EGLint[]){ - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_SURFACE_TYPE, bsurface->use_egldevice ? EGL_STREAM_BIT_KHR : EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, @@ -245,7 +396,20 @@ create_context(struct wlc_backend_surface *bsurface) if ((context->context = eglCreateContext(context->display, context->config, EGL_NO_CONTEXT, context_attribs)) == EGL_NO_CONTEXT) goto egl_fail; - if ((context->surface = eglCreateWindowSurface(context->display, context->config, bsurface->window, NULL)) == EGL_NO_SURFACE) + if (bsurface->use_egldevice) { + EGLint stream_attribs[] = { + EGL_STREAM_FIFO_LENGTH_KHR, 1, + EGL_CONSUMER_AUTO_ACQUIRE_EXT, EGL_FALSE, + EGL_NONE + }; + + context->stream = context->api.eglCreateStreamKHR(context->display, stream_attribs); + if (context->stream == EGL_NO_STREAM_KHR) + goto egl_fail; + } + + context->surface = bsurface->use_egldevice ? create_surface_egl_device(context, bsurface) : create_surface_gbm(context->display, context->config, bsurface->window); + if (context->surface == EGL_NO_SURFACE) goto egl_fail; if (!eglMakeCurrent(context->display, context->surface, context->surface, context->context)) @@ -357,6 +521,21 @@ bind_to_wl_display(struct ctx *context, struct wl_display *wl_display) return (context->wl_display ? true : false); } +static bool +output_stream_flip(struct ctx *context, struct wlc_backend_surface *bsurface) +{ + EGLAttrib acquire_attribs[] = { + EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib)bsurface, + EGL_NONE + }; + + if (context->stream != EGL_NO_STREAM_KHR) { + return context->api.eglStreamConsumerAcquireAttribNV(context->display, context->stream, acquire_attribs) == EGL_TRUE; + } + + return true; +} + static void swap(struct ctx *context, struct wlc_backend_surface *bsurface) { @@ -372,7 +551,9 @@ swap(struct ctx *context, struct wlc_backend_surface *bsurface) if (!context->flip_failed) ret = EGL_CALL(eglSwapBuffers(context->display, context->surface)); - if (ret == EGL_TRUE && bsurface->api.page_flip) + if (ret == EGL_TRUE && bsurface->use_egldevice) { + output_stream_flip(context, bsurface); + } else if (ret == EGL_TRUE && bsurface->api.page_flip) context->flip_failed = !bsurface->api.page_flip(bsurface); } diff --git a/src/platform/context/egl.h b/src/platform/context/egl.h index a58fee44..f95de45f 100644 --- a/src/platform/context/egl.h +++ b/src/platform/context/egl.h @@ -1,9 +1,14 @@ #ifndef _WLC_EGL_H_ #define _WLC_EGL_H_ +#include +#include + struct wlc_context_api; struct wlc_backend_surface; +EGLDeviceEXT get_egl_device(void); + void* wlc_egl(struct wlc_backend_surface *bsurface, struct wlc_context_api *api); #endif /* _WLC_EGL_H_ */