gl-renderer: Add FBO handling utilities

Add generic gl_fbo_init(), gl_fbo_image_init() and gl_fbo_fini()
utilities to the GL renderer and make use of these in the renderbuffer
creation functions.

This also fixes a framebuffer object leak on dma-buf renderbuffer
creation when the EGLImageTargetRenderbufferStorageOES() call fails.

Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
This commit is contained in:
Loïc Molinari 2024-08-06 13:52:55 +02:00
parent 1c7274deed
commit 0688343dec

View file

@ -637,6 +637,95 @@ timeline_submit_render_sync(struct gl_renderer *gr,
wl_list_insert(&go->timeline_render_point_list, &trp->link);
}
/* Initialise a pair of framebuffer and renderbuffer objects. Use gl_fbo_fini()
* to finalise.
*/
static bool
gl_fbo_init(GLenum internal_format,
int width,
int height,
GLuint *fb_out,
GLuint *rb_out)
{
GLuint fb, rb;
GLenum status;
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
glRenderbufferStorage(GL_RENDERBUFFER, internal_format, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rb);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("Error: FBO incomplete.\n");
goto error;
}
*fb_out = fb;
*rb_out = rb;
return true;
error:
glDeleteFramebuffers(1, &fb);
glDeleteRenderbuffers(1, &rb);
return false;
}
/* Finalise a pair of framebuffer and renderbuffer objects.
*/
static void
gl_fbo_fini(GLuint *fb,
GLuint *rb)
{
glDeleteFramebuffers(1, fb);
glDeleteRenderbuffers(1, rb);
*fb = 0;
*rb = 0;
}
/* Initialise a pair of framebuffer and renderbuffer objects to render into an
* EGL image. Use gl_fbo_fini() to finalise.
*/
static bool
gl_fbo_image_init(struct gl_renderer *gr,
EGLImageKHR image,
GLuint *fb_out,
GLuint *rb_out)
{
GLuint fb, rb;
GLenum status;
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
gr->image_target_renderbuffer_storage(GL_RENDERBUFFER, image);
if (glGetError() == GL_INVALID_OPERATION)
goto error;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rb);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("Error: FBO incomplete.\n");
goto error;
}
*fb_out = fb;
*rb_out = rb;
return true;
error:
glDeleteFramebuffers(1, &fb);
glDeleteRenderbuffers(1, &rb);
return false;
}
/** Create a texture and a framebuffer object
*
* \param fbotex To be initialized.
@ -725,12 +814,11 @@ gl_renderbuffer_fini(struct gl_renderbuffer *renderbuffer)
assert(!renderbuffer->stale);
pixman_region32_fini(&renderbuffer->damage);
glDeleteFramebuffers(1, &renderbuffer->fb);
if (renderbuffer->type == RENDERBUFFER_FBO) {
glDeleteRenderbuffers(1, &renderbuffer->fbo.rb);
gl_fbo_fini(&renderbuffer->fb, &renderbuffer->fbo.rb);
} else if (renderbuffer->type == RENDERBUFFER_DMABUF) {
glDeleteRenderbuffers(1, &renderbuffer->dmabuf.rb);
gl_fbo_fini(&renderbuffer->fb, &renderbuffer->dmabuf.rb);
renderbuffer->dmabuf.gr->destroy_image(renderbuffer->dmabuf.gr->egl_display,
renderbuffer->dmabuf.image);
}
@ -802,8 +890,7 @@ gl_renderer_create_fbo(struct weston_output *output,
{
struct gl_renderer *gr = get_renderer(output->compositor);
struct gl_renderbuffer *renderbuffer;
int fb_status;
GLuint fb;
GLuint fb, rb;
switch (format->gl_internalformat) {
case GL_RGB8:
@ -822,29 +909,14 @@ gl_renderer_create_fbo(struct weston_output *output,
return NULL;
}
renderbuffer = xzalloc(sizeof(*renderbuffer));
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenRenderbuffers(1, &renderbuffer->fbo.rb);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer->fbo.rb);
glRenderbufferStorage(GL_RENDERBUFFER, format->gl_internalformat,
width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer->fbo.rb);
fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
glDeleteFramebuffers(1, &fb);
glDeleteRenderbuffers(1, &renderbuffer->fbo.rb);
free(renderbuffer);
if (!gl_fbo_init(format->gl_internalformat, width, height, &fb, &rb)) {
weston_log("Failed to init renderbuffer\n");
return NULL;
}
renderbuffer = xzalloc(sizeof(*renderbuffer));
renderbuffer->fbo.rb = rb;
renderbuffer->fbo.pixels = pixels;
gl_renderbuffer_init(renderbuffer, RENDERBUFFER_FBO,
BORDER_STATUS_CLEAN, fb, discarded_cb, user_data,
@ -865,51 +937,25 @@ gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output,
struct gl_renderer *gr = get_renderer(output->compositor);
struct dmabuf_attributes *attributes = dmabuf->attributes;
struct gl_renderbuffer *renderbuffer;
int fb_status;
GLuint fb;
EGLImageKHR image;
GLuint fb, rb;
image = import_simple_dmabuf(gr, attributes);
if (image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import dmabuf\n");
return NULL;
}
if (!gl_fbo_image_init(gr, image, &fb, &rb)) {
weston_log("Failed to init renderbuffer from dmabuf\n");
gr->destroy_image(gr->egl_display, image);
return NULL;
}
renderbuffer = xzalloc(sizeof(*renderbuffer));
renderbuffer->dmabuf.image = import_simple_dmabuf(gr, attributes);
if (renderbuffer->dmabuf.image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import dmabuf renderbuffer\n");
free(renderbuffer);
return NULL;
}
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glGenRenderbuffers(1, &renderbuffer->dmabuf.rb);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer->dmabuf.rb);
gr->image_target_renderbuffer_storage(GL_RENDERBUFFER,
renderbuffer->dmabuf.image);
if (glGetError() == GL_INVALID_OPERATION) {
weston_log("Failed to create renderbuffer\n");
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glDeleteRenderbuffers(1, &renderbuffer->dmabuf.rb);
gr->destroy_image(gr->egl_display, renderbuffer->dmabuf.image);
free(renderbuffer);
return NULL;
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer->dmabuf.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, &fb);
glDeleteRenderbuffers(1, &renderbuffer->dmabuf.rb);
gr->destroy_image(gr->egl_display, renderbuffer->dmabuf.image);
free(renderbuffer);
return NULL;
}
renderbuffer->dmabuf.gr = gr;
renderbuffer->dmabuf.memory = dmabuf;
renderbuffer->dmabuf.image = image;
gl_renderbuffer_init(renderbuffer, RENDERBUFFER_DMABUF,
BORDER_STATUS_CLEAN, fb, discarded_cb, user_data,
output);
@ -3903,9 +3949,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface,
struct gl_buffer_state *gb;
struct weston_buffer *buffer;
int cw, ch;
GLuint fbo;
GLuint rb;
GLenum status;
GLuint fbo, rb;
int ret = -1;
gs = get_surface_state(surface);
@ -3930,20 +3974,12 @@ gl_renderer_surface_copy_content(struct weston_surface *surface,
gl_shader_config_set_input_textures(&sconf, gs);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, cw, ch);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rb);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("%s: fbo error: %#x\n", __func__, status);
goto out;
if (!gl_fbo_init(GL_RGBA, cw, ch, &fbo, &rb)) {
weston_log("Failed to init FBO\n");
goto fbo_init_error;
}
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
glViewport(0, 0, cw, ch);
glDisable(GL_BLEND);
@ -3955,7 +3991,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface,
WESTON_MATRIX_TRANSFORM_TRANSLATE;
if (!gl_renderer_use_program(gr, &sconf))
goto out;
goto use_program_error;
glEnableVertexAttribArray(SHADER_ATTRIB_LOC_POSITION);
glEnableVertexAttribArray(SHADER_ATTRIB_LOC_TEXCOORD);
@ -3971,10 +4007,9 @@ gl_renderer_surface_copy_content(struct weston_surface *surface,
GL_UNSIGNED_BYTE, target);
ret = 0;
out:
glDeleteFramebuffers(1, &fbo);
glDeleteRenderbuffers(1, &rb);
use_program_error:
gl_fbo_fini(&fbo, &rb);
fbo_init_error:
return ret;
}