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:
parent
68778dd6f4
commit
5a40ebbbf1
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue