View
@@ -3,6 +3,7 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
+#include <sys/mman.h>
#include <sys/select.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@@ -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,23 +53,21 @@ 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;
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,19 +144,69 @@ 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)
{
assert(bsurface && bsurface->internal);
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 (!drm.use_egldevice && 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;
@@ -256,13 +320,12 @@ add_output(struct gbm_device *device, struct gbm_surface *surface, struct drm_ou
}
static drmModeEncoder*
-find_encoder_for_connector(int fd, drmModeRes *resources, drmModeConnector *connector, int32_t *out_crtc_id)
+find_encoder_for_connector(int fd, drmModeRes *resources, drmModeConnector *connector)
{
- assert(resources && connector && out_crtc_id);
+ assert(resources && connector);
drmModeEncoder *encoder = drmModeGetEncoder(fd, connector->encoder_id);
if (encoder) {
- *out_crtc_id = encoder->crtc_id;
return encoder;
} else {
drmModeFreeEncoder(encoder);
@@ -273,15 +336,36 @@ find_encoder_for_connector(int fd, drmModeRes *resources, drmModeConnector *conn
if (!(encoder = drmModeGetEncoder(fd, resources->encoders[e])))
continue;
- for (int c = 0; c < resources->count_crtcs; ++c) {
- if (!(encoder->possible_crtcs & (1 << c)))
- continue;
+ return encoder;
+ }
- *out_crtc_id = resources->crtcs[c];
- return encoder;
+ return NULL;
+}
+
+static drmModeCrtc*
+find_crtc_for_encoder(int fd, drmModeRes *resources, drmModeEncoder *encoder, uint32_t *used_crtcs, int used_crtcs_num)
+{
+ assert(resources && encoder);
+
+ drmModeCrtc *crtc = drmModeGetCrtc(fd, encoder->crtc_id);
+ if (crtc)
+ return crtc;
+
+ for (int c = 0; c < resources->count_crtcs; ++c) {
+ uint32_t crtc_id = resources->crtcs[c];
+
+ bool used = false;
+ for (int i = 0; i < used_crtcs_num; i++) {
+ if (used_crtcs[i] == crtc_id) {
+ used = true;
+ break;
+ }
}
- drmModeFreeEncoder(encoder);
+ if (used || !(encoder->possible_crtcs & (1 << c)) || !(crtc = drmModeGetCrtc(fd, crtc_id)))
+ continue;
+
+ return crtc;
}
return NULL;
@@ -323,6 +407,11 @@ query_drm(int fd, struct chck_iter_pool *out_infos)
goto resources_fail;
}
+ int used_crtcs_num = 0;
+ uint32_t *used_crtcs;
+ if (!(used_crtcs = malloc(resources->count_crtcs * sizeof(uint32_t))))
+ goto resources_fail;
+
for (int c = 0; c < resources->count_connectors; c++) {
drmModeConnector *connector;
if (!(connector = drmModeGetConnector(fd, resources->connectors[c]))) {
@@ -336,17 +425,16 @@ query_drm(int fd, struct chck_iter_pool *out_infos)
continue;
}
- int32_t crtc_id;
drmModeEncoder *encoder;
- if (!(encoder = find_encoder_for_connector(fd, resources, connector, &crtc_id))) {
+ if (!(encoder = find_encoder_for_connector(fd, resources, connector))) {
wlc_log(WLC_LOG_WARN, "Failed to find encoder for connector %d", c);
drmModeFreeConnector(connector);
continue;
}
drmModeCrtc *crtc;
- if (!(crtc = drmModeGetCrtc(drm.fd, crtc_id))) {
- wlc_log(WLC_LOG_WARN, "Failed to get crtc for connector %d (with id: %d)", c, crtc_id);
+ if (!(crtc = find_crtc_for_encoder(fd, resources, encoder, used_crtcs, used_crtcs_num))) {
+ wlc_log(WLC_LOG_WARN, "Failed to get crtc for connector %d", c);
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
continue;
@@ -367,6 +455,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->crtc_id;
info->info.connector = wlc_connector_for_drm_connector(connector->connector_type);
for (int i = 0; i < connector->count_modes; ++i) {
@@ -380,24 +469,40 @@ 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];
}
}
if (crtc->mode_valid && !memcmp(&connector->modes[i], &crtc->mode, sizeof(crtc->mode))) {
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;
+
+ used_crtcs[used_crtcs_num] = crtc->crtc_id;
+ used_crtcs_num++;
}
+ free(used_crtcs);
+
return true;
resources_fail:
@@ -412,13 +517,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 +577,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 +594,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 +611,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 +638,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;
View
@@ -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,67 @@ 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);
+
+ EGLint n = 0;
+ EGLOutputLayerEXT egl_layer;
+ EGLAttrib layer_attribs[] = {
+ EGL_DRM_CRTC_EXT, output->information.crtc_id,
+ EGL_NONE,
+ };
+
+ if (!context->api.eglGetOutputLayersEXT(context->display, layer_attribs, &egl_layer, 1, &n) || !n) {
+ return EGL_NO_SURFACE;
+ }
+
+ if (!context->api.eglStreamConsumerOutputEXT(context->display, context->stream, egl_layer)) {
+ return EGL_NO_SURFACE;
+ }
+
+ EGLint surface_attribs[] = {
+ EGL_WIDTH, output->mode.w,
+ EGL_HEIGHT, output->mode.h,
+ EGL_NONE
+ };
+
+ 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 +328,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 +361,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 +392,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 +517,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 +547,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);
}
View
@@ -1,9 +1,14 @@
#ifndef _WLC_EGL_H_
#define _WLC_EGL_H_
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
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_ */