diff --git a/src/server/fb_upscale/README b/src/server/fb_upscale/README new file mode 100644 index 0000000..7fff9e6 --- /dev/null +++ b/src/server/fb_upscale/README @@ -0,0 +1,4 @@ +The 'fb_upscale' server scales a fixed-resolution framebuffer client session +to a larger parent framebuffer session. The scaling is linear at a ratio of +1:1 across the x and y axis. Clients must supply their native resolution at +the time of session creation. diff --git a/src/server/fb_upscale/component.cc b/src/server/fb_upscale/component.cc new file mode 100644 index 0000000..c212c0c --- /dev/null +++ b/src/server/fb_upscale/component.cc @@ -0,0 +1,223 @@ +/* + * \brief Linear framebuffer upscaler + * \author Emery Hemingway + * \date 2016-07-20 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + + +namespace Fb_scaler { + + using namespace Framebuffer; + + class Session_component; + class Root_component; + + typedef + Genode::Root_component + Root_component_base; + +} + + +class Fb_scaler::Session_component : public Genode::Rpc_object +{ + private: + + Genode::Env &_env; + + Framebuffer::Connection _parent_fb { _env, Framebuffer::Mode() }; + Framebuffer::Mode _parent_mode = _parent_fb.mode(); + Framebuffer::Mode _client_mode; + + Genode::Attached_ram_dataspace _client_ds + { _env.ram(), _env.rm(), + Genode::size_t(_client_mode.width()*_client_mode.height())*2 + }; + + Genode::Lazy_volatile_object _parent_ds; + + Genode::Signal_context_capability _client_sig_cap; + + enum { SHIFT = 16 }; + + int _scale_factor; + int _scale_ratio; + int _x_offset; + int _y_offset; + + void _rescale() + { + /* get a new dataspace */ + if (_parent_ds.constructed()) + _parent_ds.destruct(); + _parent_ds.construct(_env.rm(), _parent_fb.dataspace()); + Genode::memset(_parent_ds->local_addr(), + 0x00, _parent_ds->size()); + + /* calculate the scaling using the FPU */ + float x_factor = + float(_parent_mode.width()) / + float(_client_mode.width()); + + float y_factor = + float(_parent_mode.height()) / + float(_client_mode.height()); + + float factor = Genode::min(x_factor, y_factor); + + _x_offset = + (_parent_mode.width() - + (_client_mode.width()*factor)) / 2; + _y_offset = + (_parent_mode.height() - + (_client_mode.height()*factor)) / 2; + + /* shift so the scaling can be done with integeral math */ + _scale_factor = (1< _mode_handler + { _env.ep(), *this, &Session_component::_handle_mode }; + + public: + + Session_component(Genode::Env &env, Mode client_mode) + : + _env(env), + _client_mode(client_mode.width() && client_mode.height() ? + client_mode : _parent_mode) + { + if (!(_client_mode.width() && _client_mode.height())) { + /* use the parent mode maybe enlarge later */ + _client_mode = _parent_mode; + _handle_mode(); + } + + _rescale(); + _parent_fb.mode_sigh(_mode_handler); + } + + + /*********************************** + ** Framebuffer session interface ** + ***********************************/ + + Genode::Dataspace_capability dataspace() override { + return _client_ds.cap(); } + + Mode mode() const override + { + Mode parent_mode = _parent_fb.mode(); + return (!parent_mode.width() && !parent_mode.height()) ? + parent_mode : _client_mode; + } + + void refresh(int cx, int cy, int cw, int ch) override + { + using Genode::uint16_t; + + /* the shifting is to round down by dropping bits */ + + int px = (cx * _scale_factor)>>SHIFT; + int py = (cy * _scale_factor)>>SHIFT; + int pw = (cw * _scale_factor)>>SHIFT; + int ph = (ch * _scale_factor)>>SHIFT; + + uint16_t const *src = _client_ds.local_addr(); + uint16_t *dst = _parent_ds->local_addr(); + + for (int y = py; y < ph; ++y) { + int src_y = ((y*_scale_ratio) >> SHIFT)*_client_mode.width(); + + for (int x = px; x < pw; ++x) { + dst[(_y_offset+y)*_parent_mode.width()+x+_x_offset] = + src[src_y+((x*_scale_ratio) >> SHIFT)]; + } + } + + _parent_fb.refresh(_x_offset+px, _y_offset+py, + _y_offset+pw, _x_offset+ph); + } + + + void mode_sigh(Genode::Signal_context_capability sig_cap) override { + _client_sig_cap = sig_cap; } + + /* client gets sync signals from parent session */ + void sync_sigh(Genode::Signal_context_capability sig_cap) override { + _parent_fb.sync_sigh(sig_cap); } + +}; + + +class Fb_scaler::Root_component : Root_component_base +{ + private: + + Genode::Env &_env; + + protected: + + Session_component *_create_session(char const *args) override + { + using namespace Genode; + + unsigned width = Arg_string::find_arg(args, "fb_width").ulong_value(0); + unsigned height = Arg_string::find_arg(args, "fb_width").ulong_value(0); + + return new (md_alloc()) + Session_component(_env, Mode(width, height, Mode::INVALID)); + } + + public: + + Root_component(Genode::Env &env, Genode::Allocator &md_alloc) + : Root_component_base(env.ep(), md_alloc), + _env(env) + { + env.parent().announce(env.ep().manage(*this)); + } +}; + + +/*************** + ** Component ** + ***************/ + +Genode::size_t Component::stack_size() { return 4*1024*sizeof(Genode::addr_t); } + +void Component::construct(Genode::Env &env) +{ + static Genode::Sliced_heap sliced_heap(env.ram(), env.rm()); + + static Fb_scaler::Root_component root(env, sliced_heap); +} \ No newline at end of file diff --git a/src/server/fb_upscale/target.mk b/src/server/fb_upscale/target.mk new file mode 100644 index 0000000..0d78f6f --- /dev/null +++ b/src/server/fb_upscale/target.mk @@ -0,0 +1,3 @@ +TARGET = fb_upscale +SRC_CC = component.cc +LIBS = base