/* * \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. */ /* * 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 #include #include #include #include #include #include namespace Nano3d { struct Input_handler; template class Scene; } struct Nano3d::Input_handler { virtual void handle_input(Input::Event const [], unsigned num_events) = 0; }; template class Nano3d::Scene { public: class Unsupported_color_depth { }; typedef Genode::Pixel_alpha8 Pixel_alpha8; virtual void render(Genode::Surface &pixel_surface, Genode::Surface &alpha_surface) = 0; private: Genode::Env &_env; /** * Position and size of nitpicker view */ Nitpicker::Point const _pos; Nitpicker::Area const _size; Nitpicker::Connection _nitpicker { _env }; struct Mapped_framebuffer { enum { NUM_BUFFERS = 3 }; Genode::Region_map &rm; 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) { Genode::error("framebuffer mode ", (int)format, " is not supported"); 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); } Genode::Attached_dataspace ds { rm, framebuffer.dataspace() }; PT *pixel_base(unsigned i) { return (PT *)(ds.local_addr() + i*size().count()); } Pixel_alpha8 *alpha_base(unsigned i) { Pixel_alpha8 * const alpha_base = (Pixel_alpha8 *)(ds.local_addr() + 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(Nitpicker::Connection &nitpicker, Nitpicker::Area size, Genode::Region_map &rm) : rm(rm), framebuffer(_init_framebuffer(nitpicker, size)) { } } _framebuffer { _nitpicker, _size, _env.rm() }; Nitpicker::Session::View_handle _view_handle = _nitpicker.create_view(); typedef Genode::Surface Pixel_surface; typedef Genode::Surface 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 void _clear(Genode::Surface &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(), _nitpicker.input()->dataspace() }; Input_handler *_input_handler_callback = nullptr; void _handle_input() { if (!_input_handler_callback) return; while (int num = _nitpicker.input()->flush()) { auto const *ev_buf = _input_ds.local_addr(); if (_input_handler_callback) _input_handler_callback->handle_input(ev_buf, num); } } Genode::Signal_handler _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 _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; Nitpicker::Point const offset(0, buf_y); _nitpicker.enqueue(_view_handle, offset); _nitpicker.execute(); _do_sync = false; } Genode::Signal_handler _sync_handler { _env.ep(), *this, &Scene::_handle_sync }; typedef Nitpicker::Session::Command Command; public: Scene(Genode::Env &env, unsigned update_rate_ms, Nitpicker::Point pos, Nitpicker::Area size) : _env(env), _pos(pos), _size(size) { Nitpicker::Rect rect(_pos, _size); _nitpicker.enqueue(_view_handle, rect); _nitpicker.enqueue(_view_handle); _nitpicker.execute(); _nitpicker.input()->sigh(_input_handler); _timer.sigh(_periodic_handler); _timer.trigger_periodic(1000*update_rate_ms); _framebuffer.framebuffer.sync_sigh(_sync_handler); } unsigned long 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_ */