Skip to content

Commit

Permalink
Add initial EGLStream implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
vially committed Mar 30, 2017
1 parent 6a05b37 commit 4826d36
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 52 deletions.
1 change: 1 addition & 0 deletions src/compositor/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
2 changes: 2 additions & 0 deletions src/platform/backend/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
155 changes: 121 additions & 34 deletions src/platform/backend/drm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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)
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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)))
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -380,19 +445,30 @@ 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;
Expand All @@ -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");
}
Expand Down Expand Up @@ -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);
Expand All @@ -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");
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 4826d36

Please sign in to comment.