Skip to content
This repository has been archived by the owner on Nov 1, 2021. It is now read-only.

[WIP] Headless export-dmabuf #2268

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions backend/headless/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <gbm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "backend/headless.h"
#include "util/signal.h"

Expand Down Expand Up @@ -66,6 +72,10 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
wlr_renderer_destroy(backend->renderer);
wlr_egl_finish(&backend->priv_egl);
}

gbm_device_destroy(backend->gbm);
close(backend->gbm_fd);

free(backend);
}

Expand Down Expand Up @@ -94,6 +104,27 @@ static void handle_renderer_destroy(struct wl_listener *listener, void *data) {
backend_destroy(&backend->backend);
}

static int find_render_node(char *node, size_t maxlen) {
int r = -1;
drmDevice *devices[64];

int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
for (int i = 0; i < n; ++i) {
drmDevice *dev = devices[i];
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
continue;
}

strncpy(node, dev->nodes[DRM_NODE_RENDER], maxlen);
node[maxlen - 1] = '\0';
r = 0;
break;
}

drmFreeDevices(devices, n);
return r;
}

static bool backend_init(struct wlr_headless_backend *backend,
struct wl_display *display, struct wlr_renderer *renderer) {
wlr_backend_init(&backend->backend, &backend_impl);
Expand All @@ -104,6 +135,22 @@ static bool backend_init(struct wlr_headless_backend *backend,
backend->renderer = renderer;
backend->egl = wlr_gles2_renderer_get_egl(renderer);

char render_node[256];
if (find_render_node(render_node, sizeof(render_node)) < 0) {
return false;
}

backend->gbm_fd = open(render_node, O_RDWR);
if (backend->gbm_fd < 0) {
return false;
}

backend->gbm = gbm_create_device(backend->gbm_fd);
if (!backend->gbm) {
close(backend->gbm_fd);
return false;
}

if (wlr_gles2_renderer_check_ext(backend->renderer, "GL_OES_rgb8_rgba8") ||
wlr_gles2_renderer_check_ext(backend->renderer,
"GL_OES_required_internalformat") ||
Expand Down
118 changes: 97 additions & 21 deletions backend/headless/output.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include <assert.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdlib.h>
#include <gbm.h>
#include <drm_fourcc.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
Expand All @@ -14,18 +18,51 @@ static struct wlr_headless_output *headless_output_from_output(
return (struct wlr_headless_output *)wlr_output;
}

static bool create_fbo(struct wlr_headless_output *output,
static void dmabuf_attr_from_gbm_bo(struct wlr_dmabuf_attributes *attr,
struct gbm_bo *bo) {
assert(gbm_bo_get_plane_count(bo) == 1);

memset(attr, 0, sizeof(*attr));

attr->width = gbm_bo_get_width(bo);
attr->height = gbm_bo_get_height(bo);
attr->format = gbm_bo_get_format(bo);
attr->n_planes = 1;
attr->flags |= WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT;

attr->fd[0] = gbm_bo_get_fd(bo);
attr->stride[0] = gbm_bo_get_stride(bo);
attr->offset[0] = gbm_bo_get_offset(bo, 0);
attr->modifier = gbm_bo_get_modifier(bo);
}

static bool create_bo(struct wlr_headless_output *output,
struct wlr_headless_bo *bo,
unsigned int width, unsigned int height) {
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
return false;
}

GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, output->backend->internal_format,
width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
bo->bo = gbm_bo_create(output->backend->gbm, width, height,
DRM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
if (!bo->bo) {
return false;
}

struct wlr_dmabuf_attributes attr;
dmabuf_attr_from_gbm_bo(&attr, bo->bo);

bool external_only = false;
bo->image = wlr_egl_create_image_from_dmabuf(output->backend->egl, &attr,
&external_only);
wlr_dmabuf_attributes_finish(&attr);
if (!bo->image) {
gbm_bo_destroy(bo->bo);
return false;
}

GLuint rbo = wlr_renderer_renderbuffer_from_image(
output->backend->renderer, bo->image);

GLuint fbo;
glGenFramebuffers(1, &fbo);
Expand All @@ -38,27 +75,30 @@ static bool create_fbo(struct wlr_headless_output *output,
wlr_egl_unset_current(output->backend->egl);

if (status != GL_FRAMEBUFFER_COMPLETE) {
gbm_bo_destroy(bo->bo);
wlr_log(WLR_ERROR, "Failed to create FBO");
return false;
}

output->fbo = fbo;
output->rbo = rbo;
bo->fbo = fbo;
bo->rbo = rbo;
return true;
}

static void destroy_fbo(struct wlr_headless_output *output) {
static void destroy_bo(struct wlr_headless_output *output, struct wlr_headless_bo *bo) {
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
return;
}

glDeleteFramebuffers(1, &output->fbo);
glDeleteRenderbuffers(1, &output->rbo);

glDeleteFramebuffers(1, &bo->fbo);
glDeleteRenderbuffers(1, &bo->rbo);
eglDestroyImage(output->backend->egl, bo->image);
gbm_bo_destroy(bo->bo);
wlr_egl_unset_current(output->backend->egl);

output->fbo = 0;
output->rbo = 0;
bo->fbo = 0;
bo->rbo = 0;
bo->image = 0;
bo->bo = NULL;
}

static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
Expand All @@ -70,12 +110,20 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
refresh = HEADLESS_DEFAULT_REFRESH;
}

destroy_fbo(output);
if (!create_fbo(output, width, height)) {
destroy_bo(output, &output->bo[1]);
destroy_bo(output, &output->bo[0]);
if (!create_bo(output, &output->bo[0], width, height)) {
wlr_output_destroy(wlr_output);
return false;
}
if (!create_bo(output, &output->bo[1], width, height)) {
wlr_output_destroy(wlr_output);
return false;
}

output->front = &output->bo[0];
output->back = &output->bo[1];

output->frame_delay = 1000000 / refresh;

wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
Expand All @@ -91,7 +139,11 @@ static bool output_attach_render(struct wlr_output *wlr_output,
return false;
}

glBindFramebuffer(GL_FRAMEBUFFER, output->fbo);
struct wlr_headless_bo *tmp = output->front;
output->front = output->back;
output->back = tmp;

glBindFramebuffer(GL_FRAMEBUFFER, output->back->fbo);

if (buffer_age != NULL) {
*buffer_age = 0; // We only have one buffer
Expand Down Expand Up @@ -140,6 +192,22 @@ static bool output_commit(struct wlr_output *wlr_output) {
return true;
}

static bool output_export_dmabuf(struct wlr_output *wlr_output,
struct wlr_dmabuf_attributes *attributes) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);

struct wlr_egl_context saved_context;
wlr_egl_save_context(&saved_context);
wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL);
glFinish();
wlr_egl_restore_context(&saved_context);

// Note: drm exports the back-buffer, so let's just do the same here.
dmabuf_attr_from_gbm_bo(attributes, output->back->bo);
return true;
}

static void output_rollback_render(struct wlr_output *wlr_output) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
Expand All @@ -153,7 +221,8 @@ static void output_destroy(struct wlr_output *wlr_output) {
headless_output_from_output(wlr_output);
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
destroy_fbo(output);
destroy_bo(output, &output->bo[1]);
destroy_bo(output, &output->bo[0]);
free(output);
}

Expand All @@ -162,6 +231,7 @@ static const struct wlr_output_impl output_impl = {
.attach_render = output_attach_render,
.commit = output_commit,
.rollback_render = output_rollback_render,
.export_dmabuf = output_export_dmabuf,
};

bool wlr_output_is_headless(struct wlr_output *wlr_output) {
Expand Down Expand Up @@ -191,9 +261,15 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
backend->display);
struct wlr_output *wlr_output = &output->wlr_output;

if (!create_fbo(output, width, height)) {
if (!create_bo(output, &output->bo[0], width, height)) {
goto error;
}
if (!create_bo(output, &output->bo[1], width, height)) {
goto error;
}

output->front = &output->bo[0];
output->back = &output->bo[1];

output_set_custom_mode(wlr_output, width, height, 0);
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
Expand Down
11 changes: 10 additions & 1 deletion include/backend/headless.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ struct wlr_headless_backend {
struct wl_listener renderer_destroy;
bool started;
GLenum internal_format;
int gbm_fd;
struct gbm_device *gbm;
};

struct wlr_headless_bo {
struct gbm_bo *bo;
GLuint fbo, rbo;
EGLImageKHR image;
};

struct wlr_headless_output {
Expand All @@ -28,7 +36,8 @@ struct wlr_headless_output {
struct wlr_headless_backend *backend;
struct wl_list link;

GLuint fbo, rbo;
struct wlr_headless_bo bo[2];
struct wlr_headless_bo *front, *back;

struct wl_event_source *frame_timer;
int frame_delay; // ms
Expand Down
3 changes: 3 additions & 0 deletions include/wlr/render/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdbool.h>
#include <wayland-server-protocol.h>
#include <wlr/render/wlr_renderer.h>
Expand Down Expand Up @@ -67,6 +69,7 @@ struct wlr_renderer_impl {
bool (*blit_dmabuf)(struct wlr_renderer *renderer,
struct wlr_dmabuf_attributes *dst,
struct wlr_dmabuf_attributes *src);
GLuint (*renderbuffer_from_image)(EGLImageKHR image);
};

void wlr_renderer_init(struct wlr_renderer *renderer,
Expand Down
5 changes: 5 additions & 0 deletions include/wlr/render/wlr_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#define WLR_RENDER_WLR_RENDERER_H

#include <stdint.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <wayland-server-protocol.h>
#include <wlr/render/egl.h>
#include <wlr/render/wlr_texture.h>
Expand Down Expand Up @@ -130,6 +132,9 @@ bool wlr_renderer_format_supported(struct wlr_renderer *r,
bool wlr_renderer_init_wl_display(struct wlr_renderer *r,
struct wl_display *wl_display);

GLuint wlr_renderer_renderbuffer_from_image(struct wlr_renderer *r,
EGLImageKHR image);

/**
* Destroys this wlr_renderer. Textures must be destroyed separately.
*/
Expand Down
14 changes: 14 additions & 0 deletions render/gles2/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,19 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
return glGetError() == GL_NO_ERROR;
}

static GLuint gles2_renderbuffer_from_image(EGLImageKHR image) {
if (!gles2_procs.glEGLImageTargetRenderbufferStorageOES) {
return 0;
}

GLuint rbo = 0;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
gles2_procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
return rbo;
}

static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer,
struct wlr_dmabuf_attributes *dst_attr,
struct wlr_dmabuf_attributes *src_attr) {
Expand Down Expand Up @@ -538,6 +551,7 @@ static const struct wlr_renderer_impl renderer_impl = {
.texture_from_dmabuf = gles2_texture_from_dmabuf,
.init_wl_display = gles2_init_wl_display,
.blit_dmabuf = gles2_blit_dmabuf,
.renderbuffer_from_image = gles2_renderbuffer_from_image,
};

void push_gles2_marker(const char *file, const char *func) {
Expand Down
8 changes: 8 additions & 0 deletions render/wlr_renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ bool wlr_renderer_blit_dmabuf(struct wlr_renderer *r,
return r->impl->blit_dmabuf(r, dst, src);
}

GLuint wlr_renderer_renderbuffer_from_image(struct wlr_renderer *r,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaks EGL/GL implementation details into the generic renderer interface.

EGLImageKHR image) {
if (!r->impl->renderbuffer_from_image) {
return false;
}
return r->impl->renderbuffer_from_image(image);
}

bool wlr_renderer_format_supported(struct wlr_renderer *r,
enum wl_shm_format fmt) {
return r->impl->format_supported(r, fmt);
Expand Down