From 2e0618329242605e15419f0188987b79a1f55fcd Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sat, 25 Apr 2020 14:56:36 +0000 Subject: [PATCH 1/2] render: Add wlr_renderer_renderbuffer_from_image() --- include/wlr/render/interface.h | 3 +++ include/wlr/render/wlr_renderer.h | 5 +++++ render/gles2/renderer.c | 14 ++++++++++++++ render/wlr_renderer.c | 8 ++++++++ 4 files changed, 30 insertions(+) diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 93c987b7d1..32df3ff4a9 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -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, diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 513f412a16..76675ed976 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -10,6 +10,8 @@ #define WLR_RENDER_WLR_RENDERER_H #include +#include +#include #include #include #include @@ -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. */ diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 914f5b77fd..fa8a526511 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -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) { @@ -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) { diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 593d165f46..d59261bbe5 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -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, + 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); From 55abc24dd15d55b59f3e2c2fc0609e87583ce114 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sat, 25 Apr 2020 15:01:34 +0000 Subject: [PATCH 2/2] backend: headless: Implement export_dmabuf --- backend/headless/backend.c | 47 +++++++++++++++ backend/headless/output.c | 118 ++++++++++++++++++++++++++++++------- include/backend/headless.h | 11 +++- 3 files changed, 154 insertions(+), 22 deletions(-) diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 7758ece494..459c180efa 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -6,6 +6,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "backend/headless.h" #include "util/signal.h" @@ -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); } @@ -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); @@ -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") || diff --git a/backend/headless/output.c b/backend/headless/output.c index 32c2a7fde0..2091c1883f 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -1,7 +1,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include #include @@ -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); @@ -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, @@ -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); @@ -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 @@ -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); @@ -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); } @@ -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) { @@ -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)); diff --git a/include/backend/headless.h b/include/backend/headless.h index 5681c2bd29..d679457c51 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -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 { @@ -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