menu_view: configurable alpha channel and bg color

This patch enhances menu_view with the optional configuration attributes
'opaque' and 'background'. Setting 'opaque' to "yes" suppresses the use
of the alpha channel at the GUI session. This improves the drawing
performance by 20% on the PinePhone. Since the menu_view uses the
gems/gui_buffer.h utility, the 'Gui_buffer' received a new 'Alpha'
argument at construction time.

The 'background' attribute can be specified to define the reset color of
the GUI buffer. It alleviates the need to create a frame widget for the
top level.

The patch also switches the optimization level for compiling menu_view
to -O3, which increases the drawing performance on the PinePhone by 30%.

Fixes #4592
This commit is contained in:
Norman Feske 2022-08-25 14:52:49 +02:00 committed by Christian Helmuth
parent bd8c7f84dd
commit 2772abc8d7
3 changed files with 101 additions and 46 deletions

View File

@ -48,41 +48,49 @@ struct Gui_buffer
Framebuffer::Mode const mode;
bool const use_alpha;
Pixel_rgb888 reset_color { 127, 127, 127, 255 };
/**
* Return dataspace capability for virtual framebuffer
*/
Genode::Dataspace_capability _ds_cap(Gui::Connection &gui)
{
/* setup virtual framebuffer mode */
gui.buffer(mode, true);
gui.buffer(mode, use_alpha);
return gui.framebuffer()->dataspace();
}
Genode::Attached_dataspace fb_ds { rm, _ds_cap(gui) };
Genode::size_t pixel_surface_num_bytes() const
size_t pixel_surface_num_bytes() const
{
return size().count()*sizeof(Pixel_rgb888);
}
Genode::size_t alpha_surface_num_bytes() const
size_t alpha_surface_num_bytes() const
{
return size().count();
return use_alpha ? size().count() : 0;
}
Ram_ds pixel_surface_ds { ram, rm, pixel_surface_num_bytes() };
Ram_ds alpha_surface_ds { ram, rm, alpha_surface_num_bytes() };
enum class Alpha { OPAQUE, ALPHA };
/**
* Constructor
*/
Gui_buffer(Gui::Connection &gui, Area size,
Genode::Ram_allocator &ram, Genode::Region_map &rm)
Genode::Ram_allocator &ram, Genode::Region_map &rm,
Alpha alpha = Alpha::ALPHA)
:
ram(ram), rm(rm), gui(gui),
mode({ .area = { Genode::max(1U, size.w()),
Genode::max(1U, size.h()) } })
Genode::max(1U, size.h()) } }),
use_alpha(alpha == Alpha::ALPHA)
{
reset_surface();
}
@ -93,33 +101,48 @@ struct Gui_buffer
Area size() const { return mode.area; }
template <typename FN>
void apply_to_surface(FN const &fn)
void with_alpha_surface(FN const &fn)
{
Area const alpha_size = use_alpha ? size() : Area(0, 0);
Alpha_surface alpha(alpha_surface_ds.local_addr<Pixel_alpha8>(), alpha_size);
fn(alpha);
}
template <typename FN>
void with_pixel_surface(FN const &fn)
{
Pixel_surface pixel(pixel_surface_ds.local_addr<Pixel_rgb888>(), size());
Alpha_surface alpha(alpha_surface_ds.local_addr<Pixel_alpha8>(), size());
fn(pixel, alpha);
fn(pixel);
}
template <typename FN>
void apply_to_surface(FN const &fn)
{
with_alpha_surface([&] (Alpha_surface &alpha) {
with_pixel_surface([&] (Pixel_surface &pixel) {
fn(pixel, alpha); }); });
}
void reset_surface()
{
Pixel_surface pixel(pixel_surface_ds.local_addr<Pixel_rgb888>(), size());
Alpha_surface alpha(alpha_surface_ds.local_addr<Pixel_alpha8>(), size());
if (use_alpha)
with_alpha_surface([&] (Alpha_surface &alpha) {
Genode::memset(alpha.addr(), 0, alpha_surface_num_bytes()); });
Genode::size_t const num_pixels = size().count();
Genode::memset(alpha.addr(), 0, num_pixels);
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();
/*
* 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;
Pixel_rgb888 const gray(127, 127, 127, 255);
for (size_t n = num_pixels; n; n--)
*dst++ = gray;
for (size_t n = size().count(); n; n--)
*dst++ = color;
});
}
template <typename DST_PT, typename SRC_PT>
@ -136,6 +159,9 @@ struct Gui_buffer
void _update_input_mask()
{
if (!use_alpha)
return;
size_t const num_pixels = size().count();
unsigned char * const alpha_base = fb_ds.local_addr<unsigned char>()
@ -159,26 +185,31 @@ struct Gui_buffer
void flush_surface()
{
/* represent back buffer as texture */
Genode::Texture<Pixel_rgb888>
pixel_texture(pixel_surface_ds.local_addr<Pixel_rgb888>(),
nullptr, size());
Genode::Texture<Pixel_alpha8>
alpha_texture(alpha_surface_ds.local_addr<Pixel_alpha8>(),
nullptr, size());
// XXX track dirty rectangles
Rect const clip_rect(Genode::Surface_base::Point(0, 0), size());
Pixel_rgb888 *pixel_base = fb_ds.local_addr<Pixel_rgb888>();
Pixel_alpha8 *alpha_base = fb_ds.local_addr<Pixel_alpha8>()
+ mode.bytes_per_pixel()*size().count();
{
/* represent back buffer as texture */
Genode::Texture<Pixel_rgb888>
pixel_texture(pixel_surface_ds.local_addr<Pixel_rgb888>(),
nullptr, size());
Pixel_rgb888 *pixel_base = fb_ds.local_addr<Pixel_rgb888>();
_convert_back_to_front(pixel_base, pixel_texture, clip_rect);
_convert_back_to_front(alpha_base, alpha_texture, clip_rect);
_convert_back_to_front(pixel_base, pixel_texture, clip_rect);
}
_update_input_mask();
if (use_alpha) {
Genode::Texture<Pixel_alpha8>
alpha_texture(alpha_surface_ds.local_addr<Pixel_alpha8>(),
nullptr, size());
Pixel_alpha8 *alpha_base = fb_ds.local_addr<Pixel_alpha8>()
+ mode.bytes_per_pixel()*size().count();
_convert_back_to_front(alpha_base, alpha_texture, clip_rect);
_update_input_mask();
}
}
};

View File

@ -41,6 +41,11 @@ struct Menu_view::Main
Constructible<Gui_buffer> _buffer { };
/**
* Alpha surface used when operating in opaque mode
*/
Surface<Pixel_alpha8> _no_alpha { nullptr, Area(0,0) };
Gui::Session::View_handle _view_handle = _gui.create_view();
/**
@ -128,6 +133,10 @@ struct Menu_view::Main
Attached_dataspace _input_ds { _env.rm(), _gui.input()->dataspace() };
bool _opaque = false;
Color _background_color { };
Widget::Hovered _last_reported_hovered { };
void _handle_config();
@ -296,14 +305,20 @@ void Menu_view::Main::_handle_config()
{
_config.update();
Xml_node const config = _config.xml();
try {
_hover_reporter.enabled(_config.xml().sub_node("report")
.attribute_value("hover", false));
_hover_reporter.enabled(config.sub_node("report")
.attribute_value("hover", false));
} catch (...) {
_hover_reporter.enabled(false);
}
_config.xml().with_sub_node("vfs", [&] (Xml_node const &vfs_node) {
_opaque = config.attribute_value("opaque", false);
_background_color = config.attribute_value("background", Color(127, 127, 127, 255));
config.with_sub_node("vfs", [&] (Xml_node const &vfs_node) {
_vfs_env.root_dir().apply_config(vfs_node); });
_handle_dialog_update();
@ -389,10 +404,17 @@ void Menu_view::Main::_handle_frame_timer()
bool const size_increased = (max_size.w() > buffer_w)
|| (max_size.h() > buffer_h);
if (!_buffer.constructed() || size_increased)
_buffer.construct(_gui, max_size, _env.ram(), _env.rm());
else
if (!_buffer.constructed() || size_increased) {
_buffer.construct(_gui, max_size, _env.ram(), _env.rm(),
_opaque ? Gui_buffer::Alpha::OPAQUE
: Gui_buffer::Alpha::ALPHA);
_buffer->reset_color = { _background_color.r,
_background_color.g,
_background_color.b,
_background_color.a };
} else {
_buffer->reset_surface();
}
_root_widget.position(Point(0, 0));
@ -400,7 +422,7 @@ void Menu_view::Main::_handle_frame_timer()
// don't perform a full dialog update
_buffer->apply_to_surface([&] (Surface<Pixel_rgb888> &pixel,
Surface<Pixel_alpha8> &alpha) {
_root_widget.draw(pixel, alpha, Point(0, 0));
_root_widget.draw(pixel, _opaque ? _no_alpha : alpha, Point(0, 0));
});
_buffer->flush_surface();

View File

@ -3,6 +3,8 @@ SRC_CC = main.cc
LIBS = base libc libm vfs libpng zlib blit file
INC_DIR += $(PRG_DIR)
CC_OLEVEL := -O3
CUSTOM_TARGET_DEPS += menu_view_styles.tar
BUILD_ARTIFACTS := $(TARGET) menu_view_styles.tar