sync documentation tree with current master
|
|
@ -254,8 +254,10 @@ def configurePlatform(env):
|
|||
problems.append('We need boost::format (header).')
|
||||
if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'):
|
||||
problems.append('We need boost::program_options (including binary lib for linking).')
|
||||
if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'):
|
||||
problems.append('We need the boost::system support library (including binary lib).')
|
||||
if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'):
|
||||
problems.append('We need the boost::filesystem (including binary lib for linking).')
|
||||
problems.append('We need the boost::filesystem lib (including binary lib for linking).')
|
||||
if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'):
|
||||
problems.append('We need the boost regular expression lib (incl. binary lib for linking).')
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
BIN
doc/devel/uml/fig143877.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
doc/devel/uml/fig144005.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/devel/uml/fig145157.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
Trafo - transforming processing Node
|
||||
SchedulerFrontend - access point to the scheduler within the renderengine
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
|
|
@ -21,13 +21,14 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
#include "proc/engine/trafo.hpp"
|
||||
|
||||
namespace engine
|
||||
{
|
||||
#include "backend/engine/scheduler-frontend.hpp"
|
||||
|
||||
namespace backend{
|
||||
namespace engine {
|
||||
|
||||
|
||||
/** */
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
|
||||
|
||||
|
||||
}} // namespace backend::engine
|
||||
55
src/backend/engine/scheduler-frontend.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
SCHEDULER-FRONTEND.hpp - access point to the scheduler within the renderengine
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BACKEND_ENGINE_SCHEDULER_FRONTEND_H
|
||||
#define BACKEND_ENGINE_SCHEDULER_FRONTEND_H
|
||||
|
||||
|
||||
|
||||
//using std::list;
|
||||
|
||||
|
||||
namespace backend{
|
||||
namespace engine {
|
||||
|
||||
|
||||
/**
|
||||
* @todo this is planned to become the frontend
|
||||
* to the render node network, which can be considered
|
||||
* at the lower end of the middle layer; the actual
|
||||
* render operations are mostly implemented by the backend
|
||||
* ////////TODO WIP as of 12/2010
|
||||
*/
|
||||
class SchedulerFrontend
|
||||
{
|
||||
public:
|
||||
///// TODO: find out about the public operations
|
||||
// note: the play controller lives in the proc-layer,
|
||||
// but is a subsystem separate of the sesison.
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}} // namespace backend::engine
|
||||
#endif
|
||||
|
|
@ -200,7 +200,7 @@ namespace backend {
|
|||
|
||||
|
||||
/** Synchronisation barrier. In the function executing in this thread
|
||||
* needs to be a corresponding Thread::sync() call. Blocking until
|
||||
* needs to be a corresponding Thread::syncPoint() call. Blocking until
|
||||
* both the caller and the thread have reached the barrier.
|
||||
*/
|
||||
void
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ namespace gui {
|
|||
if (facade)
|
||||
{
|
||||
WARN (guifacade, "GUI subsystem terminates, but GuiFacade isn't properly closed. "
|
||||
"Closing it forcedly; this indicates broken startup logic and should be fixed.");
|
||||
"Closing it forcedly; this indicates broken startup logic and should be fixed.");
|
||||
try { facade.reset (0); }
|
||||
catch(...) { WARN_IF (lumiera_error_peek(), guifacade, "Ignoring error: %s", lumiera_error()); }
|
||||
lumiera_error(); // clear any remaining error state...
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ namespace lumiera {
|
|||
|
||||
public:
|
||||
/** Set up an InstanceHandle representing a plugin.
|
||||
* Should be placed at the client side.
|
||||
* Should be placed at the client side.
|
||||
* @param iName unmangled name of the interface
|
||||
* @param version major version
|
||||
* @param minminor minimum acceptable minor version number
|
||||
|
|
@ -203,7 +203,7 @@ namespace lumiera {
|
|||
* registration and deregistration of interface(s).
|
||||
* Should be placed at the service providing side.
|
||||
* @param a (single) interface descriptor, which can be created with
|
||||
* LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF
|
||||
* LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF
|
||||
*/
|
||||
InstanceHandle (LumieraInterface descriptor)
|
||||
: desc_(descriptor)
|
||||
|
|
@ -222,9 +222,9 @@ namespace lumiera {
|
|||
|
||||
|
||||
|
||||
/** act as smart pointer providing access through the facade.
|
||||
/** act as smart pointer providing access through the facade.
|
||||
* @note we don't provide operator* */
|
||||
FA * operator-> () const { return &(facadeLink_(*this)); }
|
||||
FA * operator-> () const { return &(facadeLink_(*this)); }
|
||||
|
||||
/** directly access the instance via the CL interface */
|
||||
I& get () const { ENSURE(instance_); return *instance_; }
|
||||
|
|
|
|||
123
src/common/interface-facade-link.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
INTERFACE-FACADE-LINK - a switchable link from interface to service implementation
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file interface-facade-link.hpp
|
||||
** Opening, accessing and closing the service access through a facade interface.
|
||||
** Client code is assumed to access an application level service through an facade
|
||||
** interface, while the actual implementation object remains an opaque internal detail.
|
||||
** Moreover, services may come up and shut down, so the implementation might change
|
||||
** during the Lifecycle. The facility defined here in this header provides a basic
|
||||
** implementation for this access mechanism, but without any adaptation, binding
|
||||
** or plugin access layer. It works only under the assumption that both the
|
||||
** interface and the actual service implementation coexist in the same
|
||||
** executable and are written in C++, so any invocation of an
|
||||
** interface method boils down to a language-level call.
|
||||
**
|
||||
** Usually, client code doesn't need to include this header. Clients are assumed
|
||||
** to use the facade interface of the service in question. This facade interface
|
||||
** contains a static member of type \c lumiera::facade::Accessor<I> (where I is
|
||||
** the type of the facade interface). The Accessor baseclass is defined in
|
||||
** interfaceproxy.hpp and typically included through the facade header.
|
||||
**
|
||||
** @note there is a way more elaborate implementation of the same mechanism
|
||||
** for use with the Lumiera Interface/Plugin system.
|
||||
**
|
||||
** @see interfaceproxy.hpp description of the more general use case
|
||||
** @see PlayService example for the simple use case
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
|
||||
#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "include/interfaceproxy.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
|
||||
namespace lumiera {
|
||||
namespace facade {
|
||||
|
||||
using lib::Literal;
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* simple access-frontend to the implementation of a service (C++ only).
|
||||
* Usually, an instance of Accessor is placed as static member right into
|
||||
* the facade interface used to access the service. This implementation
|
||||
* of the access mechanism handles the simple case that both the facade
|
||||
* and the service implementation are written in C++ and calls happen
|
||||
* within the main executable as direct language calls, without an
|
||||
* binding layer and without involving the Interface/Plugin system.
|
||||
*
|
||||
* Typically, the InterfaceFacadeLink becomes a member of the service
|
||||
* implementation class and is directly tied into the constructor of
|
||||
* the latter. Being a subclass of lumiera::facade::Accessor, it is
|
||||
* allowed to "open" the facade access just by setting the static
|
||||
* protected pointer Accessor::implProxy_
|
||||
*/
|
||||
template<class FA>
|
||||
class InterfaceFacadeLink
|
||||
: protected Accessor<FA>
|
||||
, boost::noncopyable
|
||||
{
|
||||
Literal displayName_;
|
||||
|
||||
void
|
||||
__checkLifecycle ()
|
||||
{
|
||||
if (Accessor<FA>::implProxy_)
|
||||
throw error::State("Attempt to open an already opened Facade interface."
|
||||
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||
}
|
||||
|
||||
public:
|
||||
InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0)
|
||||
: displayName_(lib::test::showType<FA>(interfaceName_for_Log))
|
||||
{
|
||||
__checkLifecycle();
|
||||
Accessor<FA>::implProxy_ = &serviceImpl;
|
||||
INFO (interface, "interface %s opened", displayName_.c());
|
||||
}
|
||||
|
||||
~InterfaceFacadeLink()
|
||||
{
|
||||
INFO (interface, "closing interface %s...", displayName_.c());
|
||||
Accessor<FA>::implProxy_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** storage for the static access pointer */
|
||||
template<class FA>
|
||||
FA* Accessor<FA>::implProxy_;
|
||||
|
||||
|
||||
}} // namespace lumiera::facade
|
||||
|
||||
#endif
|
||||
|
|
@ -28,73 +28,87 @@
|
|||
|
||||
using util::cStr;
|
||||
|
||||
namespace lumiera {
|
||||
namespace facade {
|
||||
|
||||
|
||||
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
|
||||
|
||||
|
||||
template<class IHA>
|
||||
class Holder;
|
||||
|
||||
template<class FA, class I>
|
||||
class Holder<InstanceHandle<I,FA> >
|
||||
: Accessor<FA>,
|
||||
protected FA
|
||||
{
|
||||
protected:
|
||||
typedef InstanceHandle<I,FA> IHandle;
|
||||
typedef Holder<IHandle> THolder;
|
||||
typedef Proxy<IHandle> TProxy;
|
||||
typedef Accessor<FA> Access;
|
||||
|
||||
I& _i_;
|
||||
|
||||
Holder (IHandle const& iha)
|
||||
: _i_(iha.get())
|
||||
{ }
|
||||
|
||||
public:
|
||||
static TProxy& open(IHandle const& iha)
|
||||
{
|
||||
static char buff[sizeof(TProxy)];
|
||||
TProxy* p = new(buff) TProxy(iha);
|
||||
Access::implProxy_ = p;
|
||||
return *p;
|
||||
}
|
||||
|
||||
static void close()
|
||||
{
|
||||
if (!Access::implProxy_) return;
|
||||
TProxy* p = static_cast<TProxy*> (Access::implProxy_);
|
||||
Access::implProxy_ = 0;
|
||||
p->~TProxy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class FA>
|
||||
FA* Accessor<FA>::implProxy_;
|
||||
|
||||
|
||||
template<class IHA>
|
||||
void
|
||||
openProxy (IHA const& iha)
|
||||
{
|
||||
Proxy<IHA>::open(iha);
|
||||
}
|
||||
|
||||
template<class IHA>
|
||||
void
|
||||
closeProxy ()
|
||||
{
|
||||
Proxy<IHA>::close();
|
||||
}
|
||||
|
||||
} // namespace facade
|
||||
namespace lumiera{
|
||||
namespace facade {
|
||||
|
||||
} // namespace lumiera
|
||||
|
||||
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade is closed; service currently not accessible");
|
||||
|
||||
|
||||
/**
|
||||
* Implementation Base
|
||||
* for building Facade Proxy implementations.
|
||||
* Typically the purpose of such a proxy is to route
|
||||
* any calls through the C-Bindings of the Lumiera Interface system.
|
||||
* The actual storage for the concrete proxy object is embedded,
|
||||
* inline within the #open() function. For access by the clients,
|
||||
* a frontend-object of type \c Accessor<FA> may be placed into
|
||||
* the facade interface; this accessor-frontend is basically
|
||||
* a concealed static pointer to the proxy, and will be set,
|
||||
* when the interface is opened. This opening and closing
|
||||
* of the interface itself is controlled by the
|
||||
* InstanceHandle, which in turn is typically
|
||||
* created and managed within the context
|
||||
* of the service implementation.
|
||||
*/
|
||||
template<class IHA>
|
||||
class Holder;
|
||||
|
||||
template<class FA, class I>
|
||||
class Holder<InstanceHandle<I,FA> >
|
||||
: Accessor<FA>
|
||||
, protected FA
|
||||
{
|
||||
protected:
|
||||
typedef InstanceHandle<I,FA> IHandle;
|
||||
typedef Holder<IHandle> THolder;
|
||||
typedef Proxy<IHandle> TProxy;
|
||||
typedef Accessor<FA> Access;
|
||||
|
||||
I& _i_;
|
||||
|
||||
Holder (IHandle const& iha)
|
||||
: _i_(iha.get())
|
||||
{ }
|
||||
|
||||
public:
|
||||
static TProxy& open(IHandle const& iha)
|
||||
{
|
||||
static char buff[sizeof(TProxy)];
|
||||
TProxy* p = new(buff) TProxy(iha);
|
||||
Access::implProxy_ = p;
|
||||
return *p;
|
||||
}
|
||||
|
||||
static void close()
|
||||
{
|
||||
if (!Access::implProxy_) return;
|
||||
TProxy* p = static_cast<TProxy*> (Access::implProxy_);
|
||||
Access::implProxy_ = 0;
|
||||
p->~TProxy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class FA>
|
||||
FA* Accessor<FA>::implProxy_;
|
||||
|
||||
|
||||
template<class IHA>
|
||||
void
|
||||
openProxy (IHA const& iha)
|
||||
{
|
||||
Proxy<IHA>::open(iha);
|
||||
}
|
||||
|
||||
template<class IHA>
|
||||
void
|
||||
closeProxy ()
|
||||
{
|
||||
Proxy<IHA>::close();
|
||||
}
|
||||
|
||||
}} // namespace lumiera::facade
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -104,9 +104,12 @@ namespace lumiera {
|
|||
|
||||
|
||||
/** initiate termination of this subsystem.
|
||||
* may be called repeatedly any time...
|
||||
* @warning must not block nor throw. */
|
||||
virtual void triggerShutdown () throw() =0;
|
||||
* This trigger may be called repeatedly any time...
|
||||
* When the subsystem actually has terminated,
|
||||
* the SigTerm passed to #start must be invoked.
|
||||
* @note called within a locked context (barrier)
|
||||
* @warning must not block nor throw. */
|
||||
virtual void triggerShutdown () throw() =0;
|
||||
|
||||
|
||||
const std::vector<Subsys*>
|
||||
|
|
@ -114,7 +117,7 @@ namespace lumiera {
|
|||
|
||||
|
||||
private:
|
||||
/** weather this subsystem is actually operational.
|
||||
/** whether this subsystem is actually operational.
|
||||
* When returning \c false here, the application may
|
||||
* terminate at any point without further notice
|
||||
* Note further, that a subsystem must not be in
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ namespace lumiera {
|
|||
if (!and_all (susy->getPrerequisites(), isRunning() ))
|
||||
{
|
||||
susy->triggerShutdown();
|
||||
throw error::Logic("Unable to start all prerequisites of Subsystem "+string(*susy));
|
||||
throw error::State("Unable to start all prerequisites of Subsystem "+string(*susy));
|
||||
} }
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include "common/instancehandle.hpp"
|
||||
#include "lib/singleton-ref.hpp"
|
||||
#include "lib/scoped-ptrvect.hpp"
|
||||
#include "include/logging.h"
|
||||
|
||||
#include <glibmm.h>
|
||||
#include <sigc++/sigc++.h>
|
||||
|
|
|
|||
|
|
@ -23,10 +23,12 @@
|
|||
|
||||
#include "gui/gtk-lumiera.hpp"
|
||||
#include "gui/panels/timeline-panel.hpp"
|
||||
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||
|
||||
#include "gui/workspace/workspace-window.hpp"
|
||||
#include "gui/model/project.hpp"
|
||||
#include "gui/controller/controller.hpp"
|
||||
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
|
@ -34,6 +36,7 @@
|
|||
using namespace Gtk;
|
||||
using namespace sigc;
|
||||
using namespace gui::widgets;
|
||||
using namespace gui::widgets::timeline;
|
||||
using namespace gui::model;
|
||||
|
||||
using boost::shared_ptr; ///////////////////////////////TICKET #796
|
||||
|
|
@ -60,6 +63,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
|||
, iBeamTool(Gtk::StockID("tool_i_beam"))
|
||||
, zoomIn(Stock::ZOOM_IN)
|
||||
, zoomOut(Stock::ZOOM_OUT)
|
||||
, zoomScale()
|
||||
, updatingToolbar(false)
|
||||
, currentTool(timeline::Arrow)
|
||||
{
|
||||
|
|
@ -101,14 +105,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
|||
|
||||
toolbar.append(separator2);
|
||||
|
||||
toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in));
|
||||
toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out));
|
||||
|
||||
toolbar.append(zoomScale);
|
||||
zoomScale.signal_zoom().
|
||||
connect(mem_fun(this,&TimelinePanel::on_zoom));
|
||||
|
||||
toolbar.show_all();
|
||||
panelBar.pack_start(toolbar, PACK_SHRINK);
|
||||
|
||||
// Setup tooltips
|
||||
sequenceChooser .set_tooltip_text(_("Change sequence"));
|
||||
sequenceChooser .set_tooltip_text(_("Change sequence"));
|
||||
|
||||
previousButton .set_tooltip_text(_("To beginning"));
|
||||
rewindButton .set_tooltip_text(_("Rewind"));
|
||||
|
|
@ -122,12 +127,18 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
|||
|
||||
zoomIn .set_tooltip_text(_("Zoom in"));
|
||||
zoomOut .set_tooltip_text(_("Zoom out"));
|
||||
zoomScale .set_tooltip_text(_("Adjust timeline zoom scale"));
|
||||
|
||||
// Setup the timeline widget
|
||||
shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796
|
||||
shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost
|
||||
= *get_project().get_sequences().begin();
|
||||
timelineWidget.reset(new TimelineWidget(load_state(sequence)));
|
||||
pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
|
||||
|
||||
// since TimelineWidget is now initialised,
|
||||
// wire the zoom slider to react on timeline state changes
|
||||
zoomScale.wireTimelineState (timelineWidget->get_state(),
|
||||
timelineWidget->state_changed_signal());
|
||||
|
||||
// Set the initial UI state
|
||||
update_sequence_chooser();
|
||||
|
|
@ -136,7 +147,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
|||
show_time (Time::ZERO);
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
TimelinePanel::get_title()
|
||||
{
|
||||
|
|
@ -183,6 +193,13 @@ TimelinePanel::on_ibeam_tool()
|
|||
set_tool(timeline::IBeam);
|
||||
}
|
||||
|
||||
void
|
||||
TimelinePanel::on_zoom(double time_scale_ratio)
|
||||
{
|
||||
REQUIRE(timelineWidget);
|
||||
timelineWidget->zoom_view(time_scale_ratio);
|
||||
}
|
||||
|
||||
void
|
||||
TimelinePanel::on_zoom_in()
|
||||
{
|
||||
|
|
@ -236,18 +253,19 @@ TimelinePanel::on_sequence_chosen()
|
|||
{
|
||||
weak_ptr<Sequence> sequence_ptr =
|
||||
(*iter)[sequenceChooserColumns.sequenceColumn];
|
||||
|
||||
shared_ptr<Sequence> sequence(sequence_ptr.lock());
|
||||
|
||||
if(sequence)
|
||||
{
|
||||
shared_ptr<timeline::TimelineState> old_state(
|
||||
timelineWidget->get_state());
|
||||
REQUIRE(old_state);
|
||||
|
||||
if(sequence != old_state->get_sequence())
|
||||
timelineWidget->set_state(load_state(sequence));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_zoom_buttons();
|
||||
}
|
||||
|
||||
|
|
@ -379,9 +397,10 @@ TimelinePanel::on_frame()
|
|||
shared_ptr<timeline::TimelineState>
|
||||
TimelinePanel::load_state(weak_ptr<Sequence> sequence)
|
||||
{
|
||||
/* state exists */
|
||||
if(contains(timelineStates, sequence))
|
||||
return timelineStates[sequence];
|
||||
|
||||
return timelineStates[sequence];
|
||||
|
||||
shared_ptr<Sequence> shared_sequence = sequence.lock();
|
||||
if(shared_sequence)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include "gui/panels/panel.hpp"
|
||||
#include "gui/widgets/timecode-widget.hpp"
|
||||
#include "gui/widgets/timeline-widget.hpp"
|
||||
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
|
@ -87,6 +89,7 @@ private:
|
|||
void on_arrow_tool();
|
||||
void on_ibeam_tool();
|
||||
|
||||
void on_zoom(double time_scale_ratio);
|
||||
void on_zoom_in();
|
||||
void on_zoom_out();
|
||||
|
||||
|
|
@ -171,7 +174,7 @@ private:
|
|||
boost::scoped_ptr<TimelineWidget> timelineWidget;
|
||||
|
||||
std::map< boost::weak_ptr<model::Sequence>,
|
||||
boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796
|
||||
boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
timelineStates;
|
||||
|
||||
// Toolbar Widgets
|
||||
|
|
@ -191,6 +194,7 @@ private:
|
|||
|
||||
MiniButton zoomIn;
|
||||
MiniButton zoomOut;
|
||||
gui::widgets::timeline::TimelineZoomScale zoomScale;
|
||||
|
||||
Gtk::SeparatorToolItem separator2;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const int TimelineWidget::TrackPadding = 1;
|
|||
const int TimelineWidget::HeaderWidth = 150;
|
||||
const int TimelineWidget::HeaderIndentWidth = 10;
|
||||
const double TimelineWidget::ZoomIncrement = 1.25;
|
||||
const int64_t TimelineWidget::MaxScale = 30000000;
|
||||
const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million
|
||||
|
||||
TimelineWidget::TimelineWidget(
|
||||
boost::shared_ptr<timeline::TimelineState> source_state) :
|
||||
|
|
@ -102,6 +102,7 @@ TimelineWidget::get_state()
|
|||
void
|
||||
TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
|
||||
{
|
||||
|
||||
state = new_state;
|
||||
|
||||
// Clear the track tree
|
||||
|
|
@ -125,17 +126,17 @@ TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
|
|||
update_tracks();
|
||||
|
||||
// Send the state changed signal
|
||||
stateChangedSignal.emit();
|
||||
stateChangedSignal.emit (state);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineWidget::zoom_view(int zoom_size)
|
||||
TimelineWidget::zoom_view(double timescale_ratio)
|
||||
{
|
||||
if(state)
|
||||
{
|
||||
const int view_width = body->get_allocation().get_width();
|
||||
state->get_view_window().zoom_view(view_width / 2, zoom_size);
|
||||
}
|
||||
{
|
||||
const int view_width = body->get_allocation().get_width();
|
||||
state->get_view_window().zoom_view(view_width / 2, timescale_ratio);
|
||||
}
|
||||
}
|
||||
|
||||
ToolType
|
||||
|
|
@ -178,7 +179,7 @@ TimelineWidget::hovering_track_changed_signal() const
|
|||
return hoveringTrackChangedSignal;
|
||||
}
|
||||
|
||||
sigc::signal<void>
|
||||
TimelineWidget::TimelineStateChangeSignal
|
||||
TimelineWidget::state_changed_signal() const
|
||||
{
|
||||
return stateChangedSignal;
|
||||
|
|
@ -212,10 +213,12 @@ TimelineWidget::on_view_window_changed()
|
|||
if(state)
|
||||
{
|
||||
timeline::TimelineViewWindow &window = state->get_view_window();
|
||||
|
||||
const int view_width = body->get_allocation().get_width();
|
||||
|
||||
|
||||
horizontalAdjustment.set_page_size(
|
||||
window.get_time_scale() * view_width);
|
||||
|
||||
horizontalAdjustment.set_value(_raw(window.get_time_offset()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public:
|
|||
* @param zoom_size The number of steps to zoom by. The scale factor
|
||||
* is 1.25^(-zoom_size).
|
||||
*/
|
||||
void zoom_view(int zoom_size);
|
||||
void zoom_view(double timescale_ratio);
|
||||
|
||||
/**
|
||||
* Gets the type of the tool currently active.
|
||||
|
|
@ -115,14 +115,16 @@ public:
|
|||
|
||||
public:
|
||||
/* ===== Signals ===== */
|
||||
typedef sigc::signal<void, boost::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal;
|
||||
typedef sigc::signal<void, boost::shared_ptr<timeline::Track> > HoveringTrackChangedSignal;
|
||||
|
||||
sigc::signal<void, lib::time::Time> mouse_hover_signal() const;
|
||||
|
||||
sigc::signal<void> playback_period_drag_released_signal() const;
|
||||
|
||||
sigc::signal<void, boost::shared_ptr<timeline::Track> >
|
||||
hovering_track_changed_signal() const;
|
||||
HoveringTrackChangedSignal hovering_track_changed_signal() const;
|
||||
|
||||
sigc::signal<void> state_changed_signal() const;
|
||||
TimelineStateChangeSignal state_changed_signal() const;
|
||||
|
||||
/* ===== Events ===== */
|
||||
protected:
|
||||
|
|
@ -275,9 +277,8 @@ protected:
|
|||
// Signals
|
||||
sigc::signal<void, Time> mouseHoverSignal;
|
||||
sigc::signal<void> playbackPeriodDragReleasedSignal;
|
||||
sigc::signal<void, boost::shared_ptr<timeline::Track> >
|
||||
hoveringTrackChangedSignal;
|
||||
sigc::signal<void> stateChangedSignal;
|
||||
HoveringTrackChangedSignal hoveringTrackChangedSignal;
|
||||
TimelineStateChangeSignal stateChangedSignal;
|
||||
|
||||
bool update_tracks_frozen;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ using namespace lumiera;
|
|||
|
||||
using gui::util::CairoUtil;
|
||||
|
||||
using boost::shared_ptr; ////////////////////TICKET #796
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
|
@ -61,7 +63,7 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget)
|
|||
register_styles();
|
||||
|
||||
// Reset the state
|
||||
on_state_changed();
|
||||
propagateStateChange();
|
||||
}
|
||||
|
||||
TimelineBody::~TimelineBody()
|
||||
|
|
@ -69,6 +71,13 @@ TimelineBody::~TimelineBody()
|
|||
WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here");
|
||||
}
|
||||
|
||||
TimelineViewWindow&
|
||||
TimelineBody::viewWindow() const
|
||||
{
|
||||
REQUIRE(timelineState);
|
||||
return timelineState->get_view_window();
|
||||
}
|
||||
|
||||
TimelineWidget&
|
||||
TimelineBody::getTimelineWidget () const
|
||||
{
|
||||
|
|
@ -147,7 +156,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
|
|||
// Makes sure the widget styles have been loaded
|
||||
read_styles();
|
||||
|
||||
if(timelineWidget.get_state())
|
||||
if (timelineState)
|
||||
{
|
||||
// Prepare to render via cairo
|
||||
const Allocation allocation = get_allocation();
|
||||
|
|
@ -169,9 +178,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
|
|||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
if(timelineWidget.get_state())
|
||||
if (timelineState)
|
||||
{
|
||||
TimelineViewWindow &window = view_window();
|
||||
TimelineViewWindow &window = viewWindow();
|
||||
const Allocation allocation = get_allocation();
|
||||
|
||||
if(event->state & GDK_CONTROL_MASK)
|
||||
|
|
@ -255,17 +264,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
|
|||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
if(timelineWidget.get_state())
|
||||
if (timelineState)
|
||||
{
|
||||
// Handle a middle-mouse drag if one is occuring
|
||||
switch(dragType)
|
||||
{
|
||||
case Shift:
|
||||
{
|
||||
TimelineViewWindow &window = view_window();
|
||||
|
||||
/////////////////////////////TICKET# 795 : don't reach in from outside and manipulate internals of the timeline view!
|
||||
/////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
|
||||
///////////////////////////// : either encapsulate this entirely here, or leave it to the timeline view!
|
||||
TimelineViewWindow &window = viewWindow();
|
||||
const int64_t scale = window.get_time_scale();
|
||||
window.set_time_offset(beginShiftTimeOffset
|
||||
+ TimeValue(scale * (mouseDownX - event->x)));
|
||||
|
|
@ -294,12 +302,20 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
|
|||
}
|
||||
|
||||
void
|
||||
TimelineBody::on_state_changed()
|
||||
TimelineBody::on_state_changed (shared_ptr<TimelineState> newState)
|
||||
{
|
||||
if(timelineWidget.get_state())
|
||||
REQUIRE (newState);
|
||||
timelineState = newState;
|
||||
propagateStateChange();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineBody::propagateStateChange()
|
||||
{
|
||||
if (timelineState)
|
||||
{
|
||||
// Connect up some events
|
||||
view_window().changed_signal().connect(
|
||||
viewWindow().changed_signal().connect(
|
||||
sigc::mem_fun(this, &TimelineBody::on_update_view) );
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +389,7 @@ TimelineBody::draw_track(Cairo::RefPtr<Cairo::Context> cr,
|
|||
|
||||
// Render the track
|
||||
cr->save();
|
||||
TimelineViewWindow &window = view_window();
|
||||
TimelineViewWindow &window = viewWindow();
|
||||
timeline_track->draw_track(cr, &window);
|
||||
cr->restore();
|
||||
}
|
||||
|
|
@ -386,12 +402,11 @@ TimelineBody::draw_selection(Cairo::RefPtr<Cairo::Context> cr)
|
|||
// Prepare
|
||||
const Allocation allocation = get_allocation();
|
||||
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
REQUIRE(timelineState);
|
||||
|
||||
TimelineViewWindow const& window = state->get_view_window();
|
||||
const int start_x = window.time_to_x(state->getSelectionStart());
|
||||
const int end_x = window.time_to_x(state->getSelectionEnd());
|
||||
TimelineViewWindow const& window = timelineState->get_view_window();
|
||||
const int start_x = window.time_to_x(timelineState->getSelectionStart());
|
||||
const int end_x = window.time_to_x(timelineState->getSelectionEnd());
|
||||
|
||||
// Draw the cover
|
||||
if(end_x > 0 && start_x < allocation.get_width())
|
||||
|
|
@ -427,16 +442,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr)
|
|||
{
|
||||
REQUIRE(cr);
|
||||
|
||||
// Prepare
|
||||
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
if(state)
|
||||
if (timelineState)
|
||||
{
|
||||
if (!state->isPlaying()) return;
|
||||
if (!timelineState->isPlaying()) return;
|
||||
|
||||
const Allocation allocation = get_allocation();
|
||||
Time point = state->getPlaybackPoint();
|
||||
const int x = view_window().time_to_x(point);
|
||||
Time point = timelineState->getPlaybackPoint();
|
||||
const int x = viewWindow().time_to_x(point);
|
||||
|
||||
// Set source
|
||||
cr->set_source(playbackPointColour);
|
||||
|
|
@ -455,10 +467,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr)
|
|||
void
|
||||
TimelineBody::begin_shift_drag()
|
||||
{
|
||||
if(timelineWidget.get_state())
|
||||
if (timelineState)
|
||||
{
|
||||
dragType = Shift;
|
||||
beginShiftTimeOffset = view_window().get_time_offset();
|
||||
beginShiftTimeOffset = viewWindow().get_time_offset();
|
||||
beginShiftVerticalOffset = get_vertical_offset();
|
||||
}
|
||||
}
|
||||
|
|
@ -475,14 +487,6 @@ TimelineBody::set_vertical_offset(int offset)
|
|||
timelineWidget.verticalAdjustment.set_value(offset);
|
||||
}
|
||||
|
||||
TimelineViewWindow&
|
||||
TimelineBody::view_window() const
|
||||
{
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
return state->get_view_window();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineBody::register_styles() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,10 +68,7 @@ public:
|
|||
*/
|
||||
TimelineBody(gui::widgets::TimelineWidget &timeline_widget);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~TimelineBody();
|
||||
virtual ~TimelineBody();
|
||||
|
||||
TimelineWidget&
|
||||
getTimelineWidget () const;
|
||||
|
|
@ -125,13 +122,18 @@ protected:
|
|||
bool on_motion_notify_event(GdkEventMotion *event);
|
||||
|
||||
/**
|
||||
* The event handler for when the TimelineWidget's state object is
|
||||
* replaced.
|
||||
* The event handler for when the TimelineWidget's state is switched.
|
||||
*/
|
||||
void on_state_changed();
|
||||
void on_state_changed (boost::shared_ptr<TimelineState> newState);
|
||||
|
||||
/* ===== Internals ===== */
|
||||
private:
|
||||
/**
|
||||
* Access the current timeline view window
|
||||
* @warning must not be called unless the TimlineWidget
|
||||
* has a valid state.
|
||||
*/
|
||||
TimelineViewWindow& viewWindow() const;
|
||||
|
||||
/**
|
||||
* Draws the timeline tracks.
|
||||
|
|
@ -161,13 +163,9 @@ private:
|
|||
|
||||
void set_vertical_offset(int offset);
|
||||
|
||||
/**
|
||||
* A helper function to get the view window
|
||||
* @remarks This function must not be called unless the TimlineWidget
|
||||
* has a valid state.
|
||||
*/
|
||||
TimelineViewWindow& view_window() const;
|
||||
|
||||
/** adjust to the new timeline state */
|
||||
void propagateStateChange();
|
||||
|
||||
/**
|
||||
* Registers all the styles that this class will respond to.
|
||||
*/
|
||||
|
|
@ -202,6 +200,8 @@ private:
|
|||
Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour;
|
||||
|
||||
gui::widgets::TimelineWidget &timelineWidget;
|
||||
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
|
||||
|
||||
friend class Tool;
|
||||
friend class ArrowTool;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget)
|
|||
register_styles();
|
||||
}
|
||||
|
||||
TimelineViewWindow&
|
||||
TimelineRuler::viewWindow() const
|
||||
{
|
||||
REQUIRE(timelineState);
|
||||
return timelineState->get_view_window();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TimelineRuler::set_mouse_chevron_offset(int offset)
|
||||
{
|
||||
|
|
@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
|
|||
if(!window)
|
||||
return false;
|
||||
|
||||
if(timelineWidget.get_state())
|
||||
if (timelineState)
|
||||
{
|
||||
// Prepare to render via cairo
|
||||
const Allocation allocation = get_allocation();
|
||||
|
|
@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event)
|
|||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
if(timelineWidget.get_state())
|
||||
if (timelineState)
|
||||
{
|
||||
if(event->button == 1)
|
||||
{
|
||||
pinnedDragTime = view_window().x_to_time(event->x);
|
||||
pinnedDragTime = viewWindow().x_to_time(event->x);
|
||||
isDragging = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -219,34 +227,40 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
|
|||
}
|
||||
|
||||
void
|
||||
TimelineRuler::on_state_changed()
|
||||
TimelineRuler::on_state_changed (shared_ptr<TimelineState> newState)
|
||||
{
|
||||
if(timelineWidget.get_state())
|
||||
{
|
||||
// Connect up some events
|
||||
view_window().changed_signal().connect(
|
||||
sigc::mem_fun(this, &TimelineRuler::on_update_view) );
|
||||
}
|
||||
|
||||
REQUIRE (newState);
|
||||
timelineState = newState;
|
||||
|
||||
propagateStateChange();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::propagateStateChange()
|
||||
{
|
||||
// Connect up some events
|
||||
viewWindow().changed_signal().connect(
|
||||
sigc::mem_fun(this, &TimelineRuler::on_update_view) );
|
||||
|
||||
// Redraw
|
||||
on_update_view();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
TimelineRuler::set_leading_x(const int x)
|
||||
{
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
|
||||
if(state)
|
||||
if (timelineState)
|
||||
{
|
||||
TimeVar newStartPoint (view_window().x_to_time(x));
|
||||
TimeVar newStartPoint (viewWindow().x_to_time(x));
|
||||
Offset selectionLength (pinnedDragTime, newStartPoint);
|
||||
|
||||
if (newStartPoint > pinnedDragTime)
|
||||
newStartPoint=pinnedDragTime; // use the smaller one as selection start
|
||||
|
||||
state->setPlaybackPeriod (Mutation::changeTime(newStartPoint) );
|
||||
state->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
|
||||
timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint) );
|
||||
timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
|
||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
|
||||
////////////////////TODO : code duplication with timeline-ibeam-tool 205
|
||||
}
|
||||
|
|
@ -260,7 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr,
|
|||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
|
||||
const TimelineViewWindow &window = view_window();
|
||||
const TimelineViewWindow &window = viewWindow();
|
||||
const gavl_time_t left_offset = _raw(window.get_time_offset());
|
||||
const int64_t time_scale = window.get_time_scale();
|
||||
|
||||
|
|
@ -361,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr,
|
|||
REQUIRE(cr);
|
||||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
REQUIRE(timelineState);
|
||||
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
const TimelineViewWindow &window = state->get_view_window();
|
||||
const TimelineViewWindow &window = timelineState->get_view_window();
|
||||
|
||||
Glib::RefPtr<Style> style = get_style();
|
||||
Gdk::Cairo::set_source_color(cr, style->get_fg(STATE_NORMAL));
|
||||
|
||||
// Draw the selection start chevron
|
||||
const int a = 1 + window.time_to_x(state->getSelectionStart());
|
||||
const int a = 1 + window.time_to_x(timelineState->getSelectionStart());
|
||||
if(a >= 0 && a < ruler_rect.get_width())
|
||||
{
|
||||
cr->move_to(a, ruler_rect.get_height());
|
||||
|
|
@ -380,7 +393,7 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr,
|
|||
}
|
||||
|
||||
// Draw the selection end chevron
|
||||
const int b = window.time_to_x(state->getSelectionEnd());
|
||||
const int b = window.time_to_x(timelineState->getSelectionEnd());
|
||||
if(b >= 0 && b < ruler_rect.get_width())
|
||||
{
|
||||
cr->move_to(b, ruler_rect.get_height());
|
||||
|
|
@ -397,18 +410,17 @@ TimelineRuler::draw_playback_period(Cairo::RefPtr<Cairo::Context> cr,
|
|||
REQUIRE(cr);
|
||||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
REQUIRE(timelineState);
|
||||
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
const TimelineViewWindow &window = state->get_view_window();
|
||||
const TimelineViewWindow &window = timelineState->get_view_window();
|
||||
|
||||
// Calculate coordinates
|
||||
const float halfSize = playbackPeriodArrowSize / 2;
|
||||
|
||||
const float a = 1.5f + window.time_to_x(state->getPlaybackPeriodStart());
|
||||
const float a = 1.5f + window.time_to_x(timelineState->getPlaybackPeriodStart());
|
||||
|
||||
const float b = a + halfSize;
|
||||
const float d = 0.5f + window.time_to_x(state->getPlaybackPeriodEnd());
|
||||
const float d = 0.5f + window.time_to_x(timelineState->getPlaybackPeriodEnd());
|
||||
|
||||
const float c = d - halfSize;
|
||||
|
||||
|
|
@ -465,14 +477,12 @@ TimelineRuler::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
|
|||
REQUIRE(cr);
|
||||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
REQUIRE(timelineState);
|
||||
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
if (!state->isPlaying()) return;
|
||||
if (!timelineState->isPlaying()) return;
|
||||
|
||||
TimelineViewWindow const& window = state->get_view_window();
|
||||
Time point = state->getPlaybackPoint();
|
||||
const int x = window.time_to_x(point);
|
||||
Time point = timelineState->getPlaybackPoint();
|
||||
const int x = viewWindow().time_to_x(point);
|
||||
|
||||
cr->move_to(x + 0.5, ruler_rect.get_height());
|
||||
cr->rel_line_to(0, -playbackPointSize);
|
||||
|
|
@ -494,7 +504,6 @@ TimelineRuler::calculate_major_spacing() const
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
const int64_t time_scale = view_window().get_time_scale();
|
||||
const gavl_time_t major_spacings[] = {
|
||||
GAVL_TIME_SCALE / 1000,
|
||||
GAVL_TIME_SCALE / 400,
|
||||
|
|
@ -520,6 +529,8 @@ TimelineRuler::calculate_major_spacing() const
|
|||
60ll * 60ll * GAVL_TIME_SCALE
|
||||
};
|
||||
|
||||
const int64_t time_scale = viewWindow().get_time_scale();
|
||||
|
||||
for(i = 0; i < sizeof(major_spacings) / sizeof(gavl_time_t); i++)
|
||||
{
|
||||
const int64_t division_width = major_spacings[i] / time_scale;
|
||||
|
|
@ -531,14 +542,6 @@ TimelineRuler::calculate_major_spacing() const
|
|||
return major_spacings[i];
|
||||
}
|
||||
|
||||
TimelineViewWindow&
|
||||
TimelineRuler::view_window() const
|
||||
{
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
return state->get_view_window();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::register_styles() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "gui/gtk-lumiera.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "gui/widgets/timeline/timeline-state.hpp"
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
|
|
@ -108,10 +109,9 @@ private:
|
|||
void on_size_allocate(Gtk::Allocation& allocation);
|
||||
|
||||
/**
|
||||
* The event handler for when the TimelineWidget's state object is
|
||||
* replaced.
|
||||
* The event handler for when the TimelineWidget's state is switched.
|
||||
*/
|
||||
void on_state_changed();
|
||||
void on_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
|
||||
private:
|
||||
/* ===== Internal Methods ===== */
|
||||
|
|
@ -161,7 +161,13 @@ private:
|
|||
*/
|
||||
void draw_playback_point(Cairo::RefPtr<Cairo::Context> cr,
|
||||
const Gdk::Rectangle ruler_rect);
|
||||
|
||||
|
||||
/**
|
||||
* After notification of a timeline state switch
|
||||
* do any local adjustments to adapt to the new state
|
||||
*/
|
||||
void propagateStateChange();
|
||||
|
||||
/**
|
||||
* Given the current zoom, this function calculates the preiod
|
||||
* between major graduations on the ruler scale.
|
||||
|
|
@ -170,9 +176,9 @@ private:
|
|||
gavl_time_t calculate_major_spacing() const;
|
||||
|
||||
/**
|
||||
* A helper function to get the view window
|
||||
* Access current timeline view window
|
||||
*/
|
||||
TimelineViewWindow& view_window() const;
|
||||
TimelineViewWindow& viewWindow() const;
|
||||
|
||||
/**
|
||||
* Registers all the styles that this class will respond to.
|
||||
|
|
@ -233,6 +239,11 @@ private:
|
|||
*/
|
||||
gui::widgets::TimelineWidget &timelineWidget;
|
||||
|
||||
/**
|
||||
* the currently active timeline state object
|
||||
*/
|
||||
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
|
||||
/**
|
||||
* The caches image of the ruler, over which the chevrons etc. will
|
||||
* be drawn.
|
||||
|
|
|
|||
|
|
@ -51,12 +51,13 @@ TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence
|
|||
REQUIRE(sequence);
|
||||
|
||||
////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state
|
||||
const int64_t DEFAULT_TIMELINE_SCALE =21000000;
|
||||
|
||||
viewWindow.set_time_scale(GAVL_TIME_SCALE / 200);
|
||||
|
||||
viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE);
|
||||
|
||||
setSelection (Mutation::changeTime (Time(FSecs(2))));
|
||||
setSelection (Mutation::changeDuration(Duration(FSecs(2))));
|
||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all
|
||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all
|
||||
}
|
||||
|
||||
boost::shared_ptr<model::Sequence>
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public:
|
|||
* the GUI is really connected to the Player
|
||||
*/
|
||||
void setPlaybackPoint(Time newPos);
|
||||
|
||||
|
||||
/**
|
||||
* A signal to notify when the selected period has changed.
|
||||
*/
|
||||
|
|
@ -143,10 +143,9 @@ private:
|
|||
* when actually integrated with the Player
|
||||
*/
|
||||
TimeVar playbackPoint_;
|
||||
|
||||
|
||||
bool isPlayback_;
|
||||
|
||||
|
||||
// Signals
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,29 +31,29 @@ namespace gui {
|
|||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
|
||||
|
||||
TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
|
||||
: timeOffset(offset)
|
||||
, timeScale(scale)
|
||||
{
|
||||
TODO("Create a function to limit timescale between 1 and MaxScale");
|
||||
TODO("TICKET #795 Some functions need to be private");
|
||||
}
|
||||
|
||||
Offset
|
||||
TimelineViewWindow::get_time_offset() const /////////////////////TODO: this function shouldn't be accessible from outside
|
||||
TimelineViewWindow::get_time_offset() const /////////////////////TICKET #795: this function shouldn't be accessible from outside
|
||||
{
|
||||
return Offset (timeOffset);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TODO: this function shouldn't be accessible from outside
|
||||
TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TICKET #795: this function shouldn't be accessible from outside
|
||||
{
|
||||
timeOffset = offset;
|
||||
changedSignal.emit();
|
||||
}
|
||||
|
||||
int64_t
|
||||
TimelineViewWindow::get_time_scale() const /////////////////////TODO: this function shouldn't be accessible from outside
|
||||
TimelineViewWindow::get_time_scale() const /////////////////////TICKET #795: this function shouldn't be accessible from outside
|
||||
{
|
||||
return timeScale;
|
||||
}
|
||||
|
|
@ -66,21 +66,39 @@ TimelineViewWindow::set_time_scale(int64_t scale)
|
|||
}
|
||||
|
||||
void
|
||||
TimelineViewWindow::zoom_view(int point, int zoom_size)
|
||||
{
|
||||
int64_t new_time_scale = (double)timeScale * pow(1.25, -zoom_size);
|
||||
|
||||
// Limit zooming in too close
|
||||
if(new_time_scale < 1) new_time_scale = 1;
|
||||
|
||||
// Nudge zoom problems caused by integer rounding
|
||||
if(new_time_scale == timeScale && zoom_size < 0)
|
||||
new_time_scale++;
|
||||
|
||||
// Limit zooming out too far
|
||||
TimelineViewWindow::set_time_scale(double ratio)
|
||||
{
|
||||
int64_t max = TimelineWidget::MaxScale;
|
||||
int64_t min = 1;
|
||||
|
||||
if(ratio <= 0.0)
|
||||
{
|
||||
set_time_scale((int64_t)min);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ratio > 1.0)
|
||||
{
|
||||
ratio = 1.0;
|
||||
}
|
||||
|
||||
set_time_scale((int64_t)(ratio * max));
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewWindow::zoom_view(int point, double time_scale_ratio)
|
||||
{
|
||||
TODO("Find a Central place for a Zoom Smoothing Factor Variable. Right now it is hard coded at 9.0");
|
||||
int64_t new_time_scale =
|
||||
(int64_t)( pow(time_scale_ratio, 9.0) * (double)TimelineWidget::MaxScale);
|
||||
|
||||
/* Prevent Zooming in To Close and Far */
|
||||
if(new_time_scale < 1)
|
||||
new_time_scale = 1;
|
||||
|
||||
if(new_time_scale > TimelineWidget::MaxScale)
|
||||
new_time_scale = TimelineWidget::MaxScale;
|
||||
|
||||
|
||||
// The view must be shifted so that the zoom is centred on the cursor
|
||||
TimeVar newStartPoint = get_time_offset();
|
||||
newStartPoint += TimeValue(point * (timeScale - new_time_scale));
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@ public:
|
|||
* zero
|
||||
*/
|
||||
void set_time_scale(int64_t time_scale);
|
||||
|
||||
void set_time_scale(double ratio);
|
||||
/**
|
||||
* Zooms the view in or out as by a number of steps while keeping a
|
||||
* given point on the timeline still.
|
||||
* @param zoom_size The number of steps to zoom by. The scale factor
|
||||
* is 1.25^(-zoom_size).
|
||||
* @param new_time_scale The number of steps to zoom by. The scale factor
|
||||
* is 1.25^(-new_time_scale).
|
||||
*/
|
||||
void zoom_view(int point, int zoom_size);
|
||||
void zoom_view(int point, double timescale_ratio);
|
||||
|
||||
/**
|
||||
* Scrolls the view horizontally as a proportion of the view area.
|
||||
|
|
|
|||
155
src/gui/widgets/timeline/timeline-zoom-scale.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
timeline-zoom-scale.cpp - Implementation of the zoom scale widget
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Michael R. Fisher <mfisher31@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||
#include "gui/widgets/timeline-widget.hpp"
|
||||
|
||||
using namespace Gtk;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
|
||||
class TimelineWidget;
|
||||
|
||||
namespace timeline {
|
||||
|
||||
/**
|
||||
* @todo The initial adjustment value needs to
|
||||
* match what the TimelineViewWindow's actual timeScale
|
||||
* Value is. TimelineViewWindow::get_time_scale() is
|
||||
* currently a public method, but will soon be private.
|
||||
* Maybe TimelineViewWindow can have a zoom_adjustment
|
||||
* that gets passed to this widget's Constructor?
|
||||
*
|
||||
* @todo actually there is a more involved problem.
|
||||
* The TimelineWidget maintains a TimelineState, which in turn
|
||||
* owns the TimelineViewWindow. Now, the problem is: when we
|
||||
* switch to another Sequence (View), then this TimelineState
|
||||
* gets switched too, causing also a entirely different TimelineViewWindow
|
||||
* to become effective. Thus
|
||||
* - how can we managed to be notified from that switch?
|
||||
* - problem is: TimelineZoomScale widget is owned by the TimelinePannel.
|
||||
* Likewise, TimelineWidget is owned by the TimelinePannel. But the
|
||||
* state handling/switching logic is embedded within TimelineWidget
|
||||
* - and finally: how can we translate the actual scale (in time units),
|
||||
* as maintained within TimelineViewWindow, back into the adjustment
|
||||
* used here (which uses a relative scale 0...1.0 )
|
||||
*
|
||||
*/
|
||||
|
||||
TimelineZoomScale::TimelineZoomScale()
|
||||
: HBox()
|
||||
, adjustment(0.5, 0.0, 1.0, 0.000001)
|
||||
, slider()
|
||||
, zoomIn(Stock::ZOOM_IN)
|
||||
, zoomOut(Stock::ZOOM_OUT)
|
||||
, button_step_size(0.03)
|
||||
{
|
||||
/* Setup the Slider Control */
|
||||
slider.set_adjustment(adjustment);
|
||||
slider.set_size_request(123,10);
|
||||
slider.set_digits(6);
|
||||
slider.set_inverted(true);
|
||||
slider.set_draw_value(false);
|
||||
|
||||
/* Make our connections */
|
||||
zoomIn.signal_clicked().
|
||||
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_in_clicked));
|
||||
|
||||
zoomOut.signal_clicked().
|
||||
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked));
|
||||
|
||||
adjustment.signal_value_changed().
|
||||
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom));
|
||||
|
||||
/* Add Our Widgets and show them */
|
||||
pack_start(zoomOut,PACK_SHRINK);
|
||||
pack_start(slider,PACK_SHRINK);
|
||||
pack_start(zoomIn,PACK_SHRINK);
|
||||
|
||||
show_all();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineZoomScale::wireTimelineState (boost::shared_ptr<TimelineState> currentState,
|
||||
TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
|
||||
{
|
||||
on_timeline_state_changed (currentState);
|
||||
stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
|
||||
}
|
||||
|
||||
void
|
||||
TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr<TimelineState> newState)
|
||||
{
|
||||
REQUIRE (newState);
|
||||
timelineState = newState;
|
||||
|
||||
int64_t current_scale =
|
||||
getViewWindow().get_time_scale();
|
||||
|
||||
double linear_scale =
|
||||
(double) current_scale / (double) TimelineWidget::MaxScale;
|
||||
|
||||
/* We have to Revese the Smoothing */
|
||||
TODO("Find a central place for ZoomSmoothingFactor Variable. right now it is 9.0");
|
||||
double new_relative_scale =
|
||||
pow(linear_scale,(1.0/9.0));
|
||||
|
||||
adjustment.set_value(new_relative_scale);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineZoomScale::on_zoom_in_clicked()
|
||||
{
|
||||
double newValue = adjustment.get_value() - button_step_size;
|
||||
adjustment.set_value(newValue);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineZoomScale::on_zoom_out_clicked()
|
||||
{
|
||||
double newValue = adjustment.get_value() + button_step_size;
|
||||
adjustment.set_value(newValue);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineZoomScale::on_zoom()
|
||||
{
|
||||
zoomSignal.emit(adjustment.get_value()) ;
|
||||
}
|
||||
|
||||
sigc::signal<void, double>
|
||||
TimelineZoomScale::signal_zoom()
|
||||
{
|
||||
return zoomSignal;
|
||||
}
|
||||
|
||||
TimelineViewWindow&
|
||||
TimelineZoomScale::getViewWindow()
|
||||
{
|
||||
REQUIRE (timelineState, "lifecycle error");
|
||||
return timelineState->get_view_window();
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace widgets
|
||||
} // namespace timeline
|
||||
110
src/gui/widgets/timeline/timeline-zoom-scale.hpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
timeline-zoom-scale.hpp - Declaration of the zoom scale widget
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Michael R. Fisher <mfisher31@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
/** @file timeline-zoom-scale.hpp
|
||||
** This file contains the definition of the zoom scale widget
|
||||
*/
|
||||
|
||||
#ifndef TIMELINE_ZOOM_SCALE_HPP
|
||||
#define TIMELINE_ZOOM_SCALE_HPP
|
||||
|
||||
#include "gui/gtk-lumiera.hpp"
|
||||
#include "gui/widgets/mini-button.hpp"
|
||||
#include "gui/widgets/timeline-widget.hpp"
|
||||
#include "gui/widgets/timeline/timeline-state.hpp"
|
||||
#include "gui/widgets/timeline/timeline-view-window.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
using namespace Gtk;
|
||||
using namespace gui::widgets;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
class TimelineZoomScale : public Gtk::HBox
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
TimelineZoomScale();
|
||||
|
||||
/**
|
||||
* Accessor method to the zoomSignal
|
||||
* @return the zoomSignal
|
||||
*/
|
||||
sigc::signal<void, double> signal_zoom();
|
||||
|
||||
void wireTimelineState (boost::shared_ptr<TimelineState> currentState,
|
||||
TimelineWidget::TimelineStateChangeSignal);
|
||||
|
||||
private:
|
||||
/* Event Handlers */
|
||||
|
||||
/**
|
||||
* Update the slider position when the timeline state
|
||||
* is changed.
|
||||
*/
|
||||
void on_timeline_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
|
||||
/**
|
||||
* Event handler for when the zoomIn Button
|
||||
* is clicked
|
||||
*/
|
||||
void on_zoom_in_clicked();
|
||||
|
||||
/**
|
||||
* Event handler for when the zoomIn Button
|
||||
* is clicked
|
||||
*/
|
||||
void on_zoom_out_clicked();
|
||||
|
||||
/**
|
||||
* Event handler for when the adjustment
|
||||
* value is changed
|
||||
*/
|
||||
void on_zoom();
|
||||
|
||||
/** access current timeline state */
|
||||
TimelineViewWindow& getViewWindow();
|
||||
|
||||
/* Widgets */
|
||||
Gtk::Adjustment adjustment;
|
||||
Gtk::HScale slider;
|
||||
MiniButton zoomIn;
|
||||
MiniButton zoomOut;
|
||||
|
||||
private:
|
||||
/* Signals */
|
||||
sigc::signal<void, double> zoomSignal;
|
||||
|
||||
const double button_step_size;
|
||||
|
||||
boost::shared_ptr<TimelineState> timelineState;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace widgets
|
||||
} // namespace timeline
|
||||
|
||||
#endif /* TIMELINE_ZOOM_SCALE_HPP */
|
||||
|
|
@ -21,13 +21,18 @@
|
|||
*/
|
||||
|
||||
/** @file display-facade.h
|
||||
** Major public Interface of the Lumiera GUI. While, generally speaking, the GUI
|
||||
** controls the application and thus acts on its own, it exposes some services
|
||||
** to the lower layers. Especially the lumiera::Display interface serves to
|
||||
** hand over calculated frames to the GUI for displaying them in a viewer.
|
||||
** It's a first draft as of 1/2009, probably it can be factored out into
|
||||
** a more general display service in future.
|
||||
**
|
||||
** Experimental Interface, allowing the Dummy-Player to access the
|
||||
** video display widget in the GUI. While, generally speaking, the GUI
|
||||
** controls the application and thus acts on its own, it might expose some
|
||||
** services to the lower layers.
|
||||
**
|
||||
** In the Dummy-Player design study, the lumiera::Display interface serves
|
||||
** to hand over calculated frames to the GUI for displaying them in a viewer.
|
||||
**
|
||||
** This is a first draft as of 1/2009, and likely to be superseded by a
|
||||
** better design, where rather the \em provider of an output facility
|
||||
** registers with the OutputManager in the core.
|
||||
**
|
||||
** @see gui::GuiFacade
|
||||
** @see dummy-player-facade.h
|
||||
**
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
#include "include/interfaceproxy.hpp"
|
||||
#include "lib/handle.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
** Typically there is another subclass of the Facade interfaces sitting "on the other side"
|
||||
** of the interface barrier and actually implementing the functionality. The template
|
||||
** facade::Accessor can be thought of as a factory creating such a proxy instance of the
|
||||
** facade interface for the client code to use. Typically, in instance of the \em factory
|
||||
** facade interface for the client code to use. Typically, an instance of the \em factory
|
||||
** is embedded (as a static functor member object) right within the otherwise abstract
|
||||
** facade interface, this way allowing the client code to write e.g. \c XYZInterface::facade()
|
||||
** to yield a reference to a proxy object implementing \c XYZInterface.
|
||||
|
|
@ -48,17 +48,18 @@
|
|||
** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
|
||||
** services, it is handled by the dependency of subsystems, while the plugin loader cares
|
||||
** for dependency issues regarding loadable modules, thereby building on the deployment
|
||||
** descriptors.
|
||||
** descriptors.)
|
||||
**
|
||||
** For the Layer separation interfaces, the process of loading and opening is abstracted as
|
||||
** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
|
||||
** template and ctor parameters, in addition to the registration with the Interface/Plugin
|
||||
** system, the corresponding facade::Proxy factory is addressed and "opened" by creating
|
||||
** the right proxy object instance. Similarly, when the InstanceHandle object goes out
|
||||
** of scope, prior to detaching from the Interface/Proxy system, the corresponding
|
||||
** lumiera::facade::Accessor factory is "closed", which additionally means destroying
|
||||
** the proxy object instance and switching any further access to throwing and exception.
|
||||
**
|
||||
** system, the corresponding facade::Proxy factory is addressed and the interface instance
|
||||
** is "opened" by creating the appropriate proxy object instance. Similarly, when the
|
||||
** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy
|
||||
** system, the corresponding lumiera::facade::Accessor frontend is "closed", which
|
||||
** additionally means destroying the proxy object instance and switching any
|
||||
** further access to throwing and exception.
|
||||
**
|
||||
** While client code just includes the interface header (including interfaceproxy.hpp
|
||||
** in turn), there needs to be an actual implementation of each proxy object located in
|
||||
** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into
|
||||
|
|
@ -82,45 +83,66 @@
|
|||
|
||||
|
||||
namespace lumiera {
|
||||
namespace facade {
|
||||
|
||||
/** error-ID for accessing a (currently) closed facade */
|
||||
LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE);
|
||||
namespace facade {
|
||||
|
||||
/** error-ID for accessing a (currently) closed facade */
|
||||
LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE);
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* access-frontend to the implementation of a service.
|
||||
* Usually, an instance of Accessor is placed as static member
|
||||
* right into the facade interface used to access the service.
|
||||
* This allows clients to get the current actual implementation
|
||||
* of that service, just by invoking the function operator on
|
||||
* that member, e.g. \c lumiera::Play::facade()
|
||||
*
|
||||
* The reason for this rather indirect access technique is Lifecycle:
|
||||
* Service implementations may come up and go down; moreover, a service
|
||||
* might be implemented through a plugin component and thus the actual
|
||||
* invocation needs to be passed through a binding layer. In this case,
|
||||
* clients rather access a proxy object, which then passes on any call
|
||||
* through that binding layer to the actual implementation located
|
||||
* "somewhere".
|
||||
*
|
||||
* @note the pointer to the actual implementation is a static variable.
|
||||
* This has two consequences. For one, we're dealing with kind of
|
||||
* singleton service here. And, secondly, the implementation or
|
||||
* proxy accessor can inherit from Accessor<FA> where FA is the
|
||||
* facade interface. Being a subclass, allows the implementation
|
||||
* to set that pointer when the service comes up, and to clear
|
||||
* it when the service goes down and the access needs to
|
||||
* be closed.
|
||||
*/
|
||||
template<class FA>
|
||||
class Accessor
|
||||
{
|
||||
protected:
|
||||
static FA* implProxy_;
|
||||
|
||||
|
||||
public:
|
||||
FA&
|
||||
operator() ()
|
||||
{
|
||||
if (implProxy_)
|
||||
return *implProxy_;
|
||||
else
|
||||
throw error::State("Facade interface currently closed."
|
||||
, LUMIERA_ERROR_FACADE_LIFECYCLE);
|
||||
}
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
*/
|
||||
template<class FA>
|
||||
class Accessor
|
||||
{
|
||||
protected:
|
||||
static FA* implProxy_;
|
||||
|
||||
|
||||
public:
|
||||
FA&
|
||||
operator() ()
|
||||
{
|
||||
if (implProxy_)
|
||||
return *implProxy_;
|
||||
else
|
||||
throw error::State("Facade interface currently closed.");
|
||||
}
|
||||
};
|
||||
|
||||
template<class IHA>
|
||||
void openProxy (IHA const&);
|
||||
|
||||
template<class IHA>
|
||||
void closeProxy ();
|
||||
|
||||
template<class IHA>
|
||||
class Proxy;
|
||||
|
||||
|
||||
} // namespace facade
|
||||
template<class IHA>
|
||||
void openProxy (IHA const&);
|
||||
|
||||
} // namespace lumiera
|
||||
template<class IHA>
|
||||
void closeProxy ();
|
||||
|
||||
template<class IHA>
|
||||
class Proxy;
|
||||
|
||||
|
||||
}} // namespace lumiera::facade
|
||||
|
||||
#endif
|
||||
|
|
|
|||
163
src/include/play-facade.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
PLAYER-FACADE.h - access point to the Lumiera player subsystem
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROC_INTERFACE_PLAY_H
|
||||
#define PROC_INTERFACE_PLAY_H
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus /* ============== C++ Interface ================= */
|
||||
|
||||
//#include "include/interfaceproxy.hpp"
|
||||
#include "lib/handle.hpp"
|
||||
#include "lib/iter-source.hpp"
|
||||
#include "lib/time/control.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "include/interfaceproxy.hpp"
|
||||
#include "proc/mobject/model-port.hpp"
|
||||
#include "proc/mobject/output-designation.hpp"
|
||||
#include "proc/mobject/session/clip.hpp"
|
||||
#include "proc/mobject/session/track.hpp"
|
||||
#include "proc/play/output-manager.hpp"
|
||||
#include "proc/asset/timeline.hpp"
|
||||
#include "proc/asset/viewer.hpp"
|
||||
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace play {
|
||||
|
||||
class PlayProcess;
|
||||
}}
|
||||
|
||||
|
||||
namespace lumiera {
|
||||
|
||||
namespace time = lib::time;
|
||||
|
||||
using std::tr1::weak_ptr;
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* Interface to the Player subsystem of Lumiera (Proc-Layer).
|
||||
* Global access point for starting playback and render processes,
|
||||
* calculating media data by running the render engine.
|
||||
*
|
||||
* @todo WIP-WIP-WIP 6/2011
|
||||
* @note Lumiera is not yet able actually to deliver rendered data.
|
||||
* @todo there should be an accompanying CL Interface defined for
|
||||
* the Lumiera interface system, so the player can be
|
||||
* accessed from external clients. This was left out
|
||||
* for now, as we don't have either plugins, nor
|
||||
* any script running capabilities yet. (5/2011)
|
||||
*/
|
||||
class Play
|
||||
{
|
||||
public:
|
||||
|
||||
/** get an implementation instance of this service */
|
||||
static lumiera::facade::Accessor<Play> facade;
|
||||
|
||||
|
||||
/**
|
||||
* Continuous playback process, which has been hooked up
|
||||
* and started with a fixed set of output slots. started with a specific
|
||||
* output size, format and framerate. It is a handle to a calculation process,
|
||||
* which is about to produce a stream of frames and push them to the outputs.
|
||||
*
|
||||
* The Lifecycle of the referred playback process is managed automatically
|
||||
* through this handle (by ref count). Client code is supposed to use the
|
||||
* API on this handle to navigate and control the playback mode.
|
||||
*
|
||||
* @see handle.hpp
|
||||
* @see player-service.cpp implementation
|
||||
*/
|
||||
class Controller
|
||||
: public lib::Handle<proc::play::PlayProcess>
|
||||
{
|
||||
public:
|
||||
void play(bool); ///< play/pause toggle
|
||||
void scrub(bool); ///< scrubbing playback
|
||||
void adjustSpeed(double); ///< playback speed control
|
||||
void go(lib::time::Time); ///< skip to the given point in time
|
||||
|
||||
void controlPlayhead (time::Control<time::Time> & ctrl);
|
||||
void controlDuration (time::Control<time::Duration> & ctrl);
|
||||
void controlLooping (time::Control<time::TimeSpan> & ctrl);
|
||||
|
||||
void useProxyMedia (bool);
|
||||
void setQuality (uint);
|
||||
|
||||
bool is_playing() const;
|
||||
bool is_scrubbing() const;
|
||||
double getSpeed() const;
|
||||
uint getQuality() const;
|
||||
bool usesProxy() const;
|
||||
|
||||
operator weak_ptr<proc::play::PlayProcess>() const;
|
||||
};
|
||||
|
||||
|
||||
typedef lib::IterSource<mobject::ModelPort>::iterator ModelPorts;
|
||||
typedef lib::IterSource<mobject::OutputDesignation>::iterator Pipes;
|
||||
typedef proc::play::POutputManager Output;
|
||||
typedef mobject::session::PClipMO Clip;
|
||||
typedef mobject::PTrack Track;
|
||||
typedef asset::PTimeline Timeline;
|
||||
typedef asset::PViewer Viewer;
|
||||
|
||||
/** core operation: create a new playback process
|
||||
* outputting to the given viewer/display */
|
||||
virtual Controller connect(ModelPorts, Output) =0;
|
||||
|
||||
|
||||
/* ==== convenience shortcuts for common use cases ==== */
|
||||
Controller perform(Pipes, Output);
|
||||
Controller perform(Timeline);
|
||||
Controller perform(Viewer);
|
||||
Controller perform(Track);
|
||||
Controller perform(Clip);
|
||||
|
||||
protected:
|
||||
virtual ~Play();
|
||||
};
|
||||
|
||||
|
||||
} // namespace lumiera
|
||||
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
#endif /* =========================== CL Interface ===================== */
|
||||
|
||||
|
||||
// #include "common/interface.h"
|
||||
////////////////////////////////////TODO define a C binding for the Interface system here
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
** e.g. throwing an exception instead of creating a NULL value.
|
||||
**
|
||||
** @see lumiera::WrapperPtr usage example to access a variant record
|
||||
** @see lib::InPlaceAnyHolder usage example to access a subclass in embedded storage
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ namespace lumiera {
|
|||
LUMIERA_ERROR_DECLARE (ASSERTION); ///< assertion failure
|
||||
|
||||
/* generic error situations */
|
||||
LUMIERA_ERROR_DECLARE (LIFECYCLE); ///< Lifecycle assumptions violated
|
||||
LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch
|
||||
LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
|
||||
LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ namespace lumiera {
|
|||
LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
|
||||
|
||||
/* some further generic error situations */
|
||||
LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated");
|
||||
LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
|
||||
LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
|
||||
LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");
|
||||
|
|
|
|||
|
|
@ -38,11 +38,15 @@
|
|||
#define FUNCTOR_UTIL_H_
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <cstring>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
|
||||
|
||||
namespace util { ////////////TODO: refactor it. But probably not directly into namespace lib. Needs some more consideration though
|
||||
namespace lib {
|
||||
typedef size_t HashVal;
|
||||
}
|
||||
|
||||
namespace util { ////////////TODO: refactor namespace. But probably not directly into namespace lib. Needs some more consideration though
|
||||
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
|
|
@ -86,7 +90,7 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
|||
|
||||
/** convenience shortcut to call two functors in sequence.
|
||||
* @return a Dispatch functor object which incorporates the
|
||||
* functors as copy and on invocation calls the first
|
||||
* functors as copy and on invocation calls the first
|
||||
* function and then returns the result of the second */
|
||||
template<typename SIG>
|
||||
Dispatch<SIG>
|
||||
|
|
@ -99,9 +103,12 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
|||
|
||||
|
||||
namespace { // hiding some nasty details...
|
||||
|
||||
using lib::HashVal;
|
||||
using boost::hash_combine;
|
||||
|
||||
/**
|
||||
* This Class is used to surpass the access protection
|
||||
* This Class is used to bypass the access protection
|
||||
* and break into the tr1::function implementation.
|
||||
* Thus we can implement a raw comparison function,
|
||||
* as a replacement for the missing functor comparison
|
||||
|
|
@ -136,6 +143,16 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
|||
&& (f1._M_functor._M_unused._M_const_object ==
|
||||
f2._M_functor._M_unused._M_const_object );
|
||||
} // note: we don't cover any member pointer offset
|
||||
|
||||
friend HashVal
|
||||
hash_value (HijackedFunction const& fun)
|
||||
{
|
||||
HashVal hash(0);
|
||||
hash_combine (hash, fun.invoker_);
|
||||
hash_combine (hash, fun._M_manager);
|
||||
hash_combine (hash, fun._M_functor._M_unused._M_const_object);
|
||||
return hash; // note: member pointer offset part uncovered
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -184,6 +201,41 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n
|
|||
}
|
||||
|
||||
|
||||
/** workaround to calculate a hash value for a given function object.
|
||||
* @note use with caution. This implementation relies on internal details
|
||||
* of boost/function; it can be expected to be rather conservative,
|
||||
* i.e. yielding different hash values for objects, which actually
|
||||
* are semantically equivalent.
|
||||
* @warning especially function objects bound to member functions aren't
|
||||
* fully supported. It \em may happen that we miss differences on the
|
||||
* offset part and only hash the "this" pointer on some platform.
|
||||
*/
|
||||
template<typename SIG>
|
||||
inline HashVal
|
||||
rawHashValue (function<SIG> const& fun)
|
||||
{
|
||||
typedef HijackedFunction const& Hij;
|
||||
|
||||
return hash_value (reinterpret_cast<Hij> (fun));
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
namespace std {
|
||||
namespace tr1 {
|
||||
|
||||
/** inject into std::tr1 to be picked up by ADL:
|
||||
* @return hash value of given functor
|
||||
* @note use with caution. Hash is calculated
|
||||
* relying on undocumented boost internals.
|
||||
*/
|
||||
template<typename SIG>
|
||||
inline lib::HashVal
|
||||
hash_value (function<SIG> const& fun)
|
||||
{
|
||||
return util::rawHashValue (fun);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
#endif /*UTIL_HPP_*/
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ namespace lib {
|
|||
* Generic opaque reference counting handle, for accessing a service
|
||||
* and managing its lifecycle. Usually such a handle is created by
|
||||
* an service interface and \link #activate activated \endlink by
|
||||
* setting up the link to some internal implementation object.
|
||||
* setting up the link to a suitable hidden implementation object.
|
||||
* This setup can only be done by a friend or derived class, //////////////////////////TODO: that was the intention. Why didn't this work out as expected?
|
||||
* while client code is free to copy and store handle objects.
|
||||
* Finally, any handle can be closed, thereby decrementing
|
||||
|
|
|
|||
|
|
@ -279,6 +279,14 @@ namespace lib {
|
|||
typedef typename IterSource<Val>::iterator Iter;
|
||||
};
|
||||
|
||||
template<class IT, class FUN>
|
||||
struct _TransformIterT
|
||||
{
|
||||
typedef typename _ProducedOutput<FUN>::Type ResVal;
|
||||
typedef TransformIter<IT,ResVal> TransIter;
|
||||
typedef typename IterSource<ResVal>::iterator Iter;
|
||||
};
|
||||
|
||||
template<class IT>
|
||||
struct _PairIterT
|
||||
{
|
||||
|
|
@ -329,6 +337,28 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
/** pipes a given Lumiera Forward Iterator through
|
||||
* a transformation function and wraps the resulting
|
||||
* transforming Iterator, exposing just a IterSource.
|
||||
* This convenience shortcut can be used to build a
|
||||
* processing chain; the resulting IterSource will
|
||||
* hide any involved detail types.
|
||||
* @note as with any IterSource, there is one virtual
|
||||
* function call for every fetched element.
|
||||
*/
|
||||
template<class IT, class FUN>
|
||||
typename _TransformIterT<IT,FUN>::Iter
|
||||
transform (IT const& source, FUN processingFunc)
|
||||
{
|
||||
typedef typename _TransformIterT<IT,FUN>::ResVal ValType;
|
||||
typedef typename _TransformIterT<IT,FUN>::TransIter TransIT;
|
||||
|
||||
return IterSource<ValType>::build (
|
||||
new WrappedLumieraIterator<TransIT> (
|
||||
transformIterator (source, processingFunc)));
|
||||
}
|
||||
|
||||
|
||||
/** @return a Lumiera Forward Iterator to yield
|
||||
* all the keys of the given Map or Hashtable
|
||||
*/
|
||||
|
|
@ -424,6 +454,7 @@ namespace lib {
|
|||
|
||||
}
|
||||
using iter_source::wrapIter;
|
||||
using iter_source::transform;
|
||||
using iter_source::eachMapKey;
|
||||
using iter_source::eachDistinctKey;
|
||||
using iter_source::eachValForKey;
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@ namespace lumiera {
|
|||
extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */
|
||||
|
||||
|
||||
extern const char * lumiera_ON_BASIC_INIT = lumiera::ON_BASIC_INIT;
|
||||
extern const char * lumiera_ON_GLOBAL_INIT = lumiera::ON_GLOBAL_INIT;
|
||||
extern const char * lumiera_ON_GLOBAL_SHUTDOWN = lumiera::ON_GLOBAL_SHUTDOWN;
|
||||
const char * lumiera_ON_BASIC_INIT = lumiera::ON_BASIC_INIT;
|
||||
const char * lumiera_ON_GLOBAL_INIT = lumiera::ON_GLOBAL_INIT;
|
||||
const char * lumiera_ON_GLOBAL_SHUTDOWN = lumiera::ON_GLOBAL_SHUTDOWN;
|
||||
|
||||
extern const char * lumiera_ON_EMERGENCY = lumiera::ON_EMERGENCY;
|
||||
const char * lumiera_ON_EMERGENCY = lumiera::ON_EMERGENCY;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
/** @file function-erasure.hpp
|
||||
** Generic holder for functor objects, concealing the concrete function type.
|
||||
** When working with generic function objects and function pointers typed to
|
||||
** arbitrary signatures, often there is the necessity to hold onto such a functor
|
||||
** while hiding the actual signature behind an common interface ("type erasure").
|
||||
|
|
|
|||
101
src/lib/meta/generator-combinations.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
GENERATOR-COMBINATIONS.hpp - generate combinations and variations
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file generator-combinations.hpp
|
||||
** Metaprogramming facilities to generate combination cases.
|
||||
** Similar to the plain typelist-based generators, a custom supplied
|
||||
** template will be instantiated with combinations of the parameter types
|
||||
** and then mixed into the resulting type
|
||||
**
|
||||
** @see generator-combinations-test.cpp
|
||||
** @see generator.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LUMIERA_META_GENERATOR_COMBINATIONS_H
|
||||
#define LUMIERA_META_GENERATOR_COMBINATIONS_H
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
#include "lib/meta/generator.hpp"
|
||||
|
||||
|
||||
|
||||
namespace lumiera {
|
||||
namespace typelist{
|
||||
|
||||
|
||||
template<class TYPES_1, class TYPES_2>
|
||||
struct CartesianProduct
|
||||
: Distribute< typename TYPES_1::List
|
||||
, typename TYPES_2::List
|
||||
>
|
||||
{ };
|
||||
|
||||
|
||||
|
||||
template< template<class,class,class> class _X_>
|
||||
struct PickParametersFromSublist
|
||||
{
|
||||
template<class SUBLIST, class BASE>
|
||||
struct SingleCaseInstantiation
|
||||
: _X_< typename Pick<SUBLIST,0>::Type
|
||||
, typename Pick<SUBLIST,1>::Type
|
||||
, BASE
|
||||
>
|
||||
{ };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Build a Case matrix.
|
||||
* The given parameter template _X_
|
||||
* will be instantiated for each possible combination
|
||||
* of the elements from both parameter type-lists.
|
||||
* All these instantiations will be chained up
|
||||
* into a linear inheritance chain rooted
|
||||
* at the BASE type.
|
||||
* @note the custom-supplied template _X_ needs to take a 3rd parameter,
|
||||
* and inherit from this parameter, in order to form that chain.
|
||||
* Typically you'll define some (static) functions within that
|
||||
* template, which then forward the call to the given BASE
|
||||
* (and of course, that BASE then needs to define this
|
||||
* function as well).
|
||||
*/
|
||||
template
|
||||
< class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from
|
||||
, template<class,class,class> class _X_ ///< template with two arg types and a base type
|
||||
, class BASE = NullType
|
||||
>
|
||||
struct InstantiateChainedCombinations
|
||||
: InstantiateChained< typename CartesianProduct<TYPES_1,TYPES_2>::List
|
||||
, PickParametersFromSublist<_X_>::template SingleCaseInstantiation
|
||||
, BASE
|
||||
>
|
||||
{ };
|
||||
|
||||
|
||||
|
||||
}} // namespace lumiera::typelist
|
||||
#endif
|
||||
329
src/lib/meta/typelist-manip.hpp
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
TYPELIST-MANIP.hpp - Utils for working with lists-of-types
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file typelist-manip.hpp
|
||||
** Metaprogramming: Helpers for manipulating lists-of-types.
|
||||
** Sometimes, we use metaprogramming to generate a variation of concrete
|
||||
** implementations by combining some basic building blocks. Typically, there
|
||||
** is a number of similar, but not suitably related types involved. We want to
|
||||
** process those types using a common scheme, without being forced to squeeze
|
||||
** all those types into a artificial inheritance relationship. Now, generating
|
||||
** some kind of common factory or adapter, while mixing in pieces of code tailored
|
||||
** specifically to the individual types, allows still to build a common processing
|
||||
** in such situations.
|
||||
**
|
||||
** The facilities in this header provide the basics of simple functional list
|
||||
** processing (mostly with tail recursion). Usually, there is one template parameter
|
||||
** TYPES, which accepts a \em Type-list. The result of the processing step is then
|
||||
** accessible as an embedded typedef named \c List . Here, all of the 'processing'
|
||||
** to calculate this result is performed by the compiler, as a side-effect of
|
||||
** figuring out the resulting concrete type. At run time, in the generated
|
||||
** code, typically the resulting classes are empty, maybe just
|
||||
** exposing a specifically built-up function.
|
||||
**
|
||||
** @see generator.hpp
|
||||
** @see typelist-manip-test.cpp
|
||||
** @see TimeControl_test usage example
|
||||
** @see typelist.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LUMIERA_META_TYPELIST_MANIP_H
|
||||
#define LUMIERA_META_TYPELIST_MANIP_H
|
||||
|
||||
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
|
||||
namespace lumiera {
|
||||
namespace typelist{
|
||||
|
||||
|
||||
/** pick the n-th element from a typelist */
|
||||
template<class TYPES, uint i>
|
||||
struct Pick
|
||||
{
|
||||
typedef NullType Type;
|
||||
};
|
||||
template<class TY, class TYPES>
|
||||
struct Pick<Node<TY,TYPES>, 0>
|
||||
{
|
||||
typedef TY Type;
|
||||
};
|
||||
template<class TY, class TYPES, uint i>
|
||||
struct Pick<Node<TY,TYPES>, i>
|
||||
{
|
||||
typedef typename Pick<TYPES, i-1>::Type Type;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** apply a transformation (template) to each type in the list */
|
||||
template<class TY, template<class> class _TRANS_>
|
||||
struct Apply { typedef TY List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, template<class> class _TRANS_
|
||||
>
|
||||
struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type
|
||||
, typename Apply<TYPES,_TRANS_>::List
|
||||
> List; };
|
||||
|
||||
|
||||
/** conditional node: skip an element based on evaluating a predicate */
|
||||
template<bool, class T, class TAIL>
|
||||
struct CondNode { typedef TAIL Next; };
|
||||
|
||||
template<class T, class TAIL>
|
||||
struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; };
|
||||
|
||||
/** filter away those types which don't fulfil a predicate metafunction */
|
||||
template< class TYPES
|
||||
, template<class> class _P_ ///< a template providing a boolean member \c ::value
|
||||
>
|
||||
struct Filter;
|
||||
|
||||
template<template<class> class _P_>
|
||||
struct Filter<NullType,_P_> { typedef NullType List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, template<class> class _P_
|
||||
>
|
||||
struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value
|
||||
, TY
|
||||
, typename Filter<TYPES,_P_>::List
|
||||
>::Next
|
||||
List; };
|
||||
|
||||
|
||||
/** append lists-of-types */
|
||||
template<class TY1, class TY2>
|
||||
struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, class TAIL
|
||||
>
|
||||
struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; };
|
||||
|
||||
template<class TY, class TYPES>
|
||||
struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; };
|
||||
|
||||
template<class TY, class TYPES>
|
||||
struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; };
|
||||
|
||||
template<class TY1>
|
||||
struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; };
|
||||
|
||||
template<class TY2>
|
||||
struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; };
|
||||
|
||||
template<>
|
||||
struct Append<NullType,NullType> { typedef NullType List; };
|
||||
|
||||
|
||||
|
||||
|
||||
/** access the last list element */
|
||||
template<class TYPES>
|
||||
struct SplitLast;
|
||||
|
||||
template<>
|
||||
struct SplitLast<NullType> { typedef NullType Type;
|
||||
typedef NullType List; };
|
||||
template<class TY>
|
||||
struct SplitLast<Node<TY,NullType> > { typedef TY Type;
|
||||
typedef NullType List; };
|
||||
|
||||
template<class TY, class TYPES>
|
||||
struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type;
|
||||
typedef typename Append< TY,
|
||||
typename SplitLast<TYPES>::List
|
||||
>::List
|
||||
List; };
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* splice a typelist like an overlay
|
||||
* into an base typelist, starting at given index.
|
||||
* @return either the combined (spliced) List, or
|
||||
* the Front/Back part before or after the Overlay
|
||||
* @note using a NullType as OVERLAY allows to extract
|
||||
* an arbitrary Front/Back part of the list
|
||||
*/
|
||||
template<class BASE, class OVERLAY, uint i=0>
|
||||
struct Splice;
|
||||
|
||||
template<class B, class BS,
|
||||
class OVERLAY, uint i>
|
||||
struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List;
|
||||
typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front;
|
||||
typedef typename Splice<BS, OVERLAY, i-1>::Back Back; };
|
||||
|
||||
template<class B, class BS,
|
||||
class O, class OS >
|
||||
struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List;
|
||||
typedef NullType Front;
|
||||
typedef typename Splice<BS,OS, 0>::Back Back; };
|
||||
|
||||
template<class B, class BS>
|
||||
struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List;
|
||||
typedef NullType Front;
|
||||
typedef Node<B, BS> Back; };
|
||||
|
||||
template<class XX, uint i>
|
||||
struct Splice<NullType, XX, i> { typedef NullType List;
|
||||
typedef NullType Front;
|
||||
typedef NullType Back; };
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allows to access various parts of a given typelist:
|
||||
* Start and End, Prefix and Tail...
|
||||
*/
|
||||
template<class TYPES>
|
||||
struct Dissect;
|
||||
|
||||
template<class T, class TYPES>
|
||||
struct Dissect<Node<T,TYPES> >
|
||||
{
|
||||
typedef Node<T,TYPES> List; ///< the complete list
|
||||
typedef T Head; ///< first element
|
||||
typedef Node<T,NullType> First; ///< a list containing the first element
|
||||
typedef TYPES Tail; ///< remainder of the list starting with the second elm.
|
||||
typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but excluding the last element
|
||||
typedef typename SplitLast<List>::Type End; ///< the last element
|
||||
typedef Node<End,NullType> Last; ///< a list containing the last element
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Dissect<NullType>
|
||||
{
|
||||
typedef NullType List;
|
||||
typedef NullType Head;
|
||||
typedef NullType First;
|
||||
typedef NullType Tail;
|
||||
typedef NullType Prefix;
|
||||
typedef NullType End;
|
||||
typedef NullType Last;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* prefix each of the elements,
|
||||
* yielding a list-of lists-of-types
|
||||
*/
|
||||
template<class T, class TY>
|
||||
struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; };
|
||||
|
||||
template<class T>
|
||||
struct PrefixAll<T, NullType> { typedef NullType List; };
|
||||
|
||||
template<class T>
|
||||
struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; };
|
||||
|
||||
template< class T
|
||||
, class TY, class TYPES
|
||||
>
|
||||
struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List
|
||||
, typename PrefixAll<T,TYPES>::List
|
||||
> List; };
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* build a list-of lists, where each element of the first arg list
|
||||
* gets in turn prepended to all elements of the second arg list.
|
||||
* Can be used to build all possible combinations from two
|
||||
* sources, i.e. the Cartesian product.
|
||||
*/
|
||||
template<class TY1,class TY2>
|
||||
struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; };
|
||||
|
||||
template<class TY>
|
||||
struct Distribute<NullType,TY> { typedef NullType List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, class TAIL
|
||||
>
|
||||
struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List
|
||||
, typename Distribute<TYPES,TAIL>::List
|
||||
>::List
|
||||
List; };
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* build all possible combinations, based on a enumeration of the basic cases.
|
||||
* For each of the types in the argument list, an "enumeration generator" template is invoked,
|
||||
* yielding a list of the possible base cases. These base cases are then combined with all the
|
||||
* combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
|
||||
* means that the base cases of the n-th element will appear in the n-th position of the
|
||||
* resulting lists,
|
||||
*
|
||||
* For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
|
||||
* list-of-lists, featuring all possibilities to combine these distinct toggles.
|
||||
*/
|
||||
template< class X
|
||||
, template<class> class _ENUM_>
|
||||
struct Combine { typedef typename Distribute< typename _ENUM_<X>::List
|
||||
, Node<NullType,NullType>
|
||||
>::List List; };
|
||||
template< template<class> class _ENUM_>
|
||||
struct Combine<NullType, _ENUM_ > { typedef NodeNull List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, template<class> class _ENUM_>
|
||||
struct Combine<Node<TY,TYPES>,_ENUM_> { typedef typename Distribute< typename _ENUM_<TY>::List
|
||||
, typename Combine<TYPES,_ENUM_>::List
|
||||
>::List List; };
|
||||
|
||||
/** enumeration generator for the Combine metafunction,
|
||||
* yielding an "on" and "off" case
|
||||
*/
|
||||
template<class F>
|
||||
struct FlagOnOff
|
||||
{
|
||||
typedef Node<F, Node<NullType,NullType> > List;
|
||||
};
|
||||
|
||||
|
||||
/** generate all possible on-off combinations of the given flags */
|
||||
template<class FLAGS>
|
||||
struct CombineFlags
|
||||
{
|
||||
typedef typename Combine<FLAGS, FlagOnOff>::List List;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace lumiera::typelist
|
||||
#endif
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
TYPELIST-UTIL.hpp - Utils for working with lists-of-types
|
||||
TYPELIST-UTIL.hpp - simple helpers for working with lists-of-types
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
|
@ -21,11 +21,32 @@
|
|||
*/
|
||||
|
||||
|
||||
/** @file typelist-util.hpp
|
||||
** Metaprogramming: simple helpers for working with lists-of-types.
|
||||
** This header provides some very basic "meta functions" for extracting
|
||||
** some informations from a list-of-types. In Lumiera, we use template
|
||||
** metaprogramming and especially such lists-of-types, whenever we build
|
||||
** some common implementation backbone, without being able to subsume all
|
||||
** participating types (classes) into a single inheritance hierarchy.
|
||||
**
|
||||
** The "meta functions" defined here are templates; to access the "result" of
|
||||
** such a meta function, we instantiate the template and then access one of the
|
||||
** embedded constant definitions (usually the enum constant named \c value)
|
||||
**
|
||||
** @see generator.hpp
|
||||
** @see TypelistUtil_test
|
||||
** @see lib::SimpleAllocator usage example (for isInList)
|
||||
** @see TypelistManip_test
|
||||
** @see typelist.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LUMIERA_META_TYPELIST_UTIL_H
|
||||
#define LUMIERA_META_TYPELIST_UTIL_H
|
||||
|
||||
|
||||
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
|
||||
namespace lumiera {
|
||||
|
|
@ -34,6 +55,7 @@ namespace typelist{
|
|||
|
||||
/**
|
||||
* Metafunction counting the number of Types in the collection
|
||||
* @return an embedded constant \c value holding the result
|
||||
*/
|
||||
template<class TYPES>
|
||||
struct count;
|
||||
|
|
@ -48,6 +70,7 @@ namespace typelist{
|
|||
enum{ value = 1 + count<TYPES>::value };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Metafunction " max( sizeof(T) ) for T in TYPES "
|
||||
*/
|
||||
|
|
@ -68,233 +91,35 @@ namespace typelist{
|
|||
};
|
||||
|
||||
|
||||
/** apply a transformation (template) to each type in the list */
|
||||
template<class TY, template<class> class _TRANS_>
|
||||
struct Apply { typedef TY List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, template<class> class _TRANS_
|
||||
>
|
||||
struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type
|
||||
, typename Apply<TYPES,_TRANS_>::List
|
||||
> List; };
|
||||
|
||||
|
||||
template<bool, class T, class TAIL>
|
||||
struct CondNode { typedef TAIL Next; };
|
||||
|
||||
template<class T, class TAIL>
|
||||
struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; };
|
||||
|
||||
/** filter away those types which don't fulfil a predicate metafunction */
|
||||
template< class TYPES
|
||||
, template<class> class _P_ ///< a template providing a boolean member \c ::value
|
||||
>
|
||||
struct Filter;
|
||||
|
||||
template<template<class> class _P_>
|
||||
struct Filter<NullType,_P_> { typedef NullType List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, template<class> class _P_
|
||||
>
|
||||
struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value
|
||||
, TY
|
||||
, typename Filter<TYPES,_P_>::List
|
||||
>::Next
|
||||
List; };
|
||||
|
||||
|
||||
/** append lists-of-types */
|
||||
template<class TY1, class TY2>
|
||||
struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, class TAIL
|
||||
>
|
||||
struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; };
|
||||
|
||||
template<class TY, class TYPES>
|
||||
struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; };
|
||||
|
||||
template<class TY, class TYPES>
|
||||
struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; };
|
||||
|
||||
template<class TY1>
|
||||
struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; };
|
||||
|
||||
template<class TY2>
|
||||
struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; };
|
||||
|
||||
template<>
|
||||
struct Append<NullType,NullType> { typedef NullType List; };
|
||||
|
||||
|
||||
|
||||
|
||||
/** access the last list element */
|
||||
template<class TYPES>
|
||||
struct SplitLast;
|
||||
|
||||
template<>
|
||||
struct SplitLast<NullType> { typedef NullType Type;
|
||||
typedef NullType List; };
|
||||
template<class TY>
|
||||
struct SplitLast<Node<TY,NullType> > { typedef TY Type;
|
||||
typedef NullType List; };
|
||||
|
||||
template<class TY, class TYPES>
|
||||
struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type;
|
||||
typedef typename Append< TY,
|
||||
typename SplitLast<TYPES>::List
|
||||
>::List
|
||||
List; };
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* splice a typelist like an overlay
|
||||
* into an base typelist, starting at given index.
|
||||
* @return either the combined (spliced) List, or
|
||||
* the Front/Back part before or after the Overlay
|
||||
* @note using a NullType as OVERLAY allows to extract
|
||||
* an arbitrary Front/Back part of the list
|
||||
*/
|
||||
template<class BASE, class OVERLAY, uint i=0>
|
||||
struct Splice;
|
||||
|
||||
template<class B, class BS,
|
||||
class OVERLAY, uint i>
|
||||
struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List;
|
||||
typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front;
|
||||
typedef typename Splice<BS, OVERLAY, i-1>::Back Back; };
|
||||
|
||||
template<class B, class BS,
|
||||
class O, class OS >
|
||||
struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List;
|
||||
typedef NullType Front;
|
||||
typedef typename Splice<BS,OS, 0>::Back Back; };
|
||||
|
||||
template<class B, class BS>
|
||||
struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List;
|
||||
typedef NullType Front;
|
||||
typedef Node<B, BS> Back; };
|
||||
|
||||
template<class XX, uint i>
|
||||
struct Splice<NullType, XX, i> { typedef NullType List;
|
||||
typedef NullType Front;
|
||||
typedef NullType Back; };
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allows to access various parts of a given typelist:
|
||||
* Start and End, Prefix and Tail..
|
||||
* Metafunction to check if a specific type is contained
|
||||
* in a given typelist. Only exact match is detected.
|
||||
*/
|
||||
template<class TYPES>
|
||||
struct Dissect;
|
||||
|
||||
template<class T, class TYPES>
|
||||
struct Dissect<Node<T,TYPES> >
|
||||
template<typename TY, typename TYPES>
|
||||
struct IsInList
|
||||
{
|
||||
typedef Node<T,TYPES> List; ///< the complete list
|
||||
typedef T Head; ///< first element
|
||||
typedef Node<T,NullType> First; ///< a list containing the first element
|
||||
typedef TYPES Tail; ///< remainder of the list starting with the second elm.
|
||||
typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but extcluding the last element
|
||||
typedef typename SplitLast<List>::Type End; ///< the last element
|
||||
typedef Node<End,NullType> Last; ///< a list containing the last element
|
||||
enum{ value = false };
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Dissect<NullType>
|
||||
template<typename TY, typename TYPES>
|
||||
struct IsInList<TY, Node<TY,TYPES> >
|
||||
{
|
||||
typedef NullType List;
|
||||
typedef NullType Head;
|
||||
typedef NullType First;
|
||||
typedef NullType Tail;
|
||||
typedef NullType Prefix;
|
||||
typedef NullType End;
|
||||
typedef NullType Last;
|
||||
enum{ value = true };
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* prefix each of the elements,
|
||||
* yielding a list-of lists-of-types
|
||||
*/
|
||||
template<class T, class TY>
|
||||
struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; };
|
||||
|
||||
template<class T>
|
||||
struct PrefixAll<T, NullType> { typedef NullType List; };
|
||||
|
||||
template<class T>
|
||||
struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; };
|
||||
|
||||
template< class T
|
||||
, class TY, class TYPES
|
||||
>
|
||||
struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List
|
||||
, typename PrefixAll<T,TYPES>::List
|
||||
> List; };
|
||||
|
||||
|
||||
|
||||
|
||||
template<class TY1,class TY2>
|
||||
struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; };
|
||||
|
||||
template<class TY>
|
||||
struct Distribute<NullType,TY> { typedef NullType List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, class TAIL
|
||||
>
|
||||
struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List
|
||||
, typename Distribute<TYPES,TAIL>::List
|
||||
>::List
|
||||
List; };
|
||||
|
||||
|
||||
|
||||
/** use a permutation generator
|
||||
* for creating a list of all possible combinations
|
||||
*/
|
||||
template< class X
|
||||
, template<class> class _PERMU_>
|
||||
struct Combine { typedef typename Distribute< typename _PERMU_<X>::List
|
||||
, Node<NullType,NullType>
|
||||
>::List List; };
|
||||
template< template<class> class _PERMU_>
|
||||
struct Combine<NullType, _PERMU_ > { typedef Node<NullType,NullType> List; };
|
||||
|
||||
template< class TY, class TYPES
|
||||
, template<class> class _PERMU_>
|
||||
struct Combine<Node<TY,TYPES>,_PERMU_> { typedef typename Distribute< typename _PERMU_<TY>::List
|
||||
, typename Combine<TYPES,_PERMU_>::List
|
||||
>::List List; };
|
||||
|
||||
/** permutation generator for the Combine metafunction,
|
||||
* yielding an "on" and "off" case
|
||||
*/
|
||||
template<class F>
|
||||
struct FlagOnOff
|
||||
{
|
||||
typedef Node<F, Node<NullType,NullType> > List;
|
||||
};
|
||||
|
||||
|
||||
/** generate all possible on-off combinations of the given flags */
|
||||
template<class FLAGS>
|
||||
struct CombineFlags
|
||||
{
|
||||
typedef typename Combine<FLAGS, FlagOnOff>::List List;
|
||||
template<typename TY, typename XX, typename TYPES>
|
||||
struct IsInList<TY, Node<XX,TYPES> >
|
||||
{
|
||||
enum{ value = IsInList<TY,TYPES>::value };
|
||||
};
|
||||
|
||||
/** convenience shortcut: query function */
|
||||
template<typename TY, typename TYPES>
|
||||
bool
|
||||
isInList()
|
||||
{
|
||||
return IsInList<TY,TYPES>::value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,15 +40,25 @@ This code is heavily inspired by
|
|||
|
||||
/** @file typelist.hpp
|
||||
** A template metaprogramming technique for manipulating collections of types.
|
||||
** Effectively this is a taylored and simplified version of what can be found in the Loki library.
|
||||
** Effectively this is a tailored and simplified version of what can be found in the Loki library.
|
||||
** We use it in other generic library-style code to generate repetitive code. If you tend to find
|
||||
** template metaprogramming (or functional programming in general) offending, please ignore the
|
||||
** technical details and just consider the benefit of such an simplification for the user code.
|
||||
**
|
||||
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters
|
||||
** technical details and just consider the benefit of such an simplification for the client code.
|
||||
**
|
||||
** Interface for using this facility is the template Types(.....) for up to 20 Type parameters.
|
||||
** To start typelist processing, other templates typically pick up the Types<...>::List type.
|
||||
** This allows for LISP-style list processing, with a pattern match on either Node<TY,TYPES>
|
||||
** or NullType to terminate recursion. In C++ template metaprogramming, "pattern match"
|
||||
** is done by partial template specialisations (the compiler will pick up and thus
|
||||
** match the template parameters). A typedef acts like a declaration in normal
|
||||
** programming. Because such a "declaration" can't be changed after the fact,
|
||||
** effectively this is a flavour of functional programming. Just the
|
||||
** "execution environment" is the compiler, during compilation.
|
||||
**
|
||||
** @see lumiera::visitor::Applicable usage example
|
||||
** @see typelisttest.cpp
|
||||
** @see control::CommandSignature more elaborate usage example (dissecting a functor signature)
|
||||
** @see TypeList_test
|
||||
** @see TypeListManip_test
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
#define LUMIERA_META_TYPESEQ_UTIL_H
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "lib/meta/typelist-util.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
#include "lib/meta/util.hpp"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,24 @@ namespace lumiera {
|
|||
|
||||
namespace typelist {
|
||||
|
||||
/** Compile-time Type equality:
|
||||
* Simple Trait template to pick up types considered
|
||||
* \em identical by the compiler.
|
||||
* @warning identical, not sub-type!
|
||||
*/
|
||||
template<typename T1, typename T2>
|
||||
struct is_sameType
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_sameType<T,T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
|
||||
/** semi-automatic detection if an instantiation is possible.
|
||||
* Requires help by the template to be tested, which needs to define
|
||||
* a typedef member \c is_defined. The embedded metafunction Test can be used
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ namespace lib {
|
|||
char content_[siz];
|
||||
void* ptr() { return &content_; }
|
||||
|
||||
virtual ~Buffer() {}
|
||||
virtual ~Buffer() {} ///< this is an ABC with VTable
|
||||
virtual bool isValid() const =0;
|
||||
virtual bool empty() const =0;
|
||||
virtual BaseP getBase() const =0;
|
||||
|
|
|
|||
|
|
@ -79,51 +79,51 @@ namespace lumiera {
|
|||
P ( ) : BASE() {}
|
||||
template<class Y> explicit P (Y* p) : BASE(p) {}
|
||||
template<class Y, class D> P (Y* p, D d) : BASE(p,d){}
|
||||
|
||||
|
||||
P (P const& r) : BASE(r) {}
|
||||
template<class Y> P (shared_ptr<Y> const& r) : BASE(r) {}
|
||||
template<class Y> explicit P (weak_ptr<Y> const& wr) : BASE(wr) {}
|
||||
template<class Y> explicit P (std::auto_ptr<Y> & ar) : BASE(ar) {}
|
||||
|
||||
template<class Y> explicit P (std::auto_ptr<Y> & ar) : BASE(ar) {}
|
||||
|
||||
P& operator= (P const& r) { BASE::operator= (r); return *this; }
|
||||
|
||||
P& operator= (P const& r) { BASE::operator= (r); return *this; }
|
||||
template<class Y> P& operator=(shared_ptr<Y> const& sr) { BASE::operator= (sr); return *this; }
|
||||
template<class Y> P& operator=(std::auto_ptr<Y> & ar) { BASE::operator= (ar); return *this; }
|
||||
|
||||
|
||||
TAR* get() const { return dynamic_cast<TAR*> (BASE::get()); }
|
||||
TAR& operator*() const { return *get(); }
|
||||
TAR* operator->() const { return get(); }
|
||||
|
||||
|
||||
void swap(P& b) { BASE::swap (b);}
|
||||
|
||||
|
||||
|
||||
private: /* === friend operators injected into enclosing namespace for ADL === */
|
||||
template<typename _O_>
|
||||
friend inline bool
|
||||
operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); }
|
||||
|
||||
|
||||
template<typename _O_>
|
||||
friend inline bool
|
||||
operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); }
|
||||
|
||||
|
||||
template<typename _O_>
|
||||
friend inline bool
|
||||
operator< (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p < *q; } ///< @note deliberately not allowing comparison on NIL ////TICKET #307 : problem with equality test in associative containers, where equal(a,b) := !(a < b) && !(b < a)
|
||||
|
||||
|
||||
template<typename _O_>
|
||||
friend inline bool
|
||||
operator> (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *q < *p; }
|
||||
|
||||
|
||||
template<typename _O_>
|
||||
friend inline bool
|
||||
operator<= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p <= *q;}
|
||||
|
||||
|
||||
template<typename _O_>
|
||||
friend inline bool
|
||||
operator>= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p >= *q;}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lumiera
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -389,21 +389,21 @@ namespace lib {
|
|||
}
|
||||
|
||||
template<class IMP, typename A1>
|
||||
PolymorphicValue (IMP*, A1& a1)
|
||||
PolymorphicValue (IMP*, A1 a1)
|
||||
{
|
||||
REQUIRE (siz >= sizeof(IMP));
|
||||
new(&buf_) IMP (a1);
|
||||
}
|
||||
|
||||
template<class IMP, typename A1, typename A2>
|
||||
PolymorphicValue (IMP*, A1& a1, A2& a2)
|
||||
PolymorphicValue (IMP*, A1 a1, A2 a2)
|
||||
{
|
||||
REQUIRE (siz >= sizeof(IMP));
|
||||
new(&buf_) IMP (a1,a2);
|
||||
}
|
||||
|
||||
template<class IMP, typename A1, typename A2, typename A3>
|
||||
PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3)
|
||||
PolymorphicValue (IMP*, A1 a1, A2 a2, A3 a3)
|
||||
{
|
||||
REQUIRE (siz >= sizeof(IMP));
|
||||
new(&buf_) IMP (a1,a2,a3);
|
||||
|
|
@ -444,13 +444,13 @@ namespace lib {
|
|||
Adapter() : IMP() { }
|
||||
|
||||
template<typename A1>
|
||||
Adapter (A1& a1) : IMP(a1) { }
|
||||
Adapter (A1 a1) : IMP(a1) { }
|
||||
|
||||
template<typename A1, typename A2>
|
||||
Adapter (A1& a1, A2& a2) : IMP(a1,a2) { }
|
||||
Adapter (A1 a1, A2 a2) : IMP(a1,a2) { }
|
||||
|
||||
template<typename A1, typename A2, typename A3>
|
||||
Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { }
|
||||
Adapter (A1 a1, A2 a2, A3 a3) : IMP(a1,a2,a3) { }
|
||||
|
||||
/* using default copy and assignment */
|
||||
};
|
||||
|
|
@ -505,31 +505,31 @@ namespace lib {
|
|||
static PolymorphicValue
|
||||
build ()
|
||||
{
|
||||
Adapter<IMP>* type_to_build_in_buffer;
|
||||
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||
return PolymorphicValue (type_to_build_in_buffer);
|
||||
}
|
||||
|
||||
template<class IMP, typename A1>
|
||||
static PolymorphicValue
|
||||
build (A1& a1)
|
||||
build (A1 a1)
|
||||
{
|
||||
Adapter<IMP>* type_to_build_in_buffer;
|
||||
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||
return PolymorphicValue (type_to_build_in_buffer, a1);
|
||||
}
|
||||
|
||||
template<class IMP, typename A1, typename A2>
|
||||
static PolymorphicValue
|
||||
build (A1& a1, A2& a2)
|
||||
build (A1 a1, A2 a2)
|
||||
{
|
||||
Adapter<IMP>* type_to_build_in_buffer;
|
||||
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||
return PolymorphicValue (type_to_build_in_buffer, a1,a2);
|
||||
}
|
||||
|
||||
template<class IMP, typename A1, typename A2, typename A3>
|
||||
static PolymorphicValue
|
||||
build (A1& a1, A2& a2, A3& a3)
|
||||
build (A1 a1, A2 a2, A3 a3)
|
||||
{
|
||||
Adapter<IMP>* type_to_build_in_buffer;
|
||||
Adapter<IMP>* type_to_build_in_buffer(0);
|
||||
return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@
|
|||
/** @file result.hpp
|
||||
** Intermediary value object to represent the result of an operation.
|
||||
** This operation might have produced a value result or failed with an exception.
|
||||
** Typically, the Result token used \em inline -- immediately either invoking one
|
||||
** of the member function or employing the built-in result type conversion. It
|
||||
** will be copyable iff the result value is copyable. There is an implicit
|
||||
** Typically, the Result token is used \em inline -- immediately either invoking
|
||||
** one of the member function or employing the built-in result type conversion.
|
||||
** It will be copyable iff the result value is copyable. There is an implicit
|
||||
** valid or failure state, which can be tested. Any attempt to get the value
|
||||
** of an invalid result token will cause in an exception to be thrown.
|
||||
**
|
||||
|
|
@ -58,11 +58,10 @@ namespace lib {
|
|||
|
||||
|
||||
/**
|
||||
* Result value and status of some operation.
|
||||
* It can be created for passing a result produced
|
||||
* by the operation, or the failure to do so. The value
|
||||
* can be retrieved by implicit or explicit conversion.
|
||||
* @throws on any attempt to access the value in case of failure
|
||||
* Optional Result value or status of some operation.
|
||||
* It can be created for passing a result produced by the operation, or the
|
||||
* failure to do so. The value can be retrieved by implicit or explicit conversion.
|
||||
* @throw error::State on any attempt to access the value in case of failure
|
||||
* @warning this class has a lot of implicit conversions;
|
||||
* care should be taken when defining functions
|
||||
* to take Result instances as parameter....
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ namespace lib {
|
|||
pointer address(reference r) const { return par_.address(r); }
|
||||
const_pointer address(const_reference cr) const { return par_.address(cr); }
|
||||
pointer allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
|
||||
void deallocate(pointer p, size_type n) { return par_.deallocate(p,n); }
|
||||
void destroy(pointer p) { return par_.destroy(p); }
|
||||
void deallocate(pointer p, size_type n) { par_.deallocate(p,n); }
|
||||
void destroy(pointer p) { par_.destroy(p); }
|
||||
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -173,11 +173,21 @@ namespace lib {
|
|||
return *obj;
|
||||
}
|
||||
|
||||
TY&
|
||||
create (TY const& o) ///< place new content object using copy ctor
|
||||
{
|
||||
ASSERT (!created_);
|
||||
TY * obj = new(content_) TY(o);
|
||||
++created_;
|
||||
return *obj;
|
||||
}
|
||||
|
||||
void
|
||||
clear ()
|
||||
{
|
||||
if (created_)
|
||||
get()->~TY();
|
||||
created_ = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
302
src/lib/simple-allocator.hpp
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
SIMPLE-ALLOCATOR.hpp - frontend for plain explicit allocations
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file simple-allocator.hpp
|
||||
** Frontend and marker interface for allocating small objects explicitly.
|
||||
** Contrary to the TypedAllocationManager, the SimpleAllocator doesn't provide any
|
||||
** ref-counting or tracking facilities, nor does he support bulk de-allocation.
|
||||
** Each object needs to be allocated and released by explicit call.
|
||||
** The advantage over using std::allocator directly is the shortcut for (placement) construction,
|
||||
** and -- of course -- the ability to exchange the memory model at one central location.
|
||||
**
|
||||
** SimpleAllocator instances will be defined for a specific collection of types; for each of those
|
||||
** types, there will be an embedded dedicated custom allocator (currently as of 9/2011, just
|
||||
** implemented as std::allocator<TY>). Objects of these preconfigured types can be constructed
|
||||
** and destroyed through this allocator instance. Each call needs to be done explicitly, with
|
||||
** the precise, concrete type to be created or destroyed. This is especially important for
|
||||
** the releasing of objects: there is \em no support for any kind of virtual destruction.
|
||||
**
|
||||
** @see engine::BufferMetadata
|
||||
** @see TypedAllocationManager (another more elaborate custom allocation scheme)
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef LIB_SIMPLE_ALLOCATOR_H
|
||||
#define LIB_SIMPLE_ALLOCATOR_H
|
||||
|
||||
//#include "pre.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/meta/generator.hpp"
|
||||
#include "lib/meta/typelist-util.hpp"
|
||||
#include "lib/format.hpp"
|
||||
#include "lib/typed-counter.hpp"
|
||||
#include "include/logging.h"
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
using lumiera::typelist::Types;
|
||||
using lumiera::typelist::IsInList;
|
||||
using lumiera::typelist::InstantiateForEach;
|
||||
|
||||
|
||||
|
||||
/* === Policies for simple custom allocator === */
|
||||
|
||||
/**
|
||||
* Policy: use just plain heap allocations
|
||||
*/
|
||||
template<typename TY>
|
||||
class CustomAllocator
|
||||
: public std::allocator<TY>
|
||||
{ };
|
||||
|
||||
|
||||
/**
|
||||
* Policy: maintain explicit per type instance count
|
||||
* @note this imposes additional locking
|
||||
*/
|
||||
struct UseInstantiationCounting
|
||||
{
|
||||
template<class XX>
|
||||
size_t
|
||||
allocationCount() const
|
||||
{
|
||||
return allocCnt_.get<XX>();
|
||||
}
|
||||
|
||||
template<class XX>
|
||||
void
|
||||
incrementCount()
|
||||
{
|
||||
allocCnt_.inc<XX>();
|
||||
}
|
||||
|
||||
template<class XX>
|
||||
void
|
||||
decrementCount()
|
||||
{
|
||||
allocCnt_.dec<XX>();
|
||||
}
|
||||
|
||||
private:
|
||||
lib::TypedCounter allocCnt_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Policy: no additional instantiation accounting
|
||||
*/
|
||||
struct NoInstantiationCount
|
||||
{
|
||||
template<class XX> size_t allocationCount() const { return 0; }
|
||||
template<class XX> void incrementCount() { /* NOP */ }
|
||||
template<class XX> void decrementCount() { /* NOP */ }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === Allocator frontend === */
|
||||
|
||||
/**
|
||||
* Frontend for explicit allocations, using a custom allocator.
|
||||
* This template is to be instantiated for the collection of types
|
||||
* later to be allocated through this custom memory manager/model.
|
||||
* It provides convenience shortcuts for placement-construction
|
||||
* and releasing of target objects.
|
||||
*
|
||||
* @todo currently (as of 8/09) the low-level pooled allocator
|
||||
* isn't implemented; instead we do just heap allocations.
|
||||
* ////////////////////////////////////////////////////////////////////////////////////////////Ticket #835
|
||||
*/
|
||||
template<typename TYPES
|
||||
,class COUNTER = NoInstantiationCount ///< Policy: support instance accounting?
|
||||
>
|
||||
class SimpleAllocator
|
||||
: InstantiateForEach< typename TYPES::List // for each of those types...
|
||||
, CustomAllocator // ...mix in the custom allocator
|
||||
>
|
||||
, COUNTER
|
||||
{
|
||||
|
||||
/** forward plain memory allocation */
|
||||
template<class XX>
|
||||
XX *
|
||||
allocateSlot ()
|
||||
{
|
||||
TRACE (memory, "allocate %s", util::tyStr<XX>().c_str());
|
||||
XX * newStorage = CustomAllocator<XX>::allocate (1);
|
||||
COUNTER::template incrementCount<XX>();
|
||||
return newStorage;
|
||||
}
|
||||
|
||||
template<class XX>
|
||||
void
|
||||
releaseSlot (XX* entry)
|
||||
{
|
||||
TRACE (memory, "release %s", util::tyStr<XX>().c_str());
|
||||
CustomAllocator<XX>::deallocate (entry, 1);
|
||||
COUNTER::template decrementCount<XX>();
|
||||
}
|
||||
|
||||
|
||||
template<class XX>
|
||||
void
|
||||
___assertSupportedType()
|
||||
{
|
||||
typedef typename TYPES::List PreconfiguredTypes;
|
||||
typedef IsInList<XX, PreconfiguredTypes> IsSupportedType;
|
||||
|
||||
BOOST_STATIC_ASSERT (IsSupportedType::value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public: /* ==== build objects with managed allocation ==== */
|
||||
|
||||
#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \
|
||||
\
|
||||
___assertSupportedType<XX>(); \
|
||||
XX* storage = allocateSlot<XX>(); \
|
||||
try \
|
||||
{ \
|
||||
return (new(storage) _CTOR_ ); \
|
||||
} \
|
||||
catch(...) \
|
||||
{ \
|
||||
releaseSlot<XX>(storage); \
|
||||
throw; \
|
||||
}
|
||||
|
||||
template< class XX>
|
||||
XX* //_____________________
|
||||
create () ///< invoke default ctor
|
||||
{
|
||||
_EXCEPTION_SAFE_INVOKE ( XX() )
|
||||
}
|
||||
|
||||
|
||||
template< class XX, typename P1>
|
||||
XX* //___________________
|
||||
create (P1& p1) ///< invoke 1-arg ctor
|
||||
{
|
||||
_EXCEPTION_SAFE_INVOKE ( XX (p1) )
|
||||
}
|
||||
|
||||
|
||||
template< class XX
|
||||
, typename P1
|
||||
, typename P2
|
||||
>
|
||||
XX* //___________________
|
||||
create (P1& p1, P2& p2) ///< invoke 2-arg ctor
|
||||
{
|
||||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2) )
|
||||
}
|
||||
|
||||
|
||||
template< class XX
|
||||
, typename P1
|
||||
, typename P2
|
||||
, typename P3
|
||||
>
|
||||
XX* //___________________
|
||||
create (P1& p1, P2& p2, P3& p3) ///< invoke 3-arg ctor
|
||||
{
|
||||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) )
|
||||
}
|
||||
|
||||
|
||||
template< class XX
|
||||
, typename P1
|
||||
, typename P2
|
||||
, typename P3
|
||||
, typename P4
|
||||
>
|
||||
XX* //___________________
|
||||
create (P1& p1, P2& p2, P3& p3, P4& p4) ///< invoke 4-arg ctor
|
||||
{
|
||||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) )
|
||||
}
|
||||
|
||||
|
||||
template< class XX
|
||||
, typename P1
|
||||
, typename P2
|
||||
, typename P3
|
||||
, typename P4
|
||||
, typename P5
|
||||
>
|
||||
XX* //___________________
|
||||
create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5) ///< invoke 5-arg ctor
|
||||
{
|
||||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) )
|
||||
}
|
||||
|
||||
#undef _EXCEPTION_SAFE_INVOKE
|
||||
|
||||
|
||||
|
||||
template<class XX>
|
||||
void
|
||||
destroy (XX* entry)
|
||||
{
|
||||
if (!entry) return;
|
||||
___assertSupportedType<XX>();
|
||||
try
|
||||
{
|
||||
entry->~XX();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
lumiera_err errorID = lumiera_error();
|
||||
WARN (common_dbg, "dtor of %s failed: %s", util::tyStr(entry).c_str()
|
||||
, errorID );
|
||||
}
|
||||
releaseSlot<XX> (entry);
|
||||
}
|
||||
|
||||
|
||||
/** diagnostics */
|
||||
template<class XX>
|
||||
size_t
|
||||
numSlots() const
|
||||
{
|
||||
return COUNTER::template allocationCount<XX>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -110,7 +110,12 @@ namespace lumiera {
|
|||
|
||||
class TypeTag ;
|
||||
|
||||
/** placeholder definition for the contents of a data buffer */
|
||||
/**
|
||||
* placeholder type for the contents of a data buffer.
|
||||
* The actual buffer will always be provided by a
|
||||
* library implementation; throughout the engine,
|
||||
* it's just hidden behind a DataBuffer pointer.
|
||||
*/
|
||||
struct DataBuffer { };
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ using boost::hash_combine;
|
|||
|
||||
namespace lib {
|
||||
|
||||
const size_t STRING_MAX_RELEVANT = 1000;
|
||||
|
||||
|
||||
/** equality on Symbol values is defined
|
||||
* based on the content, not the address. */
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ namespace lib {
|
|||
/** safety guard: maximum number of chars to process.
|
||||
* For comparisons, hash calculations etc., when dealing
|
||||
* with raw char ptrs (typically literal values) */
|
||||
const size_t STRING_MAX_RELEVANT = 1000;
|
||||
extern const size_t STRING_MAX_RELEVANT;
|
||||
|
||||
|
||||
/* ===== to be picked up by ADL ===== */
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
namespace test {
|
||||
|
|
@ -141,6 +143,9 @@ namespace test {
|
|||
REQUIRE( !isnil(groupID) );
|
||||
TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
|
||||
|
||||
// Seed random number generator
|
||||
std::srand (std::time (NULL));
|
||||
|
||||
if (!testcases.getGroup(groupID))
|
||||
throw lumiera::error::Invalid ("empty testsuite");
|
||||
}
|
||||
|
|
|
|||
203
src/lib/time/control-impl.hpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
CONTROL-IMPL.hpp - time::control implementation building blocks
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file control-impl.hpp
|
||||
** Implementation building blocks for time modification and propagation.
|
||||
** The time::Control element allows to impose modifications to a connected
|
||||
** time value entity and at the same time publish the changes to registered
|
||||
** listeners. Due to the various flavours of actual time value entities, this
|
||||
** is a complex undertaking, which is implemented here based on policies and
|
||||
** template metaprogramming. This header/include defines two building blocks:
|
||||
** - the actual Mutator to apply the changes to the target entity
|
||||
** - a Propagator to register listeners and forward the changes.
|
||||
**
|
||||
** \par implementation technique
|
||||
**
|
||||
** The Mutator uses functor objects to encapsulate the actual modification
|
||||
** operations. When attaching to a target time entity to be manipulated, these
|
||||
** functor objects will be configured by binding them to the appropriate
|
||||
** implementation function. And picking this actual implementation is done
|
||||
** through a time::mutation::Policy element, using the concrete time entity
|
||||
** types as template parameter. Thus, the actual implementation to be used
|
||||
** is determined by the compiler, through the template specialisations
|
||||
** contained in control-policy.hpp
|
||||
**
|
||||
** @note the header control-policy.hpp with the template specialisations
|
||||
** is included way down, after the class definitions. This is done
|
||||
** so for sake of readability
|
||||
**
|
||||
** @see TimeControl_test
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef LIB_TIME_CONTROL_IMPL_H
|
||||
#define LIB_TIME_CONTROL_IMPL_H
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
namespace mutation {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation building block: impose changes to a Time element.
|
||||
* The purpose of the Mutator is to attach a target time entity,
|
||||
* which then will be subject to any received value changes,
|
||||
* offsets and grid nudging. The actual attachment is to be
|
||||
* performed in a subclass, by using the Mutation interface.
|
||||
* When attaching to a target, the Mutator will be outfitted
|
||||
* with a set of suitable functors, incorporating the specific
|
||||
* behaviour for the concrete combination of input changes
|
||||
* ("source values") and target object type. This works by
|
||||
* binding to the appropriate implementation functionality,
|
||||
* guided by a templated policy class. After installing
|
||||
* these functors, these decisions remains opaque and
|
||||
* encapsulated within the functor objects, so the
|
||||
* mutator object doesn't need to carry this
|
||||
* type information on the interface
|
||||
*/
|
||||
template<class TI>
|
||||
class Mutator
|
||||
: public Mutation
|
||||
{
|
||||
typedef function<TI(TI const&)> ValueSetter;
|
||||
typedef function<TI(Offset const&)> Ofsetter;
|
||||
typedef function<TI(int)> Nudger;
|
||||
|
||||
protected:
|
||||
mutable ValueSetter setVal_;
|
||||
mutable Ofsetter offset_;
|
||||
mutable Nudger nudge_;
|
||||
|
||||
void
|
||||
ensure_isArmed() const
|
||||
{
|
||||
if (!setVal_)
|
||||
throw error::State("feeding time/value change "
|
||||
"while not (yet) connected to any target to change"
|
||||
,error::LUMIERA_ERROR_UNCONNECTED);
|
||||
}
|
||||
|
||||
|
||||
template<class TAR>
|
||||
void bind_to (TAR& target) const;
|
||||
|
||||
void unbind();
|
||||
|
||||
// using default construction and copy
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation building block: propagate changes to listeners.
|
||||
* The Propagator manages a set of callback signals, allowing to
|
||||
* propagate notifications for changed Time values.
|
||||
*
|
||||
* There are no specific requirements on the acceptable listeners,
|
||||
* besides exposing a function-call operator to feed the changed
|
||||
* time value to. Both Mutator and Propagator employ one primary
|
||||
* template parameter, which is the type of the time values
|
||||
* to be fed in and propagated.
|
||||
*/
|
||||
template<class TI>
|
||||
class Propagator
|
||||
{
|
||||
typedef function<void(TI const&)> ChangeSignal;
|
||||
typedef std::vector<ChangeSignal> ListenerList;
|
||||
|
||||
ListenerList listeners_;
|
||||
|
||||
public:
|
||||
/** install notification receiver */
|
||||
template<class SIG>
|
||||
void
|
||||
attach (SIG const& toNotify)
|
||||
{
|
||||
ChangeSignal newListener (ref(toNotify));
|
||||
listeners_.push_back (newListener);
|
||||
}
|
||||
|
||||
/** disconnect any observers */
|
||||
void
|
||||
disconnnect()
|
||||
{
|
||||
listeners_.clear();
|
||||
}
|
||||
|
||||
/** publish a change */
|
||||
TI
|
||||
operator() (TI const& changedVal) const
|
||||
{
|
||||
typedef typename ListenerList::const_iterator Iter;
|
||||
Iter p = listeners_.begin();
|
||||
Iter e = listeners_.end();
|
||||
|
||||
for ( ; p!=e; ++p )
|
||||
(*p) (changedVal);
|
||||
return changedVal;
|
||||
}
|
||||
|
||||
// using default construction and copy
|
||||
};
|
||||
|
||||
}}} // lib::time::mutation
|
||||
|
||||
|
||||
/* ===== Definition of actual operations ===== */
|
||||
#include "lib/time/control-policy.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
template<class TI>
|
||||
template<class TAR>
|
||||
void
|
||||
lib::time::mutation::Mutator<TI>::bind_to (TAR& target) const
|
||||
{
|
||||
using lib::time::mutation::Policy;
|
||||
|
||||
setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target);
|
||||
offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target);
|
||||
nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target);
|
||||
}
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
lib::time::mutation::Mutator<TI>::unbind()
|
||||
{
|
||||
setVal_ = ValueSetter();
|
||||
offset_ = Ofsetter();
|
||||
nudge_ = Nudger();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
400
src/lib/time/control-policy.hpp
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
CONTROL-POLICY.hpp - detail definition of actual time changing functionality
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file control-policy.hpp
|
||||
** Definition of special cases when imposing a change onto concrete time values.
|
||||
** The time::Control element allows to impose modifications to a connected
|
||||
** time value entity and at the same time publish the changes to registered
|
||||
** listeners. Due to the various flavours of actual time value entities, this
|
||||
** is a complex undertaking, which is implemented here based on policies and
|
||||
** template metaprogramming.
|
||||
**
|
||||
** The header control-impl.hpp defines the building blocks for time::Control
|
||||
** and then includes this header here to get the concrete template specialisations
|
||||
** for time::mutation::Policy. This policy class is templated by time entity types
|
||||
** - for \c TI, the \em nominal value type used on the time::Control interface
|
||||
** - for \c SRC, the actual type of values to impose as \em change
|
||||
** - for \c TAR, the target time value's type, receiving those changes.
|
||||
**
|
||||
** \par mutating a time value entity
|
||||
**
|
||||
** Actually imposing a change to the attached time value entity involves several
|
||||
** steps. Each of these steps might be adapted specifically, in accordance to
|
||||
** the concrete time value types involved.
|
||||
** - TimeValue, Time
|
||||
** - Offset
|
||||
** - Duration
|
||||
** - TimeSpan
|
||||
** - QuTime (grid aligned time value)
|
||||
** - QuTimeSpan (planned as of 6/2011)
|
||||
**
|
||||
** Moreover, the combination of types needs to be taken into account. For example,
|
||||
** it doesn't make sense to apply a Duration value as change to a TimeValue, which
|
||||
** has no duration (temporal extension). While a TimeSpan might receive a Duration
|
||||
** change, but behaves differently when imposing a Time to manipulate the starting
|
||||
** point of the time interval given by the TimeSpan.
|
||||
**
|
||||
** Incoming changes might be of any of the aforementioned types, and in addition,
|
||||
** we might receive \em nudging, which means to increment or decrement the target
|
||||
** time value in discrete steps. After maybe adapting these incoming change values,
|
||||
** they may be actually \em imposed to the target. In all cases, this is delegated
|
||||
** to the time::Mutation base class, which is declared fried to TimeValue and thus
|
||||
** has the exceptional ability to manipulate time values, which otherwise are defined
|
||||
** to be immutable. Additionally, these protected functions in the time::Mutation
|
||||
** baseclass also know how to handle \em nudge values, either by using the native
|
||||
** (embedded) time grid of a quantised time value, or by falling back to a standard
|
||||
** nudging grid, defined in the session context (TODO as of 6/2011). //////////////////////TICKET #810
|
||||
**
|
||||
** After (maybe) imposing a change to the target, the change \em notification value
|
||||
** needs to be built. This is the time value entity to be forwarded to registered
|
||||
** listeners. This notification value has to be given as the type \c TI, in accordance
|
||||
** to the \c time::Control<TI> frontend definition used in the concrete usage situation.
|
||||
** As this type \c TI might be different to the actual target type, and again different
|
||||
** to the type of the change handed in, in some cases this involves a second conversion
|
||||
** step, to represent the current state of the target \c TAR in terms of the interface
|
||||
** type \c TI.
|
||||
**
|
||||
** \par changing quantised (grid aligned) time entities
|
||||
**
|
||||
** The time::Control element includes the capability to handle grid aligned time values,
|
||||
** both as target and as change/notification value. This ability is compiled in conditionally,
|
||||
** as including mutation.hpp causes several additional includes, which isn't desirable when
|
||||
** it comes just to changing plain time values. Thus, to get these additional specialisations,
|
||||
** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
|
||||
** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
|
||||
**
|
||||
** As a special convention, any \em quantised (grid aligned) types involved in these
|
||||
** time changes will be \em materialised, whenever a type conversion happens. Generally
|
||||
** speaking, a quantised time value contains an (opaque) raw time value, plus a reference
|
||||
** to a time grid definition to apply. In this context \em materialising means actually
|
||||
** to apply this time grid to yield a grid aligned value. Thus, when using a quantised
|
||||
** value to impose as change (or to receive a change), its grid aligning nature
|
||||
** becomes effective, by applying the \em current definition of the grid to
|
||||
** create a fixed (materialised) time value, aligned to that current grid.
|
||||
**
|
||||
** @todo 6/2011 include all the special cases for QuTimeSpan ////////////////////TICKET #760
|
||||
**
|
||||
** @see TimeControl_test
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef LIB_TIME_CONTROL_POLICY_H
|
||||
#define LIB_TIME_CONTROL_POLICY_H
|
||||
|
||||
#include "lib/meta/util.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <tr1/functional>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
|
||||
namespace mutation {
|
||||
|
||||
using boost::disable_if;
|
||||
using lumiera::typelist::is_sameType;
|
||||
using std::tr1::placeholders::_1;
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::ref;
|
||||
|
||||
|
||||
|
||||
namespace { // metaprogramming helpers to pick a suitable implementation branch...
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
isDuration()
|
||||
{
|
||||
return is_sameType<T,Duration>::value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
isTimeSpan()
|
||||
{
|
||||
return is_sameType<T,TimeSpan>::value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T const&
|
||||
maybeMaterialise (T const& non_grid_aligned_TimeValue)
|
||||
{
|
||||
return non_grid_aligned_TimeValue;
|
||||
}
|
||||
|
||||
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||
inline QuTime
|
||||
maybeMaterialise (QuTime const& alignedTime)
|
||||
{
|
||||
PQuant const& grid(alignedTime);
|
||||
return QuTime(grid->materialise(alignedTime), grid);
|
||||
}
|
||||
#endif //--quantised-time-support
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation policy: how to build a new
|
||||
* notification value of type \c TI, given a
|
||||
* target time value entity of type \c TAR
|
||||
*/
|
||||
template<class TI, class TAR>
|
||||
struct Builder
|
||||
{
|
||||
static TI
|
||||
buildChangedValue (TAR const& target)
|
||||
{
|
||||
return TI(target);
|
||||
}
|
||||
};
|
||||
template<class TAR>
|
||||
struct Builder<TimeSpan, TAR>
|
||||
{
|
||||
static TimeSpan
|
||||
buildChangedValue (TAR const& target)
|
||||
{
|
||||
return TimeSpan (target, Duration::NIL);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Builder<TimeSpan, Duration>
|
||||
{
|
||||
static TimeSpan
|
||||
buildChangedValue (Duration const& targetDuration)
|
||||
{
|
||||
return TimeSpan (Time::ZERO, targetDuration);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Builder<TimeSpan, TimeSpan>
|
||||
{
|
||||
static TimeSpan
|
||||
buildChangedValue (TimeSpan const& target)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
};
|
||||
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||
template<class TAR>
|
||||
struct Builder<QuTime, TAR>
|
||||
{
|
||||
static QuTime
|
||||
buildChangedValue (TAR const& target)
|
||||
{
|
||||
return QuTime (target
|
||||
,getDefaultGridFallback() //////////////////TICKET #810
|
||||
);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Builder<QuTime, QuTime>
|
||||
{
|
||||
static QuTime
|
||||
buildChangedValue (QuTime const& target)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
};
|
||||
#endif //--quantised-time-support
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Policy to tie the various detail policies together
|
||||
* for providing actual value change operations.
|
||||
* The standard case uses the (inherited) time::Mutation
|
||||
* base implementation to impose a new value onto the
|
||||
* target entity and then uses the Builder policy to
|
||||
* create a notification value reflecting this change.
|
||||
*/
|
||||
template<class TI, class TAR>
|
||||
struct Link
|
||||
: Mutation
|
||||
, Builder<TI,TAR>
|
||||
{
|
||||
|
||||
template<class SRC>
|
||||
static TI
|
||||
processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change
|
||||
{
|
||||
imposeChange (target, maybeMaterialise(change));
|
||||
return buildChangedValue (maybeMaterialise(target));
|
||||
}
|
||||
|
||||
static TI
|
||||
useLengthAsChange (TAR& target, TimeSpan const& change)
|
||||
{
|
||||
return processValueChange(target, change.duration());
|
||||
}
|
||||
|
||||
static TI
|
||||
mutateLength (TimeSpan& target, Duration const& change)
|
||||
{
|
||||
imposeChange (target.duration(), change);
|
||||
return Builder<TI,TimeSpan>::buildChangedValue(target);
|
||||
}
|
||||
|
||||
static TimeSpan
|
||||
mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
|
||||
{
|
||||
imposeChange (target.duration(), change.duration());
|
||||
imposeChange (target,change.start());
|
||||
return Builder<TimeSpan,TimeSpan>::buildChangedValue(target);
|
||||
}
|
||||
|
||||
static TI
|
||||
dontChange (TAR& target) ///< @note: not touching the target
|
||||
{
|
||||
return buildChangedValue(target);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Policy how to impose changes onto a connected target time value entity
|
||||
* This policy will be parametrised with the concrete time entity types
|
||||
* involved in the usage situation of time::Control. The purpose of the
|
||||
* policy is to \em bind a functor object to the concrete implementation
|
||||
* of the value change applicable for this combination of types.
|
||||
* This functor will then be stored within time::Control and
|
||||
* invoked for each actual value change.
|
||||
* @param TI the nominal (interface) type of the change, propagated to listeners
|
||||
* @param SRC the actual type of the change to be imposed
|
||||
* @param TAR the actual type of the target entity to receive the changes
|
||||
* @note typically either SRC is identical to TI, or it is an
|
||||
* time::Offset, or an int for \em nudging the target
|
||||
*/
|
||||
template<class TI, class SRC, class TAR>
|
||||
struct Policy
|
||||
{
|
||||
static function<TI(SRC const&)>
|
||||
buildChangeHandler (TAR& target)
|
||||
{
|
||||
return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// special treatment of Durations as target------------------------------------
|
||||
|
||||
namespace {
|
||||
template<class T>
|
||||
struct canMutateDuration
|
||||
{
|
||||
static const bool value = is_sameType<T,Duration>::value
|
||||
|| is_sameType<T,Offset>::value
|
||||
|| is_sameType<T,int>::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct canReceiveDuration
|
||||
{
|
||||
static const bool value = is_sameType<T,Duration>::value
|
||||
|| is_sameType<T,TimeSpan>::value;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* special case: a Duration target value can't be changed by plain time values.
|
||||
* This specialisation is \em not used (\c disable_if ) when the given change (SRC)
|
||||
* is applicable to a Duration in a sensible way. We either define explicit
|
||||
* specialisations (for TimeSpan) or fall back to the default in these cases.
|
||||
*/
|
||||
template<class TI, class SRC>
|
||||
struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>,
|
||||
Duration>::type>
|
||||
{
|
||||
static function<TI(SRC const&)>
|
||||
buildChangeHandler (Duration& target)
|
||||
{
|
||||
return bind (Link<TI,Duration>::dontChange, ref(target) );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* special case: a Duration change value can't be imposed to a plain time value.
|
||||
* In these cases, we even propagate a Duration::ZERO to the listeners.
|
||||
* As above, there are exceptions to this behaviour, where a Duration change
|
||||
* can sensibly be applied.
|
||||
*/
|
||||
template<class TAR>
|
||||
struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>,
|
||||
Duration>::type, TAR>
|
||||
{
|
||||
static function<Duration(Duration const&)>
|
||||
buildChangeHandler (TAR&)
|
||||
{
|
||||
return bind ( ignore_change_and_return_Zero );
|
||||
}
|
||||
|
||||
static Duration
|
||||
ignore_change_and_return_Zero()
|
||||
{
|
||||
return Duration::NIL;
|
||||
}
|
||||
};
|
||||
|
||||
template<class TI>
|
||||
struct Policy<TI,TimeSpan,Duration>
|
||||
{
|
||||
static function<TI(TimeSpan const&)>
|
||||
buildChangeHandler (Duration& target)
|
||||
{
|
||||
return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// special treatment for TimeSpan values---------------------------------------
|
||||
|
||||
template<class TI>
|
||||
struct Policy<TI,Duration,TimeSpan>
|
||||
{
|
||||
static function<TI(Duration const&)>
|
||||
buildChangeHandler (TimeSpan& target)
|
||||
{
|
||||
return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 );
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Policy<TimeSpan,TimeSpan,TimeSpan>
|
||||
{
|
||||
static function<TimeSpan(TimeSpan const&)>
|
||||
buildChangeHandler (TimeSpan& target)
|
||||
{
|
||||
return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}}} // namespace lib::time::mutation
|
||||
#endif
|
||||
256
src/lib/time/control.hpp
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
CONTROL.hpp - a life time control for feedback and mutation
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file control.hpp
|
||||
** Manipulating and monitoring time entities with life changes.
|
||||
** This is an control- and callback element to handle any kind of "running"
|
||||
** time entities. This element is to be provided by the client and then attached
|
||||
** to the target time entity as a time::Mutation. Internally, a life connection to
|
||||
** the target is built, allowing both to
|
||||
** - to manipulate the target by invoking the function operator
|
||||
** - to receive change notifications by installing a callback functor.
|
||||
**
|
||||
** The actual type of the changes and modifications is specified as template parameter;
|
||||
** when later attached to some time entity as a Mutation, the actual changes to be performed
|
||||
** depend both on this change type and the type of the target time entity (double dispatch).
|
||||
** The behaviour is similar to applying a static time::Mutation
|
||||
**
|
||||
** \par relevance
|
||||
** This control element is intended to be used for all kinds of editing and monitoring
|
||||
** of time-like entities -- be it the running time display in a GUI widget, a ruler marker
|
||||
** which can be dragged, a modifiable selection or the animated playhead cursor.
|
||||
**
|
||||
** \par usage scenarios
|
||||
** The time::Control element provides mediating functionality, but doesn't assume or provide
|
||||
** anything special regarding the usage pattern or the lifecycle, beyond the ability to
|
||||
** attach listeners, attach to a (different) target and to detach from all connections.
|
||||
** Especially, no assumptions are made about which side is the server or the client
|
||||
** and who owns the time::Control element.
|
||||
**
|
||||
** Thus an interface might accept a time::Control element \em reference (e.g. the
|
||||
** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
|
||||
** Control element and might attach listeners, while the implementation (server side)
|
||||
** will attach the Control to mutate an time value entity otherwise not disclosed
|
||||
** (e.g. the playhead position of the playback process). Of course, in this case
|
||||
** the client is responsible for keeping the Control element and all listeners
|
||||
** alive, and to invoke Control#disconnect prior to destroying the element.
|
||||
**
|
||||
** Of course, the reversed usage situation would be possible as well: an interface
|
||||
** exposing a time::Control, thus allowing to attach target and listeners, while the
|
||||
** actual changes will originate somewhere within the service implementation.
|
||||
**
|
||||
** Another usage pattern would be to expose a time::Control \c const&, allowing only to
|
||||
** impose changes, but not to change the target or listener attachments. To the contrary,
|
||||
** when exposing only a time::Mutation \c const& through an interface allows only to
|
||||
** attach new target elements, but not to change listeners or feed any value changes.
|
||||
**
|
||||
** Using time::Control as an implementation building block and just exposing the
|
||||
** change (function) operators or the listener attachment through an forwarding sub
|
||||
** interface is another option.
|
||||
**
|
||||
** @note time::Control is default constructible and freely copyable.
|
||||
**
|
||||
**
|
||||
** \par changing quantised (grid aligned) time entities
|
||||
**
|
||||
** The time::Control element includes the functionality to handle grid aligned time values,
|
||||
** both as target and as change/notification value. This ability is compiled in conditionally,
|
||||
** as including mutation.hpp causes several additional includes, which isn't desirable when
|
||||
** it comes just to changing plain time values. Thus, to get these additional specialisations,
|
||||
** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
|
||||
** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
|
||||
**
|
||||
** \par implementation notes
|
||||
** - the validity of a given combination of change and target is checked immediately,
|
||||
** when connecting to the target. Depending on the situation, the actual changes later
|
||||
** are subject to specific treatment (e.g. frame quantisation)
|
||||
** - by default time::Control is <b>not threadsafe</b>. But, as each change is basically
|
||||
** processed within its own call context (function invocation), parallelism is only
|
||||
** a concern with respect to the value finally visible within the target.
|
||||
** - the change notification is processed right away, after applying the change to the
|
||||
** target; of course there is a race between applying the value and building the
|
||||
** response value passed on as notification. In all cases, the effective change
|
||||
** notification value is built from the state of the target after applying
|
||||
** the change, which might or might not reflect the change value passed in.
|
||||
**
|
||||
** @todo include support for QuTimeSpan values ////////////////////TICKET #760
|
||||
**
|
||||
** @see TimeControl_test
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef LIB_TIME_CONTROL_H
|
||||
#define LIB_TIME_CONTROL_H
|
||||
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/time/control-impl.hpp"
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Frontend/Interface: controller-element to retrieve
|
||||
* and change running time values. time::Control is
|
||||
* a mediator element, which can be attached to some
|
||||
* time value entities as \em mutation, and at the
|
||||
* same time allows to register listeners. When
|
||||
* configured this way, \em changes may be fed
|
||||
* to the function operator(s). These changes
|
||||
* will be imposed to the connected target
|
||||
* and the result propagated to the listeners.
|
||||
*
|
||||
* @see time::Mutation
|
||||
* @see time::TimeSpan#accept(Mutation const&)
|
||||
*/
|
||||
template<class TI>
|
||||
class Control
|
||||
: public mutation::Mutator<TI>
|
||||
{
|
||||
mutation::Propagator<TI> notifyListeners_;
|
||||
|
||||
virtual void change (Duration&) const;
|
||||
virtual void change (TimeSpan&) const;
|
||||
virtual void change (QuTime&) const;
|
||||
|
||||
public:
|
||||
void operator() (TI const&) const;
|
||||
void operator() (Offset const&) const;
|
||||
void operator() (int) const;
|
||||
|
||||
|
||||
/** install a callback functor to be invoked as notification
|
||||
* for any changes imposed onto the observed time entity.
|
||||
* @param toNotify object with \c operator()(TI const&) */
|
||||
template<class SIG>
|
||||
void connectChangeNotification (SIG const& toNotify);
|
||||
|
||||
/** disconnect from observed entity and
|
||||
* cease any change notification */
|
||||
void disconnnect();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* === forward to implementation === */
|
||||
|
||||
/** impose a new value to the connected target.
|
||||
* If applicable, the target will afterwards reflect
|
||||
* that change, and listeners will be notified, passing
|
||||
* the target's new state.
|
||||
* @throw error::State if not connected to a target
|
||||
* @note the actual change in the target also depends
|
||||
* on the concrete target type and the type of
|
||||
* the change. By default, the time value is
|
||||
* changed; this may include grid alignment.
|
||||
*/
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::operator () (TI const& newValue) const
|
||||
{
|
||||
this->ensure_isArmed();
|
||||
notifyListeners_(
|
||||
this->setVal_(newValue));
|
||||
}
|
||||
|
||||
/** impose an offset to the connected target.
|
||||
* If applicable, the target will be adjusted by the
|
||||
* time offset, and listeners will be notified.
|
||||
* @throw error::State if not connected to a target
|
||||
*/
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::operator () (Offset const& adjustment) const
|
||||
{
|
||||
this->ensure_isArmed();
|
||||
notifyListeners_(
|
||||
this->offset_(adjustment));
|
||||
}
|
||||
|
||||
/** nudge the connected target by the given offset steps,
|
||||
* using either the target's own grid (when quantised),
|
||||
* or a 'natural' nudge grid
|
||||
* @throw error::State if not connected to a target
|
||||
*/
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::operator () (int offset_by_steps) const
|
||||
{
|
||||
this->ensure_isArmed();
|
||||
notifyListeners_(
|
||||
this->nudge_(offset_by_steps));
|
||||
}
|
||||
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::disconnnect()
|
||||
{
|
||||
notifyListeners_.disconnect();
|
||||
this->unbind();
|
||||
}
|
||||
|
||||
template<class TI>
|
||||
template<class SIG>
|
||||
void
|
||||
Control<TI>::connectChangeNotification (SIG const& toNotify)
|
||||
{
|
||||
if (this->offset_)
|
||||
{ // we're already connected: thus propagate current value
|
||||
TI currentValue = this->offset_(Offset::ZERO);
|
||||
toNotify (currentValue);
|
||||
}
|
||||
notifyListeners_.attach (toNotify);
|
||||
}
|
||||
|
||||
|
||||
/* ==== Implementation of the Mutation interface ==== */
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::change (Duration& targetDuration) const
|
||||
{
|
||||
this->bind_to (targetDuration);
|
||||
}
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::change (TimeSpan& targetInterval) const
|
||||
{
|
||||
this->bind_to (targetInterval);
|
||||
}
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::change (QuTime& targetQuTime) const
|
||||
{
|
||||
this->bind_to (targetQuTime);
|
||||
}
|
||||
|
||||
}} // lib::time
|
||||
#endif
|
||||
|
|
@ -41,6 +41,9 @@ namespace time {
|
|||
const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
|
||||
const Time Time::ZERO;
|
||||
|
||||
const Offset Offset::ZERO (Time::ZERO);
|
||||
|
||||
|
||||
|
||||
/** convenience constructor to build an
|
||||
* internal Lumiera Time value from the usual parts
|
||||
|
|
@ -116,14 +119,21 @@ namespace time {
|
|||
}
|
||||
|
||||
|
||||
/** duration of the given number of frames */
|
||||
Duration::Duration (ulong count, FrameRate const& fps)
|
||||
: Offset(TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL)))
|
||||
/** offset by the given number of frames. */
|
||||
Offset::Offset (int64_t count, FrameRate const& fps)
|
||||
: TimeValue (count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
|
||||
: _raw(Duration::NIL))
|
||||
{ }
|
||||
|
||||
/** duration of the given number of frames.
|
||||
* @note always positive; count used absolute */
|
||||
Duration::Duration (int64_t count, FrameRate const& fps)
|
||||
: TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL))
|
||||
{ }
|
||||
|
||||
|
||||
/** constant to indicate "no duration" */
|
||||
const Duration Duration::NIL = Offset(TimeValue(0));
|
||||
const Duration Duration::NIL (Time::ZERO);
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -48,17 +48,6 @@ namespace time {
|
|||
Mutation::~Mutation() { } // emit VTable here....
|
||||
|
||||
|
||||
/** @internal actually force a change
|
||||
* into a target time entity to mutate.
|
||||
* Mutation is declared fried to TimeValue
|
||||
* and thus is allowed to influence the basic
|
||||
* value stored in each time entity
|
||||
*/
|
||||
void
|
||||
Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet)
|
||||
{
|
||||
target = valueToSet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -157,14 +146,14 @@ namespace time {
|
|||
virtual void
|
||||
change (Duration& target) const
|
||||
{
|
||||
imposeChange (target, TimeVar(target)+=adjustment_);
|
||||
imposeChange (target, adjustment_);
|
||||
}
|
||||
|
||||
|
||||
virtual void
|
||||
change (TimeSpan& target) const
|
||||
{
|
||||
imposeChange (target, TimeVar(target)+=adjustment_);
|
||||
imposeChange (target, adjustment_);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -173,7 +162,7 @@ namespace time {
|
|||
virtual void
|
||||
change (QuTime& target) const
|
||||
{
|
||||
imposeChange (target, TimeVar(target)+=adjustment_);
|
||||
imposeChange (target, adjustment_);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -205,7 +194,7 @@ namespace time {
|
|||
/**
|
||||
* concrete time value mutation:
|
||||
* nudge target value by the given number of 'steps',
|
||||
* relative to either the given grid.
|
||||
* relative to the given grid.
|
||||
*/
|
||||
class NudgeMutation
|
||||
: public ImposeOffsetMutation
|
||||
|
|
@ -234,6 +223,8 @@ namespace time {
|
|||
* @note currently the natural grid is hard wired,
|
||||
* just interpreting the step parameter as
|
||||
* offset in seconds.
|
||||
* @see mutation#imposeChange (TimeValue, int)
|
||||
* @see mutation#imposeChange (QuTime, int)
|
||||
*/
|
||||
class NaturalNudgeMutation
|
||||
: public ClonableMutation
|
||||
|
|
@ -243,31 +234,22 @@ namespace time {
|
|||
virtual void
|
||||
change (Duration& target) const
|
||||
{
|
||||
imposeChange (target, TimeVar(target)+=Time(FSecs(steps_)));
|
||||
imposeChange (target, steps_);
|
||||
}
|
||||
|
||||
|
||||
virtual void
|
||||
change (TimeSpan& target) const
|
||||
{
|
||||
imposeChange (target, TimeVar(target)+=Time(FSecs(steps_)));
|
||||
imposeChange (target, steps_);
|
||||
}
|
||||
|
||||
|
||||
/** Special treatment: use the quantised time's own grid;
|
||||
* retrieve the corresponding grid point, offset it by the step-parameter,
|
||||
* then retrieve the corresponding time from the quantised time's
|
||||
* underlying quantiser (grid).
|
||||
* @note when the #steps_ parameter is zero, what happens here effectively
|
||||
* is the materialisation of the quantised target time, i.e. making
|
||||
* the quantisation explicit and storing the resulting value. */
|
||||
/** @note special treatment: use the quantised time's own grid */
|
||||
virtual void
|
||||
change (QuTime& target) const
|
||||
{
|
||||
PQuant const& grid (target);
|
||||
int64_t originalGridPoint = grid->gridPoint(target);
|
||||
int64_t adjustedGridPoint = originalGridPoint + steps_;
|
||||
imposeChange (target, grid->timeOf (adjustedGridPoint));
|
||||
imposeChange (target, steps_);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,10 @@ namespace time {
|
|||
static EncapsulatedMutation nudge (int adjustment, Symbol gridID);
|
||||
|
||||
protected:
|
||||
static void imposeChange (TimeValue&, TimeValue const&);
|
||||
static TimeValue& imposeChange (TimeValue&, TimeValue const&);
|
||||
static TimeValue& imposeChange (TimeValue&, Offset const&);
|
||||
static TimeValue& imposeChange (TimeValue&, int);
|
||||
static TimeValue& imposeChange (QuTime&, int);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -126,5 +129,53 @@ namespace time {
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
/* === implementing the actual changes === */
|
||||
|
||||
/** @internal actually force a change into a target time entity to mutate.
|
||||
* Mutation is declared fried to TimeValue and thus is allowed to influence
|
||||
* the basic value stored in each time entity
|
||||
*/
|
||||
inline TimeValue&
|
||||
Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet)
|
||||
{
|
||||
return target = valueToSet;
|
||||
}
|
||||
|
||||
/** @internal variation to mutate a target time value by applying an offset */
|
||||
inline TimeValue&
|
||||
Mutation::imposeChange (TimeValue& target, Offset const& offset)
|
||||
{
|
||||
return imposeChange (target, TimeVar(target) += offset);
|
||||
}
|
||||
|
||||
/** @internal nudge a target time value by a step wise offset.
|
||||
* The standard case uses a fixed offset of 1 second per step //////////////////TICKET #810
|
||||
*/
|
||||
inline TimeValue&
|
||||
Mutation::imposeChange (TimeValue& target, int steps)
|
||||
{
|
||||
return imposeChange (target, TimeVar(target) += Time(FSecs(steps)));
|
||||
}
|
||||
|
||||
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||
/** @internal Special treatment for quantised target values:
|
||||
* use the quantised time's own grid; retrieve the corresponding grid point,
|
||||
* offset it by the step-parameter, then retrieve the corresponding time from
|
||||
* the quantised time's underlying quantiser (grid) and impose that as change.
|
||||
* @note when the #steps parameter is zero, what happens here effectively
|
||||
* is the materialisation of the quantised target time, i.e. making
|
||||
* the quantisation explicit and storing the resulting value. */
|
||||
inline TimeValue&
|
||||
Mutation::imposeChange (QuTime& target, int steps)
|
||||
{
|
||||
PQuant const& grid (target);
|
||||
int64_t originalGridPoint = grid->gridPoint(target);
|
||||
int64_t adjustedGridPoint = originalGridPoint + steps;
|
||||
return imposeChange (target, grid->timeOf (adjustedGridPoint));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}} // lib::time
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -51,10 +51,19 @@ namespace time {
|
|||
, LUMIERA_ERROR_UNKNOWN_GRID);
|
||||
return grid_found;
|
||||
}
|
||||
|
||||
}//(End) implementation helpers
|
||||
|
||||
|
||||
PQuant
|
||||
getDefaultGridFallback()
|
||||
{
|
||||
static PQuant globalDefaultGrid (new FixedFrameQuantiser(1));
|
||||
return globalDefaultGrid; ///////////////////////TICKET #810
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Grid::~Grid() { } // hint to emit the VTable here...
|
||||
|
||||
|
|
@ -191,7 +200,7 @@ namespace time {
|
|||
{
|
||||
Time gt(gridTime);
|
||||
TimeVar timePoint = gt + origin_;
|
||||
timePoint += gridOffset * raster_;
|
||||
timePoint += gridOffset * Offset(raster_);
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,17 @@ namespace time {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/** @todo placeholder for accessing
|
||||
* a current or default session grid.
|
||||
* To be implemented later.
|
||||
*/
|
||||
PQuant getDefaultGridFallback(); ///////////////////////TICKET #810
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Facility to create grid-aligned time values.
|
||||
* Effectively, a quantiser exposes the value Grid API, but
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ namespace time {
|
|||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
// forwards...
|
||||
class FrameRate;
|
||||
class TimeSpan;
|
||||
class Mutation;
|
||||
|
||||
|
||||
/**
|
||||
* basic constant internal time value.
|
||||
|
|
@ -193,6 +198,11 @@ namespace time {
|
|||
: TimeValue(TimeVar(target) -= origin)
|
||||
{ }
|
||||
|
||||
Offset (int64_t count, FrameRate const& fps);
|
||||
|
||||
static const Offset ZERO;
|
||||
|
||||
|
||||
TimeValue
|
||||
abs() const
|
||||
{
|
||||
|
|
@ -241,7 +251,6 @@ namespace time {
|
|||
* @warning do not mix up gavl_time_t and FSecs */
|
||||
typedef boost::rational<long> FSecs;
|
||||
|
||||
class FrameRate;
|
||||
|
||||
/**
|
||||
* Lumiera's internal time value datatype.
|
||||
|
|
@ -304,8 +313,6 @@ namespace time {
|
|||
|
||||
|
||||
|
||||
class TimeSpan;
|
||||
class Mutation;
|
||||
|
||||
/**
|
||||
* Duration is the internal Lumiera time metric.
|
||||
|
|
@ -315,33 +322,63 @@ namespace time {
|
|||
* possibility to send a \em Mutation message.
|
||||
*/
|
||||
class Duration
|
||||
: public Offset
|
||||
: public TimeValue
|
||||
{
|
||||
/// direct assignment prohibited
|
||||
Duration& operator= (Duration const&);
|
||||
|
||||
public:
|
||||
Duration (Offset const& distance)
|
||||
: Offset(distance.abs())
|
||||
: TimeValue(distance.abs())
|
||||
{ }
|
||||
|
||||
explicit
|
||||
Duration (TimeValue const& timeSpec)
|
||||
: Offset(Offset(timeSpec).abs())
|
||||
: TimeValue(Offset(timeSpec).abs())
|
||||
{ }
|
||||
|
||||
explicit
|
||||
Duration (FSecs const& timeSpan_in_secs)
|
||||
: Offset(Offset(Time(timeSpan_in_secs)).abs())
|
||||
: TimeValue(Offset(Time(timeSpan_in_secs)).abs())
|
||||
{ }
|
||||
|
||||
Duration (TimeSpan const& interval);
|
||||
Duration (ulong count, FrameRate const& fps);
|
||||
Duration (int64_t count, FrameRate const& fps);
|
||||
|
||||
static const Duration NIL;
|
||||
|
||||
void accept (Mutation const&);
|
||||
|
||||
/// Supporting backwards use as offset
|
||||
Offset operator- () const;
|
||||
|
||||
};
|
||||
|
||||
//-- support using a Duration to build offsets ---------------
|
||||
|
||||
inline Duration
|
||||
operator+ (Duration const& base, Duration const& toAdd)
|
||||
{
|
||||
return Offset(base) + Offset(toAdd);
|
||||
}
|
||||
|
||||
inline Offset
|
||||
operator* (int factor, Duration const& dur)
|
||||
{
|
||||
return factor * Offset(dur);
|
||||
}
|
||||
|
||||
inline Offset
|
||||
operator* (Duration const& dur, int factor)
|
||||
{
|
||||
return factor*dur;
|
||||
}
|
||||
|
||||
inline Offset
|
||||
Duration::operator- () const
|
||||
{
|
||||
return -1 * (*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -493,7 +530,7 @@ namespace time {
|
|||
|
||||
inline
|
||||
Duration::Duration (TimeSpan const& interval)
|
||||
: Offset(interval.duration())
|
||||
: TimeValue(interval.duration())
|
||||
{ }
|
||||
|
||||
inline
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@
|
|||
|
||||
|
||||
|
||||
#ifndef CONTROL_TYPED_ALLOCATION_MANAGER_H
|
||||
#define CONTROL_TYPED_ALLOCATION_MANAGER_H
|
||||
#ifndef LIB_TYPED_ALLOCATION_MANAGER_H
|
||||
#define LIB_TYPED_ALLOCATION_MANAGER_H
|
||||
|
||||
//#include "pre.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
|
|
|||
|
|
@ -37,15 +37,15 @@
|
|||
using lib::Cmdline;
|
||||
using lumiera::Subsys;
|
||||
using lumiera::AppState;
|
||||
using lumiera::ON_GLOBAL_INIT;
|
||||
|
||||
namespace {
|
||||
Subsys& engine = backend::EngineFacade::getDescriptor();
|
||||
Subsys& netNode = backend::NetNodeFacade::getDescriptor();
|
||||
Subsys& script = backend::ScriptRunnerFacade::getDescriptor();
|
||||
Subsys& player = lumiera::DummyPlayer::getDescriptor();
|
||||
Subsys& player = lumiera::DummyPlayer::getDescriptor(); ///////TODO: just a dummy, until we're able to render
|
||||
Subsys& builder = proc::Facade::getBuilderDescriptor();
|
||||
Subsys& session = proc::Facade::getSessionDescriptor();
|
||||
Subsys& playOut = proc::Facade::getPlayOutDescriptor();
|
||||
Subsys& lumigui = gui::GuiFacade::getDescriptor();
|
||||
}
|
||||
|
||||
|
|
@ -63,16 +63,20 @@ main (int argc, const char* argv[])
|
|||
lumiera::Option options (args);
|
||||
application.init (options);
|
||||
|
||||
session.depends (builder);
|
||||
// session.depends (builder);
|
||||
netNode.depends (session);
|
||||
netNode.depends (engine);
|
||||
// playOut.depends (engine);
|
||||
// playOut.depends (session);
|
||||
// lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin
|
||||
// lumigui.depends (engine);
|
||||
player.depends (playOut); //////TODO dummy player, until we're able to render
|
||||
lumigui.depends (player);
|
||||
script.depends (session);
|
||||
script.depends (engine);
|
||||
|
||||
application.maybeStart (session);
|
||||
application.maybeStart (playOut);
|
||||
application.maybeStart (netNode);
|
||||
application.maybeStart (lumigui);
|
||||
application.maybeStart (script);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#include "lib/hash-indexed.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
|
|
|||
70
src/proc/asset/viewer.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Viewer - asset corresponding to a viewer element in the GUI
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "proc/asset/viewer.hpp"
|
||||
//#include "proc/mobject/session/track.hpp"
|
||||
//#include "proc/mobject/placement.hpp"
|
||||
//#include "proc/mobject/session/mobjectfactory.hpp"
|
||||
//#include "proc/mobject/session/binding.hpp"
|
||||
//#include "proc/assetmanager.hpp"
|
||||
|
||||
|
||||
namespace asset {
|
||||
|
||||
// using lib::AutoRegistered;
|
||||
|
||||
|
||||
|
||||
/** @todo anything significant to do here??? */
|
||||
Viewer::Viewer (const Asset::Ident& idi)
|
||||
: Struct (idi)
|
||||
{
|
||||
UNIMPLEMENTED ("anything regarding Viewer Assets");
|
||||
}
|
||||
|
||||
|
||||
//PViewer
|
||||
//Viewer::create (Asset::Ident const& idi)
|
||||
//{
|
||||
// REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation");
|
||||
//
|
||||
// PTimeline newElement (AssetManager::instance().wrap (*new Viewer(idi)));
|
||||
// getRegistry().append (newElement);
|
||||
//
|
||||
// ENSURE (newElement);
|
||||
// ENSURE (getRegistry().isRegistered (*newElement));
|
||||
// return newElement;
|
||||
//}
|
||||
|
||||
|
||||
void
|
||||
Viewer::unlink ()
|
||||
{
|
||||
// AutoRegistered<Timeline>::detach();
|
||||
Struct::unlink();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace asset
|
||||
107
src/proc/asset/viewer.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
VIEWER.hpp - asset corresponding to a viewer element in the GUI
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file viewer.hpp
|
||||
** structural element corresponding to a viewer in the GUI.
|
||||
** This Asset marks an attachment point, allowing other (output producing)
|
||||
** elements to be connected to ("viewer attachment"). The typical standard case
|
||||
** is a Timeline: It features a set of global pipes (busses), which collect all
|
||||
** produced data. Yet without an explicit output connection, this data can't be
|
||||
** generated, because in Lumiera, actual output is always retrieved ("pulled")
|
||||
** starting from an output sink. Thus, in order to \em perform (play, render)
|
||||
** the timeline, an "view connection" needs to be established: this connection
|
||||
** is represented by an session::BindingMO, linking the Timeline to an
|
||||
** asset::Viewer. Consequently, the same output mapping and translation
|
||||
** mechanism used for Sequence-Timeline- (and VirtualClip-)Bindings
|
||||
** is employed in this situation to figure out the real output port.
|
||||
**
|
||||
** @todo WIP-WIP-WIP as of 5/11
|
||||
**
|
||||
** @see Session
|
||||
** @see Timeline
|
||||
** @see StructFactory
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ASSET_VIEWER_H
|
||||
#define ASSET_VIEWER_H
|
||||
|
||||
#include "proc/asset/struct.hpp"
|
||||
//#include "proc/mobject/mobject.hpp"
|
||||
//#include "proc/mobject/placement.hpp"
|
||||
#include "proc/mobject/mobject-ref.hpp"
|
||||
//#include "proc/mobject/session/binding.hpp" ////TODO avoidable??
|
||||
#include "lib/p.hpp"
|
||||
#include "lib/element-tracker.hpp"
|
||||
|
||||
|
||||
//#include <vector>
|
||||
//#include <string>
|
||||
|
||||
//using std::vector;
|
||||
//using std::string;
|
||||
|
||||
namespace mobject {
|
||||
namespace session {
|
||||
|
||||
class Binding;
|
||||
typedef MORef<Binding> RBinding;
|
||||
}}
|
||||
|
||||
|
||||
namespace asset {
|
||||
|
||||
|
||||
using lumiera::P;
|
||||
class Viewer;
|
||||
typedef lumiera::P<Viewer> PViewer;
|
||||
|
||||
|
||||
/**
|
||||
* TODO type comment
|
||||
*/
|
||||
class Viewer
|
||||
: public Struct
|
||||
// , public lib::AutoRegistered<Viewer>
|
||||
{
|
||||
|
||||
Viewer (Ident const&); /////TODO ctor params????
|
||||
|
||||
public:
|
||||
/** create and register a new Timeline instance */
|
||||
// static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding);
|
||||
|
||||
protected:
|
||||
virtual void unlink ();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////TODO currently just fleshing the API
|
||||
|
||||
|
||||
} // namespace asset
|
||||
#endif
|
||||
|
|
@ -54,7 +54,6 @@
|
|||
|
||||
namespace control {
|
||||
|
||||
using lib::TypedAllocationManager;
|
||||
using lib::InPlaceBuffer;
|
||||
using std::string;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
#include "lib/bool-checkable.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "lib/meta/typelist-util.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
#include "lib/meta/tuple.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@
|
|||
|
||||
namespace control {
|
||||
|
||||
using lib::TypedAllocationManager;
|
||||
using std::tr1::function;
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
** we need to accept functor objects with a very specific and predetermined
|
||||
** signature, thus allowing for strict type checking by the compiler.
|
||||
**
|
||||
** \par Relation of function signatures
|
||||
** \par Relation of function signatures (MEM = type of the "memento" for Undo)
|
||||
** - operation: void(P1,..PN)
|
||||
** - captureUndo: MEM(P1,..PN)
|
||||
** - undoOperation void(P1,..PN,MEM)
|
||||
|
|
@ -47,14 +47,11 @@
|
|||
#define CONTROL_COMMAND_SIGNATURE_H
|
||||
|
||||
//#include "pre.hpp"
|
||||
//#include "lib/symbol.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "lib/meta/typelist-util.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
#include "lib/meta/typeseq-util.hpp"
|
||||
//#include "lib/meta/tuple.hpp"
|
||||
|
||||
//#include <tr1/memory>
|
||||
#include <tr1/functional>
|
||||
|
||||
|
||||
|
|
@ -62,24 +59,20 @@
|
|||
|
||||
namespace control {
|
||||
|
||||
// using lib::Symbol;
|
||||
// using std::tr1::shared_ptr;
|
||||
using std::tr1::function;
|
||||
|
||||
using lumiera::typelist::FunctionSignature;
|
||||
using lumiera::typelist::FunctionTypedef;
|
||||
using lumiera::typelist::Types;
|
||||
//using lumiera::typelist::NullType;
|
||||
//using lumiera::typelist::Tuple;
|
||||
using lumiera::typelist::Append;
|
||||
using lumiera::typelist::SplitLast;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Metaprogramming helper for building Command function signatures.
|
||||
* Metaprogramming helper for building Command function signatures.
|
||||
* The complete definition context of any command is templated to the signature
|
||||
* of the actual command operation and to the memento type. The typedefs embedded
|
||||
* within CommandSignature<SIG,MEM> allows accepting suitable typed functions
|
||||
* within CommandSignature<SIG,MEM> allows for accepting suitable typed functions
|
||||
* to implement the command in question.
|
||||
*/
|
||||
template<typename SIG, typename MEM>
|
||||
|
|
@ -93,7 +86,7 @@ namespace control {
|
|||
|
||||
public:
|
||||
typedef typename FunctionTypedef<void, Args>::Sig OperateSig;
|
||||
typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig;
|
||||
typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig;
|
||||
typedef typename FunctionTypedef<void, ExtendedArgs>::Sig UndoOp_Sig;
|
||||
typedef Args CmdArgs;
|
||||
typedef MEM Memento;
|
||||
|
|
@ -103,16 +96,23 @@ namespace control {
|
|||
|
||||
|
||||
/**
|
||||
* Type analysis helper template.
|
||||
* Type analysis helper template.
|
||||
* Used for dissecting a given type signature to derive
|
||||
* the related basic operation signature, the signature of a possible Undo-function
|
||||
* and the signature necessary for capturing undo information. The implementation
|
||||
* relies on re-binding an embedded type defining template, based on the actual
|
||||
* case, as identified by the structure of the given parameter signature.
|
||||
*
|
||||
* To use this template, it is instantiated with the signature of a functor object
|
||||
* in question. Depending on the actual situation, the compiler will then either
|
||||
* pick Case1 or Case2 -- thus allowing the client in any case to pick up the
|
||||
* correct signatures for Operation, Capture and Undo-function from the
|
||||
* public typedefs within \c UndoSignature
|
||||
*/
|
||||
template<typename SIG>
|
||||
class UndoSignature
|
||||
{
|
||||
// preparation: dissect the function signature into arguments and result
|
||||
typedef typename FunctionSignature< function<SIG> >::Args Args;
|
||||
typedef typename FunctionSignature< function<SIG> >::Ret Ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ namespace control {
|
|||
* It is returned when invoking a HandlingPattern
|
||||
* and can be used to check for success and/or re-throw
|
||||
* any Exception encountered during the command execution.
|
||||
* @todo couldn't that be replaced by a lib::Result<void> instance??
|
||||
*/
|
||||
class ExecResult
|
||||
: public lib::BoolCheckable<ExecResult>
|
||||
|
|
|
|||
584
src/proc/engine/buffer-metadata.hpp
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
/*
|
||||
BUFFER-METADATA.hpp - internal metadata for data buffer providers
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file buffer-metadate.hpp
|
||||
** Metadata for managing and accessing buffers. The Lumiera Engine uses the
|
||||
** Abstraction of an BufferProvider to handle various kinds of buffer organisation
|
||||
** and access in a uniform way. Actually, buffers can be exposed and provided by several
|
||||
** facilities, which might even be implemented through an external library. Thus the engine
|
||||
** and the abstraction placed in between needs a common set of control data, to be able to
|
||||
** expose the correct buffer for each request. Typically -- and independent of the actual
|
||||
** implementation -- the following properties need to be tracked
|
||||
** - that overall storage size available within the buffer
|
||||
** - a pair of custom \em creator and \em destructor functions to use together with this buffer
|
||||
** - an additional client key to distinguish otherwise otherwise identical client requests
|
||||
** These three distinctions are applied in sequence, thus forming a tree with 3 levels.
|
||||
** Only the first distinguishing level (the size) is mandatory. The others are provided,
|
||||
** because some of the foreseeable buffer providers allow to re-access the data placed
|
||||
** into the buffer, by assigning an internally managed ID to the buffer. The most
|
||||
** prominent example is the frame cache, which obviously needs to keep track of
|
||||
** the buffers after the render engine is finished, while the engine code
|
||||
** just accesses yet another buffer to place the results of calculations.
|
||||
**
|
||||
** These additional distinctions and properties are associated with the help of the
|
||||
** BufferDescriptor, embedded into each BuffHandle. While the engine just uses these
|
||||
** handles in the way of a pointer, the buffer descriptor acts as an additional tag
|
||||
** attached to the buffer access, allowing to re-access a context within the
|
||||
** buffer provider implementation.
|
||||
**
|
||||
** @see buffer-provider.hpp
|
||||
** @see BufferMetadata_test
|
||||
** @see BufferProviderProtocol_test
|
||||
*/
|
||||
|
||||
#ifndef PROC_ENGINE_BUFFR_METADATA_H
|
||||
#define PROC_ENGINE_BUFFR_METADATA_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/functor-util.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
using lib::HashVal;
|
||||
using lib::Literal;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::function;
|
||||
using std::tr1::placeholders::_1;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
namespace metadata {
|
||||
class Key;
|
||||
class Entry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum BufferState
|
||||
{ NIL,
|
||||
FREE,
|
||||
LOCKED,
|
||||
EMITTED,
|
||||
BLOCKED
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* an opaque ID to be used by the BufferProvider implementation.
|
||||
* Typically this will be used, to set apart some pre-registered
|
||||
* kinds of buffers. It is treated as being part of the buffer type.
|
||||
* LocalKey objects may be copied but not re-assigned or changed.
|
||||
*/
|
||||
class LocalKey
|
||||
{
|
||||
uint64_t privateID_;
|
||||
|
||||
public:
|
||||
LocalKey (uint64_t opaqueValue=0)
|
||||
: privateID_(opaqueValue)
|
||||
{ }
|
||||
|
||||
operator uint64_t() const { return privateID_; }
|
||||
|
||||
friend size_t
|
||||
hash_value (LocalKey const& lkey)
|
||||
{
|
||||
boost::hash<uint64_t> hashFunction;
|
||||
return hashFunction(lkey.privateID_);
|
||||
}
|
||||
|
||||
private:
|
||||
/** assignment usually prohibited */
|
||||
LocalKey& operator= (LocalKey const& o)
|
||||
{
|
||||
privateID_ = o.privateID_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** but Key assignments are acceptable */
|
||||
friend class metadata::Key;
|
||||
};
|
||||
|
||||
|
||||
namespace { // Helpers for construction within the buffer...
|
||||
|
||||
template<class X>
|
||||
inline void
|
||||
buildIntoBuffer (void* storageBuffer)
|
||||
{
|
||||
new(storageBuffer) X();
|
||||
}
|
||||
|
||||
template<class X, typename A1>
|
||||
inline void
|
||||
buildIntoBuffer_A1 (void* storageBuffer, A1 arg1)
|
||||
{
|
||||
new(storageBuffer) X(arg1);
|
||||
}
|
||||
|
||||
template<class X>
|
||||
inline void
|
||||
destroyInBuffer (void* storageBuffer)
|
||||
{
|
||||
X* embedded = static_cast<X*> (storageBuffer);
|
||||
embedded->~X();
|
||||
}
|
||||
}//(End)placement-new helpers
|
||||
|
||||
|
||||
/**
|
||||
* A pair of functors to maintain a datastructure within the buffer.
|
||||
* TypeHandler describes how to outfit the buffer in a specific way.
|
||||
* When defined, the buffer will be prepared when locking and cleanup
|
||||
* will be invoked automatically when releasing. Especially, this
|
||||
* can be used to \em attach an object to the buffer (placement-new)
|
||||
*/
|
||||
struct TypeHandler
|
||||
{
|
||||
typedef function<void(void*)> DoInBuffer;
|
||||
|
||||
DoInBuffer createAttached;
|
||||
DoInBuffer destroyAttached;
|
||||
|
||||
/** build an invalid NIL TypeHandler */
|
||||
TypeHandler()
|
||||
: createAttached()
|
||||
, destroyAttached()
|
||||
{ }
|
||||
|
||||
/** build a TypeHandler
|
||||
* binding to arbitrary constructor and destructor functions.
|
||||
* On invocation, these functions get a void* to the buffer.
|
||||
* @note the functor objects created from these operations
|
||||
* might be shared for handling multiple buffers.
|
||||
* Be careful with any state or arguments.
|
||||
*/
|
||||
template<typename CTOR, typename DTOR>
|
||||
TypeHandler(CTOR ctor, DTOR dtor)
|
||||
: createAttached (ctor)
|
||||
, destroyAttached (dtor)
|
||||
{ }
|
||||
|
||||
/** builder function defining a TypeHandler
|
||||
* to place a default-constructed object
|
||||
* into the buffer. */
|
||||
template<class X>
|
||||
static TypeHandler
|
||||
create ()
|
||||
{
|
||||
return TypeHandler (buildIntoBuffer<X>, destroyInBuffer<X>);
|
||||
}
|
||||
|
||||
template<class X, typename A1>
|
||||
static TypeHandler
|
||||
create (A1 a1)
|
||||
{
|
||||
return TypeHandler ( bind (buildIntoBuffer_A1<X,A1>, _1, a1)
|
||||
, destroyInBuffer<X>);
|
||||
}
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return bool(createAttached)
|
||||
&& bool(destroyAttached);
|
||||
}
|
||||
|
||||
friend HashVal
|
||||
hash_value (TypeHandler const& handler)
|
||||
{
|
||||
HashVal hash(0);
|
||||
if (handler.isValid())
|
||||
{
|
||||
boost::hash_combine(hash, handler.createAttached);
|
||||
boost::hash_combine(hash, handler.destroyAttached);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator== (TypeHandler const& left, TypeHandler const& right)
|
||||
{
|
||||
return (!left.isValid() && !right.isValid())
|
||||
|| ( util::rawComparison(left.createAttached, right.createAttached)
|
||||
&& util::rawComparison(left.destroyAttached, right.destroyAttached)
|
||||
);
|
||||
}
|
||||
friend bool
|
||||
operator!= (TypeHandler const& left, TypeHandler const& right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace { // internal constants to mark the default case
|
||||
|
||||
const LocalKey UNSPECIFIC;
|
||||
const TypeHandler RAW_BUFFER;
|
||||
|
||||
inline bool
|
||||
nontrivial (TypeHandler const& toVerify)
|
||||
{
|
||||
return RAW_BUFFER != toVerify;
|
||||
}
|
||||
|
||||
inline bool
|
||||
nontrivial (LocalKey const& toVerify)
|
||||
{
|
||||
return UNSPECIFIC != toVerify;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* === Implementation === */
|
||||
|
||||
namespace metadata {
|
||||
|
||||
using error::LUMIERA_ERROR_LIFECYCLE;
|
||||
using error::LUMIERA_ERROR_BOTTOM_VALUE;
|
||||
|
||||
namespace { // details of hash calculation
|
||||
template<typename VAL>
|
||||
HashVal
|
||||
chainedHash(HashVal accumulatedHash, VAL changedValue)
|
||||
{
|
||||
boost::hash_combine (accumulatedHash, changedValue);
|
||||
return accumulatedHash;
|
||||
}
|
||||
}
|
||||
|
||||
class Key
|
||||
{
|
||||
HashVal parent_;
|
||||
HashVal hashID_;
|
||||
|
||||
protected:
|
||||
size_t storageSize_;
|
||||
TypeHandler instanceFunc_;
|
||||
LocalKey specifics_;
|
||||
|
||||
|
||||
public:
|
||||
/** build a standard basic key describing a kind of Buffer.
|
||||
* @param familyID basic hash seed value to distinguish
|
||||
* families of buffer types managed by
|
||||
* different BufferProvider instances
|
||||
* @param storageSize fundamental info: buffer size
|
||||
*/
|
||||
Key (HashVal familyID, size_t storageSize)
|
||||
: parent_(familyID)
|
||||
, hashID_(chainedHash (familyID, storageSize))
|
||||
, storageSize_(storageSize)
|
||||
, instanceFunc_(RAW_BUFFER)
|
||||
, specifics_(UNSPECIFIC)
|
||||
{ }
|
||||
|
||||
// standard copy operations permitted
|
||||
|
||||
/** create a derived buffer type description.
|
||||
* Using a different storage size than the parent type,
|
||||
* all else remaining the same
|
||||
*/
|
||||
Key (Key const& parent, size_t differingStorageSize)
|
||||
: parent_(parent.hashID_)
|
||||
, hashID_(chainedHash (parent_, differingStorageSize))
|
||||
, storageSize_(differingStorageSize) // differing from parent
|
||||
, instanceFunc_(parent.instanceFunc_)
|
||||
, specifics_(parent.specifics_)
|
||||
{ }
|
||||
|
||||
|
||||
/** create a derived buffer type description.
|
||||
* Using different ctor and dtor functions,
|
||||
* all else remaining the same as with parent
|
||||
*/
|
||||
Key (Key const& parent, TypeHandler const& differingTypeHandlerFunctions)
|
||||
: parent_(parent.hashID_)
|
||||
, hashID_(chainedHash (parent_, differingTypeHandlerFunctions))
|
||||
, storageSize_(parent.storageSize_)
|
||||
, instanceFunc_(differingTypeHandlerFunctions) // differing from parent
|
||||
, specifics_(parent.specifics_)
|
||||
{ }
|
||||
|
||||
|
||||
/** create a derived buffer type description.
|
||||
* Using a different private ID than the parent type,
|
||||
* all else remaining the same
|
||||
*/
|
||||
Key (Key const& parent, LocalKey anotherTypeSpecificInternalID)
|
||||
: parent_(parent.hashID_)
|
||||
, hashID_(chainedHash (parent_, anotherTypeSpecificInternalID))
|
||||
, storageSize_(parent.storageSize_)
|
||||
, instanceFunc_(parent.instanceFunc_)
|
||||
, specifics_(anotherTypeSpecificInternalID) // differing from parent
|
||||
{ }
|
||||
|
||||
|
||||
HashVal parentKey() const { return parent_;}
|
||||
operator HashVal() const { return hashID_;}
|
||||
};
|
||||
|
||||
|
||||
class Entry
|
||||
: public Key
|
||||
{
|
||||
BufferState state_;
|
||||
const void* buffer_;
|
||||
|
||||
public:
|
||||
virtual BufferState
|
||||
state() const
|
||||
{
|
||||
__must_not_be_NIL();
|
||||
return state_;
|
||||
}
|
||||
|
||||
virtual const void*
|
||||
access() const
|
||||
{
|
||||
__must_not_be_NIL();
|
||||
__must_not_be_FREE();
|
||||
|
||||
ENSURE (buffer_);
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
virtual Entry&
|
||||
mark (BufferState newState)
|
||||
{
|
||||
switch (this->state_)
|
||||
{
|
||||
case NIL: __must_not_be_NIL();
|
||||
case FREE: __must_not_be_FREE();
|
||||
|
||||
case LOCKED:
|
||||
if (newState == EMITTED) break; // allow transition
|
||||
|
||||
case EMITTED:
|
||||
if (newState == BLOCKED) break; // allow transition
|
||||
|
||||
case BLOCKED:
|
||||
if (newState == FREE) // note fall through for LOCKED and EMITTED too
|
||||
{
|
||||
buffer_ = 0;
|
||||
break; // allow transition
|
||||
}
|
||||
default:
|
||||
throw error::Fatal ("Invalid buffer state encountered.");
|
||||
}
|
||||
state_ = newState;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
__must_not_be_NIL() const
|
||||
{
|
||||
if (NIL == state_)
|
||||
throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
|
||||
"State transition logic broken (programming error)");
|
||||
}
|
||||
|
||||
void
|
||||
__must_not_be_FREE() const
|
||||
{
|
||||
if (FREE == state_)
|
||||
throw error::Logic ("Buffer is inaccessible (marked as free). "
|
||||
"Need a new buffer pointer in order to lock an entry. "
|
||||
"You should invoke markLocked(buffer) prior to access."
|
||||
, LUMIERA_ERROR_LIFECYCLE );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Metadata
|
||||
: boost::noncopyable
|
||||
{
|
||||
Literal id_;
|
||||
HashVal family_;
|
||||
|
||||
public:
|
||||
|
||||
typedef metadata::Key Key;
|
||||
typedef metadata::Entry Entry;
|
||||
|
||||
/** establish a metadata registry.
|
||||
* Such will maintain a family of buffer type entries
|
||||
* and provide a service for storing and retrieving metadata
|
||||
* for concrete buffer entries associated with these types.
|
||||
* @param implementationID to distinguish families
|
||||
* of type keys belonging to different registries.
|
||||
*/
|
||||
Metadata (Literal implementationID)
|
||||
: id_(implementationID)
|
||||
, family_(hash_value(id_))
|
||||
{ }
|
||||
|
||||
/** combine the distinguishing properties
|
||||
* into a single type key, which will be known/remembered
|
||||
* from that point on. Properties are combined according to
|
||||
* a fixed type specialisation order, with the buffer size
|
||||
* forming the base level, possible TypeHandler functors the
|
||||
* second level, and implementation defined LocalKey entries
|
||||
* the third level. All these levels describe abstract type
|
||||
* keys, not entries for concrete buffers. The latter are
|
||||
* always created as children of a known type key.
|
||||
*/
|
||||
Key
|
||||
key ( size_t storageSize
|
||||
, TypeHandler instanceFunc =RAW_BUFFER
|
||||
, LocalKey specifics =UNSPECIFIC)
|
||||
{
|
||||
REQUIRE (storageSize);
|
||||
Key typeKey = trackKey (family_, storageSize);
|
||||
|
||||
if (nontrivial(instanceFunc))
|
||||
{
|
||||
typeKey = trackKey (typeKey, instanceFunc);
|
||||
}
|
||||
|
||||
if (nontrivial(specifics))
|
||||
{
|
||||
typeKey = trackKey (typeKey, specifics);
|
||||
}
|
||||
|
||||
return typeKey;
|
||||
}
|
||||
|
||||
/** create a sub-type, using a different type/handler functor */
|
||||
Key
|
||||
key (Key const& parentKey, TypeHandler const& instanceFunc)
|
||||
{
|
||||
return trackKey (parentKey, instanceFunc);
|
||||
}
|
||||
|
||||
/** create a sub-type, using a different private-ID (implementation defined) */
|
||||
Key
|
||||
key (Key const& parentKey, LocalKey specifics)
|
||||
{
|
||||
return trackKey (parentKey, specifics);
|
||||
}
|
||||
|
||||
Key
|
||||
key (Key const& parentKey, const void* concreteBuffer)
|
||||
{
|
||||
UNIMPLEMENTED ("create sub-object key for concrete buffer");
|
||||
}
|
||||
|
||||
Key const&
|
||||
get (HashVal hashID)
|
||||
{
|
||||
UNIMPLEMENTED ("access the plain key entry");
|
||||
}
|
||||
|
||||
Entry&
|
||||
get (Key key)
|
||||
{
|
||||
UNIMPLEMENTED ("access, possibly create metadata records");
|
||||
}
|
||||
|
||||
bool
|
||||
isKnown (HashVal key) const
|
||||
{
|
||||
UNIMPLEMENTED ("diagnostics: known record?");
|
||||
}
|
||||
|
||||
bool
|
||||
isLocked (HashVal key) const
|
||||
{
|
||||
UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
|
||||
}
|
||||
|
||||
|
||||
/* == memory management == */
|
||||
|
||||
Entry& markLocked (Key const& parentKey, const void* buffer);
|
||||
void release (HashVal key);
|
||||
|
||||
private:
|
||||
|
||||
template<typename PAR, typename DEF>
|
||||
Key
|
||||
trackKey (PAR parent, DEF specialisation)
|
||||
{
|
||||
Key newKey (parent,specialisation);
|
||||
maybeStore (newKey);
|
||||
return newKey;
|
||||
}
|
||||
|
||||
void
|
||||
maybeStore (Key const& key)
|
||||
{
|
||||
if (isKnown (key)) return;
|
||||
UNIMPLEMENTED ("registry for type keys");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
inline Metadata::Entry&
|
||||
Metadata::markLocked (Key const& parentKey, const void* buffer)
|
||||
{
|
||||
UNIMPLEMENTED ("transition to locked state");
|
||||
if (!buffer)
|
||||
throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
|
||||
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
|
||||
Key newKey = this->key (parentKey, buffer);
|
||||
if (isLocked(newKey))
|
||||
throw error::Logic ("Attempt to lock a slot for a new buffer, "
|
||||
"while actually the old buffer is still locked."
|
||||
, error::LUMIERA_ERROR_LIFECYCLE );
|
||||
|
||||
return this->get(newKey);
|
||||
}
|
||||
|
||||
inline void
|
||||
Metadata::release (HashVal key)
|
||||
{
|
||||
UNIMPLEMENTED ("metadata memory management");
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
82
src/proc/engine/buffer-provider.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
BufferProvider - Abstraction for Buffer management during playback/render
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "proc/engine/buffer-provider.hpp"
|
||||
#include "proc/engine/buffer-metadata.hpp"
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
|
||||
namespace { // impl. details and definitions
|
||||
|
||||
const uint DEFAULT_DESCRIPTOR = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
BufferProvider::BufferProvider (Literal implementationID)
|
||||
: meta_(new Metadata (implementationID))
|
||||
{ }
|
||||
|
||||
BufferProvider::~BufferProvider() { }
|
||||
|
||||
|
||||
/** @internal verify the given descriptor.
|
||||
* @return true if it corresponds to a buffer
|
||||
* currently locked and usable by client code
|
||||
*/
|
||||
bool
|
||||
BufferProvider::verifyValidity (BufferDescriptor const&)
|
||||
{
|
||||
UNIMPLEMENTED ("BufferProvider basic and default implementation");
|
||||
}
|
||||
|
||||
|
||||
BufferDescriptor
|
||||
BufferProvider::getDescriptorFor (size_t storageSize)
|
||||
{
|
||||
return BufferDescriptor (*this, meta_->key (storageSize));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* === BufferDescriptor and BuffHandle === */
|
||||
|
||||
bool
|
||||
BufferDescriptor::verifyValidity() const
|
||||
{
|
||||
return provider_->verifyValidity(*this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BuffHandle::release()
|
||||
{
|
||||
UNIMPLEMENTED ("forward buffer release call to buffer provider");
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
135
src/proc/engine/buffer-provider.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
BUFFER-PROVIDER.hpp - Abstraction for Buffer management during playback/render
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file buffer-provider.hpp
|
||||
** Abstraction to represent buffer management and lifecycle within the render engine.
|
||||
** It turns out that -- throughout the render engine implementation -- we never need
|
||||
** direct access to the buffers holding media data. Buffers are just some entity to be \em managed,
|
||||
** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementation detail.
|
||||
** The code within the render engine just pushes around BufferHandle objects, which act as a front-end,
|
||||
** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle
|
||||
** of buffers automatically, because the use of buffers is embedded into the render calculation cycle,
|
||||
** which follows a rather strict protocol anyway. Relying on the capabilities of the scheduler,
|
||||
** the sequence of individual jobs in the engine ensures...
|
||||
** - that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
|
||||
** - that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
|
||||
** - that buffers are marked as free ("released") after doing the actual calculations.
|
||||
**
|
||||
** @see state.hpp
|
||||
** @see output-slot.hpp
|
||||
*/
|
||||
|
||||
#ifndef PROC_ENGINE_BUFFR_PROVIDER_H
|
||||
#define PROC_ENGINE_BUFFR_PROVIDER_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
using boost::scoped_ptr;
|
||||
using lib::Literal;
|
||||
|
||||
|
||||
class Metadata;
|
||||
|
||||
|
||||
/**
|
||||
* Interface: a facility providing and managing working buffers for media calculations.
|
||||
* The pointer to actual buffer storage can be retrieved by
|
||||
* - optionally announcing the required buffer(s) beforehand
|
||||
* - "locking" a buffer to yield a buffer handle
|
||||
* - dereferencing this smart-handle class
|
||||
*
|
||||
* @todo as of 6/2011 buffer management within the engine is still a bit vague
|
||||
*/
|
||||
class BufferProvider
|
||||
: boost::noncopyable
|
||||
{
|
||||
scoped_ptr<Metadata> meta_;
|
||||
|
||||
protected:
|
||||
BufferProvider (Literal implementationID);
|
||||
|
||||
public:
|
||||
virtual ~BufferProvider(); ///< this is an interface
|
||||
|
||||
|
||||
virtual uint announce (uint count, BufferDescriptor const&) =0;
|
||||
|
||||
virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0;
|
||||
virtual void releaseBuffer (BuffHandle const&) =0;
|
||||
|
||||
template<typename BU>
|
||||
BuffHandle lockBufferFor ();
|
||||
|
||||
|
||||
/** describe the kind of buffer managed by this provider */
|
||||
BufferDescriptor getDescriptorFor(size_t storageSize=0);
|
||||
|
||||
template<typename BU>
|
||||
BufferDescriptor getDescriptor();
|
||||
|
||||
|
||||
|
||||
/* === API for BuffHandle internal access === */
|
||||
|
||||
bool verifyValidity (BufferDescriptor const&);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* === Implementation === */
|
||||
|
||||
/** convenience shortcut:
|
||||
* prepare and claim ("lock") a buffer suitable
|
||||
* to hold an object of the given type.
|
||||
* @return a handle embedding a suitably configured
|
||||
* buffer descriptor. The corresponding buffer
|
||||
* has been allocated and marked for exclusive use
|
||||
*/
|
||||
template<typename BU>
|
||||
BuffHandle
|
||||
BufferProvider::lockBufferFor()
|
||||
{
|
||||
UNIMPLEMENTED ("convenience shortcut to announce and lock for a specific object type");
|
||||
}
|
||||
|
||||
|
||||
template<typename BU>
|
||||
BufferDescriptor
|
||||
BufferProvider::getDescriptor()
|
||||
{
|
||||
UNIMPLEMENTED ("build descriptor for automatically placing an object instance into the buffer");
|
||||
}
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
@ -24,12 +24,17 @@
|
|||
** Various bits needed to support the buffer management within the render nodes.
|
||||
** When pulling data from predecessor nodes and calculating new data, each render node
|
||||
** needs several input and output buffers. These may be allocated and provided by several
|
||||
** different "buffer providers" (for example the frame cache). For accessing those buffers,
|
||||
** the node needs to keep a table of buffer pointers, and for releasing the buffers later
|
||||
** on, we need some handles. The usage pattern of those buffer pointer tables is stack-like,
|
||||
** thus it makes sense to utilise a single large buffer pointer array per pull() calldown
|
||||
** sequence and dynamically claim small chunks for each node.
|
||||
**
|
||||
** different "buffer providers" (for example the frame cache). Typically, the real buffers
|
||||
** will be passed as parameters to the actual job instance when scheduled, drawing on the
|
||||
** results of prerequisite jobs. Yet the actual job implementation remains agnostic with
|
||||
** respect to the way actual buffers are provided; the invocation just pushes BuffHandle
|
||||
** objects around. The actual render function gets an array of C-pointers to the actual
|
||||
** buffers, and for accessing those buffers, the node needs to keep a table of buffer
|
||||
** pointers, and for releasing the buffers later on, we utilise the buffer handles.
|
||||
** The usage pattern of those buffer pointer tables is stack-like, thus the actual
|
||||
** implementation utilises a single large buffer pointer array per pull() call
|
||||
** sequence and dynamically claims small chunks for each node.
|
||||
**
|
||||
** @see nodewiring-def.hpp
|
||||
** @see nodeoperation.hpp
|
||||
** @see bufftable.hpp storage for the buffer table
|
||||
|
|
@ -47,67 +52,48 @@
|
|||
|
||||
namespace engine {
|
||||
|
||||
class BufferProvider;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handle for a buffer for processing data, abstracting away the actual implementation.
|
||||
* The real buffer pointer can be retrieved by dereferencing this smart-handle class.
|
||||
* An opaque descriptor to identify the type and further properties of a data buffer.
|
||||
* For each kind of buffer, there is somewhere a BufferProvider responsible for the
|
||||
* actual storage management. This provider may "lock" a buffer for actual use,
|
||||
* returning a BuffHandle.
|
||||
* @note this descriptor and especially the #subClassification_ is really owned
|
||||
* by the BufferProvider, which may use (and even change) the opaque contents
|
||||
* to organise the internal buffer management.
|
||||
*
|
||||
* @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249
|
||||
*/
|
||||
struct BuffHandle
|
||||
: lib::BoolCheckable<BuffHandle>
|
||||
class BufferDescriptor
|
||||
{
|
||||
typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
|
||||
typedef Buff* PBuff;
|
||||
BufferProvider* provider_;
|
||||
uint64_t subClassification_;
|
||||
|
||||
PBuff
|
||||
operator->() const
|
||||
{
|
||||
return pBuffer_;
|
||||
}
|
||||
Buff&
|
||||
operator* () const
|
||||
{
|
||||
ENSURE (pBuffer_);
|
||||
return *pBuffer_;
|
||||
}
|
||||
BufferDescriptor(BufferProvider& manager, uint64_t detail)
|
||||
: provider_(&manager)
|
||||
, subClassification_(detail)
|
||||
{ }
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return pBuffer_;
|
||||
}
|
||||
friend class BufferProvider;
|
||||
|
||||
public:
|
||||
// using standard copy operations
|
||||
|
||||
//////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> Ticket 249
|
||||
BuffHandle()
|
||||
: pBuffer_(0),
|
||||
sourceID_(0)
|
||||
{ }
|
||||
|
||||
private:
|
||||
PBuff pBuffer_;
|
||||
long sourceID_;
|
||||
bool verifyValidity() const;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Buffer Type information.
|
||||
* Given a BufferDescriptor, it is possible to allocate a buffer
|
||||
* of suitable size and type by using State::allocateBuffer().
|
||||
*/
|
||||
struct BufferDescriptor
|
||||
{
|
||||
lumiera::StreamType& sType_;
|
||||
};
|
||||
|
||||
|
||||
class ProcNode;
|
||||
typedef ProcNode* PNode;
|
||||
|
||||
|
||||
struct ChannelDescriptor ///////TODO collapse this with BufferDescriptor?
|
||||
struct ChannelDescriptor ///////TODO really need to define that here? it is needed for node wiring only
|
||||
{
|
||||
BufferDescriptor bufferType;
|
||||
const lumiera::StreamType * bufferType; /////////////////////////////////////////TICKET #828
|
||||
};
|
||||
|
||||
struct InChanDescriptor : ChannelDescriptor
|
||||
|
|
@ -118,5 +104,99 @@ namespace engine {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handle for a buffer for processing data, abstracting away the actual implementation.
|
||||
* The real buffer pointer can be retrieved by dereferencing this smart-handle class.
|
||||
*
|
||||
* @todo as of 6/2011 it isn't clear how buffer handles are actually created
|
||||
* and how the lifecycle (and memory) management works //////////////////////TICKET #249 rework BuffHandle creation and usage
|
||||
*/
|
||||
class BuffHandle
|
||||
: public lib::BoolCheckable<BuffHandle>
|
||||
{
|
||||
typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
|
||||
|
||||
BufferDescriptor descriptor_;
|
||||
Buff* pBuffer_;
|
||||
|
||||
|
||||
public:
|
||||
typedef Buff* PBuff;
|
||||
|
||||
/** @internal a buffer handle may be obtained by "locking"
|
||||
* a buffer from the corresponding BufferProvider */
|
||||
BuffHandle(BufferDescriptor const& typeInfo, PBuff storage = 0)
|
||||
: descriptor_(typeInfo)
|
||||
, pBuffer_(storage)
|
||||
{ }
|
||||
|
||||
// using standard copy operations
|
||||
|
||||
|
||||
|
||||
void release();
|
||||
|
||||
|
||||
template<typename BU>
|
||||
BU& create();
|
||||
|
||||
template<typename BU>
|
||||
BU& accessAs();
|
||||
|
||||
|
||||
Buff&
|
||||
operator* () const
|
||||
{
|
||||
ENSURE (pBuffer_);
|
||||
return *pBuffer_;
|
||||
}
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return bool(pBuffer_)
|
||||
&& descriptor_.verifyValidity();
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
UNIMPLEMENTED ("forward to the buffer provider for storage size diagnostics");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* === Implementation details === */
|
||||
|
||||
/** convenience shortcut: place and maintain an object within the buffer.
|
||||
* This operation performs the necessary steps to attach an object;
|
||||
* if the buffer isn't locked yet, it will do so. Moreover, the created
|
||||
* object will be owned by the buffer management facilities, i.e. the
|
||||
* destructor is registered as cleanup function.
|
||||
*/
|
||||
template<typename BU>
|
||||
BU&
|
||||
BuffHandle::create()
|
||||
{
|
||||
UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway");
|
||||
}
|
||||
|
||||
|
||||
/** convenience shortcut: access the buffer contents in a typesafe fashion.
|
||||
* This is equivalent to a plain dereferentiation with additional metadata check
|
||||
* @throw error::Logic in case of type mismatch \c LUMIERA_ERROR_WRONG_TYPE
|
||||
*/
|
||||
template<typename BU>
|
||||
BU&
|
||||
BuffHandle::accessAs()
|
||||
{
|
||||
UNIMPLEMENTED ("convenience shortcut to access buffer contents typesafe");
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
|
|||
186
src/proc/engine/bufftable-obsolete.hpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
BUFFTABLE-OBSOLTE.hpp - Old dead code to be removed when rewriting ProcNode!!!!!
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ENGINE_BUFFHTABLE_OBSOLETE_H
|
||||
#define ENGINE_BUFFHTABLE_OBSOLETE_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
|
||||
////////////////////////////////WARNING: obsolete code
|
||||
////////////////////////////////WARNING: ...just left in tree to keep it compiling
|
||||
////////////////////////////////TICKET #826 need to be reworked entirely
|
||||
|
||||
namespace engine {
|
||||
|
||||
using std::pair;
|
||||
using std::vector;
|
||||
|
||||
|
||||
/**
|
||||
* Obsolete, to be rewritten /////TICKET #826
|
||||
*
|
||||
* Tables of buffer handles and corresponding dereferenced buffer pointers.
|
||||
* Used within the invocation of a processing node to calculate data.
|
||||
* The tables are further differentiated into input data buffers and output
|
||||
* data buffers. The tables are supposed to be implemented as bare "C" arrays,
|
||||
* thus the array of real buffer pointers can be fed directly to the
|
||||
* processing function of the respective node.
|
||||
*
|
||||
* @todo this whole design is a first attempt and rather clumsy. It should be reworked
|
||||
* to use a single contiguous memory area and just layer the object structure on top
|
||||
* (by using placement new). Yet the idea of an stack-like organisation should be retained
|
||||
*/
|
||||
struct BuffTable
|
||||
{
|
||||
typedef BuffHandle * PHa;
|
||||
typedef BuffHandle::PBuff * PBu;
|
||||
typedef pair<PHa const,PBu const> Chunk;
|
||||
|
||||
PHa outHandle;
|
||||
PHa inHandle;
|
||||
PBu outBuff;
|
||||
PBu inBuff;
|
||||
};
|
||||
|
||||
|
||||
/** Obsolete, to be rewritten /////TICKET #826 */
|
||||
class BuffTableStorage
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////TICKET #826 need to be reworked entirely
|
||||
/** just a placeholder to decouple the existing code
|
||||
* from the reworked BuffHandle logic. The existing
|
||||
* code in turn will be reworked rather fundamentally
|
||||
*/
|
||||
struct BuffHaXXXX
|
||||
: BuffHandle
|
||||
{
|
||||
BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ }
|
||||
static BufferDescriptor const&
|
||||
just_satisfy_the_compiler() { }
|
||||
};
|
||||
|
||||
////////////////////////////////////TICKET #825 should be backed by mpool and integrated with node invocation
|
||||
vector<BuffHaXXXX> hTab_;
|
||||
vector<BuffHandle::PBuff> pTab_;
|
||||
size_t level_;
|
||||
|
||||
public:
|
||||
BuffTableStorage (const size_t maxSiz)
|
||||
: hTab_(maxSiz),
|
||||
pTab_(maxSiz),
|
||||
level_(0)
|
||||
{ }
|
||||
|
||||
~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
|
||||
|
||||
protected:
|
||||
|
||||
friend class BuffTableChunk;
|
||||
|
||||
/** allocate the given number of slots
|
||||
* starting at current level to be used
|
||||
* by the newly created BuffTableChunk
|
||||
*/
|
||||
BuffTable::Chunk
|
||||
claim (uint slots)
|
||||
{
|
||||
ASSERT (pTab_.size() == hTab_.size());
|
||||
REQUIRE (level_+slots <= hTab_.size());
|
||||
|
||||
size_t prev_level (level_);
|
||||
level_ += slots;
|
||||
return std::make_pair (&hTab_[prev_level],
|
||||
&pTab_[prev_level]);
|
||||
}
|
||||
|
||||
void
|
||||
release (uint slots)
|
||||
{
|
||||
ASSERT (slots <= level_);
|
||||
REQUIRE (level_ <= hTab_.size());
|
||||
REQUIRE (level_ <= pTab_.size());
|
||||
|
||||
level_ -= slots;
|
||||
}
|
||||
|
||||
bool
|
||||
level_check (BuffTable::Chunk& prev_level)
|
||||
{
|
||||
return prev_level.first == &hTab_[level_]
|
||||
&& prev_level.second == &pTab_[level_];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Obsolete, to be rewritten /////TICKET #826
|
||||
* to be allocated on the stack while evaluating a ProcNode#pull() call.
|
||||
* The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
|
||||
* which can be used to crate such chunks. The claiming and releasing of
|
||||
* slots in the BuffTableStorage is automatically tied to BuffTableChunk
|
||||
* object's lifecycle.
|
||||
*/
|
||||
class BuffTableChunk
|
||||
: public BuffTable,
|
||||
boost::noncopyable
|
||||
{
|
||||
const uint siz_;
|
||||
BuffTable::Chunk tab_;
|
||||
BuffTableStorage& sto_;
|
||||
|
||||
public:
|
||||
BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
|
||||
: siz_(wd.nrI + wd.nrO),
|
||||
tab_(storage.claim (siz_)),
|
||||
sto_(storage)
|
||||
{
|
||||
const uint nrO(wd.nrO);
|
||||
|
||||
// Setup the publicly visible table locations
|
||||
this->outHandle = &tab_.first[ 0 ];
|
||||
this->inHandle = &tab_.first[nrO];
|
||||
this->outBuff = &tab_.second[ 0 ];
|
||||
this->inBuff = &tab_.second[nrO];
|
||||
}
|
||||
|
||||
~BuffTableChunk ()
|
||||
{
|
||||
sto_.release (siz_);
|
||||
ASSERT ( sto_.level_check (tab_),
|
||||
"buffer management logic broken.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <vector>
|
||||
|
|
@ -48,113 +49,108 @@ namespace engine {
|
|||
* data buffers. The tables are supposed to be implemented as bare "C" arrays,
|
||||
* thus the array of real buffer pointers can be fed directly to the
|
||||
* processing function of the respective node.
|
||||
*
|
||||
* @todo this whole design is a first attempt and rather clumsy. It should be reworked
|
||||
* to use a single contiguous memory area and just layer the object structure on top
|
||||
* (by using placement new). Yet the idea of an stack-like organisation should be retained
|
||||
*/
|
||||
struct BuffTable
|
||||
{
|
||||
typedef BuffHandle * PHa;
|
||||
typedef BuffHandle::PBuff * PBu;
|
||||
typedef pair<PHa const,PBu const> Chunk;
|
||||
|
||||
PHa outHandle;
|
||||
PHa inHandle;
|
||||
PBu outBuff;
|
||||
PBu inBuff;
|
||||
struct StorageChunk
|
||||
{ };
|
||||
|
||||
template<uint count>
|
||||
struct Storage
|
||||
{
|
||||
enum{size = count * sizeof(StorageChunk)};
|
||||
};
|
||||
|
||||
class Builder
|
||||
{
|
||||
public:
|
||||
Builder& announce (uint count, BufferDescriptor const& type);
|
||||
BuffTable& build();
|
||||
};
|
||||
|
||||
static Builder& prepare (const size_t STORAGE_SIZE, void* storage);
|
||||
|
||||
void lockBuffers();
|
||||
void releaseBuffers();
|
||||
|
||||
typedef vector<BuffHandle> BuffHandleTable;
|
||||
typedef lib::RangeIter<BuffHandleTable::iterator> iterator;
|
||||
|
||||
iterator buffers();
|
||||
iterator inBuffers();
|
||||
iterator outBuffers();
|
||||
};
|
||||
|
||||
|
||||
class BuffTableStorage
|
||||
{
|
||||
vector<BuffHandle> hTab_;
|
||||
vector<BuffHandle::PBuff> pTab_;
|
||||
size_t level_;
|
||||
|
||||
public:
|
||||
BuffTableStorage (const size_t maxSiz)
|
||||
: hTab_(maxSiz),
|
||||
pTab_(maxSiz),
|
||||
level_(0)
|
||||
{ }
|
||||
|
||||
~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
|
||||
|
||||
protected:
|
||||
|
||||
friend class BuffTableChunk;
|
||||
|
||||
/** allocate the given number of slots
|
||||
* starting at current level to be used
|
||||
* by the newly created BuffTableChunk
|
||||
*/
|
||||
BuffTable::Chunk
|
||||
claim (uint slots)
|
||||
{
|
||||
ASSERT (pTab_.size() == hTab_.size());
|
||||
REQUIRE (level_+slots <= hTab_.size());
|
||||
|
||||
size_t prev_level (level_);
|
||||
level_ += slots;
|
||||
return std::make_pair (&hTab_[prev_level],
|
||||
&pTab_[prev_level]);
|
||||
}
|
||||
|
||||
void
|
||||
release (uint slots)
|
||||
{
|
||||
ASSERT (slots <= level_);
|
||||
REQUIRE (level_ <= hTab_.size());
|
||||
REQUIRE (level_ <= pTab_.size());
|
||||
|
||||
level_ -= slots;
|
||||
}
|
||||
|
||||
bool
|
||||
level_check (BuffTable::Chunk& prev_level)
|
||||
{
|
||||
return prev_level.first == &hTab_[level_]
|
||||
&& prev_level.second == &pTab_[level_];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* to be allocated on the stack while evaluating a ProcNode#pull() call.
|
||||
* The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
|
||||
* which can be used to crate such chunks. The claiming and releasing of
|
||||
* slots in the BuffTableStorage is automatically tied to BuffTableChunk
|
||||
* object's lifecycle.
|
||||
*/
|
||||
class BuffTableChunk
|
||||
: public BuffTable,
|
||||
boost::noncopyable
|
||||
{
|
||||
const uint siz_;
|
||||
BuffTable::Chunk tab_;
|
||||
BuffTableStorage& sto_;
|
||||
|
||||
public:
|
||||
BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
|
||||
: siz_(wd.nrI + wd.nrO),
|
||||
tab_(storage.claim (siz_)),
|
||||
sto_(storage)
|
||||
{
|
||||
const uint nrO(wd.nrO);
|
||||
|
||||
// Setup the public visible table locations
|
||||
this->outHandle = &tab_.first[ 0 ];
|
||||
this->inHandle = &tab_.first[nrO];
|
||||
this->outBuff = &tab_.second[ 0 ];
|
||||
this->inBuff = &tab_.second[nrO];
|
||||
}
|
||||
|
||||
~BuffTableChunk ()
|
||||
{
|
||||
sto_.release (siz_);
|
||||
ASSERT ( sto_.level_check (tab_),
|
||||
"buffer management logic broken.");
|
||||
}
|
||||
};
|
||||
/* === Implementation === */
|
||||
|
||||
inline BuffTable::Builder&
|
||||
BuffTable::prepare (const size_t STORAGE_SIZE, void* storage)
|
||||
{
|
||||
UNIMPLEMENTED ("expose a builder object for outfitting a buffer pointer table");
|
||||
}
|
||||
|
||||
|
||||
inline BuffTable::Builder&
|
||||
BuffTable::Builder::announce (uint count, BufferDescriptor const& type)
|
||||
{
|
||||
UNIMPLEMENTED ("accept announcement of additional buffer table entries required");
|
||||
}
|
||||
|
||||
|
||||
inline BuffTable&
|
||||
BuffTable::Builder::build()
|
||||
{
|
||||
UNIMPLEMENTED ("finally drop off the newly configured buffer pointer table");
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
BuffTable::lockBuffers()
|
||||
{
|
||||
UNIMPLEMENTED ("convenience shortcut: lock all preconfigured buffers within this table through the underlying buffer provider");
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
BuffTable::releaseBuffers()
|
||||
{
|
||||
UNIMPLEMENTED ("convenience shortcut: release all the buffers managed through this buffer table, by forwarding to the underlying buffer provider");
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline BuffTable::iterator
|
||||
BuffTable::buffers()
|
||||
{
|
||||
UNIMPLEMENTED ("expose an iterator to yield all prepared buffers within this buffer table");
|
||||
}
|
||||
|
||||
|
||||
inline BuffTable::iterator
|
||||
BuffTable::inBuffers()
|
||||
{
|
||||
UNIMPLEMENTED ("expose an iterator to access all the input buffer slots of this buffer table");
|
||||
}
|
||||
|
||||
|
||||
inline BuffTable::iterator
|
||||
BuffTable::outBuffers()
|
||||
{
|
||||
UNIMPLEMENTED ("expose an iterator to access all the output buffer slots of this buffer table");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
94
src/proc/engine/calc-stream.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
CALC-STREAM.hpp - abstraction representing a series of scheduled calculations
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef PROC_ENGINE_CALC_STREAM_H
|
||||
#define PROC_ENGINE_CALC_STREAM_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
//#include "include/dummy-player-facade.h"
|
||||
//#include "include/display-facade.h"
|
||||
//#include "common/instancehandle.hpp"
|
||||
//#include "lib/singleton-ref.hpp"
|
||||
//
|
||||
//#include <boost/noncopyable.hpp>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
//#include <string>
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace engine{
|
||||
|
||||
// using std::string;
|
||||
// using lumiera::Subsys;
|
||||
// using lumiera::Display;
|
||||
// using lumiera::DummyPlayer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************************************
|
||||
* A calculation stream groups and abstracts a series of
|
||||
* calculation jobs, delivering frames into the configured
|
||||
* OutputSlot in a timely fashion. Behind the scenes, this
|
||||
* gets translated into several jobs enqueued with the
|
||||
* scheduler in the backend layer. The calculation stream
|
||||
* implementation cares to create and configure these
|
||||
* jobs and to manage the necessary dependencies and
|
||||
* callbacks.
|
||||
*
|
||||
* Regarding the implementation, a CalcStream is an const
|
||||
* value object holding the metadata necessary to manage
|
||||
* the underlying jobs. The only way to create a CalcStream
|
||||
* properly is to retrieve it from the factory functions
|
||||
* of the EngineService. At that point, the corresponding
|
||||
* jobs will be configured and enqueued.
|
||||
*/
|
||||
class CalcStream
|
||||
{
|
||||
|
||||
friend class EngineService;
|
||||
|
||||
CalcStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
CalcStream (CalcStream const& o)
|
||||
{
|
||||
UNIMPLEMENTED("build a calculation stream");
|
||||
}
|
||||
|
||||
~CalcStream() { }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
} // namespace proc
|
||||
#endif
|
||||
220
src/proc/engine/diagnostic-buffer-provider.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
DiagnosticBufferProvider - helper for testing against the BufferProvider interface
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/scoped-ptrvect.hpp"
|
||||
|
||||
#include "proc/engine/diagnostic-buffer-provider.hpp"
|
||||
|
||||
#include <boost/scoped_array.hpp>
|
||||
//#include <vector>
|
||||
|
||||
using lib::ScopedPtrVect;
|
||||
using boost::scoped_array;
|
||||
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
|
||||
/** Storage for the diagnostics frontend */
|
||||
lib::Singleton<DiagnosticBufferProvider> DiagnosticBufferProvider::diagnostics;
|
||||
|
||||
|
||||
class Block
|
||||
: boost::noncopyable
|
||||
{
|
||||
size_t size_;
|
||||
scoped_array<char> storage_;
|
||||
|
||||
bool was_locked_;
|
||||
|
||||
public:
|
||||
Block()
|
||||
: size_(0)
|
||||
, storage_()
|
||||
, was_locked_(false)
|
||||
{ }
|
||||
|
||||
bool
|
||||
was_used() const
|
||||
{
|
||||
return was_locked_;
|
||||
}
|
||||
|
||||
bool
|
||||
was_closed() const
|
||||
{
|
||||
return was_locked_;
|
||||
}
|
||||
|
||||
void*
|
||||
accessMemory() const
|
||||
{
|
||||
return storage_.get();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace { // Details of allocation and accounting
|
||||
|
||||
const uint MAX_BUFFERS = 50;
|
||||
|
||||
} // (END) Details of allocation and accounting
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @internal DiagnosticBufferProvider's PImpl.
|
||||
* Uses a linearly growing table of heap allocated buffer blocks,
|
||||
* which will never be discarded, unless the PImpl is discarded as a whole.
|
||||
* This way, the tracked usage information remains available after the fact.
|
||||
*/
|
||||
class DiagnosticBufferProvider::HeapMemProvider
|
||||
: public BufferProvider
|
||||
, public ScopedPtrVect<Block>
|
||||
{
|
||||
|
||||
virtual uint
|
||||
announce (uint count, BufferDescriptor const& type)
|
||||
{
|
||||
UNIMPLEMENTED ("pre-register storage for buffers of a specific kind");
|
||||
}
|
||||
|
||||
|
||||
virtual BuffHandle
|
||||
lockBufferFor (BufferDescriptor const& descriptor)
|
||||
{
|
||||
UNIMPLEMENTED ("lock buffer for exclusive use");
|
||||
}
|
||||
|
||||
virtual void
|
||||
releaseBuffer (BuffHandle const& handle)
|
||||
{
|
||||
UNIMPLEMENTED ("release a buffer and invalidate the handle");
|
||||
}
|
||||
|
||||
public:
|
||||
HeapMemProvider()
|
||||
: BufferProvider ("Diagnostic_HeapAllocated")
|
||||
{ }
|
||||
|
||||
virtual ~HeapMemProvider()
|
||||
{
|
||||
INFO (proc_mem, "discarding %zu diagnostic buffer entries", HeapMemProvider::size());
|
||||
}
|
||||
|
||||
Block&
|
||||
access_or_create (uint bufferID)
|
||||
{
|
||||
while (!withinStorageSize (bufferID))
|
||||
manage (new Block);
|
||||
|
||||
ENSURE (withinStorageSize (bufferID));
|
||||
return (*this)[bufferID];
|
||||
}
|
||||
|
||||
private:
|
||||
bool
|
||||
withinStorageSize (uint bufferID) const
|
||||
{
|
||||
if (bufferID >= MAX_BUFFERS)
|
||||
throw error::Fatal ("hardwired internal limit for test buffers exceeded");
|
||||
|
||||
return bufferID < size();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
DiagnosticBufferProvider::DiagnosticBufferProvider()
|
||||
: pImpl_() //////////TODO create PImpl here
|
||||
{ }
|
||||
|
||||
DiagnosticBufferProvider::~DiagnosticBufferProvider() { }
|
||||
|
||||
|
||||
BufferProvider&
|
||||
DiagnosticBufferProvider::build()
|
||||
{
|
||||
return diagnostics().reset();
|
||||
}
|
||||
|
||||
|
||||
DiagnosticBufferProvider&
|
||||
DiagnosticBufferProvider::access (BufferProvider const& provider)
|
||||
{
|
||||
if (!diagnostics().isCurrent (provider))
|
||||
throw error::Invalid("given Provider doesn't match (current) diagnostic data record."
|
||||
"This might be an lifecycle error. Did you build() this instance beforehand?");
|
||||
|
||||
return diagnostics();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DiagnosticBufferProvider::HeapMemProvider&
|
||||
DiagnosticBufferProvider::reset()
|
||||
{
|
||||
pImpl_.reset(new HeapMemProvider());
|
||||
return *pImpl_;
|
||||
}
|
||||
|
||||
bool
|
||||
DiagnosticBufferProvider::isCurrent (BufferProvider const& implInstance)
|
||||
{
|
||||
return &implInstance == pImpl_.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === diagnostic API === */
|
||||
|
||||
bool
|
||||
DiagnosticBufferProvider::buffer_was_used (uint bufferID) const
|
||||
{
|
||||
return pImpl_->access_or_create(bufferID).was_used();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DiagnosticBufferProvider::buffer_was_closed (uint bufferID) const
|
||||
{
|
||||
return pImpl_->access_or_create(bufferID).was_closed();
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
DiagnosticBufferProvider::accessMemory (uint bufferID) const
|
||||
{
|
||||
return pImpl_->access_or_create(bufferID).accessMemory();
|
||||
}
|
||||
|
||||
|
||||
} // namespace engine
|
||||
125
src/proc/engine/diagnostic-buffer-provider.hpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
DIAGNOSTIC-BUFFER-PROVIDER.hpp - helper for testing against the BufferProvider interface
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file diagnostic-buffer-provider.hpp
|
||||
** An facility for writing unit-tests targetting the BufferProvider interface.
|
||||
**
|
||||
** @see buffer-provider-protocol-test.cpp
|
||||
*/
|
||||
|
||||
#ifndef PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
|
||||
#define PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/singleton.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "proc/engine/buffer-provider.hpp"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Helper for unit tests: Buffer provider reference implementation.
|
||||
*
|
||||
* @todo write type comment
|
||||
*/
|
||||
class DiagnosticBufferProvider
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
/**
|
||||
* simple BufferProvider implementation
|
||||
* with additional allocation tracking
|
||||
*/
|
||||
class HeapMemProvider;
|
||||
|
||||
|
||||
boost::scoped_ptr<HeapMemProvider> pImpl_;
|
||||
static lib::Singleton<DiagnosticBufferProvider> diagnostics;
|
||||
|
||||
|
||||
HeapMemProvider& reset();
|
||||
bool isCurrent (BufferProvider const&);
|
||||
|
||||
|
||||
DiagnosticBufferProvider();
|
||||
~DiagnosticBufferProvider();
|
||||
|
||||
friend class lib::singleton::StaticCreate<DiagnosticBufferProvider>;
|
||||
|
||||
public:
|
||||
/** build a new Diagnostic Buffer Provider instance,
|
||||
* discard the existing one. Use the static query API
|
||||
* for investigating collected data. */
|
||||
static BufferProvider& build();
|
||||
|
||||
|
||||
/** access the diagnostic API of the buffer provider
|
||||
* @throw error::Invalid if the given provider doesn't allow
|
||||
* for diagnostic access or wasn't registered beforehand.
|
||||
*/
|
||||
static DiagnosticBufferProvider&
|
||||
access (BufferProvider const& );
|
||||
|
||||
|
||||
|
||||
|
||||
/* === diagnostic API === */
|
||||
|
||||
bool buffer_was_used (uint bufferID) const;
|
||||
bool buffer_was_closed (uint bufferID) const;
|
||||
void* accessMemory (uint bufferID) const;
|
||||
bool all_buffers_released() const;
|
||||
|
||||
|
||||
template<typename BU>
|
||||
bool
|
||||
object_was_attached (uint bufferID) const
|
||||
{
|
||||
UNIMPLEMENTED ("verify object attachment status of a specific buffer");
|
||||
}
|
||||
|
||||
|
||||
template<typename BU>
|
||||
bool
|
||||
object_was_destroyed (uint bufferID) const
|
||||
{
|
||||
UNIMPLEMENTED ("verify object attachment status of a specific buffer");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
BuffHandle - Buffer handling support for the render engine
|
||||
Dispatcher - translating calculation streams into frame jobs
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
|
|
@ -21,12 +21,17 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/dispatcher.hpp"
|
||||
//#include "lib/frameid.hpp"
|
||||
//#include "proc/state.hpp"
|
||||
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
|
||||
/** */
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
62
src/proc/engine/dispatcher.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
DISPATCHER.hpp - translating calculation streams into frame jobs
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROC_ENGINE_DISPATCHER_H
|
||||
#define PROC_ENGINE_DISPATCHER_H
|
||||
|
||||
#include "proc/common.hpp"
|
||||
#include "proc/state.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
using lib::time::TimeSpan;
|
||||
using lib::time::FSecs;
|
||||
using lib::time::Time;
|
||||
//
|
||||
// class ExitNode;
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
class Dispatcher
|
||||
{
|
||||
protected:
|
||||
/** timerange covered by this RenderGraph */
|
||||
TimeSpan segment_;
|
||||
|
||||
public:
|
||||
Dispatcher()
|
||||
: segment_(Time::ZERO, FSecs(5))
|
||||
{
|
||||
UNIMPLEMENTED ("anything regarding the Engine backbone");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
113
src/proc/engine/engine-diagnostics.hpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
ENGINE-DIAGNOSTICS.hpp - diagnostic facility to investigate engine operation
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file engine-diagnostics.hpp
|
||||
** An facility to check and monitor engine operations.
|
||||
** Once created, an EngineDiagnostics object connects to the EngineService
|
||||
** interface to activate additional tracing facilities within the engine,
|
||||
** allowing to watch and verify the creation of individual jobs and further
|
||||
** engine state parameters.
|
||||
**
|
||||
** @see EngineInterface
|
||||
** @see engine-interface-test.cpp
|
||||
** @see calc-stream-test.cpp
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROC_ENGINE_ENGINE_DIAGNOSTICS_H
|
||||
#define PROC_ENGINE_ENGINE_DIAGNOSTICS_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/engine/engine-service.hpp"
|
||||
//#include "include/dummy-player-facade.h"
|
||||
//#include "include/display-facade.h"
|
||||
//#include "proc/engine/calc-stream.hpp"
|
||||
//#include "proc/mobject/model-port.hpp"
|
||||
#include "proc/play/timings.hpp"
|
||||
//#include "proc/play/output-slot.hpp"
|
||||
//#include "common/instancehandle.hpp"
|
||||
//#include "lib/singleton-ref.hpp"
|
||||
//#include "lib/polymorphic-value.hpp"
|
||||
//#include "lib/singleton.hpp"
|
||||
//
|
||||
#include <boost/noncopyable.hpp>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
//#include <string>
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace engine{
|
||||
|
||||
// using std::string;
|
||||
// using lumiera::Subsys;
|
||||
// using lumiera::Display;
|
||||
// using lumiera::DummyPlayer;
|
||||
using proc::play::Timings;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Render engine diagnostic facility. Creating an instance
|
||||
* will activate additional tracing facilities within the
|
||||
* render engine; results may be investigated through
|
||||
* EngineDiagnostics public functions. The object acts
|
||||
* like a smart handle, i.e. the tracing facilities will
|
||||
* be disabled and disconnected when going out of scope.
|
||||
*/
|
||||
class EngineDiagnostics
|
||||
: boost::noncopyable
|
||||
{
|
||||
EngineService& engine_;
|
||||
|
||||
public:
|
||||
EngineDiagnostics (EngineService& e)
|
||||
: engine_(e)
|
||||
{
|
||||
UNIMPLEMENTED ("attach tracing connector");
|
||||
engine_.activateTracing();
|
||||
}
|
||||
|
||||
~EngineDiagnostics()
|
||||
{
|
||||
TODO ("detach tracing connector");
|
||||
engine_.disableTracing();
|
||||
}
|
||||
|
||||
/** */
|
||||
bool
|
||||
has_scheduled_jobs_for (Timings const& timings)
|
||||
{
|
||||
UNIMPLEMENTED ("Engine Diagnostics: query scheduled jobs");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
} // namespace proc
|
||||
#endif
|
||||
135
src/proc/engine/engine-service.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
EngineService - primary service access point for using the renderengine
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "proc/engine/engine-service.hpp"
|
||||
|
||||
//#include <string>
|
||||
//#include <memory>
|
||||
//#include <tr1/functional>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace engine{
|
||||
|
||||
// using std::string;
|
||||
// using lumiera::Subsys;
|
||||
// using std::auto_ptr;
|
||||
// using boost::scoped_ptr;
|
||||
// using std::tr1::bind;
|
||||
|
||||
|
||||
namespace { // hidden local details of the service implementation....
|
||||
|
||||
} // (End) hidden service impl details
|
||||
|
||||
|
||||
|
||||
/** storage for the EngineService interface object */
|
||||
lib::Singleton<EngineService> EngineService::instance;
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
EngineService::EngineService()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
CalcStream
|
||||
EngineService::calculate(ModelPort mPort,
|
||||
Timings nominalTimings,
|
||||
OutputConnection output,
|
||||
Quality serviceQuality)
|
||||
{
|
||||
UNIMPLEMENTED ("build a standard calculation stream");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
CalcStream
|
||||
EngineService::calculateBackground(ModelPort mPort,
|
||||
Timings nominalTimings,
|
||||
Quality serviceQuality)
|
||||
{
|
||||
UNIMPLEMENTED ("build a calculation stream for background rendering");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ===== Quality-of-Service ===== */
|
||||
|
||||
|
||||
EngineService::Quality::~Quality() { } // emit vtables here...
|
||||
|
||||
enum CalcType {
|
||||
PLAYBACK,
|
||||
RENDER,
|
||||
BACKGROUND
|
||||
};
|
||||
|
||||
|
||||
class DefaultQoS
|
||||
: public EngineService::Quality
|
||||
{
|
||||
CalcType type_;
|
||||
|
||||
public:
|
||||
DefaultQoS (CalcType type)
|
||||
: type_(type)
|
||||
{ }
|
||||
};
|
||||
|
||||
class PriorityQoS
|
||||
: public DefaultQoS
|
||||
{
|
||||
|
||||
public:
|
||||
PriorityQoS ()
|
||||
: DefaultQoS(PLAYBACK)
|
||||
{ }
|
||||
};
|
||||
|
||||
class Compromise
|
||||
: public DefaultQoS
|
||||
{
|
||||
|
||||
public:
|
||||
Compromise (CalcType type)
|
||||
: DefaultQoS(type)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
EngineService::QoS_Definition EngineService::QoS_DEFAULT = QoS_Definition::build<DefaultQoS> (PLAYBACK);
|
||||
EngineService::QoS_Definition EngineService::QoS_BACKGROUND = QoS_Definition::build<DefaultQoS> (BACKGROUND);
|
||||
EngineService::QoS_Definition EngineService::QoS_COMPROMISE = QoS_Definition::build<Compromise> (PLAYBACK);
|
||||
EngineService::QoS_Definition EngineService::QoS_PERFECT_RESULT = QoS_Definition::build<DefaultQoS> (RENDER);
|
||||
EngineService::QoS_Definition EngineService::QoS_SYNC_PRIORITY = QoS_Definition::build<PriorityQoS>();
|
||||
|
||||
|
||||
|
||||
}} // namespace proc::engine
|
||||
167
src/proc/engine/engine-service.hpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
ENGINE-SERVICE.hpp - primary service access point for using the renderengine
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file engine-service.hpp
|
||||
** Access point for the (core) calculation service of the render engine.
|
||||
** This Proc-Layer internal service is provided for use by the Player subsystem.
|
||||
** The actual implementation is forwarded to backend services (especially the scheduler).
|
||||
** The EngineService singleton has no state beyond the jobs currently managed by the
|
||||
** scheduler; when the latter isn't available, any invocation will throw.
|
||||
**
|
||||
** The central concept provided through this facade interface is the <i>calculation stream</i>.
|
||||
** This represents a series of calculations, expected to happen in a timely fashion and in order
|
||||
** to deliver a frame data stream onto an opened output connection. On the implementation side,
|
||||
** a calculation stream will be translated into a series of jobs invoking render nodes,
|
||||
** to be executed through the scheduler in the backend layer.
|
||||
**
|
||||
** While the individual CalcStram is simple, linear and unmodifiable, any CalcStream may be
|
||||
** \em superseded by a new definition. In this case, the engine will care for a seamless
|
||||
** switch and continuation; under the hood, there is a mechanism to discard resources
|
||||
** tied to the original CalcStream, once the switch to the new definition is complete.
|
||||
**
|
||||
** @see EngineInterface_test
|
||||
** @see CalcStream_test
|
||||
** @see proc::play::PlayerService
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROC_ENGINE_ENGINE_SERVICE_H
|
||||
#define PROC_ENGINE_ENGINE_SERVICE_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
//#include "include/dummy-player-facade.h"
|
||||
//#include "include/display-facade.h"
|
||||
#include "proc/engine/calc-stream.hpp"
|
||||
#include "proc/mobject/model-port.hpp"
|
||||
#include "proc/play/timings.hpp"
|
||||
#include "proc/play/output-slot.hpp"
|
||||
//#include "common/instancehandle.hpp"
|
||||
//#include "lib/singleton-ref.hpp"
|
||||
#include "lib/polymorphic-value.hpp"
|
||||
#include "lib/singleton.hpp"
|
||||
//
|
||||
#include <boost/noncopyable.hpp>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
//#include <string>
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace engine{
|
||||
|
||||
// using std::string;
|
||||
// using lumiera::Subsys;
|
||||
// using lumiera::Display;
|
||||
// using lumiera::DummyPlayer;
|
||||
using mobject::ModelPort;
|
||||
using proc::play::Timings;
|
||||
|
||||
typedef proc::play::OutputSlot::Allocation OutputConnection;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************
|
||||
* A service to schedule series of calculations,
|
||||
* delivering the rendered data into an external output
|
||||
* sink in a timely fashion. Actually the CalculationStream
|
||||
* instances provided through this (facade) interface are
|
||||
* backed by jobs executed through the scheduler in the
|
||||
* backend layer. The implementation of this service
|
||||
* cares to create the right job entries in the correct
|
||||
* order and to enqueue these into the scheduler.
|
||||
*/
|
||||
class EngineService
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
|
||||
/* The following typedefs allow to hand out predefined
|
||||
* Quality-of-Service strategy definitions as value objects,
|
||||
* without disclosing implementation details here in this header.
|
||||
*/
|
||||
enum{ QoS_IMPL_SIZE = sizeof(size_t) }; /////////////////////////////////////////TODO is this correct??
|
||||
|
||||
|
||||
public:
|
||||
/*************************************************************
|
||||
* Quality-of-Service definition for an Render Engine usage.
|
||||
* This strategy defines how to decide between conflicting goals like
|
||||
* - timely delivery
|
||||
* - image quality
|
||||
* - niceness and resource usage
|
||||
*/
|
||||
class Quality
|
||||
{
|
||||
public:
|
||||
virtual ~Quality(); ///< this is an Interface
|
||||
};
|
||||
|
||||
|
||||
typedef lib::polyvalue::CloneValueSupport<Quality> _Clonable_QoS_Strategy;
|
||||
typedef lib::PolymorphicValue<Quality, QoS_IMPL_SIZE, _Clonable_QoS_Strategy> QoS_Definition;
|
||||
|
||||
static QoS_Definition QoS_DEFAULT;
|
||||
static QoS_Definition QoS_BACKGROUND;
|
||||
static QoS_Definition QoS_SYNC_PRIORITY;
|
||||
static QoS_Definition QoS_PERFECT_RESULT;
|
||||
static QoS_Definition QoS_COMPROMISE;
|
||||
|
||||
|
||||
/** access point to the Engine Interface.
|
||||
* @internal this is an facade interface for internal use
|
||||
* by the player. Client code should use the Player.
|
||||
*/
|
||||
static lib::Singleton<EngineService> instance;
|
||||
|
||||
|
||||
EngineService();
|
||||
~EngineService() { }
|
||||
|
||||
CalcStream
|
||||
calculate(ModelPort mPort,
|
||||
Timings nominalTimings,
|
||||
OutputConnection output,
|
||||
Quality serviceQuality =QoS_DEFAULT);
|
||||
|
||||
CalcStream
|
||||
calculateBackground(ModelPort mPort,
|
||||
Timings nominalTimings,
|
||||
Quality serviceQuality =QoS_BACKGROUND);
|
||||
|
||||
|
||||
protected:
|
||||
void activateTracing();
|
||||
void disableTracing(); ///< EX_FREE
|
||||
|
||||
friend class EngineDiagnostics;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
} // namespace proc
|
||||
#endif
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
#ifndef ENGINE_MASK_H
|
||||
#define ENGINE_MASK_H
|
||||
|
||||
#include "proc/engine/trafo.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
|
||||
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ namespace engine
|
|||
{
|
||||
|
||||
|
||||
class Mask : public Trafo
|
||||
class Mask : public ProcNode
|
||||
{};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
#include "proc/state.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/bufftable.hpp"
|
||||
#include "proc/engine/bufftable-obsolete.hpp"
|
||||
|
||||
|
||||
|
||||
|
|
@ -172,6 +172,7 @@ namespace engine {
|
|||
};
|
||||
|
||||
|
||||
////////////TICKET #249 this strategy should better be hidden within the BuffHandle ctor (and type-erased after creation)
|
||||
struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations
|
||||
: Invocation
|
||||
{
|
||||
|
|
@ -179,7 +180,7 @@ namespace engine {
|
|||
: Invocation(sta, w, outCh) {}
|
||||
|
||||
virtual BuffHandle
|
||||
allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); } ////////////TODO: actually implement the "allocate from parent" logic!
|
||||
allocateBuffer (const lumiera::StreamType* ty) { return parent_.allocateBuffer(ty); } ////////////TODO: actually implement the "allocate from parent" logic!
|
||||
};
|
||||
|
||||
struct AllocBufferFromCache ///< using the global current State, which will delegate to Cache
|
||||
|
|
@ -189,7 +190,7 @@ namespace engine {
|
|||
: Invocation(sta, w, outCh) {}
|
||||
|
||||
virtual BuffHandle
|
||||
allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
|
||||
allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
#include "proc/state.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/bufftable.hpp"
|
||||
#include "proc/engine/bufftable-obsolete.hpp"
|
||||
#include "proc/engine/nodeinvocation.hpp"
|
||||
|
||||
#include "lib/meta/util.hpp"
|
||||
|
|
@ -245,7 +245,7 @@ namespace config {
|
|||
|
||||
template<class NEXT>
|
||||
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
|
||||
{ ///////////////// this would require BuffHandle to be a smart ref....
|
||||
{ ///////////////// this would require BuffHandle to be a smart ref.... --> ///TICKET #249
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include "proc/engine/nodeoperation.hpp"
|
||||
#include "proc/engine/nodewiring-config.hpp"
|
||||
|
||||
#include "lib/meta/typelist-util.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#ifndef ENGINE_PLUGINADAPTER_H
|
||||
#define ENGINE_PLUGINADAPTER_H
|
||||
|
||||
#include "proc/engine/trafo.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
|
||||
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ namespace engine
|
|||
* Effects processors are typically defined in a separate library and
|
||||
* will be loaded at runtime using Lumiera's plugin interface.
|
||||
*/
|
||||
class PluginAdapter : public Trafo
|
||||
class PluginAdapter : public ProcNode
|
||||
{
|
||||
/////////////
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
//#include "proc/state.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
//#include "proc/engine/bufftable.hpp"
|
||||
//#include "proc/engine/bufftable-obsolete.hpp"
|
||||
|
||||
|
||||
|
||||
|
|
|
|||