gl-renderer: add FBO output support

Add support for creating surfaceless outputs and rendering to FBOs.

The backend has to create FBOs with the create_fbo API and pass the
resulting weston_renderbuffer handles to repaint_output.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
This commit is contained in:
Philipp Zabel 2021-03-30 13:27:00 +02:00 committed by Daniel Stone
parent 68778dd6f4
commit 5a40ebbbf1
3 changed files with 159 additions and 25 deletions

View File

@ -177,7 +177,9 @@ struct gl_renderer {
bool has_texture_type_2_10_10_10_rev;
bool has_gl_texture_rg;
bool has_texture_norm16;
bool has_texture_storage;
bool has_pack_reverse;
bool has_rgb8_rgba8;
struct gl_shader *current_shader;
struct gl_shader *fallback_shader;

View File

@ -95,6 +95,7 @@ struct gl_renderbuffer {
enum gl_border_status border_damage;
/* The fbo value zero represents the default surface framebuffer. */
GLuint fbo;
GLuint rb;
struct wl_list link;
int age;
};
@ -720,12 +721,19 @@ gl_fbo_texture_fini(struct gl_fbo_texture *fbotex)
fbotex->tex = 0;
}
static inline struct gl_renderbuffer *
to_gl_renderbuffer(struct weston_renderbuffer *renderbuffer)
{
return container_of(renderbuffer, struct gl_renderbuffer, base);
}
static void
gl_renderer_renderbuffer_destroy(struct weston_renderbuffer *renderbuffer)
{
struct gl_renderbuffer *rb;
struct gl_renderbuffer *rb = to_gl_renderbuffer(renderbuffer);
rb = container_of(renderbuffer, struct gl_renderbuffer, base);
glDeleteFramebuffers(1, &rb->fbo);
glDeleteRenderbuffers(1, &rb->rb);
pixman_region32_fini(&rb->base.damage);
free(rb);
}
@ -754,6 +762,66 @@ gl_renderer_create_dummy_renderbuffer(struct weston_output *output)
return renderbuffer;
}
static struct weston_renderbuffer *
gl_renderer_create_fbo(struct weston_output *output,
const struct pixel_format_info *format,
int width, int height)
{
struct gl_renderer *gr = get_renderer(output->compositor);
struct gl_output_state *go = get_output_state(output);
struct gl_renderbuffer *renderbuffer;
int fb_status;
switch (format->gl_internalformat) {
case GL_RGB8:
case GL_RGBA8:
if (!gr->has_rgb8_rgba8)
return NULL;
break;
case GL_RGB10_A2:
if (!gr->has_texture_type_2_10_10_10_rev ||
!gr->has_texture_storage)
return NULL;
break;
default:
return NULL;
}
renderbuffer = xzalloc(sizeof(*renderbuffer));
glGenFramebuffers(1, &renderbuffer->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, renderbuffer->fbo);
glGenRenderbuffers(1, &renderbuffer->rb);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer->rb);
glRenderbufferStorage(GL_RENDERBUFFER, format->gl_internalformat,
width, height);
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) {
glDeleteFramebuffers(1, &renderbuffer->fbo);
glDeleteRenderbuffers(1, &renderbuffer->rb);
free(renderbuffer);
return NULL;
}
pixman_region32_init(&renderbuffer->base.damage);
/*
* One reference is kept on the renderbuffer_list,
* the other is returned to the calling backend.
*/
renderbuffer->base.refcount = 2;
renderbuffer->base.destroy = gl_renderer_renderbuffer_destroy;
wl_list_insert(&go->renderbuffer_list, &renderbuffer->link);
return &renderbuffer->base;
}
static bool
gl_renderer_do_capture(struct gl_renderer *gr, struct weston_buffer *into,
const struct weston_geometry *rect)
@ -1589,7 +1657,8 @@ output_get_buffer_age(struct weston_output *output)
EGLint buffer_age = 0;
EGLBoolean ret;
if (gr->has_egl_buffer_age || gr->has_egl_partial_update) {
if ((gr->has_egl_buffer_age || gr->has_egl_partial_update) &&
go->egl_surface != EGL_NO_SURFACE) {
ret = eglQuerySurface(gr->egl_display, go->egl_surface,
EGL_BUFFER_AGE_EXT, &buffer_age);
if (ret == EGL_FALSE) {
@ -1808,7 +1877,6 @@ gl_renderer_repaint_output(struct weston_output *output,
struct gl_output_state *go = get_output_state(output);
struct weston_compositor *compositor = output->compositor;
struct gl_renderer *gr = get_renderer(compositor);
EGLBoolean ret;
static int errored;
struct weston_paint_node *pnode;
const int32_t area_inv_y =
@ -1830,7 +1898,10 @@ gl_renderer_repaint_output(struct weston_output *output,
rb->border_damage |= go->border_status;
}
rb = output_get_dummy_renderbuffer(output);
if (renderbuffer)
rb = to_gl_renderbuffer(renderbuffer);
else
rb = output_get_dummy_renderbuffer(output);
/* Clear the used_in_output_repaint flag, so that we can properly track
* which surfaces were used in this output repaint. */
@ -1922,27 +1993,33 @@ gl_renderer_repaint_output(struct weston_output *output,
gr->destroy_sync(gr->egl_display, go->render_sync);
go->render_sync = create_render_sync(gr);
if (gr->swap_buffers_with_damage && !gr->fan_debug) {
int n_egl_rects;
EGLint *egl_rects;
if (go->egl_surface != EGL_NO_SURFACE) {
EGLBoolean ret;
/* For swap_buffers_with_damage, we need to pass the region
* which has changed since the previous SwapBuffers on this
* surface - this is output_damage. */
pixman_region_to_egl_y_invert(output, output_damage,
&egl_rects, &n_egl_rects);
ret = gr->swap_buffers_with_damage(gr->egl_display,
go->egl_surface,
egl_rects, n_egl_rects);
free(egl_rects);
if (gr->swap_buffers_with_damage && !gr->fan_debug) {
int n_egl_rects;
EGLint *egl_rects;
/* For swap_buffers_with_damage, we need to pass the region
* which has changed since the previous SwapBuffers on this
* surface - this is output_damage. */
pixman_region_to_egl_y_invert(output, output_damage,
&egl_rects, &n_egl_rects);
ret = gr->swap_buffers_with_damage(gr->egl_display,
go->egl_surface,
egl_rects, n_egl_rects);
free(egl_rects);
} else {
ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
}
if (ret == EGL_FALSE && !errored) {
errored = 1;
weston_log("Failed in eglSwapBuffers.\n");
gl_renderer_print_egl_error_state();
}
} else {
ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
}
if (ret == EGL_FALSE && !errored) {
errored = 1;
weston_log("Failed in eglSwapBuffers.\n");
gl_renderer_print_egl_error_state();
glFlush();
}
pixman_region32_clear(&rb->base.damage);
@ -3624,7 +3701,7 @@ gl_renderer_output_window_create(struct weston_output *output,
struct weston_compositor *ec = output->compositor;
struct gl_renderer *gr = get_renderer(ec);
EGLSurface egl_surface = EGL_NO_SURFACE;
int ret = 0;
int ret;
egl_surface = gl_renderer_create_window_surface(gr,
options->window_for_legacy,
@ -3699,6 +3776,14 @@ gl_renderer_output_pbuffer_create(struct weston_output *output,
return ret;
}
static int
gl_renderer_output_fbo_create(struct weston_output *output,
const struct gl_renderer_fbo_options *options)
{
return gl_renderer_output_create(output, EGL_NO_SURFACE,
&options->fb_size, &options->area);
}
static void
gl_renderer_output_destroy(struct weston_output *output)
{
@ -4165,6 +4250,10 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
if (weston_check_egl_extension(extensions, "GL_EXT_texture_norm16"))
gr->has_texture_norm16 = true;
if (gr->gl_version >= gr_gl_version(3, 0) ||
weston_check_egl_extension(extensions, "GL_EXT_texture_storage"))
gr->has_texture_storage = true;
if (weston_check_egl_extension(extensions, "GL_ANGLE_pack_reverse_row_order"))
gr->has_pack_reverse = true;
@ -4175,6 +4264,10 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external"))
gr->has_egl_image_external = true;
if (gr->gl_version >= gr_gl_version(3, 0) ||
weston_check_egl_extension(extensions, "GL_OES_rgb8_rgba8"))
gr->has_rgb8_rgba8 = true;
if (gr->gl_version >= gr_gl_version(3, 0) &&
weston_check_egl_extension(extensions, "GL_OES_texture_float_linear") &&
weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float") &&
@ -4263,7 +4356,9 @@ WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
.display_create = gl_renderer_display_create,
.output_window_create = gl_renderer_output_window_create,
.output_pbuffer_create = gl_renderer_output_pbuffer_create,
.output_fbo_create = gl_renderer_output_fbo_create,
.output_destroy = gl_renderer_output_destroy,
.output_set_border = gl_renderer_output_set_border,
.create_fence_fd = gl_renderer_create_fence_fd,
.create_fbo = gl_renderer_create_fbo,
};

View File

@ -105,6 +105,13 @@ struct gl_renderer_pbuffer_options {
unsigned formats_count;
};
struct gl_renderer_fbo_options {
/** Size of the framebuffer in pixels, including borders */
struct weston_size fb_size;
/** Area inside the framebuffer in pixels for composited content */
struct weston_geometry area;
};
struct gl_renderer_interface {
/**
* Initialize GL-renderer with the given EGL platform and native display
@ -185,6 +192,20 @@ struct gl_renderer_interface {
int (*output_pbuffer_create)(struct weston_output *output,
const struct gl_renderer_pbuffer_options *options);
/**
* Attach GL-renderer to the output with a frame buffer object
*
* \param output The output to prepare for FBO rendering.
* \param options The options struct describing the render geometry
* \return 0 on success, -1 on failure.
*
* This function creates the renderer data structures needed to repaint
* the output. The repaint results will be stored in FBO renderbuffers
* passed to \c repaint_output.
*/
int (*output_fbo_create)(struct weston_output *output,
const struct gl_renderer_fbo_options *options);
void (*output_destroy)(struct weston_output *output);
/* Sets the output border.
@ -225,4 +246,20 @@ struct gl_renderer_interface {
* EGL_ANDROID_native_fence_sync extension.
*/
int (*create_fence_fd)(struct weston_output *output);
/**
* Create an FBO renderbuffer that repaint_output can render to
*
* \param output The output to create an FBO renderbuffer for.
* \param format The renderbuffer pixel format.
* \param width The renderbuffer width.
* \param height The renderbuffer height.
* \return 0 on success, -1 on failure.
*
* This function creates an FBO renderbuffer that can be passed to \c
* repaint_output.
*/
struct weston_renderbuffer *(*create_fbo)(struct weston_output *output,
const struct pixel_format_info *format,
int width, int height);
};