diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index 888df8470..1659368f6 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -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; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 14f37ebbe..efd8a1d7d 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -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, }; diff --git a/libweston/renderer-gl/gl-renderer.h b/libweston/renderer-gl/gl-renderer.h index 7407b79c6..b6d0a06c3 100644 --- a/libweston/renderer-gl/gl-renderer.h +++ b/libweston/renderer-gl/gl-renderer.h @@ -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); };