From 6522158caadfb398a8207cd304e3b8bb25669dc4 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 24 Sep 2024 17:54:34 +0200 Subject: [PATCH] gems/gui_buffer.h: atomic back-to-front blitting This patch updates menu_view, themed_decorator, and rom_osci to use the new frame-buffer blit operation, mitigating tearing artifacts Issue #5350 --- repos/gems/include/gems/gui_buffer.h | 153 ++++++++++++--------------- 1 file changed, 67 insertions(+), 86 deletions(-) diff --git a/repos/gems/include/gems/gui_buffer.h b/repos/gems/include/gems/gui_buffer.h index cfc87a17a9..efd12f2ecd 100644 --- a/repos/gems/include/gems/gui_buffer.h +++ b/repos/gems/include/gems/gui_buffer.h @@ -36,6 +36,7 @@ struct Gui_buffer : Genode::Noncopyable using Point = Genode::Surface_base::Point; using Ram_ds = Genode::Attached_ram_dataspace; using size_t = Genode::size_t; + using uint8_t = Genode::uint8_t; Genode::Ram_allocator &ram; Genode::Region_map &rm; @@ -53,31 +54,59 @@ struct Gui_buffer : Genode::Noncopyable */ Genode::Dataspace_capability _ds_cap(Gui::Connection &gui) { - /* setup virtual framebuffer mode */ - gui.buffer(mode, use_alpha); + /* + * Setup virtual framebuffer, the upper part containing the front + * buffer, the lower part containing the back buffer. + */ + gui.buffer({ .area = { mode.area.w, mode.area.h*2 } }, use_alpha); return gui.framebuffer.dataspace(); } - Genode::Attached_dataspace fb_ds { rm, _ds_cap(gui) }; + Genode::Attached_dataspace _fb_ds { rm, _ds_cap(gui) }; - size_t pixel_surface_num_bytes() const + size_t _pixel_num_bytes() const { return size().count()*sizeof(Pixel_rgb888); } + size_t _alpha_num_bytes() const { return use_alpha ? size().count() : 0; } + size_t _input_num_bytes() const { return use_alpha ? size().count() : 0; } + + void _with_pixel_ptr(auto const &fn) { - return size().count()*sizeof(Pixel_rgb888); + /* skip pixel front buffer */ + uint8_t * const ptr = _fb_ds.local_addr() + _pixel_num_bytes(); + fn((Pixel_rgb888 *)ptr); } - size_t alpha_surface_num_bytes() const + void _with_alpha_ptr(auto const &fn) { - return use_alpha ? size().count() : 0; + if (!use_alpha) + return; + + /* skip pixel front buffer, pixel back buffer, and alpha front buffer */ + uint8_t * const ptr = _fb_ds.local_addr() + + _pixel_num_bytes()*2 + _alpha_num_bytes(); + fn((Pixel_alpha8 *)ptr); } - Ram_ds pixel_surface_ds { ram, rm, pixel_surface_num_bytes() }; - Ram_ds alpha_surface_ds { ram, rm, alpha_surface_num_bytes() }; + void _with_input_ptr(auto const &fn) + { + if (!use_alpha) + return; + + /* skip pixel buffers, alpha buffers, and input front buffer */ + uint8_t * const ptr = _fb_ds.local_addr() + + _pixel_num_bytes()*2 + _alpha_num_bytes()*2 + _input_num_bytes(); + fn(ptr); + } enum class Alpha { OPAQUE, ALPHA }; static Genode::Color default_reset_color() { + /* + * Do not use black by default to limit the bleeding of black into + * antialiased drawing operations applied onto an initially transparent + * background. + */ return Genode::Color(127, 127, 127, 255); } @@ -99,27 +128,25 @@ struct Gui_buffer : Genode::Noncopyable } /** - * Return size of virtual framebuffer + * Return size of the drawing surface */ Area size() const { return mode.area; } - template - void with_alpha_surface(FN const &fn) + void with_alpha_surface(auto const &fn) { - Area const alpha_size = use_alpha ? size() : Area(0, 0); - Alpha_surface alpha(alpha_surface_ds.local_addr(), alpha_size); - fn(alpha); + _with_alpha_ptr([&] (Pixel_alpha8 *ptr) { + Alpha_surface alpha { ptr, size() }; + fn(alpha); }); } - template - void with_pixel_surface(FN const &fn) + void with_pixel_surface(auto const &fn) { - Pixel_surface pixel(pixel_surface_ds.local_addr(), size()); - fn(pixel); + _with_pixel_ptr([&] (Pixel_rgb888 *ptr) { + Pixel_surface pixel { ptr, size() }; + fn(pixel); }); } - template - void apply_to_surface(FN const &fn) + void apply_to_surface(auto const &fn) { with_alpha_surface([&] (Alpha_surface &alpha) { with_pixel_surface([&] (Pixel_surface &pixel) { @@ -128,18 +155,11 @@ struct Gui_buffer : Genode::Noncopyable void reset_surface() { - if (use_alpha) - with_alpha_surface([&] (Alpha_surface &alpha) { - Genode::memset(alpha.addr(), 0, alpha_surface_num_bytes()); }); + with_alpha_surface([&] (Alpha_surface &alpha) { + Genode::memset(alpha.addr(), 0, _alpha_num_bytes()); }); with_pixel_surface([&] (Pixel_surface &pixel) { - /* - * Initialize color buffer with 50% gray - * - * We do not use black to limit the bleeding of black into antialiased - * drawing operations applied onto an initially transparent background. - */ Pixel_rgb888 *dst = pixel.addr(); Pixel_rgb888 const color = reset_color; @@ -148,71 +168,32 @@ struct Gui_buffer : Genode::Noncopyable }); } - template - void _convert_back_to_front(DST_PT *front_base, - Genode::Texture const &texture, - Rect const clip_rect) - { - Genode::Surface surface(front_base, size()); - - surface.clip(clip_rect); - - Blit_painter::paint(surface, texture, Point()); - } - void _update_input_mask() { - if (!use_alpha) - return; + _with_alpha_ptr([&] (Pixel_alpha8 const * const alpha_ptr) { + _with_input_ptr([&] (uint8_t *dst) { - size_t const num_pixels = size().count(); + /* + * Set input mask for all pixels where the alpha value is above + * a given threshold. The threshold is defined such that + * typical drop shadows are below the value. + */ + uint8_t const threshold = 100; + uint8_t const * src = (uint8_t const *)alpha_ptr; + size_t const num_pixels = size().count(); - unsigned char * const alpha_base = fb_ds.local_addr() - + mode.bytes_per_pixel()*num_pixels; - - unsigned char * const input_base = alpha_base + num_pixels; - - unsigned char const *src = alpha_base; - unsigned char *dst = input_base; - - /* - * Set input mask for all pixels where the alpha value is above a - * given threshold. The threshold is defined such that typical - * drop shadows are below the value. - */ - unsigned char const threshold = 100; - - for (unsigned i = 0; i < num_pixels; i++) - *dst++ = (*src++) > threshold; + for (unsigned i = 0; i < num_pixels; i++) + *dst++ = (*src++) > threshold; + }); + }); } void flush_surface() { - // XXX track dirty rectangles - Rect const clip_rect(Genode::Surface_base::Point(0, 0), size()); + _update_input_mask(); - { - /* represent back buffer as texture */ - Genode::Texture - pixel_texture(pixel_surface_ds.local_addr(), - nullptr, size()); - Pixel_rgb888 *pixel_base = fb_ds.local_addr(); - - _convert_back_to_front(pixel_base, pixel_texture, clip_rect); - } - - if (use_alpha) { - Genode::Texture - alpha_texture(alpha_surface_ds.local_addr(), - nullptr, size()); - - Pixel_alpha8 *alpha_base = fb_ds.local_addr() - + mode.bytes_per_pixel()*size().count(); - - _convert_back_to_front(alpha_base, alpha_texture, clip_rect); - - _update_input_mask(); - } + /* copy lower part of virtual framebuffer to upper part */ + gui.framebuffer.blit({ { 0, int(size().h) }, size() }, { 0, 0 }); } };