gl-renderer: add dmabuf renderbuffer support

Support importing dmabuf buffers as renderbuffers and binding them to
FBOs. These can then be rendered to directly, or they can be blitted
into from the shadow render buffer.

How to best create those dmabuf buffers in the backend is an open
question and may vary depending on what external API the backend is
interfacing with.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
This commit is contained in:
Michael Tretter 2023-09-22 09:56:07 +02:00
parent c9c4549064
commit 5a779b7804
3 changed files with 154 additions and 0 deletions

View File

@ -62,6 +62,12 @@ weston_renderbuffer_unref(struct weston_renderbuffer *renderbuffer);
struct weston_renderer_options {
};
struct linux_dmabuf_memory {
struct dmabuf_attributes *attributes;
void (*destroy)(struct linux_dmabuf_memory *dmabuf);
};
struct weston_renderer {
int (*read_pixels)(struct weston_output *output,
const struct pixel_format_info *format, void *pixels,
@ -102,6 +108,40 @@ struct weston_renderer {
void (*buffer_init)(struct weston_compositor *ec,
struct weston_buffer *buffer);
/**
* Add DMABUF as renderbuffer to the output
*
* \param output The output to add the DMABUF renderbuffer for.
* \param dmabuf The description object of the DMABUF to import.
* \return A weston_renderbuffer on success, NULL on failure.
*
* This function imports the DMABUF memory as renderbuffer and adds
* it to the output. The returned weston_renderbuffer can be passed to
* repaint_output() to render into the DMABUF.
*
* The ownership of the linux_dmabuf_memory is transferred to the
* returned weston_renderbuffer. The linux_dmabuf_memory will be
* destroyed automatically when the weston_renderbuffer is destroyed.
*/
struct weston_renderbuffer *
(*create_renderbuffer_dmabuf)(struct weston_output *output,
struct linux_dmabuf_memory *dmabuf);
/**
* Remove the DAMBUF renderbuffer from the output
*
* \param output The output to remove a DMABUF renderbuffer from.
* \param renderbuffer The weston_renderbuffer that shall be removed
*
* This function removes the DMABUF renderbuffer from the output.
*
* This allows the backend to signal the renderer that it will no longer
* use the renderbuffer for rendering and the renderer may free the
* resources of the renderbuffer.
*/
void (*remove_renderbuffer_dmabuf)(struct weston_output *output,
struct weston_renderbuffer *renderbuffer);
enum weston_renderer_type type;
const struct gl_renderer_interface *gl;
const struct pixman_renderer_interface *pixman;

View File

@ -193,6 +193,7 @@ struct gl_renderer {
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
PFNGLTEXIMAGE3DOESPROC tex_image_3d;
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC image_target_renderbuffer_storage;
PFNEGLCREATEIMAGEKHRPROC create_image;
PFNEGLDESTROYIMAGEKHRPROC destroy_image;
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;

View File

@ -148,6 +148,14 @@ struct gl_capture_task {
int fd;
};
struct dmabuf_renderbuffer {
struct gl_renderbuffer base;
struct gl_renderer *gr;
/* The wrapped dmabuf memory */
struct linux_dmabuf_memory *dmabuf;
EGLImageKHR image;
};
struct dmabuf_format {
uint32_t format;
struct wl_list link;
@ -597,6 +605,12 @@ to_gl_renderbuffer(struct weston_renderbuffer *renderbuffer)
return container_of(renderbuffer, struct gl_renderbuffer, base);
}
static inline struct dmabuf_renderbuffer *
to_dmabuf_renderbuffer(struct gl_renderbuffer *renderbuffer)
{
return container_of(renderbuffer, struct dmabuf_renderbuffer, base);
}
static void
gl_renderer_renderbuffer_destroy(struct weston_renderbuffer *renderbuffer)
{
@ -4157,6 +4171,100 @@ gl_renderer_output_fbo_create(struct weston_output *output,
&options->fb_size, &options->area);
}
static void
gl_renderer_dmabuf_renderbuffer_destroy(struct weston_renderbuffer *renderbuffer)
{
struct gl_renderbuffer *gl_renderbuffer = to_gl_renderbuffer(renderbuffer);
struct dmabuf_renderbuffer *dmabuf_renderbuffer = to_dmabuf_renderbuffer(gl_renderbuffer);
struct gl_renderer *gr = dmabuf_renderbuffer->gr;
glDeleteFramebuffers(1, &gl_renderbuffer->fbo);
glDeleteRenderbuffers(1, &gl_renderbuffer->rb);
pixman_region32_fini(&gl_renderbuffer->base.damage);
gr->destroy_image(gr->egl_display, dmabuf_renderbuffer->image);
/* Destroy the owned dmabuf */
dmabuf_renderbuffer->dmabuf->destroy(dmabuf_renderbuffer->dmabuf);
free(dmabuf_renderbuffer);
}
static struct weston_renderbuffer *
gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output,
struct linux_dmabuf_memory *dmabuf)
{
struct gl_renderer *gr = get_renderer(output->compositor);
struct gl_output_state *go = get_output_state(output);
struct dmabuf_attributes *attributes = dmabuf->attributes;
struct dmabuf_renderbuffer *rb;
struct gl_renderbuffer *renderbuffer;
int fb_status;
rb = xzalloc(sizeof(*rb));
renderbuffer = &rb->base;
rb->image = import_simple_dmabuf(gr, attributes);
if (rb->image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import dmabuf renderbuffer\n");
free(rb);
return NULL;
}
glGenFramebuffers(1, &renderbuffer->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, renderbuffer->fbo);
glGenRenderbuffers(1, &renderbuffer->rb);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer->rb);
gr->image_target_renderbuffer_storage(GL_RENDERBUFFER, rb->image);
if (glGetError() == GL_INVALID_OPERATION) {
weston_log("Failed to create renderbuffer\n");
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glDeleteRenderbuffers(1, &renderbuffer->rb);
gr->destroy_image(gr->egl_display, rb->image);
free(rb);
return NULL;
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer->rb);
fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("failed to bind renderbuffer to fbo\n");
glDeleteFramebuffers(1, &renderbuffer->fbo);
glDeleteRenderbuffers(1, &renderbuffer->rb);
gr->destroy_image(gr->egl_display, rb->image);
free(rb);
return NULL;
}
rb->gr = gr;
rb->dmabuf = dmabuf;
pixman_region32_init(&rb->base.base.damage);
/*
* One reference is kept on the renderbuffer_list,
* the other is returned to the calling backend.
*/
rb->base.base.refcount = 2;
rb->base.base.destroy = gl_renderer_dmabuf_renderbuffer_destroy;
wl_list_insert(&go->renderbuffer_list, &rb->base.link);
return &rb->base.base;
}
static void
gl_renderer_remove_renderbuffer_dmabuf(struct weston_output *output,
struct weston_renderbuffer *renderbuffer)
{
struct gl_renderbuffer *gl_renderbuffer = to_gl_renderbuffer(renderbuffer);
gl_renderer_remove_renderbuffer(gl_renderbuffer);
}
static void
gl_renderer_output_destroy(struct weston_output *output)
{
@ -4366,6 +4474,8 @@ gl_renderer_display_create(struct weston_compositor *ec,
if (gr->has_dmabuf_import) {
gr->base.import_dmabuf = gl_renderer_import_dmabuf;
gr->base.get_supported_formats = gl_renderer_get_supported_formats;
gr->base.create_renderbuffer_dmabuf = gl_renderer_create_renderbuffer_dmabuf;
gr->base.remove_renderbuffer_dmabuf = gl_renderer_remove_renderbuffer_dmabuf;
ret = populate_supported_formats(ec, &gr->supported_formats);
if (ret < 0)
goto fail_terminate;
@ -4562,6 +4672,9 @@ gl_renderer_setup(struct weston_compositor *ec)
gr->image_target_texture_2d =
(void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
gr->image_target_renderbuffer_storage =
(void *)eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES");
extensions = (const char *) glGetString(GL_EXTENSIONS);
if (!extensions) {
weston_log("Retrieving GL extension string failed.\n");