genode/repos/gems/include/nano3d/scene.h
Norman Feske ef741ef80d Change pixel format to 32 bits per pixel
Until now, Genode's framebuffer session interface was based on the
RGB565 pixel format. This patch changes the pixel format to 32-bit
XRGB where the X part is ignored. It adapts all graphical applications
and device drivers accordingly.

The patch also adjusts the users of the drivers_interactive packages,
assigning 64 MiB RAM and 1500 caps to the drivers subsystem, which is
sufficient for covering high resolutions at 32 bits per pixel and to
accommodate multi-component USB HID input stacks.

Fixes #3784
2020-06-29 14:22:29 +02:00

309 lines
7.4 KiB
C++

/*
* \brief Simple framework for rendering an animated scene
* \author Norman Feske
* \date 2015-06-22
*
* The 'Scene' class template contains the code for setting up a GUI
* view with a triple-buffer for rendering tearing-free animations.
* A derrived class implements the to-be-displayed content in the virtual
* 'render' method.
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__NANO3D__SCENE_H_
#define _INCLUDE__NANO3D__SCENE_H_
/* Genode includes */
#include <base/entrypoint.h>
#include <timer_session/connection.h>
#include <gui_session/connection.h>
#include <os/surface.h>
#include <os/pixel_alpha8.h>
#include <base/attached_dataspace.h>
#include <input/event.h>
namespace Nano3d {
struct Input_handler;
template <typename> class Scene;
}
struct Nano3d::Input_handler : Genode::Interface
{
virtual void handle_input(Input::Event const [], unsigned num_events) = 0;
};
template <typename PT>
class Nano3d::Scene
{
public:
class Unsupported_color_depth { };
typedef Genode::Pixel_alpha8 Pixel_alpha8;
virtual void render(Genode::Surface<PT> &pixel_surface,
Genode::Surface<Pixel_alpha8> &alpha_surface) = 0;
private:
/**
* Noncopyable
*/
Scene(Scene const &);
Scene &operator = (Scene const &);
Genode::Env &_env;
/**
* Position and size of GUI view
*/
Gui::Point const _pos;
Gui::Area const _size;
Gui::Connection _gui { _env };
struct Mapped_framebuffer
{
enum { NUM_BUFFERS = 3 };
Genode::Region_map &rm;
static Framebuffer::Session &
_init_framebuffer(Gui::Connection &gui,
Gui::Area const size)
{
/*
* Dimension the virtual framebuffer 3 times as high as the
* visible view because it contains the visible buffer, the
* front buffer, and the back buffer.
*/
bool const use_alpha = true;
unsigned const height = size.h()*NUM_BUFFERS;
gui.buffer(Framebuffer::Mode { .area = { size.w(), height } },
use_alpha);
return *gui.framebuffer();
}
Framebuffer::Session &framebuffer;
Framebuffer::Mode const mode = framebuffer.mode();
/**
* Return visible size
*/
Gui::Area size() const
{
return Gui::Area(mode.area.w(), mode.area.h()/NUM_BUFFERS);
}
Genode::Attached_dataspace ds { rm, framebuffer.dataspace() };
PT *pixel_base(unsigned i)
{
return (PT *)(ds.local_addr<PT>() + i*size().count());
}
Pixel_alpha8 *alpha_base(unsigned i)
{
Pixel_alpha8 * const alpha_base =
(Pixel_alpha8 *)(ds.local_addr<PT>() + NUM_BUFFERS*size().count());
return alpha_base + i*size().count();
}
/**
* Set or clear the input mask for the virtual framebuffer
*/
void input_mask(bool input_enabled)
{
/*
* The input-mask buffer follows the alpha buffer. Hence, we
* can obtain the base address by requesting the base of
* the (non-exiting) alpha buffer (using NUM_BUFFERS as index)
* beyond the actual alpha buffers.
*/
Genode::memset(alpha_base(NUM_BUFFERS), input_enabled,
NUM_BUFFERS*size().count());
}
Mapped_framebuffer(Gui::Connection &gui, Gui::Area size,
Genode::Region_map &rm)
:
rm(rm), framebuffer(_init_framebuffer(gui, size))
{ }
} _framebuffer { _gui, _size, _env.rm() };
Gui::Session::View_handle _view_handle = _gui.create_view();
typedef Genode::Surface<PT> Pixel_surface;
typedef Genode::Surface<Genode::Pixel_alpha8> Alpha_surface;
struct Surface
{
Pixel_surface pixel;
Alpha_surface alpha;
Surface(PT *pixel_base, Genode::Pixel_alpha8 *alpha_base,
Genode::Surface_base::Area size)
:
pixel(pixel_base, size), alpha(alpha_base, size)
{ }
Genode::Surface_base::Area size() const { return pixel.size(); }
template <typename T>
void _clear(Genode::Surface<T> &surface)
{
Genode::size_t n = (surface.size().count()*sizeof(T))/sizeof(long);
for (long *dst = (long *)surface.addr(); n--; dst++)
*dst = 0;
}
void clear()
{
_clear(pixel);
_clear(alpha);
}
};
Surface _surface_0 { _framebuffer.pixel_base(0), _framebuffer.alpha_base(0),
_framebuffer.size() };
Surface _surface_1 { _framebuffer.pixel_base(1), _framebuffer.alpha_base(1),
_framebuffer.size() };
Surface _surface_2 { _framebuffer.pixel_base(2), _framebuffer.alpha_base(2),
_framebuffer.size() };
Surface *_surface_visible = &_surface_0;
Surface *_surface_front = &_surface_1;
Surface *_surface_back = &_surface_2;
bool _do_sync = false;
Timer::Connection _timer { _env };
Genode::Attached_dataspace _input_ds { _env.rm(), _gui.input()->dataspace() };
Input_handler *_input_handler_callback = nullptr;
void _handle_input()
{
if (!_input_handler_callback)
return;
while (int num = _gui.input()->flush()) {
auto const *ev_buf = _input_ds.local_addr<Input::Event>();
if (_input_handler_callback)
_input_handler_callback->handle_input(ev_buf, num);
}
}
Genode::Signal_handler<Scene> _input_handler {
_env.ep(), *this, &Scene::_handle_input };
void _swap_back_and_front_surfaces()
{
Surface *tmp = _surface_back;
_surface_back = _surface_front;
_surface_front = tmp;
}
void _swap_visible_and_front_surfaces()
{
Surface *tmp = _surface_visible;
_surface_visible = _surface_front;
_surface_front = tmp;
}
void _handle_period()
{
if (_do_sync)
return;
_surface_back->clear();
render(_surface_back->pixel, _surface_back->alpha);
_swap_back_and_front_surfaces();
/* swap front and back buffers on next sync */
_do_sync = true;
}
Genode::Signal_handler<Scene> _periodic_handler {
_env.ep(), *this, &Scene::_handle_period };
void _handle_sync()
{
/* rendering of scene is not complete, yet */
if (!_do_sync)
return;
_swap_visible_and_front_surfaces();
_swap_back_and_front_surfaces();
int const h = _framebuffer.size().h();
int const buf_y = (_surface_visible == &_surface_0) ? 0
: (_surface_visible == &_surface_1) ? -h
: -2*h;
Gui::Point const offset(0, buf_y);
_gui.enqueue<Command::Offset>(_view_handle, offset);
_gui.execute();
_do_sync = false;
}
Genode::Signal_handler<Scene> _sync_handler {
_env.ep(), *this, &Scene::_handle_sync };
typedef Gui::Session::Command Command;
public:
Scene(Genode::Env &env, Genode::uint64_t update_rate_ms,
Gui::Point pos, Gui::Area size)
:
_env(env), _pos(pos), _size(size)
{
typedef Gui::Session::View_handle View_handle;
Gui::Rect rect(_pos, _size);
_gui.enqueue<Command::Geometry>(_view_handle, rect);
_gui.enqueue<Command::To_front>(_view_handle, View_handle());
_gui.execute();
_gui.input()->sigh(_input_handler);
_timer.sigh(_periodic_handler);
_timer.trigger_periodic(1000*update_rate_ms);
_framebuffer.framebuffer.sync_sigh(_sync_handler);
}
virtual ~Scene() { }
Genode::uint64_t elapsed_ms() const { return _timer.elapsed_ms(); }
void input_handler(Input_handler *input_handler)
{
_framebuffer.input_mask(input_handler ? true : false);
_input_handler_callback = input_handler;
}
};
#endif /* _INCLUDE__NANO3D__SCENE_H_ */