2015-06-30 09:47:59 +00:00
|
|
|
/*
|
|
|
|
* \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 nitpicker
|
|
|
|
* view with a triple-buffer for rendering tearing-free animations.
|
|
|
|
* A derrived class implements the to-be-displayed content in the virtual
|
|
|
|
* 'render' method.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 12:23:52 +00:00
|
|
|
* Copyright (C) 2015-2017 Genode Labs GmbH
|
2015-06-30 09:47:59 +00:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 12:23:52 +00:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2015-06-30 09:47:59 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _INCLUDE__NANO3D__SCENE_H_
|
|
|
|
#define _INCLUDE__NANO3D__SCENE_H_
|
|
|
|
|
|
|
|
/* Genode includes */
|
2016-11-25 15:54:49 +00:00
|
|
|
#include <base/entrypoint.h>
|
2015-06-30 09:47:59 +00:00
|
|
|
#include <timer_session/connection.h>
|
|
|
|
#include <nitpicker_session/connection.h>
|
|
|
|
#include <os/surface.h>
|
|
|
|
#include <os/pixel_alpha8.h>
|
2017-01-30 10:35:12 +00:00
|
|
|
#include <base/attached_dataspace.h>
|
2015-06-30 09:47:59 +00:00
|
|
|
#include <input/event.h>
|
|
|
|
|
|
|
|
namespace Nano3d {
|
|
|
|
|
|
|
|
struct Input_handler;
|
|
|
|
template <typename> class Scene;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Nano3d::Input_handler
|
|
|
|
{
|
|
|
|
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:
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Genode::Env &_env;
|
2015-06-30 09:47:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Position and size of nitpicker view
|
|
|
|
*/
|
|
|
|
Nitpicker::Point const _pos;
|
|
|
|
Nitpicker::Area const _size;
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Nitpicker::Connection _nitpicker { _env };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
|
|
|
struct Mapped_framebuffer
|
|
|
|
{
|
|
|
|
enum { NUM_BUFFERS = 3 };
|
|
|
|
|
2017-02-24 16:19:21 +00:00
|
|
|
Genode::Region_map &rm;
|
|
|
|
|
2015-06-30 09:47:59 +00:00
|
|
|
static Framebuffer::Session &
|
|
|
|
_init_framebuffer(Nitpicker::Connection &nitpicker,
|
|
|
|
Nitpicker::Area const size)
|
|
|
|
{
|
|
|
|
Framebuffer::Mode::Format const format = nitpicker.mode().format();
|
|
|
|
if (format != Framebuffer::Mode::RGB565) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("framebuffer mode ", (int)format, " is not supported");
|
2015-06-30 09:47:59 +00:00
|
|
|
throw Unsupported_color_depth();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
nitpicker.buffer(Framebuffer::Mode(size.w(), height, format),
|
|
|
|
use_alpha);
|
|
|
|
|
|
|
|
return *nitpicker.framebuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
Framebuffer::Session &framebuffer;
|
|
|
|
|
|
|
|
Framebuffer::Mode const mode = framebuffer.mode();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return visible size
|
|
|
|
*/
|
|
|
|
Nitpicker::Area size() const
|
|
|
|
{
|
|
|
|
return Nitpicker::Area(mode.width(), mode.height()/NUM_BUFFERS);
|
|
|
|
}
|
|
|
|
|
2017-02-24 16:19:21 +00:00
|
|
|
Genode::Attached_dataspace ds { rm, framebuffer.dataspace() };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2017-02-24 16:19:21 +00:00
|
|
|
Mapped_framebuffer(Nitpicker::Connection &nitpicker, Nitpicker::Area size,
|
|
|
|
Genode::Region_map &rm)
|
2015-06-30 09:47:59 +00:00
|
|
|
:
|
2017-02-24 16:19:21 +00:00
|
|
|
rm(rm), framebuffer(_init_framebuffer(nitpicker, size))
|
2015-06-30 09:47:59 +00:00
|
|
|
{ }
|
|
|
|
|
2017-02-24 16:19:21 +00:00
|
|
|
} _framebuffer { _nitpicker, _size, _env.rm() };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
|
|
|
Nitpicker::Session::View_handle _view_handle = _nitpicker.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;
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Timer::Connection _timer { _env };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
2017-02-24 16:19:21 +00:00
|
|
|
Genode::Attached_dataspace _input_ds { _env.rm(), _nitpicker.input()->dataspace() };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Input_handler *_input_handler_callback = nullptr;
|
2015-06-30 09:47:59 +00:00
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
void _handle_input()
|
2015-06-30 09:47:59 +00:00
|
|
|
{
|
2016-11-25 15:54:49 +00:00
|
|
|
if (!_input_handler_callback)
|
2015-06-30 09:47:59 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
while (int num = _nitpicker.input()->flush()) {
|
|
|
|
|
|
|
|
auto const *ev_buf = _input_ds.local_addr<Input::Event>();
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
if (_input_handler_callback)
|
|
|
|
_input_handler_callback->handle_input(ev_buf, num);
|
2015-06-30 09:47:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Genode::Signal_handler<Scene> _input_handler {
|
|
|
|
_env.ep(), *this, &Scene::_handle_input };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
void _handle_period()
|
2015-06-30 09:47:59 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Genode::Signal_handler<Scene> _periodic_handler {
|
|
|
|
_env.ep(), *this, &Scene::_handle_period };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
void _handle_sync()
|
2015-06-30 09:47:59 +00:00
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
Nitpicker::Point const offset(0, buf_y);
|
|
|
|
_nitpicker.enqueue<Command::Offset>(_view_handle, offset);
|
|
|
|
_nitpicker.execute();
|
|
|
|
|
|
|
|
_do_sync = false;
|
|
|
|
}
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Genode::Signal_handler<Scene> _sync_handler {
|
|
|
|
_env.ep(), *this, &Scene::_handle_sync };
|
2015-06-30 09:47:59 +00:00
|
|
|
|
|
|
|
typedef Nitpicker::Session::Command Command;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
Scene(Genode::Env &env, unsigned update_rate_ms,
|
2015-06-30 09:47:59 +00:00
|
|
|
Nitpicker::Point pos, Nitpicker::Area size)
|
|
|
|
:
|
2016-11-25 15:54:49 +00:00
|
|
|
_env(env), _pos(pos), _size(size)
|
2015-06-30 09:47:59 +00:00
|
|
|
{
|
|
|
|
Nitpicker::Rect rect(_pos, _size);
|
|
|
|
_nitpicker.enqueue<Command::Geometry>(_view_handle, rect);
|
|
|
|
_nitpicker.enqueue<Command::To_front>(_view_handle);
|
|
|
|
_nitpicker.execute();
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
_nitpicker.input()->sigh(_input_handler);
|
2015-06-30 09:47:59 +00:00
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
_timer.sigh(_periodic_handler);
|
2015-06-30 09:47:59 +00:00
|
|
|
_timer.trigger_periodic(1000*update_rate_ms);
|
|
|
|
|
2016-11-25 15:54:49 +00:00
|
|
|
_framebuffer.framebuffer.sync_sigh(_sync_handler);
|
2015-06-30 09:47:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long elapsed_ms() const { return _timer.elapsed_ms(); }
|
|
|
|
|
|
|
|
void input_handler(Input_handler *input_handler)
|
|
|
|
{
|
|
|
|
_framebuffer.input_mask(input_handler ? true : false);
|
2016-11-25 15:54:49 +00:00
|
|
|
_input_handler_callback = input_handler;
|
2015-06-30 09:47:59 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* _INCLUDE__NANO3D__SCENE_H_ */
|