sync documentation with newly merged master
This commit is contained in:
commit
d62029492c
112 changed files with 4507 additions and 1278 deletions
|
|
@ -55,11 +55,15 @@
|
|||
#include "gui/window-manager.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <tr1/memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace gui {
|
||||
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
|
||||
|
||||
/* ====== The Application Class ====== */
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "clip.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
using namespace std::tr1;
|
||||
|
||||
namespace gui {
|
||||
namespace model {
|
||||
|
|
@ -33,7 +33,7 @@ namespace model {
|
|||
{
|
||||
// TEST CODE: add a clip to the track
|
||||
|
||||
boost::shared_ptr<model::Clip> modelClip(new model::Clip());
|
||||
shared_ptr<model::Clip> modelClip(new model::Clip());
|
||||
modelClip->setName("Clip Name");
|
||||
clips.push_back(modelClip);
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ namespace model {
|
|||
return os.str();
|
||||
}
|
||||
|
||||
lumiera::observable_list< boost::shared_ptr<Clip> >&
|
||||
lumiera::observable_list< shared_ptr<Clip> >&
|
||||
ClipTrack::getClipList()
|
||||
{
|
||||
return clips;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#ifndef CLIP_TRACK_HPP
|
||||
#define CLIP_TRACK_HPP
|
||||
|
||||
#include "track.hpp"
|
||||
#include "gui/model/track.hpp"
|
||||
#include "lib/observable-list.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -53,12 +53,12 @@ namespace model {
|
|||
/**
|
||||
* Gets the list of clips associated with this track.
|
||||
*/
|
||||
lumiera::observable_list< boost::shared_ptr<Clip> >&
|
||||
lumiera::observable_list<shared_ptr<Clip> >&
|
||||
getClipList(void);
|
||||
|
||||
private:
|
||||
|
||||
lumiera::observable_list< boost::shared_ptr<Clip> > clips;
|
||||
lumiera::observable_list<shared_ptr<Clip> > clips;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace gui {
|
|||
namespace model {
|
||||
|
||||
Clip::Clip()
|
||||
: timeCoord_(Time(FSecs(1)), FSecs(2))
|
||||
: timeCoord_(Time(FSecs(1)), FSecs(3))
|
||||
{ }
|
||||
|
||||
|
||||
|
|
@ -40,14 +40,14 @@ namespace model {
|
|||
Clip::setBegin (Time newStartTime)
|
||||
{
|
||||
timeCoord_.accept (Mutation::changeTime (newStartTime));
|
||||
// TODO: emit signal
|
||||
TODO("Emit A Signal");
|
||||
}
|
||||
|
||||
void
|
||||
Clip::setDuration (Duration newLength)
|
||||
{
|
||||
timeCoord_.accept (Mutation::changeDuration(newLength));
|
||||
// TODO: emit signal
|
||||
TODO("Emit A Signal");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "parent-track.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace boost;
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
namespace gui {
|
||||
namespace model {
|
||||
|
|
@ -32,13 +32,13 @@ ParentTrack::ParentTrack()
|
|||
{
|
||||
}
|
||||
|
||||
const std::list< boost::shared_ptr<Track> >&
|
||||
const std::list<shared_ptr<Track> >&
|
||||
ParentTrack::get_child_tracks() const
|
||||
{
|
||||
return tracks.get_list();
|
||||
}
|
||||
|
||||
lumiera::observable_list< boost::shared_ptr<Track> >&
|
||||
lumiera::observable_list<shared_ptr<Track> >&
|
||||
ParentTrack::get_child_track_list()
|
||||
{
|
||||
return tracks;
|
||||
|
|
@ -52,11 +52,11 @@ ParentTrack::can_host_children() const
|
|||
|
||||
bool
|
||||
ParentTrack::remove_descendant_track(
|
||||
const boost::shared_ptr<Track> track)
|
||||
const shared_ptr<Track> track)
|
||||
{
|
||||
REQUIRE(track);
|
||||
|
||||
boost::shared_ptr<ParentTrack> parent =
|
||||
shared_ptr<ParentTrack> parent =
|
||||
find_descendant_track_parent(track);
|
||||
if(parent)
|
||||
{
|
||||
|
|
@ -67,23 +67,24 @@ ParentTrack::remove_descendant_track(
|
|||
return false;
|
||||
}
|
||||
|
||||
boost::shared_ptr<ParentTrack>
|
||||
ParentTrack::find_descendant_track_parent(
|
||||
boost::shared_ptr<Track> child)
|
||||
shared_ptr<ParentTrack>
|
||||
ParentTrack::find_descendant_track_parent(shared_ptr<Track> child)
|
||||
{
|
||||
using namespace boost;
|
||||
|
||||
REQUIRE(child != NULL);
|
||||
BOOST_FOREACH(shared_ptr<Track> track, tracks)
|
||||
BOOST_FOREACH(std::tr1::shared_ptr<Track> track, tracks)
|
||||
{
|
||||
if(track == child)
|
||||
return shared_from_this();
|
||||
|
||||
shared_ptr<ParentTrack> result =
|
||||
|
||||
std::tr1::shared_ptr<ParentTrack> result =
|
||||
track->find_descendant_track_parent(child);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
return shared_ptr<ParentTrack>();
|
||||
return std::tr1::shared_ptr<ParentTrack>();
|
||||
}
|
||||
|
||||
} // namespace model
|
||||
|
|
|
|||
|
|
@ -28,13 +28,9 @@
|
|||
#ifndef PARENT_TRACK_HPP
|
||||
#define PARENT_TRACK_HPP
|
||||
|
||||
#include "track.hpp"
|
||||
#include "gui/model/track.hpp"
|
||||
#include "lib/observable-list.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
|
||||
namespace gui {
|
||||
namespace model {
|
||||
|
||||
|
|
@ -44,7 +40,7 @@ namespace model {
|
|||
*/
|
||||
class ParentTrack :
|
||||
public Track,
|
||||
public boost::enable_shared_from_this<ParentTrack>
|
||||
public std::tr1::enable_shared_from_this<ParentTrack>
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
|
|
@ -57,13 +53,13 @@ public:
|
|||
/**
|
||||
* Gets a read-only reference to the the list of child tracks.
|
||||
*/
|
||||
const std::list< boost::shared_ptr<Track> >&
|
||||
const std::list<shared_ptr<Track> >&
|
||||
get_child_tracks() const;
|
||||
|
||||
/**
|
||||
* Gets read-write access to the list of child tracks.
|
||||
*/
|
||||
lumiera::observable_list< boost::shared_ptr<Track> >&
|
||||
lumiera::observable_list<shared_ptr<Track> >&
|
||||
get_child_track_list();
|
||||
|
||||
/**
|
||||
|
|
@ -79,7 +75,7 @@ public:
|
|||
* @param The model track to try and remove.
|
||||
* @return Returns true if the track was successfully removed.
|
||||
*/
|
||||
bool remove_descendant_track(const boost::shared_ptr<Track> track);
|
||||
bool remove_descendant_track (const shared_ptr<Track> track);
|
||||
|
||||
/**
|
||||
* A utility function that attempts to find the parent of a track by
|
||||
|
|
@ -88,14 +84,14 @@ public:
|
|||
* @return Returns the parent track if one was found, or an empty
|
||||
* shared_ptr if none was found.
|
||||
*/
|
||||
boost::shared_ptr<ParentTrack>
|
||||
find_descendant_track_parent(boost::shared_ptr<Track> child);
|
||||
shared_ptr<ParentTrack>
|
||||
find_descendant_track_parent (shared_ptr<Track> child);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The internal list of child tracks of this parent.
|
||||
*/
|
||||
lumiera::observable_list< boost::shared_ptr<Track> > tracks;
|
||||
lumiera::observable_list<shared_ptr<Track> > tracks;
|
||||
};
|
||||
|
||||
} // namespace model
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include "sequence.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
|
||||
namespace gui {
|
||||
namespace model {
|
||||
|
|
@ -43,7 +43,7 @@ Project::~Project()
|
|||
|
||||
}
|
||||
|
||||
lumiera::observable_list< boost::shared_ptr<Sequence> >&
|
||||
lumiera::observable_list< shared_ptr<Sequence> >&
|
||||
Project::get_sequences()
|
||||
{
|
||||
return sequences;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#ifndef PROJECT_HPP
|
||||
#define PROJECT_HPP
|
||||
|
||||
#include "sequence.hpp"
|
||||
#include "gui/model/sequence.hpp"
|
||||
#include "lib/observable-list.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -40,14 +40,14 @@ public:
|
|||
|
||||
~Project();
|
||||
|
||||
lumiera::observable_list< boost::shared_ptr<Sequence> >&
|
||||
lumiera::observable_list<shared_ptr<Sequence> >&
|
||||
get_sequences();
|
||||
|
||||
void add_new_sequence(uString name);
|
||||
|
||||
private:
|
||||
|
||||
lumiera::observable_list< boost::shared_ptr<Sequence> > sequences;
|
||||
lumiera::observable_list<shared_ptr<Sequence> > sequences;
|
||||
};
|
||||
|
||||
} // namespace model
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include "group-track.hpp"
|
||||
#include "clip-track.hpp"
|
||||
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
|
||||
namespace gui {
|
||||
namespace model {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#ifndef SEQUENCE_HPP
|
||||
#define SEQUENCE_HPP
|
||||
|
||||
#include "parent-track.hpp"
|
||||
#include "gui/model/parent-track.hpp"
|
||||
|
||||
namespace gui {
|
||||
namespace model {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include "parent-track.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
using namespace std;
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -35,11 +35,12 @@ const list< shared_ptr<Track> > Track::NoChildren;
|
|||
Track::Track()
|
||||
: enabled(true),
|
||||
locked(false)
|
||||
{
|
||||
{ }
|
||||
|
||||
}
|
||||
Track::~Track() { }
|
||||
|
||||
const std::list< boost::shared_ptr<Track> >&
|
||||
|
||||
const std::list< shared_ptr<Track> >&
|
||||
Track::get_child_tracks() const
|
||||
{
|
||||
return Track::NoChildren;
|
||||
|
|
@ -96,9 +97,9 @@ Track::print_branch()
|
|||
return print_branch_recursive(0);
|
||||
}
|
||||
|
||||
boost::shared_ptr<ParentTrack>
|
||||
shared_ptr<ParentTrack>
|
||||
Track::find_descendant_track_parent(
|
||||
boost::shared_ptr<Track> /*child*/)
|
||||
shared_ptr<Track> /*child*/)
|
||||
{
|
||||
return shared_ptr<ParentTrack>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ protected:
|
|||
Track();
|
||||
|
||||
public:
|
||||
virtual ~Track(); /// this is an interface
|
||||
|
||||
/**
|
||||
* Returns true if this track can own any child tracks.
|
||||
|
|
@ -57,7 +58,7 @@ public:
|
|||
/**
|
||||
* Gets the list of child tracks.
|
||||
*/
|
||||
virtual const std::list< boost::shared_ptr<Track> >&
|
||||
virtual const std::list<shared_ptr<Track> >&
|
||||
get_child_tracks () const;
|
||||
|
||||
/**
|
||||
|
|
@ -106,8 +107,8 @@ public:
|
|||
* @return Returns the parent track if one was found, or an empty
|
||||
* shared_ptr if none was found.
|
||||
*/
|
||||
virtual boost::shared_ptr<ParentTrack>
|
||||
find_descendant_track_parent (boost::shared_ptr<Track> child);
|
||||
virtual shared_ptr<ParentTrack>
|
||||
find_descendant_track_parent (shared_ptr<Track> child);
|
||||
|
||||
/**
|
||||
* A signal which fires when the enabled status changes.
|
||||
|
|
@ -154,7 +155,7 @@ protected:
|
|||
* An object used internally as a return value for when there's no
|
||||
* children.
|
||||
*/
|
||||
static const std::list< boost::shared_ptr<Track> > NoChildren;
|
||||
static const std::list< shared_ptr<Track> > NoChildren;
|
||||
|
||||
/**
|
||||
* The internal implementation of print_branch.
|
||||
|
|
|
|||
|
|
@ -27,11 +27,12 @@
|
|||
#ifndef PANEL_HPP
|
||||
#define PANEL_HPP
|
||||
|
||||
#include <gdl/gdl-dock-item.h>
|
||||
|
||||
#include "gui/gtk-lumiera.hpp"
|
||||
#include "gui/widgets/panel-bar.hpp"
|
||||
|
||||
#include <gdl/gdl-dock-item.h>
|
||||
|
||||
namespace gui {
|
||||
|
||||
namespace workspace {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ using namespace gui::widgets;
|
|||
using namespace gui::widgets::timeline;
|
||||
using namespace gui::model;
|
||||
|
||||
using boost::shared_ptr; ///////////////////////////////TICKET #796
|
||||
using boost::weak_ptr; ///////////////////////////////TICKET #796
|
||||
using std::tr1::shared_ptr;
|
||||
using std::tr1::weak_ptr;
|
||||
using util::contains;
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -130,8 +130,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
|||
zoomScale .set_tooltip_text(_("Adjust timeline zoom scale"));
|
||||
|
||||
// Setup the timeline widget
|
||||
shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost
|
||||
= *get_project().get_sequences().begin();
|
||||
shared_ptr<Sequence> sequence = *(get_project().get_sequences().begin());
|
||||
timelineWidget.reset(new TimelineWidget(load_state(sequence)));
|
||||
pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
|
||||
|
||||
|
|
@ -139,7 +138,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
|
|||
// 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();
|
||||
update_tool_buttons();
|
||||
|
|
@ -334,19 +333,9 @@ TimelinePanel::update_tool_buttons()
|
|||
void
|
||||
TimelinePanel::update_zoom_buttons()
|
||||
{
|
||||
REQUIRE(timelineWidget);
|
||||
|
||||
const shared_ptr<timeline::TimelineState> state =
|
||||
timelineWidget->get_state();
|
||||
if(state)
|
||||
{
|
||||
timeline::TimelineViewWindow &viewWindow =
|
||||
state->get_view_window();
|
||||
|
||||
zoomIn.set_sensitive(viewWindow.get_time_scale() != 1);
|
||||
zoomOut.set_sensitive(viewWindow.get_time_scale()
|
||||
!= TimelineWidget::MaxScale);
|
||||
}
|
||||
/* This function is no longer needed
|
||||
* TODO: Let the ZoomScaleWidget perform
|
||||
* the update on its own */
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -36,13 +36,15 @@
|
|||
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <tr1/memory>
|
||||
|
||||
using namespace gui::widgets;
|
||||
|
||||
|
||||
namespace gui {
|
||||
|
||||
using std::tr1::shared_ptr;
|
||||
using std::tr1::weak_ptr;
|
||||
|
||||
|
||||
namespace model {
|
||||
class Sequence;
|
||||
|
|
@ -53,6 +55,7 @@ namespace panels {
|
|||
using lib::time::Time;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The definition of the timeline panel class, which holds timeline
|
||||
* widgets.
|
||||
|
|
@ -127,8 +130,8 @@ private:
|
|||
|
||||
void show_time (Time);
|
||||
|
||||
boost::shared_ptr<widgets::timeline::TimelineState> ///////////////////////////////TICKET #796
|
||||
load_state (boost::weak_ptr<model::Sequence> sequence);
|
||||
shared_ptr<widgets::timeline::TimelineState>
|
||||
load_state (weak_ptr<model::Sequence> sequence);
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -148,7 +151,7 @@ private:
|
|||
* An invisible column which will be used to identify the sequence
|
||||
* of a row.
|
||||
*/
|
||||
Gtk::TreeModelColumn< boost::weak_ptr<model::Sequence> >
|
||||
Gtk::TreeModelColumn< weak_ptr<model::Sequence> >
|
||||
sequenceColumn;
|
||||
|
||||
/**
|
||||
|
|
@ -172,9 +175,10 @@ private:
|
|||
|
||||
// Body Widgets
|
||||
boost::scoped_ptr<TimelineWidget> timelineWidget;
|
||||
|
||||
std::map< boost::weak_ptr<model::Sequence>,
|
||||
boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
|
||||
std::map< weak_ptr<model::Sequence>
|
||||
, shared_ptr<widgets::timeline::TimelineState>
|
||||
>
|
||||
timelineStates;
|
||||
|
||||
// Toolbar Widgets
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@
|
|||
* *****************************************************/
|
||||
|
||||
#include "gui/gtk-lumiera.hpp"
|
||||
#include "gui/panels/viewer-panel.hpp"
|
||||
|
||||
#include "gui/workspace/workspace-window.hpp"
|
||||
#include "gui/controller/controller.hpp"
|
||||
#include "gui/controller/playback-controller.hpp"
|
||||
#include "gui/display-service.hpp"
|
||||
#include "gui/workspace/workspace-window.hpp"
|
||||
|
||||
#include "viewer-panel.hpp"
|
||||
|
||||
using namespace Gtk;
|
||||
using namespace gui::widgets;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,12 @@
|
|||
#ifndef VIEWER_PANEL_HPP
|
||||
#define VIEWER_PANEL_HPP
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
#include "panel.hpp"
|
||||
#include "gui/panels/panel.hpp"
|
||||
#include "gui/widgets/video-display-widget.hpp"
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
namespace gui {
|
||||
namespace panels {
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@
|
|||
|
||||
using namespace Gtk;
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
|
||||
using namespace util;
|
||||
using namespace gui::widgets::timeline;
|
||||
|
||||
|
|
@ -40,21 +41,19 @@ namespace widgets {
|
|||
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; // 30 Million
|
||||
|
||||
TimelineWidget::TimelineWidget(
|
||||
boost::shared_ptr<timeline::TimelineState> source_state) :
|
||||
Table(2, 2),
|
||||
layoutHelper(*this),
|
||||
headerContainer(NULL),
|
||||
body(NULL),
|
||||
ruler(NULL),
|
||||
horizontalAdjustment(0, 0, 0),
|
||||
verticalAdjustment(0, 0, 0),
|
||||
horizontalScroll(horizontalAdjustment),
|
||||
verticalScroll(verticalAdjustment),
|
||||
update_tracks_frozen(false)
|
||||
|
||||
TimelineWidget::TimelineWidget(shared_ptr<timeline::TimelineState> source_state)
|
||||
: Table(2, 2)
|
||||
, layoutHelper(*this)
|
||||
, headerContainer(NULL)
|
||||
, body(NULL)
|
||||
, ruler(NULL)
|
||||
, horizontalAdjustment(0, 0, 0)
|
||||
, verticalAdjustment(0, 0, 0)
|
||||
, horizontalScroll(horizontalAdjustment)
|
||||
, verticalScroll(verticalAdjustment)
|
||||
, update_tracks_frozen(false)
|
||||
{
|
||||
body = manage(new TimelineBody(*this));
|
||||
ENSURE(body != NULL);
|
||||
|
|
@ -62,7 +61,7 @@ TimelineWidget::TimelineWidget(
|
|||
ENSURE(headerContainer != NULL);
|
||||
ruler = manage(new TimelineRuler(*this));
|
||||
ENSURE(ruler != NULL);
|
||||
|
||||
|
||||
horizontalAdjustment.signal_value_changed().connect( sigc::mem_fun(
|
||||
this, &TimelineWidget::on_scroll) );
|
||||
verticalAdjustment.signal_value_changed().connect( sigc::mem_fun(
|
||||
|
|
@ -71,7 +70,7 @@ TimelineWidget::TimelineWidget(
|
|||
this, &TimelineWidget::on_motion_in_body_notify_event) );
|
||||
|
||||
update_tracks();
|
||||
|
||||
|
||||
attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND);
|
||||
attach(*ruler, 1, 2, 0, 1, FILL|EXPAND, SHRINK);
|
||||
attach(*headerContainer, 0, 1, 1, 2, SHRINK, FILL|EXPAND);
|
||||
|
|
@ -93,7 +92,7 @@ TimelineWidget::~TimelineWidget()
|
|||
|
||||
/* ===== Data Access ===== */
|
||||
|
||||
boost::shared_ptr<timeline::TimelineState>
|
||||
shared_ptr<timeline::TimelineState>
|
||||
TimelineWidget::get_state()
|
||||
{
|
||||
return state;
|
||||
|
|
@ -334,8 +333,8 @@ TimelineWidget::create_timeline_track_from_modelTrack(
|
|||
void
|
||||
TimelineWidget::remove_orphaned_tracks()
|
||||
{
|
||||
std::map<boost::shared_ptr<model::Track>,
|
||||
boost::shared_ptr<timeline::Track> >
|
||||
std::map<shared_ptr<model::Track>,
|
||||
shared_ptr<timeline::Track> >
|
||||
orphan_track_map(trackMap);
|
||||
|
||||
// Remove all tracks which are still present in the sequence
|
||||
|
|
@ -356,9 +355,9 @@ TimelineWidget::remove_orphaned_tracks()
|
|||
|
||||
void
|
||||
TimelineWidget::search_orphaned_tracks_in_branch(
|
||||
boost::shared_ptr<model::Track> modelTrack,
|
||||
std::map<boost::shared_ptr<model::Track>,
|
||||
boost::shared_ptr<timeline::Track> > &orphan_track_map)
|
||||
shared_ptr<model::Track> modelTrack,
|
||||
std::map<shared_ptr<model::Track>,
|
||||
shared_ptr<timeline::Track> > &orphan_track_map)
|
||||
{
|
||||
REQUIRE(modelTrack);
|
||||
|
||||
|
|
@ -414,12 +413,13 @@ TimelineWidget::update_scroll()
|
|||
|
||||
if(state)
|
||||
{
|
||||
///////////////////////////////////////////////TICKET #861 shoudln't that be performed by TimelineViewWindow, instead of manipulating values from the outside?
|
||||
timeline::TimelineViewWindow &window = state->get_view_window();
|
||||
|
||||
//----- Horizontal Scroll ------//
|
||||
|
||||
// TEST CODE
|
||||
horizontalAdjustment.set_upper(1000 * GAVL_TIME_SCALE / 200);
|
||||
horizontalAdjustment.set_upper( 1000 * GAVL_TIME_SCALE / 200);
|
||||
horizontalAdjustment.set_lower(-1000 * GAVL_TIME_SCALE / 200);
|
||||
|
||||
// Set the page size
|
||||
|
|
@ -480,7 +480,7 @@ TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event)
|
|||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<model::Sequence>
|
||||
shared_ptr<model::Sequence>
|
||||
TimelineWidget::sequence() const
|
||||
{
|
||||
if(!state)
|
||||
|
|
|
|||
|
|
@ -64,17 +64,13 @@ class TimelineWidget : public Gtk::Table
|
|||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param source_state The state that will be used as the data source
|
||||
* for this timeline widget.
|
||||
*/
|
||||
TimelineWidget(
|
||||
boost::shared_ptr<timeline::TimelineState> source_state);
|
||||
TimelineWidget (shared_ptr<timeline::TimelineState> source_state);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~TimelineWidget();
|
||||
virtual ~TimelineWidget();
|
||||
|
||||
|
||||
/* ===== Data Access ===== */
|
||||
public:
|
||||
|
|
@ -84,13 +80,13 @@ public:
|
|||
* @return The state object that the timeline widget is currently
|
||||
* working with.
|
||||
*/
|
||||
boost::shared_ptr<timeline::TimelineState> get_state();
|
||||
shared_ptr<timeline::TimelineState> get_state();
|
||||
|
||||
/**
|
||||
* Replaces the current TimelineState object with another.
|
||||
* @param new_state The new state to swap in.
|
||||
*/
|
||||
void set_state(boost::shared_ptr<timeline::TimelineState> new_state);
|
||||
void set_state(shared_ptr<timeline::TimelineState> new_state);
|
||||
|
||||
/**
|
||||
* Zooms the view in or out as by a number of steps while keeping a
|
||||
|
|
@ -110,13 +106,13 @@ public:
|
|||
*/
|
||||
void set_tool(timeline::ToolType tool_type);
|
||||
|
||||
boost::shared_ptr<timeline::Track>
|
||||
shared_ptr<timeline::Track>
|
||||
get_hovering_track() const;
|
||||
|
||||
public:
|
||||
/* ===== Signals ===== */
|
||||
typedef sigc::signal<void, boost::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal;
|
||||
typedef sigc::signal<void, boost::shared_ptr<timeline::Track> > HoveringTrackChangedSignal;
|
||||
typedef sigc::signal<void, shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal;
|
||||
typedef sigc::signal<void, shared_ptr<timeline::Track> > HoveringTrackChangedSignal;
|
||||
|
||||
sigc::signal<void, lib::time::Time> mouse_hover_signal() const;
|
||||
|
||||
|
|
@ -169,8 +165,8 @@ private:
|
|||
* already exist in trackMap.
|
||||
* @param list The parent track of the branch.
|
||||
*/
|
||||
void create_timeline_tracks_from_branch(
|
||||
boost::shared_ptr<model::Track> modelTrack);
|
||||
void
|
||||
create_timeline_tracks_from_branch (shared_ptr<model::Track> modelTrack);
|
||||
|
||||
/**
|
||||
* Creates a timeline UI track to correspond to a model track.
|
||||
|
|
@ -178,9 +174,8 @@ private:
|
|||
* @return The timeline track created, or an empty shared_ptr if
|
||||
* modelTrack has an unreckognised type (this is an error condition).
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track>
|
||||
create_timeline_track_from_modelTrack(
|
||||
boost::shared_ptr<model::Track> modelTrack);
|
||||
shared_ptr<timeline::Track>
|
||||
create_timeline_track_from_modelTrack(shared_ptr<model::Track> modelTrack);
|
||||
|
||||
/**
|
||||
* Removes any UI tracks which no longer have corresponding model
|
||||
|
|
@ -188,10 +183,10 @@ private:
|
|||
*/
|
||||
void remove_orphaned_tracks();
|
||||
|
||||
void search_orphaned_tracks_in_branch(
|
||||
boost::shared_ptr<model::Track> modelTrack,
|
||||
std::map<boost::shared_ptr<model::Track>,
|
||||
boost::shared_ptr<timeline::Track> > &orphan_track_map);
|
||||
void
|
||||
search_orphaned_tracks_in_branch (shared_ptr<model::Track> modelTrack,
|
||||
std::map<shared_ptr<model::Track>,
|
||||
shared_ptr<timeline::Track> > &orphan_track_map);
|
||||
|
||||
/**
|
||||
* Looks up a timeline UI track in trackMap that corresponds to a
|
||||
|
|
@ -201,8 +196,8 @@ private:
|
|||
* modelTrack has no corresponding timeline UI track (this is an
|
||||
* error condition).
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track> lookup_timeline_track(
|
||||
boost::shared_ptr<model::Track> modelTrack) const;
|
||||
shared_ptr<timeline::Track>
|
||||
lookup_timeline_track (shared_ptr<model::Track> modelTrack) const;
|
||||
|
||||
// ----- Layout Functions ----- //
|
||||
|
||||
|
|
@ -232,12 +227,12 @@ private:
|
|||
* Helper to get the sequence object from the state.
|
||||
* @return Returns a shared pointer to the sequence.
|
||||
*/
|
||||
boost::shared_ptr<model::Sequence> sequence() const;
|
||||
shared_ptr<model::Sequence> sequence() const;
|
||||
|
||||
// ----- Other Functions ----- //
|
||||
|
||||
void set_hovering_track(
|
||||
boost::shared_ptr<timeline::Track> hovering_track);
|
||||
void
|
||||
set_hovering_track (shared_ptr<timeline::Track> hovering_track);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
@ -245,7 +240,7 @@ protected:
|
|||
* The state that will be used as the data source for this timeline
|
||||
* widget.
|
||||
*/
|
||||
boost::shared_ptr<timeline::TimelineState> state;
|
||||
shared_ptr<timeline::TimelineState> state;
|
||||
|
||||
// Model Data
|
||||
|
||||
|
|
@ -256,11 +251,11 @@ protected:
|
|||
* widget is updated with update_tracks, timeline tracks are added and
|
||||
* removed from the map in correspondence with the tree.
|
||||
*/
|
||||
std::map<boost::shared_ptr<model::Track>,
|
||||
boost::shared_ptr<timeline::Track> >
|
||||
std::map<shared_ptr<model::Track>
|
||||
,shared_ptr<timeline::Track> >
|
||||
trackMap;
|
||||
|
||||
boost::shared_ptr<timeline::Track> hoveringTrack;
|
||||
shared_ptr<timeline::Track> hoveringTrack;
|
||||
|
||||
// Helper Classes
|
||||
timeline::TimelineLayoutHelper layoutHelper;
|
||||
|
|
@ -283,19 +278,10 @@ protected:
|
|||
bool update_tracks_frozen;
|
||||
|
||||
/* ===== Constants ===== */
|
||||
public:
|
||||
/**
|
||||
* The maximum scale for timeline display.
|
||||
* @remarks At MaxScale, every pixel on the timeline is equivalent
|
||||
* to 30000000 lumiera::Time increments.
|
||||
*/
|
||||
static const int64_t MaxScale;
|
||||
|
||||
protected:
|
||||
static const int TrackPadding;
|
||||
static const int HeaderWidth;
|
||||
static const int HeaderIndentWidth;
|
||||
static const double ZoomIncrement;
|
||||
|
||||
friend class timeline::TimelineBody;
|
||||
friend class timeline::TimelineHeaderContainer;
|
||||
|
|
|
|||
|
|
@ -55,17 +55,17 @@ namespace timeline {
|
|||
Tool::on_button_press_event(event);
|
||||
|
||||
// Convert the mouse click position to a Time
|
||||
boost::shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state();
|
||||
shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state();
|
||||
REQUIRE(state);
|
||||
TimelineViewWindow const& window = state->get_view_window();
|
||||
Time tpoint = window.x_to_time(mousePoint.get_x());
|
||||
|
||||
// Get the clip, if any
|
||||
boost::shared_ptr<timeline::Track> track = getHoveringTrack();
|
||||
boost::shared_ptr<Clip> clip = track->getClipAt(tpoint);
|
||||
shared_ptr<timeline::Track> track = getHoveringTrack();
|
||||
shared_ptr<Clip> clip = track->getClipAt(tpoint);
|
||||
|
||||
// Nothing to do if there is no clip
|
||||
if (clip == boost::shared_ptr<Clip>())
|
||||
if (!clip)
|
||||
return;
|
||||
|
||||
clip->setSelected(true);
|
||||
|
|
@ -77,8 +77,7 @@ namespace timeline {
|
|||
REQUIRE (event != NULL);
|
||||
Tool::on_button_release_event(event);
|
||||
|
||||
boost::shared_ptr<timeline::Track> track =
|
||||
getHoveringTrack();
|
||||
shared_ptr<timeline::Track> track = getHoveringTrack();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -92,10 +91,10 @@ namespace timeline {
|
|||
return;
|
||||
}
|
||||
|
||||
boost::shared_ptr<timeline::Track>
|
||||
shared_ptr<timeline::Track>
|
||||
ArrowTool::getHoveringTrack ()
|
||||
{
|
||||
boost::shared_ptr<timeline::Track> track(
|
||||
shared_ptr<timeline::Track> track(
|
||||
timelineBody.getTimelineWidget().get_hovering_track());
|
||||
return track;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ namespace timeline {
|
|||
|
||||
private:
|
||||
|
||||
boost::shared_ptr<timeline::Track>
|
||||
shared_ptr<timeline::Track>
|
||||
getHoveringTrack ();
|
||||
|
||||
bool selectionRectangleActive;
|
||||
|
|
|
|||
|
|
@ -34,12 +34,10 @@
|
|||
|
||||
using namespace Gtk;
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace lumiera;
|
||||
|
||||
using gui::util::CairoUtil;
|
||||
|
||||
using boost::shared_ptr; ////////////////////TICKET #796
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
|
|
@ -59,9 +57,12 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget)
|
|||
timelineWidget.state_changed_signal().connect(
|
||||
sigc::mem_fun(this, &TimelineBody::on_state_changed) );
|
||||
|
||||
// Set a default Tool
|
||||
this->set_tool(Arrow);
|
||||
|
||||
// Install style properties
|
||||
register_styles();
|
||||
|
||||
|
||||
// Reset the state
|
||||
propagateStateChange();
|
||||
}
|
||||
|
|
@ -92,10 +93,10 @@ TimelineBody::get_tool() const
|
|||
}
|
||||
|
||||
void
|
||||
TimelineBody::set_tool(timeline::ToolType tool_type)
|
||||
TimelineBody::set_tool(timeline::ToolType tool_type, bool force)
|
||||
{
|
||||
// Tidy up old tool
|
||||
if(tool)
|
||||
if(tool && !force)
|
||||
{
|
||||
// Do we need to change tools?
|
||||
if(tool->get_type() == tool_type)
|
||||
|
|
@ -266,7 +267,8 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
|
|||
|
||||
if (timelineState)
|
||||
{
|
||||
// Handle a middle-mouse drag if one is occuring
|
||||
// Handle a middle-mouse drag if one is occurring
|
||||
/////////////////////////////TICKET #861 : shoudln't all of that be performed by TimelineViewWindow, instead of manipulating values from the outside?
|
||||
switch(dragType)
|
||||
{
|
||||
case Shift:
|
||||
|
|
@ -318,7 +320,10 @@ TimelineBody::propagateStateChange()
|
|||
viewWindow().changed_signal().connect(
|
||||
sigc::mem_fun(this, &TimelineBody::on_update_view) );
|
||||
}
|
||||
|
||||
|
||||
// Need to reload the current tool...
|
||||
set_tool (get_tool(), true);
|
||||
|
||||
// Redraw
|
||||
queue_draw();
|
||||
}
|
||||
|
|
@ -351,7 +356,7 @@ TimelineBody::draw_tracks(Cairo::RefPtr<Cairo::Context> cr)
|
|||
const shared_ptr<timeline::Track> timeline_track =
|
||||
timelineWidget.lookup_timeline_track(*iterator);
|
||||
|
||||
optional<Gdk::Rectangle> rect =
|
||||
boost::optional<Gdk::Rectangle> rect =
|
||||
layout_helper.get_track_header_rect(timeline_track);
|
||||
|
||||
// Is this track visible?
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class TimelineWidget;
|
|||
namespace timeline {
|
||||
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::TimeSpan;
|
||||
|
||||
|
||||
class Track;
|
||||
|
|
@ -84,7 +85,7 @@ public:
|
|||
* @param tool_type The type of tool to set.
|
||||
*/
|
||||
void
|
||||
set_tool(ToolType tool_type);
|
||||
set_tool(ToolType tool_type, bool force=false);
|
||||
|
||||
/* ===== Events ===== */
|
||||
protected:
|
||||
|
|
@ -124,7 +125,7 @@ protected:
|
|||
/**
|
||||
* The event handler for when the TimelineWidget's state is switched.
|
||||
*/
|
||||
void on_state_changed (boost::shared_ptr<TimelineState> newState);
|
||||
void on_state_changed (shared_ptr<TimelineState> newState);
|
||||
|
||||
/* ===== Internals ===== */
|
||||
private:
|
||||
|
|
@ -142,8 +143,8 @@ private:
|
|||
void draw_tracks(Cairo::RefPtr<Cairo::Context> cr);
|
||||
|
||||
void draw_track(Cairo::RefPtr<Cairo::Context> cr,
|
||||
boost::shared_ptr<timeline::Track> timeline_track,
|
||||
const int view_width) const;
|
||||
shared_ptr<timeline::Track> timeline_track,
|
||||
const int view_width) const;
|
||||
|
||||
/**
|
||||
* Draws the selected timeline period.
|
||||
|
|
@ -200,7 +201,7 @@ private:
|
|||
Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour;
|
||||
|
||||
gui::widgets::TimelineWidget &timelineWidget;
|
||||
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
shared_ptr<TimelineState> timelineState;
|
||||
|
||||
|
||||
friend class Tool;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@
|
|||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "timeline-clip.hpp"
|
||||
#include "timeline-clip-track.hpp"
|
||||
#include "timeline-view-window.hpp"
|
||||
#include "gui/widgets/timeline/timeline-clip.hpp"
|
||||
#include "gui/widgets/timeline/timeline-clip-track.hpp"
|
||||
#include "gui/widgets/timeline/timeline-view-window.hpp"
|
||||
|
||||
using namespace Gtk;
|
||||
using boost::dynamic_pointer_cast;
|
||||
using boost::shared_ptr;
|
||||
|
||||
using std::tr1::shared_ptr;
|
||||
using util::contains;
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -76,7 +76,7 @@ namespace timeline {
|
|||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<timeline::Clip>
|
||||
shared_ptr<timeline::Clip>
|
||||
ClipTrack::getClipAt(Time position) const
|
||||
{
|
||||
std::pair<shared_ptr<model::Clip>, shared_ptr<timeline::Clip> >
|
||||
|
|
@ -88,7 +88,7 @@ namespace timeline {
|
|||
}
|
||||
|
||||
// Nothing found
|
||||
return boost::shared_ptr<timeline::Clip>();
|
||||
return shared_ptr<timeline::Clip>();
|
||||
}
|
||||
|
||||
//// private methods
|
||||
|
|
@ -97,8 +97,8 @@ namespace timeline {
|
|||
ClipTrack::createTimelineClips()
|
||||
{
|
||||
// Share the draw strategy between all objects
|
||||
// TODO: use factory/builder here
|
||||
static boost::shared_ptr<timeline::DrawStrategy> drawStrategy(new BasicDrawStrategy());
|
||||
TODO("Use factory/builder to create Timline Clips");
|
||||
static shared_ptr<timeline::DrawStrategy> drawStrategy(new BasicDrawStrategy());
|
||||
BOOST_FOREACH (shared_ptr<model::Clip> modelClip, getModelTrack()->getClipList())
|
||||
{
|
||||
// Is a timeline UI clip present in the map already?
|
||||
|
|
@ -115,7 +115,7 @@ namespace timeline {
|
|||
shared_ptr<model::ClipTrack>
|
||||
ClipTrack::getModelTrack ()
|
||||
{
|
||||
return dynamic_pointer_cast<model::ClipTrack>(modelTrack);
|
||||
return std::tr1::dynamic_pointer_cast<model::ClipTrack>(modelTrack);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace timeline {
|
|||
* Constructor.
|
||||
*/
|
||||
ClipTrack(TimelineWidget &timelineWidget,
|
||||
boost::shared_ptr<model::ClipTrack> track);
|
||||
shared_ptr<model::ClipTrack> track);
|
||||
|
||||
/**
|
||||
* Draw the track in the timeline.
|
||||
|
|
@ -66,7 +66,7 @@ namespace timeline {
|
|||
* pointer.
|
||||
* @param the given time
|
||||
*/
|
||||
boost::shared_ptr<timeline::Clip>
|
||||
shared_ptr<timeline::Clip>
|
||||
getClipAt(Time position) const;
|
||||
|
||||
private:
|
||||
|
|
@ -80,7 +80,7 @@ namespace timeline {
|
|||
/**
|
||||
* Gets the modelTrack as a ClipTrack.
|
||||
*/
|
||||
boost::shared_ptr<model::ClipTrack>
|
||||
shared_ptr<model::ClipTrack>
|
||||
getModelTrack ();
|
||||
|
||||
/**
|
||||
|
|
@ -107,8 +107,8 @@ namespace timeline {
|
|||
* The clipMap maps model clips to timeline widget clips which are responsible for the
|
||||
* UI representation of a clip.
|
||||
*/
|
||||
std::map<boost::shared_ptr<model::Clip>,
|
||||
boost::shared_ptr<timeline::Clip> >
|
||||
std::map<shared_ptr<model::Clip>,
|
||||
shared_ptr<timeline::Clip> >
|
||||
clipMap;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ namespace gui {
|
|||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
using boost::shared_ptr; //////////////////////TICKET #796
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
|
||||
Clip::Clip (boost::shared_ptr<model::Clip> clip,
|
||||
boost::shared_ptr<timeline::DrawStrategy> drawStrategy)
|
||||
Clip::Clip (shared_ptr<model::Clip> clip,
|
||||
shared_ptr<timeline::DrawStrategy> drawStrategy)
|
||||
: Entity(drawStrategy)
|
||||
, modelClip(clip)
|
||||
, selected(false)
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ namespace timeline {
|
|||
class Clip : public Entity
|
||||
{
|
||||
public:
|
||||
Clip (boost::shared_ptr<model::Clip> clip,
|
||||
boost::shared_ptr<timeline::DrawStrategy> drawStrategy);
|
||||
Clip (shared_ptr<model::Clip> clip,
|
||||
shared_ptr<timeline::DrawStrategy> drawStrategy);
|
||||
|
||||
|
||||
Time getBegin() const;
|
||||
|
|
@ -62,7 +62,7 @@ namespace timeline {
|
|||
|
||||
private:
|
||||
|
||||
boost::shared_ptr<model::Clip> modelClip; ////////////////////////TICKET #796 : should use std::tr1
|
||||
shared_ptr<model::Clip> modelClip;
|
||||
|
||||
/**
|
||||
* True when this clip is selected in the GUI.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace gui {
|
|||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
Entity::Entity(boost::shared_ptr<timeline::DrawStrategy> drawStrategy)
|
||||
Entity::Entity (shared_ptr<timeline::DrawStrategy> drawStrategy)
|
||||
: enabled(true),
|
||||
drawStrategy(drawStrategy)
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -31,14 +31,16 @@
|
|||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <tr1/memory>
|
||||
#include <cairomm/cairomm.h>
|
||||
#include <boost/shared_ptr.hpp> //////////////////////TICKET #796
|
||||
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
using lib::time::Time;
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
|
||||
class DrawStrategy;
|
||||
|
|
@ -51,7 +53,7 @@ namespace timeline {
|
|||
class Entity {
|
||||
protected:
|
||||
|
||||
Entity(boost::shared_ptr<timeline::DrawStrategy> drawStrategy);
|
||||
Entity (shared_ptr<timeline::DrawStrategy> drawStrategy);
|
||||
|
||||
virtual ~Entity();
|
||||
|
||||
|
|
@ -81,7 +83,7 @@ namespace timeline {
|
|||
|
||||
bool enabled;
|
||||
|
||||
boost::shared_ptr<timeline::DrawStrategy> drawStrategy;
|
||||
shared_ptr<timeline::DrawStrategy> drawStrategy;
|
||||
};
|
||||
|
||||
} // namespace timeline
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include "gui/widgets/timeline-widget.hpp"
|
||||
|
||||
using namespace Gtk;
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
using namespace sigc;
|
||||
|
||||
namespace gui {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#ifndef TIMELINE_GROUP_TRACK_HPP
|
||||
#define TIMELINE_GROUP_TRACK_HPP
|
||||
|
||||
#include "timeline-track.hpp"
|
||||
#include "gui/widgets/timeline/timeline-track.hpp"
|
||||
#include "gui/model/group-track.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -36,19 +36,16 @@ namespace timeline {
|
|||
class GroupTrack : public timeline::Track
|
||||
{
|
||||
public:
|
||||
GroupTrack(TimelineWidget &timeline_widget,
|
||||
boost::shared_ptr<model::GroupTrack> track);
|
||||
GroupTrack (TimelineWidget &timeline_widget,
|
||||
shared_ptr<model::GroupTrack> track);
|
||||
|
||||
void draw_track(Cairo::RefPtr<Cairo::Context> cairo,
|
||||
TimelineViewWindow* constwindow)
|
||||
const;
|
||||
void draw_track (Cairo::RefPtr<Cairo::Context> cairo,
|
||||
TimelineViewWindow* constwindow) const;
|
||||
|
||||
protected:
|
||||
void on_child_list_changed();
|
||||
};
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace widgets
|
||||
} // namespace gui
|
||||
|
||||
}}} // namespace gui::widgets::timeline
|
||||
#endif // TIMELINE_GROUP_TRACK_HPP
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
using namespace Gtk;
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
using namespace util;
|
||||
|
||||
namespace gui {
|
||||
|
|
@ -342,12 +342,9 @@ TimelineHeaderContainer::on_scroll()
|
|||
}
|
||||
|
||||
void
|
||||
TimelineHeaderContainer::on_hovering_track_changed(
|
||||
boost::shared_ptr<timeline::Track> hovering_track)
|
||||
TimelineHeaderContainer::on_hovering_track_changed( shared_ptr<timeline::Track>)
|
||||
{
|
||||
(void)hovering_track;
|
||||
|
||||
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -392,7 +389,7 @@ TimelineHeaderContainer::layout_headers()
|
|||
|
||||
Widget &widget = timeline_track->get_header_widget();
|
||||
|
||||
optional<Gdk::Rectangle> header_rect =
|
||||
boost::optional<Gdk::Rectangle> header_rect =
|
||||
layout_helper.get_track_header_rect(timeline_track);
|
||||
|
||||
if(header_rect)
|
||||
|
|
|
|||
|
|
@ -139,8 +139,7 @@ private:
|
|||
*/
|
||||
void on_scroll();
|
||||
|
||||
void on_hovering_track_changed(
|
||||
boost::shared_ptr<timeline::Track> hovering_track);
|
||||
void on_hovering_track_changed(shared_ptr<timeline::Track> hovering_track);
|
||||
|
||||
private:
|
||||
/* ===== Internal Event Handlers ===== */
|
||||
|
|
@ -168,9 +167,8 @@ private:
|
|||
* to control the amount of indention.
|
||||
* @param offset The vertical offset of the headers in pixels.
|
||||
*/
|
||||
void draw_header_decoration(
|
||||
boost::shared_ptr<model::Track> modelTrack,
|
||||
const Gdk::Rectangle &clip_rect);
|
||||
void draw_header_decoration(shared_ptr<model::Track> modelTrack,
|
||||
const Gdk::Rectangle &clip_rect);
|
||||
|
||||
/**
|
||||
* A helper function which calls lookup_timeline_track within the
|
||||
|
|
@ -182,8 +180,8 @@ private:
|
|||
* @remarks If the return value is going to be NULL, an ENSURE will
|
||||
* fail.
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track> lookup_timeline_track(
|
||||
boost::shared_ptr<model::Track> modelTrack);
|
||||
shared_ptr<timeline::Track>
|
||||
lookup_timeline_track (shared_ptr<model::Track> modelTrack);
|
||||
|
||||
void begin_drag();
|
||||
|
||||
|
|
@ -248,7 +246,7 @@ private:
|
|||
int scrollSlideRate;
|
||||
|
||||
//----- User Interaction State -----//
|
||||
boost::shared_ptr<timeline::Track> hoveringTrack;
|
||||
shared_ptr<timeline::Track> hoveringTrack;
|
||||
|
||||
Gdk::Point mousePoint;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@
|
|||
|
||||
using namespace Gtk;
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace util;
|
||||
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
|
|
|||
|
|
@ -24,12 +24,10 @@
|
|||
#include "gui/widgets/timeline-widget.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
using namespace gui::widgets;
|
||||
|
||||
using lib::time::Mutation;
|
||||
using boost::shared_ptr; /////////////////////////TICKET #796
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
|
|
@ -45,15 +43,18 @@ const int IBeamTool::ScrollSlideEventInterval = 40;
|
|||
|
||||
IBeamTool::IBeamTool(TimelineBody &timeline_body) :
|
||||
Tool(timeline_body),
|
||||
selectionControl(),
|
||||
dragType(None),
|
||||
pinnedDragTime(),
|
||||
scrollSlideRate(0)
|
||||
{
|
||||
|
||||
// Connect the timlinebody selection to the selectionControl
|
||||
get_state()->set_selection_control (selectionControl);
|
||||
}
|
||||
|
||||
IBeamTool::~IBeamTool()
|
||||
{
|
||||
selectionControl.disconnect();
|
||||
end_scroll_slide();
|
||||
}
|
||||
|
||||
|
|
@ -120,9 +121,7 @@ IBeamTool::on_button_press_event(GdkEventButton* event)
|
|||
// User began the drag in clear space, begin a Select drag
|
||||
dragType = Selection;
|
||||
pinnedDragTime = time;
|
||||
state->setSelection (Mutation::changeTime(time));
|
||||
state->setSelection (Mutation::changeDuration(Duration::NIL));
|
||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all
|
||||
selectionControl (TimeSpan(time, Duration::NIL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,8 +180,8 @@ IBeamTool::on_motion_notify_event(GdkEventMotion *event)
|
|||
bool
|
||||
IBeamTool::on_scroll_slide_timer()
|
||||
{
|
||||
const Gdk::Rectangle body_rect(get_body_rectangle());
|
||||
view_window().shift_view(body_rect.get_width(), scrollSlideRate);
|
||||
const Gdk::Rectangle body_rect (get_body_rectangle());
|
||||
view_window().shift_view (body_rect.get_width(), scrollSlideRate);
|
||||
|
||||
// Return true to keep the timer going
|
||||
return true;
|
||||
|
|
@ -193,16 +192,17 @@ IBeamTool::set_leading_x(const int x)
|
|||
{
|
||||
shared_ptr<TimelineState> state = get_state();
|
||||
|
||||
const bool set_playback_period = dragType == Selection;
|
||||
// The line below needs handled differently now;
|
||||
//
|
||||
//const bool set_playback_period = dragType == Selection;
|
||||
|
||||
TimeVar newStartPoint (state->get_view_window().x_to_time(x));
|
||||
Offset selectionLength (pinnedDragTime, newStartPoint);
|
||||
|
||||
|
||||
if (newStartPoint > pinnedDragTime)
|
||||
newStartPoint=pinnedDragTime; // use the smaller one as selection start
|
||||
|
||||
state->setSelection (Mutation::changeTime(newStartPoint) , set_playback_period);
|
||||
state->setSelection (Mutation::changeDuration(selectionLength), set_playback_period);
|
||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
|
||||
|
||||
selectionControl (TimeSpan (newStartPoint, Duration(selectionLength)));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -30,14 +30,17 @@
|
|||
|
||||
#include "gui/widgets/timeline/timeline-tool.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/time/timequant.hpp"
|
||||
#include "lib/time/control.hpp"
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::TimeSpan;
|
||||
using lib::time::Control;
|
||||
|
||||
/**
|
||||
* A helper class to implement the timeline i-beam tool
|
||||
|
|
@ -135,12 +138,12 @@ private:
|
|||
enum DragType
|
||||
{
|
||||
/**
|
||||
* No drag is occuring
|
||||
* No drag is occurring
|
||||
*/
|
||||
None,
|
||||
|
||||
/**
|
||||
* A selection drag is occuring.
|
||||
* A selection drag is occurring.
|
||||
* @remarks The position of one end of the selection was set at
|
||||
* mouse-down of the drag, and the other end is set by
|
||||
* drag-release.
|
||||
|
|
@ -159,6 +162,8 @@ private:
|
|||
};
|
||||
|
||||
/* ==== Internals ===== */
|
||||
Control<TimeSpan> selectionControl;
|
||||
|
||||
/**
|
||||
* Specifies the type of drag currently taking place.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
using namespace Gtk;
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace std::tr1;
|
||||
using namespace lumiera;
|
||||
using namespace util;
|
||||
using namespace gui::util;
|
||||
|
|
@ -80,17 +80,17 @@ TimelineLayoutHelper::add_branch(
|
|||
}
|
||||
}
|
||||
|
||||
optional<Gdk::Rectangle>
|
||||
boost::optional<Gdk::Rectangle>
|
||||
TimelineLayoutHelper::get_track_header_rect(
|
||||
boost::weak_ptr<timeline::Track> track)
|
||||
std::tr1::weak_ptr<timeline::Track> track)
|
||||
{
|
||||
if(contains(headerBoxes, track))
|
||||
{
|
||||
Gdk::Rectangle rect(headerBoxes[track]);
|
||||
rect.set_y(rect.get_y() - timelineWidget.get_y_scroll_offset());
|
||||
return optional<Gdk::Rectangle>(rect);
|
||||
return boost::optional<Gdk::Rectangle>(rect);
|
||||
}
|
||||
return optional<Gdk::Rectangle>();
|
||||
return boost::optional<Gdk::Rectangle>();
|
||||
}
|
||||
|
||||
shared_ptr<timeline::Track>
|
||||
|
|
@ -112,8 +112,8 @@ TimelineLayoutHelper::header_from_point(Gdk::Point point)
|
|||
return shared_ptr<timeline::Track>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<timeline::Track>
|
||||
TimelineLayoutHelper::track_from_y(int y)
|
||||
shared_ptr<timeline::Track>
|
||||
TimelineLayoutHelper::track_from_y (int y)
|
||||
{
|
||||
// Apply the scroll offset
|
||||
y += timelineWidget.get_y_scroll_offset();
|
||||
|
|
@ -299,8 +299,7 @@ TimelineLayoutHelper::is_animating() const
|
|||
}
|
||||
|
||||
TimelineLayoutHelper::TrackTree::pre_order_iterator
|
||||
TimelineLayoutHelper::iterator_from_track(
|
||||
boost::shared_ptr<model::Track> modelTrack)
|
||||
TimelineLayoutHelper::iterator_from_track(shared_ptr<model::Track> modelTrack)
|
||||
{
|
||||
REQUIRE(modelTrack);
|
||||
|
||||
|
|
@ -666,7 +665,7 @@ TimelineLayoutHelper::apply_drop_to_modelTree(
|
|||
timelineWidget.thaw_update_tracks();
|
||||
}
|
||||
|
||||
boost::shared_ptr<model::Sequence>
|
||||
shared_ptr<model::Sequence>
|
||||
TimelineLayoutHelper::get_sequence() const
|
||||
{
|
||||
REQUIRE(timelineWidget.state);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
/**
|
||||
* Definition of the layout track tree type.
|
||||
*/
|
||||
typedef lumiera::tree< boost::shared_ptr<model::Track> > TrackTree;
|
||||
typedef lumiera::tree< std::tr1::shared_ptr<model::Track> > TrackTree;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -101,7 +101,7 @@ public:
|
|||
* @see update_layout()
|
||||
*/
|
||||
boost::optional<Gdk::Rectangle> get_track_header_rect(
|
||||
boost::weak_ptr<timeline::Track> track);
|
||||
std::tr1::weak_ptr<timeline::Track> track);
|
||||
|
||||
/**
|
||||
* Searches for a header which has the specified point inside of it.
|
||||
|
|
@ -114,8 +114,7 @@ public:
|
|||
* tracks.
|
||||
* @see update_layout()
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track> header_from_point(
|
||||
Gdk::Point point);
|
||||
shared_ptr<timeline::Track> header_from_point (Gdk::Point point);
|
||||
|
||||
/**
|
||||
* Searches for a tack which has the specified y-offset inside of it.
|
||||
|
|
@ -128,15 +127,15 @@ public:
|
|||
* tracks.
|
||||
* @see update_layout()
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track> track_from_y(int y);
|
||||
shared_ptr<timeline::Track> track_from_y (int y);
|
||||
|
||||
/**
|
||||
* Begins to drag the track under mouse_point, if there is one.
|
||||
* @param mouse_point The mouse point to begin dragging from, measured
|
||||
* in pixels from the top left of the header container widget.
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track>
|
||||
begin_dragging_track(const Gdk::Point &mouse_point);
|
||||
shared_ptr<timeline::Track>
|
||||
begin_dragging_track (Gdk::Point const& mouse_point);
|
||||
|
||||
/**
|
||||
* Drops the dragging track.
|
||||
|
|
@ -185,8 +184,8 @@ public:
|
|||
* @return Returns the model iterator of layoutTree.end() if no
|
||||
* iterator was found.
|
||||
*/
|
||||
TrackTree::pre_order_iterator iterator_from_track(
|
||||
boost::shared_ptr<model::Track> modelTrack);
|
||||
TrackTree::pre_order_iterator
|
||||
iterator_from_track (shared_ptr<model::Track> modelTrack);
|
||||
|
||||
/**
|
||||
* A function that recursively calculates the visible height of a
|
||||
|
|
@ -262,7 +261,7 @@ protected:
|
|||
* @see clone_tree_from_sequence()
|
||||
*/
|
||||
void add_branch(TrackTree::iterator_base parent_iterator,
|
||||
boost::shared_ptr<model::Track> parent);
|
||||
shared_ptr<model::Track> parent);
|
||||
|
||||
/**
|
||||
* Recursively calculates the boxes for a given branch in the timeline
|
||||
|
|
@ -298,8 +297,8 @@ protected:
|
|||
* @remarks If the return value is going to be NULL, an ENSURE will
|
||||
* fail.
|
||||
*/
|
||||
boost::shared_ptr<timeline::Track> lookup_timeline_track(
|
||||
boost::shared_ptr<model::Track> modelTrack);
|
||||
shared_ptr<timeline::Track>
|
||||
lookup_timeline_track(shared_ptr<model::Track> modelTrack);
|
||||
|
||||
/**
|
||||
* A helper function which kicks off the animation timer.
|
||||
|
|
@ -343,7 +342,8 @@ protected:
|
|||
* Helper to get the sequence object from the state.
|
||||
* @return Returns a shared pointer to the sequence.
|
||||
*/
|
||||
boost::shared_ptr<model::Sequence> get_sequence() const;
|
||||
shared_ptr<model::Sequence>
|
||||
get_sequence() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
|
@ -363,7 +363,7 @@ protected:
|
|||
* the update_layout method.
|
||||
* @see update_layout()
|
||||
*/
|
||||
std::map<boost::weak_ptr<timeline::Track>, Gdk::Rectangle>
|
||||
std::map<std::tr1::weak_ptr<timeline::Track>, Gdk::Rectangle>
|
||||
headerBoxes;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ using namespace gui;
|
|||
using namespace gui::widgets;
|
||||
using namespace gui::widgets::timeline;
|
||||
|
||||
using boost::shared_ptr; ////////////////////TICKET #796
|
||||
using std::tr1::shared_ptr;
|
||||
using gui::util::CairoUtil;
|
||||
using lib::time::Time;
|
||||
using lib::time::TimeVar;
|
||||
|
|
@ -274,6 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr,
|
|||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
|
||||
/////////////////////////////TICKET #861 : what part of these calculation could be centralised within TimelineViewWindow?
|
||||
const TimelineViewWindow &window = viewWindow();
|
||||
const gavl_time_t left_offset = _raw(window.get_time_offset());
|
||||
const int64_t time_scale = window.get_time_scale();
|
||||
|
|
@ -504,6 +505,7 @@ TimelineRuler::calculate_major_spacing() const
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
/////////////////////////////TICKET #861 : couldn't that be a library function in TimelineViewWindow?
|
||||
const gavl_time_t major_spacings[] = {
|
||||
GAVL_TIME_SCALE / 1000,
|
||||
GAVL_TIME_SCALE / 400,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ private:
|
|||
/**
|
||||
* The event handler for when the TimelineWidget's state is switched.
|
||||
*/
|
||||
void on_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
void on_state_changed (shared_ptr<TimelineState> newState);
|
||||
|
||||
private:
|
||||
/* ===== Internal Methods ===== */
|
||||
|
|
@ -242,7 +242,7 @@ private:
|
|||
/**
|
||||
* the currently active timeline state object
|
||||
*/
|
||||
boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr
|
||||
shared_ptr<TimelineState> timelineState;
|
||||
|
||||
/**
|
||||
* The caches image of the ruler, over which the chevrons etc. will
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "gui/widgets/timeline/timeline-state.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/control.hpp"
|
||||
|
||||
using namespace Gtk;
|
||||
using namespace sigc;
|
||||
|
|
@ -36,31 +37,37 @@ using lib::time::FSecs;
|
|||
using lib::time::Offset;
|
||||
using lib::time::Duration;
|
||||
using lib::time::Mutation;
|
||||
using boost::shared_ptr; /////////////////////////TICKET #796
|
||||
using lib::time::Control;
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
|
||||
|
||||
TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence)
|
||||
TimelineState::TimelineState (shared_ptr<model::Sequence> source_sequence)
|
||||
: sequence(source_sequence)
|
||||
, viewWindow(Offset(Time::ZERO), 1)
|
||||
, selection_(Time::ZERO, Duration::NIL)
|
||||
, selectionListener()
|
||||
, playbackPeriod_(Time::ZERO, Duration::NIL)
|
||||
, playbackPoint_(Time::ZERO)
|
||||
, isPlayback_(false)
|
||||
{
|
||||
REQUIRE(sequence);
|
||||
|
||||
|
||||
// Initialize the selection listener
|
||||
selectionListener (TimeSpan (Time::ZERO, Duration::NIL));
|
||||
selectionListener.connect(
|
||||
mem_fun(*this, &TimelineState::on_selection_changed));
|
||||
|
||||
////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state
|
||||
const int64_t DEFAULT_TIMELINE_SCALE =21000000;
|
||||
const int64_t DEFAULT_TIMELINE_SCALE =6400;
|
||||
|
||||
viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE);
|
||||
|
||||
setSelection (Mutation::changeTime (Time(FSecs(2))));
|
||||
setSelection (Mutation::changeDuration(Duration(FSecs(2))));
|
||||
setSelection (Mutation::changeDuration (Duration(FSecs(2))));
|
||||
//////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all
|
||||
}
|
||||
|
||||
boost::shared_ptr<model::Sequence>
|
||||
shared_ptr<model::Sequence>
|
||||
TimelineState::get_sequence() const
|
||||
{
|
||||
return sequence;
|
||||
|
|
@ -97,6 +104,14 @@ TimelineState::setPlaybackPoint (Time newPosition)
|
|||
playbackChangedSignal.emit();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineState::set_selection_control (SelectionControl &control)
|
||||
{
|
||||
control.disconnect();
|
||||
selection_.accept (control);
|
||||
control.connectChangeNotification (selectionListener);
|
||||
}
|
||||
|
||||
sigc::signal<void>
|
||||
TimelineState::selection_changed_signal() const
|
||||
{
|
||||
|
|
@ -109,6 +124,12 @@ TimelineState::playback_changed_signal() const
|
|||
return playbackChangedSignal;
|
||||
}
|
||||
|
||||
void
|
||||
TimelineState::on_selection_changed (TimeSpan selection)
|
||||
{
|
||||
selectionChangedSignal.emit();
|
||||
}
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace widgets
|
||||
} // namespace gui
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@
|
|||
|
||||
#include "gui/widgets/timeline/timeline-view-window.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/control.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp> /////////////////////////TICKET #796
|
||||
|
||||
namespace gui {
|
||||
|
||||
|
|
@ -39,9 +39,44 @@ class Sequence;
|
|||
|
||||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
|
||||
using lib::time::Control;
|
||||
using lib::time::Mutation;
|
||||
|
||||
typedef Control<TimeSpan> SelectionControl;
|
||||
|
||||
|
||||
/**
|
||||
* SelectionListener is a template class which emits a signal when
|
||||
* the value is changed by it's associated time::Control object.
|
||||
* SelectionListener wraps a sigc::signal that emits every time
|
||||
* the selection is changed by the time::Control object.
|
||||
* SelectionListener does NOT emit the signal if a change to the
|
||||
* selection is made outside of the Control/Listener partnership.
|
||||
*/
|
||||
template<class TI>
|
||||
class SelectionListener
|
||||
: boost::noncopyable
|
||||
{
|
||||
sigc::signal<void, TI> valueChangedSignal;
|
||||
|
||||
public:
|
||||
SelectionListener() { }
|
||||
|
||||
void
|
||||
operator() (TI const& changeValue) const
|
||||
{
|
||||
valueChangedSignal.emit (changeValue);
|
||||
}
|
||||
|
||||
|
||||
void connect (const sigc::slot<void, TI> &connection)
|
||||
{
|
||||
valueChangedSignal.connect (connection);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* TimelineState is a container for the state data for TimelineWidget.
|
||||
* @remarks TimelineState s can be swapped out so that TimelineWidget
|
||||
|
|
@ -52,11 +87,10 @@ class TimelineState
|
|||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param source_sequence The sequence on which the TimelineWidget
|
||||
* will operate when this TimelineState is attached.
|
||||
*/
|
||||
TimelineState(boost::shared_ptr<model::Sequence> source_sequence);
|
||||
TimelineState (shared_ptr<model::Sequence> source_sequence);
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -64,7 +98,7 @@ public:
|
|||
* Gets the sequence that is attached to this timeline state object.
|
||||
* @return Returns a shared_ptr to the sequence object.
|
||||
*/
|
||||
boost::shared_ptr<model::Sequence> get_sequence() const;
|
||||
shared_ptr<model::Sequence> get_sequence() const;
|
||||
|
||||
/**
|
||||
* Gets a reference to the timeline view window object.
|
||||
|
|
@ -72,7 +106,9 @@ public:
|
|||
*/
|
||||
timeline::TimelineViewWindow& get_view_window();
|
||||
|
||||
|
||||
SelectionListener<TimeSpan>&
|
||||
get_selection_listener() { return selectionListener; }
|
||||
|
||||
Time getSelectionStart() const { return selection_.start();}
|
||||
Time getSelectionEnd() const { return selection_.end(); }
|
||||
Time getPlaybackPeriodStart() const { return selection_.start();}
|
||||
|
|
@ -80,10 +116,12 @@ public:
|
|||
|
||||
Time getPlaybackPoint() const { return playbackPoint_; }
|
||||
|
||||
|
||||
/** is there currently any ongoing playback process?
|
||||
* Otherwise the #getPlaybackPoint is meaningless */
|
||||
bool isPlaying() const { return isPlayback_; }
|
||||
|
||||
void set_selection_control (SelectionControl &control);
|
||||
|
||||
/**
|
||||
* Sets the period of the selection.
|
||||
|
|
@ -114,6 +152,15 @@ public:
|
|||
* changed.
|
||||
*/
|
||||
sigc::signal<void> playback_changed_signal() const;
|
||||
|
||||
private:
|
||||
|
||||
/* ========= Event Handlers ========== */
|
||||
|
||||
/**
|
||||
* Event handler for when the selection is changed
|
||||
*/
|
||||
void on_selection_changed (TimeSpan selection);
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -122,19 +169,23 @@ private:
|
|||
* @remarks This pointer is set by the constructor and is constant, so
|
||||
* will not change in value during the lifetime of the class.
|
||||
*/
|
||||
boost::shared_ptr<model::Sequence> sequence;
|
||||
shared_ptr<model::Sequence> sequence;
|
||||
|
||||
// View State
|
||||
/**
|
||||
* The ViewWindow for the TimelineWidget display with.
|
||||
*/
|
||||
|
||||
/* == View State == */
|
||||
|
||||
/** ViewWindow for the TimelineWidget display */
|
||||
timeline::TimelineViewWindow viewWindow;
|
||||
|
||||
// Selection State
|
||||
|
||||
/* == Selection State == */
|
||||
|
||||
/** currently selected time period. */
|
||||
TimeSpan selection_;
|
||||
|
||||
/** listens for a selection change */
|
||||
SelectionListener<TimeSpan> selectionListener;
|
||||
|
||||
/** current playback period. */
|
||||
TimeSpan playbackPeriod_;
|
||||
|
||||
|
|
@ -146,7 +197,8 @@ private:
|
|||
|
||||
bool isPlayback_;
|
||||
|
||||
// Signals
|
||||
|
||||
/* == Signals == */
|
||||
|
||||
/**
|
||||
* A signal to notify when the selected period has changed.
|
||||
|
|
@ -160,8 +212,7 @@ private:
|
|||
sigc::signal<void> playbackChangedSignal;
|
||||
};
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace widgets
|
||||
} // namespace gui
|
||||
|
||||
}}} // namespace gui::widgets::timeline
|
||||
|
||||
#endif // TIMELINE_STATE_HPP
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include "gui/widgets/timeline-widget.hpp"
|
||||
|
||||
using namespace Gdk;
|
||||
using namespace boost;
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ protected:
|
|||
/**
|
||||
* A helper function to get the state
|
||||
*/
|
||||
boost::shared_ptr<TimelineState> get_state() const;
|
||||
shared_ptr<TimelineState> get_state() const;
|
||||
|
||||
/**
|
||||
* A helper function to get the view window
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
using namespace Gtk;
|
||||
using namespace sigc;
|
||||
|
||||
using boost::shared_ptr; //////////////////////////////TICKET #796 : should use std::tr1
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
namespace gui {
|
||||
namespace widgets {
|
||||
|
|
@ -125,7 +125,7 @@ shared_ptr<timeline::Clip>
|
|||
Track::getClipAt(Time) const
|
||||
{
|
||||
// Default implementation returns empty pointer
|
||||
return boost::shared_ptr<timeline::Clip>();
|
||||
return shared_ptr<timeline::Clip>();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -272,7 +272,7 @@ void
|
|||
Track::on_remove_track()
|
||||
{
|
||||
REQUIRE(modelTrack);
|
||||
boost::shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
shared_ptr<TimelineState> state = timelineWidget.get_state();
|
||||
REQUIRE(state);
|
||||
|
||||
state->get_sequence()->remove_descendant_track(modelTrack);
|
||||
|
|
|
|||
|
|
@ -72,20 +72,14 @@ public:
|
|||
Collapse
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Track(TimelineWidget &timeline_widget,
|
||||
boost::shared_ptr<model::Track> track);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Track();
|
||||
shared_ptr<model::Track> track);
|
||||
|
||||
virtual ~Track();
|
||||
|
||||
Gtk::Widget& get_header_widget();
|
||||
|
||||
boost::shared_ptr<model::Track> //////////////////////////////TICKET #796 : should use std::tr1
|
||||
shared_ptr<model::Track>
|
||||
getModelTrack() const;
|
||||
|
||||
/**
|
||||
|
|
@ -155,7 +149,7 @@ public:
|
|||
* The default implementation simply returns an empty pointer.
|
||||
* @param the given time
|
||||
*/
|
||||
virtual boost::shared_ptr<timeline::Clip> //////////////////////////////TICKET #796 : should use std::tr1
|
||||
virtual shared_ptr<timeline::Clip>
|
||||
getClipAt (Time position) const;
|
||||
|
||||
private:
|
||||
|
|
@ -209,7 +203,7 @@ private:
|
|||
protected:
|
||||
|
||||
TimelineWidget &timelineWidget;
|
||||
boost::shared_ptr<model::Track> modelTrack;
|
||||
shared_ptr<model::Track> modelTrack;
|
||||
|
||||
private:
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,12 +31,19 @@ namespace gui {
|
|||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
/* == public constants == */
|
||||
|
||||
const int64_t TimelineViewWindow::MaxScale = 30000000; // 30 Million
|
||||
const double TimelineViewWindow::ZoomIncrement = 1.25; // Not currently used
|
||||
const double TimelineViewWindow::ZoomSmoothing = 9.0;
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -68,7 +75,7 @@ TimelineViewWindow::set_time_scale(int64_t scale)
|
|||
void
|
||||
TimelineViewWindow::set_time_scale(double ratio)
|
||||
{
|
||||
int64_t max = TimelineWidget::MaxScale;
|
||||
int64_t max = MaxScale;
|
||||
int64_t min = 1;
|
||||
|
||||
if(ratio <= 0.0)
|
||||
|
|
@ -85,24 +92,32 @@ TimelineViewWindow::set_time_scale(double ratio)
|
|||
set_time_scale((int64_t)(ratio * max));
|
||||
}
|
||||
|
||||
double
|
||||
TimelineViewWindow::get_smoothed_time_scale() const
|
||||
{
|
||||
double linear_scale ( 1.0 / MaxScale * timeScale);
|
||||
|
||||
// reverse the effect of zoom scale smoothing
|
||||
return pow (linear_scale, (1.0 / ZoomSmoothing));
|
||||
}
|
||||
|
||||
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);
|
||||
// Apply the smoothing factor
|
||||
int64_t new_time_scale(pow (time_scale_ratio, ZoomSmoothing)
|
||||
* double(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;
|
||||
if(new_time_scale > MaxScale)
|
||||
new_time_scale = 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));
|
||||
set_time_offset(newStartPoint);
|
||||
TimeVar newStartPoint = timeOffset + TimeValue(point * (timeScale - new_time_scale));
|
||||
set_time_offset (newStartPoint);
|
||||
|
||||
// Apply the new scale
|
||||
set_time_scale(new_time_scale);
|
||||
|
|
@ -111,13 +126,13 @@ TimelineViewWindow::zoom_view(int point, double time_scale_ratio)
|
|||
void
|
||||
TimelineViewWindow::shift_view(int view_width, int shift_size)
|
||||
{
|
||||
set_time_offset(timeOffset + TimeValue(timeScale * shift_size * view_width / 256));
|
||||
set_time_offset (timeOffset + TimeValue(timeScale * shift_size * view_width / 256));
|
||||
}
|
||||
|
||||
int
|
||||
TimelineViewWindow::time_to_x(TimeValue const& time) const
|
||||
{
|
||||
return int(_raw(time - timeOffset) / timeScale); //////TODO protect against values out-of range
|
||||
return int (_raw(time - timeOffset) / timeScale); //////TODO protect against values out-of range
|
||||
}
|
||||
|
||||
Time
|
||||
|
|
@ -133,7 +148,5 @@ TimelineViewWindow::changed_signal() const
|
|||
return changedSignal;
|
||||
}
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace widgets
|
||||
} // namespace gui
|
||||
}}} // namespace gui::widgets::timeline
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,16 @@ using lib::time::Offset;
|
|||
class TimelineViewWindow
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* The maximum scale for timeline display.
|
||||
* @remarks At MaxScale, every pixel on the timeline is equivalent
|
||||
* to 30000000 lumiera::Time increments.
|
||||
*/
|
||||
static const int64_t MaxScale;
|
||||
static const double ZoomSmoothing;
|
||||
static const double ZoomIncrement;
|
||||
|
||||
|
||||
/**
|
||||
* @param offset The initial view offset.
|
||||
|
|
@ -95,6 +105,10 @@ public:
|
|||
*/
|
||||
void set_time_scale(int64_t time_scale);
|
||||
void set_time_scale(double ratio);
|
||||
|
||||
/** get the current time scale with zoom smoothing applied */
|
||||
double get_smoothed_time_scale() const;
|
||||
|
||||
/**
|
||||
* Zooms the view in or out as by a number of steps while keeping a
|
||||
* given point on the timeline still.
|
||||
|
|
@ -138,6 +152,13 @@ public:
|
|||
|
||||
private:
|
||||
TimeVar timeOffset;
|
||||
|
||||
/**
|
||||
* The scale of the Timline Body.
|
||||
* @remarks This value represents the time span that is visible in
|
||||
* the TimelineBodyWidget. Smaller numbers here will "zoom in"
|
||||
* while larger numbers will "zoom out"
|
||||
*/
|
||||
int64_t timeScale;
|
||||
|
||||
sigc::signal<void> changedSignal;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
* *****************************************************/
|
||||
|
||||
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||
#include "gui/widgets/timeline-widget.hpp"
|
||||
#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
|
||||
|
||||
using namespace Gtk;
|
||||
|
||||
|
|
@ -65,56 +65,47 @@ TimelineZoomScale::TimelineZoomScale()
|
|||
, 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);
|
||||
slider.set_adjustment (adjustment);
|
||||
slider.set_size_request (123,10);
|
||||
slider.set_digits (6);
|
||||
|
||||
/* Inverted because smaller values "zoom in" */
|
||||
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));
|
||||
|
||||
connect (sigc::mem_fun(this, &TimelineZoomScale::on_zoom_in_clicked));
|
||||
zoomOut.signal_clicked().
|
||||
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked));
|
||||
|
||||
connect (sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked));
|
||||
adjustment.signal_value_changed().
|
||||
connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom));
|
||||
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);
|
||||
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,
|
||||
TimelineZoomScale::wireTimelineState (shared_ptr<TimelineState> currentState,
|
||||
TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
|
||||
{
|
||||
on_timeline_state_changed (currentState);
|
||||
stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
|
||||
stateChangeSignal.connect (
|
||||
sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
|
||||
}
|
||||
|
||||
void
|
||||
TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr<TimelineState> newState)
|
||||
TimelineZoomScale::on_timeline_state_changed (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);
|
||||
adjustment.set_value (getViewWindow().get_smoothed_time_scale());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -134,7 +125,7 @@ TimelineZoomScale::on_zoom_out_clicked()
|
|||
void
|
||||
TimelineZoomScale::on_zoom()
|
||||
{
|
||||
zoomSignal.emit(adjustment.get_value()) ;
|
||||
zoomSignal.emit (adjustment.get_value()) ;
|
||||
}
|
||||
|
||||
sigc::signal<void, double>
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@
|
|||
#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;
|
||||
|
|
@ -55,7 +51,9 @@ public:
|
|||
*/
|
||||
sigc::signal<void, double> signal_zoom();
|
||||
|
||||
void wireTimelineState (boost::shared_ptr<TimelineState> currentState,
|
||||
void set_value(double val) { adjustment.set_value(val); }
|
||||
|
||||
void wireTimelineState (shared_ptr<TimelineState> currentState,
|
||||
TimelineWidget::TimelineStateChangeSignal);
|
||||
|
||||
private:
|
||||
|
|
@ -65,7 +63,7 @@ private:
|
|||
* 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
|
||||
void on_timeline_state_changed (shared_ptr<TimelineState> newState);
|
||||
|
||||
/**
|
||||
* Event handler for when the zoomIn Button
|
||||
|
|
@ -100,7 +98,7 @@ private:
|
|||
|
||||
const double button_step_size;
|
||||
|
||||
boost::shared_ptr<TimelineState> timelineState;
|
||||
shared_ptr<TimelineState> timelineState;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
|
|
|
|||
|
|
@ -33,13 +33,12 @@ using util::cStr;
|
|||
|
||||
using namespace Gtk;
|
||||
using namespace Glib;
|
||||
using namespace boost;
|
||||
using namespace std;
|
||||
using namespace std::tr1;
|
||||
using namespace gui::workspace;
|
||||
|
||||
namespace fsys = boost::filesystem;
|
||||
|
||||
|
||||
namespace gui {
|
||||
|
||||
IconSize WindowManager::GiantIconSize = ICON_SIZE_INVALID;
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@
|
|||
#include "gui/gtk-base.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <cairomm/cairomm.h>
|
||||
#include <string>
|
||||
|
||||
#include <tr1/memory>
|
||||
|
||||
namespace gui {
|
||||
|
||||
using std::string;
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
|
||||
namespace model { class Project; }
|
||||
|
|
@ -195,7 +195,7 @@ private:
|
|||
|
||||
private:
|
||||
|
||||
std::list< boost::shared_ptr<workspace::WorkspaceWindow> > windowList;
|
||||
std::list<shared_ptr<workspace::WorkspaceWindow> > windowList;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( fileheader_dbg, backend_dbg);
|
|||
/** base of debug logging for the proc layer */
|
||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_dbg, debugging);
|
||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( command_dbg, proc_dbg);
|
||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( session_dbg, proc_dbg);
|
||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( player_dbg, proc_dbg);
|
||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( engine_dbg, proc_dbg);
|
||||
/** base of debug logging for the gui */
|
||||
NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging);
|
||||
/** base if debug logging for the support library */
|
||||
|
|
|
|||
|
|
@ -215,6 +215,27 @@ namespace lumiera {
|
|||
|
||||
} // namespace lumiera
|
||||
|
||||
/******************************************************
|
||||
* convenience shortcut for a sequence of catch blocks
|
||||
* just logging and consuming an error. Typically
|
||||
* this sequence will be used within destructors,
|
||||
* which, by convention, must not throw
|
||||
*/
|
||||
#define ERROR_LOG_AND_IGNORE(_FLAG_,_OP_DESCR_) \
|
||||
catch (std::exception& problem) \
|
||||
{ \
|
||||
const char* errID = lumiera_error(); \
|
||||
WARN (_FLAG_, "%s failed: %s", _OP_DESCR_, problem.what()); \
|
||||
TRACE (debugging, "Error flag was: %s", errID);\
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
const char* errID = lumiera_error(); \
|
||||
ERROR (_FLAG_, "%s failed with unknown exception; " \
|
||||
"error flag is: %s" \
|
||||
, _OP_DESCR_, errID); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************************************
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@
|
|||
|
||||
/** @file iter-adapter.hpp
|
||||
** Helper template(s) for creating <b>lumiera forward iterators</b>.
|
||||
** These are the foundation to build up iterator like types from scratch.
|
||||
** Usually, these templates will be created and provided by a custom
|
||||
** container type and accessed by the client through a typedef name
|
||||
** "iterator" (similar to the usage within the STL). For more advanced
|
||||
** usage, the providing container might want to subclass these iterators,
|
||||
** e.g. to provide an additional, specialised API.
|
||||
**
|
||||
** Depending on the concrete situation, there are several flavours
|
||||
** Depending on the concrete situation, several flavours are provided:
|
||||
** - the IterAdapter retains an active callback connection to the
|
||||
** controlling container, thus allowing arbitrary complex behaviour.
|
||||
** - the RangeIter allows just to expose a range of elements defined
|
||||
|
|
@ -36,21 +37,32 @@
|
|||
** - often, objects are managed internally by pointers, while allowing
|
||||
** the clients to use direct references; to support this usage scenario,
|
||||
** PtrDerefIter wraps an existing iterator, while dereferencing any value
|
||||
** automatically on access.
|
||||
** automatically on access.
|
||||
**
|
||||
** There are many further ways of yielding a Lumiera forward iterator.
|
||||
** For example, lib::IterSource builds a "iterable" source of data elements,
|
||||
** while hiding the actual container or generator implementation behind a
|
||||
** vtable call. Besides, there are adapters for the most common usages
|
||||
** with STL containers, and such iterators can also be combined and
|
||||
** extended with the help of itertools.hpp
|
||||
**
|
||||
** Basically every class in compliance with our specific iterator concept
|
||||
** can be used as a building block in this framework.
|
||||
**
|
||||
**
|
||||
** \par Lumiera forward iterator concept
|
||||
**
|
||||
** Similar to the STL, instead of using a common "Iterator" base class,
|
||||
** instead we define a common set of functions and behaviour which can
|
||||
** we rather define a common set of functions and behaviour which can
|
||||
** be expected from any such iterator. These rules are similar to STL's
|
||||
** "forward iterator", with the addition of an bool check to detect
|
||||
** iteration end. The latter s inspired by the \c hasNext() function
|
||||
** iteration end. The latter is inspired by the \c hasNext() function
|
||||
** found in many current languages supporting iterators. In a similar
|
||||
** vein (inspired from functional programming), we deliberately don't
|
||||
** support the various extended iterator concepts from STL and boost
|
||||
** (random access iterators, output iterators and the like). According
|
||||
** to this concept, <i>an iterator is a promise for pulling values,</i>
|
||||
** (random access iterators, output iterators, arithmetics, difference
|
||||
** between iterators and the like). According to this concept,
|
||||
** <i>an iterator is a promise for pulling values,</i>
|
||||
** and nothing beyond that.
|
||||
**
|
||||
** - Any Lumiera forward iterator can be in a "exhausted" (invalid) state,
|
||||
|
|
@ -58,7 +70,7 @@
|
|||
** created by the default ctor is always fixed to that state. This
|
||||
** state is final and can't be reset, meaning that any iterator is
|
||||
** a disposable one-way-off object.
|
||||
** - iterators are copyable and comparable
|
||||
** - iterators are copyable and equality comparable
|
||||
** - when an iterator is \em not in the exhausted state, it may be
|
||||
** \em dereferenced to yield the "current" value.
|
||||
** - moreover, iterators may be incremented until exhaustion.
|
||||
|
|
@ -408,7 +420,7 @@ namespace lib {
|
|||
typedef typename RemovePtr<TY>::Type ValueType;
|
||||
|
||||
template<class T2>
|
||||
struct SimilarIter
|
||||
struct SimilarIter ///< rebind to a similarly structured Iterator with value type T2
|
||||
{
|
||||
typedef Iter<T2,CON> Type;
|
||||
};
|
||||
|
|
@ -445,7 +457,12 @@ namespace lib {
|
|||
public:
|
||||
typedef typename IT::value_type pointer;
|
||||
typedef typename RemovePtr<pointer>::Type value_type;
|
||||
typedef value_type& reference;
|
||||
typedef value_type& reference;
|
||||
|
||||
// for use with STL algorithms
|
||||
typedef void difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
|
||||
// the purpose of the following typedefs is to ease building a correct "const iterator"
|
||||
|
||||
|
|
@ -462,6 +479,7 @@ namespace lib {
|
|||
/** PtrDerefIter is always created
|
||||
* by wrapping an existing iterator.
|
||||
*/
|
||||
explicit
|
||||
PtrDerefIter (IT srcIter)
|
||||
: i_(srcIter)
|
||||
{ }
|
||||
|
|
@ -485,9 +503,31 @@ namespace lib {
|
|||
operator= (PtrDerefIter<WrappedIterType> const& ref)
|
||||
{
|
||||
i_ = reinterpret_cast<IT const&> (ref.getBase());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/** explicit builder to allow creating a const variant from the basic srcIter type.
|
||||
* Again, the reason necessitating this "backdoor" is that we want to swallow one level
|
||||
* of indirection. Generally speaking \code const T ** \endcode is not the same as
|
||||
* \code T * const * \endcode, but in our specific case the API ensures that a
|
||||
* PtrDerefIter<WrappedConstIterType> only exposes const elements.
|
||||
*/
|
||||
static PtrDerefIter
|
||||
build_by_cast (WrappedIterType const& srcIter)
|
||||
{
|
||||
return PtrDerefIter (reinterpret_cast<IT const&> (srcIter));
|
||||
}
|
||||
|
||||
static PtrDerefIter
|
||||
nil()
|
||||
{
|
||||
return PtrDerefIter (IT());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === lumiera forward iterator concept === */
|
||||
|
|
|
|||
122
src/lib/maybe.hpp
Normal file
122
src/lib/maybe.hpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
MAYBE.hpp - dealing with optional values
|
||||
|
||||
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 maybe.hpp
|
||||
** Support for representation of optional values.
|
||||
** This implements a concept ("option monad") known from functional programming,
|
||||
** allowing to express the fact of some value possibly be unavailable. Using this
|
||||
** approach allows to avoid the dangerous technique of (ab)using NULL pointers to
|
||||
** represent missing values.
|
||||
**
|
||||
** While a NULL pointer carries this special meaning just by convention, marking a
|
||||
** parameter or return value as optional states this fact first class, and enforces
|
||||
** the necessary "is available" check through the type system. Surprisingly, this
|
||||
** leads not only to more secure, but also much more compact code, as we're now
|
||||
** able to substitute a fallback just by a "or else use this" clause.
|
||||
** Basically, there are different ways to access the actual value
|
||||
** - access through implicit conversion raises an exception for missing values
|
||||
** - evaluation as boolean allows to check, if the value is available
|
||||
** - an alternative or fallback value may be attached.
|
||||
**
|
||||
** @todo WIP and rather brainstorming as of 2/10
|
||||
**
|
||||
** @see backend::ThreadJob usage example
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef LIB_MAYBE_H
|
||||
#define LIB_MAYBE_H
|
||||
|
||||
//#include "pre.hpp"
|
||||
#include "lib/error.hpp"
|
||||
//#include "lib/wrapper.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
using util::isnil;
|
||||
using std::string;
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
namespace maybe {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A value, which might be unavailable
|
||||
* @throw error::State on any attempt to access a missing value
|
||||
* without prior checking the availability
|
||||
*/
|
||||
template<typename VAL>
|
||||
class Maybe
|
||||
{
|
||||
VAL value_;
|
||||
|
||||
public:
|
||||
/** mark an invalid/failed result */
|
||||
Maybe ()
|
||||
{ }
|
||||
|
||||
/** standard case: valid result */
|
||||
Maybe (VAL const& value)
|
||||
: value_(value)
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
UNIMPLEMENTED ("check if optional value is available");
|
||||
}
|
||||
|
||||
void
|
||||
maybeThrow(Literal explanation =0) const
|
||||
{
|
||||
if (!isValid())
|
||||
throw error::State (explanation.empty()? "optional value not available" : string(explanation),
|
||||
error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
}
|
||||
|
||||
|
||||
VAL
|
||||
get() const
|
||||
{
|
||||
maybeThrow();
|
||||
return value_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -48,8 +48,10 @@
|
|||
#include "include/logging.h"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
|
|
@ -123,22 +125,43 @@ namespace lib {
|
|||
} }
|
||||
|
||||
|
||||
/** withdraw responsibility for a specific object.
|
||||
* This object will be removed form this collection
|
||||
* and returned as-is; it won't be deleted when the
|
||||
* ScopedPtrVect goes out of scope.
|
||||
* @param obj address of the object in question.
|
||||
* @return pointer to the object, if found.
|
||||
* Otherwise, NULL will be returned and the
|
||||
* collection of managed objects remains unaltered
|
||||
* @note EX_STRONG
|
||||
* @todo TICKET #856 better return a Maybe<T&> instead of a pointer?
|
||||
*/
|
||||
T*
|
||||
detach (void* objAddress)
|
||||
{
|
||||
T* extracted = static_cast<T*> (objAddress);
|
||||
VIter pos = std::find (_Vec::begin(),_Vec::end(), extracted);
|
||||
if (pos != _Vec::end() && bool(*pos))
|
||||
{
|
||||
extracted = *pos;
|
||||
_Vec::erase(pos); // EX_STRONG
|
||||
return extracted;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
VIter e = _Vec::end();
|
||||
for (VIter i = _Vec::begin(); i!=e; ++i)
|
||||
{
|
||||
if (*i)
|
||||
try
|
||||
{
|
||||
delete *i;
|
||||
*i = 0;
|
||||
}
|
||||
catch(std::exception& ex)
|
||||
{
|
||||
WARN (library, "Problem while deallocating ScopedPtrVect: %s", ex.what());
|
||||
} }
|
||||
if (*i)
|
||||
try {
|
||||
delete *i;
|
||||
*i = 0;
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (library, "Clean-up of ScopedPtrVect");
|
||||
_Vec::clear();
|
||||
}
|
||||
|
||||
|
|
@ -155,9 +178,9 @@ namespace lib {
|
|||
typedef ConstIterType const_iterator;
|
||||
|
||||
iterator begin() { return iterator (allPtrs()); }
|
||||
const_iterator begin() const { return const_iterator (allPtrs()); }
|
||||
iterator end() { return iterator ( RIter() ); }
|
||||
const_iterator end() const { return const_iterator (RcIter() ); }
|
||||
const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); }
|
||||
const_iterator end() const { return const_iterator::nil(); }
|
||||
|
||||
|
||||
|
||||
|
|
@ -188,6 +211,12 @@ namespace lib {
|
|||
{
|
||||
return RIter (_Vec::begin(), _Vec::end());
|
||||
}
|
||||
RIter
|
||||
allPtrs () const
|
||||
{
|
||||
_Vec& elements = util::unConst(*this);
|
||||
return RIter (elements.begin(), elements.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/test/testdummy.hpp"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
|
|
@ -55,7 +56,12 @@ namespace test{
|
|||
garbage[--p] = alpha[rand() % MAXAL];
|
||||
return garbage;
|
||||
}
|
||||
|
||||
|
||||
/** storage for testdummy flags */
|
||||
|
||||
long Dummy::_local_checksum = 0;
|
||||
bool Dummy::_throw_in_ctor = false;
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
|
|||
99
src/lib/test/testdummy.hpp
Normal file
99
src/lib/test/testdummy.hpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
TESTDUMMY.hpp - yet another test dummy for tracking ctor/dtor calls
|
||||
|
||||
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 <boost/noncopyable.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
|
||||
class Dummy
|
||||
: boost::noncopyable
|
||||
{
|
||||
int val_;
|
||||
|
||||
/** to verify ctor/dtor calls */
|
||||
static long _local_checksum;
|
||||
static bool _throw_in_ctor;
|
||||
|
||||
public:
|
||||
Dummy ()
|
||||
: val_(1 + (rand() % 100000000))
|
||||
{ init(); }
|
||||
|
||||
Dummy (int v)
|
||||
: val_(v)
|
||||
{ init(); }
|
||||
|
||||
~Dummy()
|
||||
{
|
||||
checksum() -= val_;
|
||||
}
|
||||
|
||||
long add (int i) { return val_+i; }
|
||||
|
||||
int getVal() const { return val_; }
|
||||
|
||||
void
|
||||
setVal (int newVal)
|
||||
{
|
||||
checksum() += newVal - val_;
|
||||
val_ = newVal;
|
||||
}
|
||||
|
||||
friend void
|
||||
swap (Dummy& dum1, Dummy& dum2) ///< checksum neutral
|
||||
{
|
||||
std::swap(dum1.val_, dum2.val_);
|
||||
}
|
||||
|
||||
static long&
|
||||
checksum()
|
||||
{
|
||||
return _local_checksum;
|
||||
}
|
||||
|
||||
static void
|
||||
activateCtorFailure(bool indeed =true)
|
||||
{
|
||||
_throw_in_ctor = indeed;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
void
|
||||
init()
|
||||
{
|
||||
checksum() += val_;
|
||||
if (_throw_in_ctor)
|
||||
throw val_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
||||
|
|
@ -57,6 +57,7 @@
|
|||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
|
@ -64,6 +65,8 @@ namespace lib {
|
|||
namespace time {
|
||||
namespace mutation {
|
||||
|
||||
using std::tr1::function;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -148,7 +151,7 @@ namespace mutation {
|
|||
|
||||
/** disconnect any observers */
|
||||
void
|
||||
disconnnect()
|
||||
disconnect()
|
||||
{
|
||||
listeners_.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ namespace time {
|
|||
|
||||
/** disconnect from observed entity and
|
||||
* cease any change notification */
|
||||
void disconnnect();
|
||||
void disconnect();
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ namespace time {
|
|||
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::disconnnect()
|
||||
Control<TI>::disconnect()
|
||||
{
|
||||
notifyListeners_.disconnect();
|
||||
this->unbind();
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ namespace wrapper {
|
|||
|
||||
|
||||
/**
|
||||
* Extension to boost::reference_wrapper:
|
||||
* Extension to std::reference_wrapper:
|
||||
* Allows additionally to re-bind to another reference,
|
||||
* almost like a pointer. Helpful for building templates.
|
||||
* @warning potentially dangerous
|
||||
|
|
@ -128,7 +128,7 @@ namespace wrapper {
|
|||
* A copyable, assignable value object, not managing ownership.
|
||||
* It can be default constructed and \c bool evaluated to detect
|
||||
* an empty holder. The value is retrieved pointer-like, by
|
||||
* explicit dereferentiation. (Contrast this to boost::ref,
|
||||
* explicit dereferentiation. (Contrast this to std::ref,
|
||||
* where the original reference is retrieved by conversion)
|
||||
*
|
||||
* The purpose of this template is to be able to remember
|
||||
|
|
|
|||
128
src/proc/engine/buffer-local-key.hpp
Normal file
128
src/proc/engine/buffer-local-key.hpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
BUFFER-LOCAL-KEY.hpp - opaque data for BufferProvider 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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROC_ENGINE_BUFFR_LOCAL_KEY_H
|
||||
#define PROC_ENGINE_BUFFR_LOCAL_KEY_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
|
||||
namespace lib {
|
||||
typedef size_t HashVal;
|
||||
}
|
||||
|
||||
namespace engine {
|
||||
|
||||
namespace metadata {
|
||||
class Key;
|
||||
class Entry;
|
||||
}
|
||||
class BufferMetadata;
|
||||
|
||||
using lib::HashVal;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
union OpaqueData
|
||||
{
|
||||
uint64_t _as_number;
|
||||
void* _as_pointer;
|
||||
};
|
||||
|
||||
OpaqueData privateID_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
LocalKey (uint64_t opaqueValue=0)
|
||||
{
|
||||
privateID_._as_number = opaqueValue;
|
||||
}
|
||||
|
||||
LocalKey (void* impl_related_ptr)
|
||||
{
|
||||
privateID_._as_number = 0;
|
||||
privateID_._as_pointer = impl_related_ptr;
|
||||
}
|
||||
|
||||
|
||||
operator uint64_t() const
|
||||
{
|
||||
return privateID_._as_number;
|
||||
}
|
||||
|
||||
operator void*() const
|
||||
{
|
||||
return privateID_._as_pointer;
|
||||
}
|
||||
|
||||
bool
|
||||
isDefined() const
|
||||
{
|
||||
return bool(privateID_._as_number);
|
||||
}
|
||||
|
||||
friend size_t
|
||||
hash_value (LocalKey const& lkey)
|
||||
{
|
||||
boost::hash<uint64_t> hashFunction;
|
||||
return hashFunction(lkey.privateID_._as_number);
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator== (LocalKey const& left, LocalKey const& right)
|
||||
{
|
||||
return uint64_t(left) == uint64_t(right);
|
||||
}
|
||||
friend bool
|
||||
operator!= (LocalKey const& left, LocalKey const& right)
|
||||
{
|
||||
return uint64_t(left) != uint64_t(right);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/** assignment usually prohibited */
|
||||
LocalKey& operator= (LocalKey const& o)
|
||||
{
|
||||
privateID_ = o.privateID_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** but Key assignments are acceptable */
|
||||
friend class metadata::Key;
|
||||
};
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
** - 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.
|
||||
** These three distinctions are applied in sequence, thus forming a type 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
|
||||
|
|
@ -56,10 +56,12 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/functor-util.hpp"
|
||||
#include "lib/util-foreach.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "proc/engine/type-handler.hpp"
|
||||
#include "proc/engine/buffer-local-key.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <tr1/unordered_map>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
|
|
@ -67,9 +69,7 @@ namespace engine {
|
|||
|
||||
using lib::HashVal;
|
||||
using lib::Literal;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::function;
|
||||
using std::tr1::placeholders::_1;
|
||||
using util::for_each;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
|
@ -77,165 +77,27 @@ namespace engine {
|
|||
class Key;
|
||||
class Entry;
|
||||
}
|
||||
class BufferMetadata;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Buffer states
|
||||
* usable within BufferProvider
|
||||
* and stored within the metadata
|
||||
*/
|
||||
enum BufferState
|
||||
{ NIL,
|
||||
FREE,
|
||||
LOCKED,
|
||||
EMITTED,
|
||||
BLOCKED
|
||||
{ NIL, ///< abstract entry, not yet allocated
|
||||
FREE, ///< allocated buffer, no longer in use
|
||||
LOCKED, ///< allocated buffer actively in use
|
||||
EMITTED, ///< allocated buffer, returned from client
|
||||
BLOCKED ///< allocated buffer blocked by protocol failure
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -258,7 +120,7 @@ namespace engine {
|
|||
|
||||
|
||||
|
||||
/* === Implementation === */
|
||||
/* === Metadata Implementation === */
|
||||
|
||||
namespace metadata {
|
||||
|
||||
|
|
@ -275,6 +137,13 @@ namespace engine {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Description of a Buffer-"type".
|
||||
* Key elements will be used to generate hash IDs,
|
||||
* to be embedded into a BufferDescriptor.
|
||||
* Keys are chained hierarchically.
|
||||
*/
|
||||
class Key
|
||||
{
|
||||
HashVal parent_;
|
||||
|
|
@ -342,27 +211,108 @@ namespace engine {
|
|||
{ }
|
||||
|
||||
|
||||
/** build derived Key for a concrete buffer Entry
|
||||
* @param parent type key to subsume this buffer
|
||||
* @param bufferAddr pointer to the concrete buffer
|
||||
* @return Child key with hashID based on the buffer address.
|
||||
* For NULL buffer a copy of the parent is returned.
|
||||
*/
|
||||
static Key
|
||||
forEntry (Key const& parent, const void* bufferAddr, LocalKey const& implID =UNSPECIFIC)
|
||||
{
|
||||
Key newKey(parent);
|
||||
if (bufferAddr)
|
||||
{
|
||||
newKey.parent_ = HashVal(parent);
|
||||
newKey.hashID_ = chainedHash(parent, bufferAddr);
|
||||
if (nontrivial(implID))
|
||||
{
|
||||
REQUIRE (!newKey.specifics_.isDefined(),
|
||||
"Implementation defined local key should not be overridden. "
|
||||
"Underlying buffer type already defines a nontrivial LocalKey");
|
||||
newKey.specifics_ = implID;
|
||||
} }
|
||||
return newKey;
|
||||
}
|
||||
|
||||
void
|
||||
useTypeHandlerFrom (Key const& ref)
|
||||
{
|
||||
if (nontrivial(this->instanceFunc_))
|
||||
throw error::Logic ("unable to supersede an already attached TypeHandler"
|
||||
, LUMIERA_ERROR_LIFECYCLE);
|
||||
instanceFunc_ = ref.instanceFunc_;
|
||||
}
|
||||
|
||||
|
||||
LocalKey const& localKey() const { return specifics_;}
|
||||
size_t storageSize() const { return storageSize_; }
|
||||
|
||||
HashVal parentKey() const { return parent_;}
|
||||
operator HashVal() const { return hashID_;}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A complete metadata Entry, based on a Key.
|
||||
* This special Key element usually describes an actual Buffer.
|
||||
* Entries are to be managed in a hashtable, which is "the metadata table".
|
||||
* As a special case, an entry without a concrete buffer storage pointer
|
||||
* can be created. This corresponds to a (plain) key and describes just
|
||||
* a buffer type. Such type-only entries are fixed to the NIL state.
|
||||
* All other entries allow for state transitions.
|
||||
*
|
||||
* The "metadata table" with its entries is maintained by an engine::BufferMetadata
|
||||
* instance. For the latter, Entry serves as representation and access point
|
||||
* to the individual metadata; this includes using the TypeHandler for
|
||||
* building and destroying buffer structures.
|
||||
*/
|
||||
class Entry
|
||||
: public Key
|
||||
{
|
||||
BufferState state_;
|
||||
const void* buffer_;
|
||||
void* buffer_;
|
||||
|
||||
protected:
|
||||
Entry (Key const& parent, void* bufferPtr =0, LocalKey const& implID =UNSPECIFIC)
|
||||
: Key (Key::forEntry (parent, bufferPtr, implID))
|
||||
, state_(bufferPtr? LOCKED:NIL)
|
||||
, buffer_(bufferPtr)
|
||||
{ }
|
||||
|
||||
/// BufferMetadata is allowed to create
|
||||
friend class engine::BufferMetadata;
|
||||
|
||||
// standard copy operations permitted
|
||||
|
||||
public:
|
||||
virtual BufferState
|
||||
/** is this Entry currently associated to a
|
||||
* concrete buffer? Is this buffer in use? */
|
||||
bool
|
||||
isLocked() const
|
||||
{
|
||||
ASSERT (!buffer_ || (NIL != state_ && FREE != state_));
|
||||
return bool(buffer_);
|
||||
}
|
||||
|
||||
/** is this Entry just an (abstract) placeholder for a type?
|
||||
* @return false if it's a real entry corresponding to a concrete buffer
|
||||
*/
|
||||
bool
|
||||
isTypeKey() const
|
||||
{
|
||||
return NIL == state_ && !buffer_;
|
||||
}
|
||||
|
||||
|
||||
BufferState
|
||||
state() const
|
||||
{
|
||||
__must_not_be_NIL();
|
||||
return state_;
|
||||
}
|
||||
|
||||
virtual const void*
|
||||
access() const
|
||||
void*
|
||||
access()
|
||||
{
|
||||
__must_not_be_NIL();
|
||||
__must_not_be_FREE();
|
||||
|
|
@ -371,40 +321,83 @@ namespace engine {
|
|||
return buffer_;
|
||||
}
|
||||
|
||||
virtual Entry&
|
||||
/** Buffer state machine */
|
||||
Entry&
|
||||
mark (BufferState newState)
|
||||
{
|
||||
switch (this->state_)
|
||||
__must_not_be_NIL();
|
||||
|
||||
if ( (state_ == FREE && newState == LOCKED)
|
||||
||(state_ == LOCKED && newState == EMITTED)
|
||||
||(state_ == LOCKED && newState == BLOCKED)
|
||||
||(state_ == LOCKED && newState == FREE)
|
||||
||(state_ == EMITTED && newState == BLOCKED)
|
||||
||(state_ == EMITTED && newState == FREE)
|
||||
||(state_ == BLOCKED && newState == FREE))
|
||||
{
|
||||
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.");
|
||||
// allowed transition
|
||||
if (newState == FREE)
|
||||
invokeEmbeddedDtor_and_clear();
|
||||
if (newState == LOCKED)
|
||||
invokeEmbeddedCtor();
|
||||
state_ = newState;
|
||||
return *this;
|
||||
}
|
||||
state_ = newState;
|
||||
|
||||
throw error::Fatal ("Invalid buffer state transition.");
|
||||
}
|
||||
|
||||
Entry&
|
||||
lock (void* newBuffer)
|
||||
{
|
||||
__must_be_FREE();
|
||||
buffer_ = newBuffer;
|
||||
return mark (LOCKED);
|
||||
}
|
||||
|
||||
Entry&
|
||||
invalidate (bool invokeDtor =true)
|
||||
{
|
||||
if (buffer_ && invokeDtor)
|
||||
invokeEmbeddedDtor_and_clear();
|
||||
buffer_ = 0;
|
||||
state_ = FREE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
/** @internal maybe invoke a registered TypeHandler's
|
||||
* constructor function, which typically builds some
|
||||
* content object into the buffer by placement new. */
|
||||
void
|
||||
invokeEmbeddedCtor()
|
||||
{
|
||||
__buffer_required();
|
||||
if (nontrivial (instanceFunc_))
|
||||
instanceFunc_.createAttached (buffer_);
|
||||
}
|
||||
|
||||
/** @internal maybe invoke a registered TypeHandler's
|
||||
* destructor function, which typically clears up some
|
||||
* content object living within the buffer */
|
||||
void
|
||||
invokeEmbeddedDtor_and_clear()
|
||||
{
|
||||
__buffer_required();
|
||||
if (nontrivial (instanceFunc_))
|
||||
instanceFunc_.destroyAttached (buffer_);
|
||||
buffer_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
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)");
|
||||
throw error::Fatal ("Buffer metadata entry with state==NIL encountered."
|
||||
"State transition logic broken (programming error)"
|
||||
, LUMIERA_ERROR_LIFECYCLE);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -416,21 +409,144 @@ namespace engine {
|
|||
"You should invoke markLocked(buffer) prior to access."
|
||||
, LUMIERA_ERROR_LIFECYCLE );
|
||||
}
|
||||
|
||||
void
|
||||
__must_be_FREE() const
|
||||
{
|
||||
if (FREE != state_)
|
||||
throw error::Logic ("Buffer already in use"
|
||||
, LUMIERA_ERROR_LIFECYCLE );
|
||||
REQUIRE (!buffer_, "Buffer marked as free, "
|
||||
"but buffer pointer is set.");
|
||||
}
|
||||
|
||||
void
|
||||
__buffer_required() const
|
||||
{
|
||||
if (!buffer_)
|
||||
throw error::Fatal ("Need concrete buffer for any further operations");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* (Hash)Table to store and manage buffer metadata.
|
||||
* Buffer metadata entries are comprised of a Key part and an extended
|
||||
* Entry, holding the actual management and housekeeping metadata. The
|
||||
* Keys are organised hierarchically and denote the "kind" of buffer.
|
||||
* The hash values for lookup are based on the key part, chained with
|
||||
* the actual memory location of the concrete buffer corresponding
|
||||
* to the metadata entry to be retrieved.
|
||||
*/
|
||||
class Table
|
||||
{
|
||||
typedef std::tr1::unordered_map<HashVal,Entry> MetadataStore;
|
||||
|
||||
MetadataStore entries_;
|
||||
|
||||
public:
|
||||
~Table() { verify_all_buffers_freed(); }
|
||||
|
||||
/** fetch metadata record, if any
|
||||
* @param hashID for the Key part of the metadata entry
|
||||
* @return pointer to the entry in the table or NULL
|
||||
*/
|
||||
Entry*
|
||||
fetch (HashVal hashID)
|
||||
{
|
||||
MetadataStore::iterator pos = entries_.find (hashID);
|
||||
if (pos != entries_.end())
|
||||
return &(pos->second);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const Entry*
|
||||
fetch (HashVal hashID) const
|
||||
{
|
||||
MetadataStore::const_iterator pos = entries_.find (hashID);
|
||||
if (pos != entries_.end())
|
||||
return &(pos->second);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** store a copy of the given new metadata entry.
|
||||
* The hash key for lookup is retrieved from the given Entry, by conversion to HashVal.
|
||||
* Consequently, this will be the hashID of the parent Key (type), when the entry holds
|
||||
* a NULL buffer (i.e a "pseudo entry"). Otherwise, it will be this parent Key hash,
|
||||
* extended by hashing the actual buffer address.
|
||||
* @return reference to relevant entry for this Key. This might be a copy
|
||||
* of the new entry, or an already existing entry with the same Key
|
||||
*/
|
||||
Entry&
|
||||
store (Entry const& newEntry)
|
||||
{
|
||||
using std::make_pair;
|
||||
REQUIRE (!fetch (newEntry), "duplicate buffer metadata entry");
|
||||
MetadataStore::iterator pos = entries_.insert (make_pair (HashVal(newEntry), newEntry))
|
||||
.first;
|
||||
|
||||
ENSURE (pos != entries_.end());
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
void
|
||||
remove (HashVal hashID)
|
||||
{
|
||||
uint cnt = entries_.erase (hashID);
|
||||
ENSURE (cnt, "entry to remove didn't exist");
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
verify_all_buffers_freed()
|
||||
try
|
||||
{
|
||||
for_each (entries_, verify_is_free);
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (engine,"Shutdown of BufferProvider metadata store")
|
||||
|
||||
static void
|
||||
verify_is_free (std::pair<HashVal, Entry> const& e)
|
||||
{
|
||||
WARN_IF (e.second.isLocked(), engine,
|
||||
"Buffer still in use while shutting down BufferProvider? ");
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace metadata
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Metadata
|
||||
/* ===== Buffer Metadata Frontend ===== */
|
||||
|
||||
/**
|
||||
* Registry for managing buffer metadata.
|
||||
* This is an implementation level service,
|
||||
* used by the standard BufferProvider implementation.
|
||||
* Each metadata registry (instance) defines and maintains
|
||||
* a family of "buffer types"; beyond the buffer storage size,
|
||||
* the concrete meaning of those types is tied to the corresponding
|
||||
* BufferProvider implementation and remains opaque. These types are
|
||||
* represented as hierarchically linked hash keys. The implementation
|
||||
* may bind a TypeHandler to a specific type, allowing automatic invocation
|
||||
* of a "constructor" and "destructor" function on each buffer of this type,
|
||||
* when \em locking or \em freeing the corresponding buffer.
|
||||
*/
|
||||
class BufferMetadata
|
||||
: boost::noncopyable
|
||||
{
|
||||
Literal id_;
|
||||
HashVal family_;
|
||||
|
||||
public:
|
||||
metadata::Table table_;
|
||||
|
||||
|
||||
public:
|
||||
typedef metadata::Key Key;
|
||||
typedef metadata::Entry Entry;
|
||||
|
||||
|
|
@ -438,10 +554,10 @@ namespace engine {
|
|||
* 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.
|
||||
* @param implementationID to distinguish families of
|
||||
* type keys belonging to different registries.
|
||||
*/
|
||||
Metadata (Literal implementationID)
|
||||
BufferMetadata (Literal implementationID)
|
||||
: id_(implementationID)
|
||||
, family_(hash_value(id_))
|
||||
{ }
|
||||
|
|
@ -459,20 +575,16 @@ namespace engine {
|
|||
Key
|
||||
key ( size_t storageSize
|
||||
, TypeHandler instanceFunc =RAW_BUFFER
|
||||
, LocalKey specifics =UNSPECIFIC)
|
||||
, 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;
|
||||
}
|
||||
|
|
@ -484,50 +596,159 @@ namespace engine {
|
|||
return trackKey (parentKey, instanceFunc);
|
||||
}
|
||||
|
||||
/** create a sub-type, using a different private-ID (implementation defined) */
|
||||
/** 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)
|
||||
/** shortcut to access the Key part of a (probably new) Entry
|
||||
* describing a concrete buffer at the given address
|
||||
* @note might create/register a new Entry as a side-effect
|
||||
*/
|
||||
Key const&
|
||||
key (Key const& parentKey, void* concreteBuffer, LocalKey const& implID =UNSPECIFIC)
|
||||
{
|
||||
UNIMPLEMENTED ("create sub-object key for concrete buffer");
|
||||
Key derivedKey = Key::forEntry (parentKey, concreteBuffer);
|
||||
Entry* existing = table_.fetch (derivedKey);
|
||||
|
||||
return existing? *existing
|
||||
: markLocked (parentKey,concreteBuffer,implID);
|
||||
}
|
||||
|
||||
Key const&
|
||||
/** core operation to access or create a concrete buffer metadata entry.
|
||||
* The hashID of the entry in question is built, based on the parentKey,
|
||||
* which denotes a buffer type, and the concrete buffer address. If yet
|
||||
* unknown, a new concrete buffer metadata Entry is created and initialised
|
||||
* to LOCKED state. Otherwise just the existing Entry is fetched.
|
||||
* @note this function really \em activates the buffer.
|
||||
* In case the type (Key) involves a TypeHandler (functor),
|
||||
* its constructor function will be invoked, if actually a new
|
||||
* entry gets created. Typically this mechanism will be used
|
||||
* to placement-create an object into the buffer.
|
||||
* @param parentKey a key describing the \em type of the buffer
|
||||
* @param concreteBuffer storage pointer, must not be NULL
|
||||
* @param onlyNew disallow fetching an existing entry
|
||||
* @throw error::Logic when #onlyNew is set, but an equivalent entry
|
||||
* was registered previously. This indicates a serious error
|
||||
* in buffer lifecycle management.
|
||||
* @throw error::Invalid when invoked with NULL buffer. Use the #key
|
||||
* functions instead to register and track type keys.
|
||||
* @return reference to the entry stored in the metadata table.
|
||||
* @warning the exposed reference might become invalid when the
|
||||
* buffer is released or re-used later.
|
||||
*/
|
||||
Entry&
|
||||
lock (Key const& parentKey
|
||||
,void* concreteBuffer
|
||||
,LocalKey const& implID =UNSPECIFIC
|
||||
,bool onlyNew =false)
|
||||
{
|
||||
if (!concreteBuffer)
|
||||
throw error::Invalid ("Attempt to lock a slot for a NULL buffer"
|
||||
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
|
||||
Entry newEntry(parentKey, concreteBuffer, implID);
|
||||
Entry* existing = table_.fetch (newEntry);
|
||||
|
||||
if (existing && onlyNew)
|
||||
throw error::Logic ("Attempt to lock a slot for a new buffer, "
|
||||
"while actually the old buffer is still locked"
|
||||
, error::LUMIERA_ERROR_LIFECYCLE );
|
||||
if (existing && existing->isLocked())
|
||||
throw error::Logic ("Attempt to re-lock a buffer still in use"
|
||||
, error::LUMIERA_ERROR_LIFECYCLE );
|
||||
|
||||
if (!existing)
|
||||
return store_and_lock (newEntry); // actual creation
|
||||
else
|
||||
return existing->lock (concreteBuffer);
|
||||
}
|
||||
|
||||
/** access the metadata record registered with the given hash key.
|
||||
* This might be a pseudo entry in case of a Key describing a buffer type.
|
||||
* Otherwise, the entry associated with a concrete buffer pointer is returned
|
||||
* by reference, an can be modified (e.g. state change)
|
||||
* @param hashID which can be calculated from the Key
|
||||
* @throw error::Invalid when there is no such entry
|
||||
* @note use #isKnown to check existence
|
||||
*/
|
||||
Entry&
|
||||
get (HashVal hashID)
|
||||
{
|
||||
UNIMPLEMENTED ("access the plain key entry");
|
||||
}
|
||||
|
||||
Entry&
|
||||
get (Key key)
|
||||
{
|
||||
UNIMPLEMENTED ("access, possibly create metadata records");
|
||||
Entry* entry = table_.fetch (hashID);
|
||||
if (!entry)
|
||||
throw error::Invalid ("Attempt to access an unknown buffer metadata entry");
|
||||
|
||||
return *entry;
|
||||
}
|
||||
|
||||
bool
|
||||
isKnown (HashVal key) const
|
||||
{
|
||||
UNIMPLEMENTED ("diagnostics: known record?");
|
||||
return bool(table_.fetch (key));
|
||||
}
|
||||
|
||||
bool
|
||||
isLocked (HashVal key) const
|
||||
{
|
||||
UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
|
||||
const Entry* entry = table_.fetch (key);
|
||||
return entry
|
||||
&& entry->isLocked();
|
||||
}
|
||||
|
||||
|
||||
/* == memory management == */
|
||||
|
||||
Entry& markLocked (Key const& parentKey, const void* buffer);
|
||||
void release (HashVal key);
|
||||
/* == memory management operations == */
|
||||
|
||||
private:
|
||||
/** combine the type (Key) with a concrete buffer,
|
||||
* thereby marking this buffer as locked. Store a concrete
|
||||
* metadata Entry to account for this fact. This might include
|
||||
* invoking a constructor function, in case the type (Key)
|
||||
* defines a (nontrivial) TypeHandler.
|
||||
* @throw error::Fatal when locking a NULL buffer
|
||||
* @throw exceptions which might be raised by a TypeHandler's
|
||||
* constructor function. In this case, the Entry remains
|
||||
* created, but is marked as FREE
|
||||
*/
|
||||
Entry&
|
||||
markLocked (Key const& parentKey, void* buffer, LocalKey const& implID =UNSPECIFIC)
|
||||
{
|
||||
if (!buffer)
|
||||
throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
|
||||
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
|
||||
return this->lock(parentKey, buffer, implID, true); // force creation of a new entry
|
||||
}
|
||||
|
||||
/** purge the bare metadata Entry from the metadata tables.
|
||||
* @throw error::Logic if the entry isn't marked FREE already
|
||||
*/
|
||||
void
|
||||
release (HashVal key)
|
||||
{
|
||||
Entry* entry = table_.fetch (key);
|
||||
if (!entry) return;
|
||||
|
||||
ASSERT (entry && (key == HashVal(*entry)));
|
||||
release (*entry);
|
||||
}
|
||||
|
||||
void
|
||||
release (Entry const& entry)
|
||||
{
|
||||
if (FREE != entry.state())
|
||||
throw error::Logic ("Attempt to release a buffer still in use"
|
||||
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||
|
||||
table_.remove (HashVal(entry));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
template<typename PAR, typename DEF>
|
||||
Key
|
||||
|
|
@ -542,43 +763,31 @@ namespace engine {
|
|||
maybeStore (Key const& key)
|
||||
{
|
||||
if (isKnown (key)) return;
|
||||
UNIMPLEMENTED ("registry for type keys");
|
||||
table_.store (Entry (key, NULL));
|
||||
}
|
||||
|
||||
Entry&
|
||||
store_and_lock (Entry const& metadata)
|
||||
{
|
||||
Entry& newEntry = table_.store (metadata);
|
||||
try
|
||||
{
|
||||
newEntry.invokeEmbeddedCtor();
|
||||
ENSURE (LOCKED == newEntry.state());
|
||||
ENSURE (newEntry.access());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
newEntry.mark(FREE);
|
||||
throw;
|
||||
}
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
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
|
||||
|
|
|
|||
|
|
@ -21,9 +21,12 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/engine/buffer-provider.hpp"
|
||||
#include "proc/engine/buffer-metadata.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
using util::isSameObject;
|
||||
|
||||
namespace engine {
|
||||
|
||||
|
|
@ -33,10 +36,17 @@ namespace engine {
|
|||
const uint DEFAULT_DESCRIPTOR = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
LUMIERA_ERROR_DEFINE (BUFFER_MANAGEMENT, "Problem providing working buffers");
|
||||
|
||||
|
||||
/** build a new provider instance, managing a family of buffers.
|
||||
* The metadata of these buffers is organised hierarchically based on
|
||||
* chained hash values, using the #implementationID as a seed.
|
||||
* @param implementationID symbolic ID setting these family of buffers apart.
|
||||
*/
|
||||
BufferProvider::BufferProvider (Literal implementationID)
|
||||
: meta_(new Metadata (implementationID))
|
||||
: meta_(new BufferMetadata (implementationID))
|
||||
{ }
|
||||
|
||||
BufferProvider::~BufferProvider() { }
|
||||
|
|
@ -47,9 +57,9 @@ namespace engine {
|
|||
* currently locked and usable by client code
|
||||
*/
|
||||
bool
|
||||
BufferProvider::verifyValidity (BufferDescriptor const&)
|
||||
BufferProvider::verifyValidity (BufferDescriptor const& bufferID) const
|
||||
{
|
||||
UNIMPLEMENTED ("BufferProvider basic and default implementation");
|
||||
return meta_->isLocked (bufferID);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -58,8 +68,176 @@ namespace engine {
|
|||
{
|
||||
return BufferDescriptor (*this, meta_->key (storageSize));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BufferDescriptor
|
||||
BufferProvider::getDescriptorFor(size_t storageSize, TypeHandler specialTreatment)
|
||||
{
|
||||
return BufferDescriptor (*this, meta_->key (storageSize, specialTreatment));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
BufferProvider::getBufferSize (HashVal typeID) const
|
||||
{
|
||||
metadata::Key& typeKey = meta_->get (typeID);
|
||||
return typeKey.storageSize();
|
||||
}
|
||||
|
||||
|
||||
/** callback from implementation to build and enrol a BufferHandle,
|
||||
* to be returned to the client as result of the #lockBuffer call.
|
||||
* Performs the necessary metadata state transition leading from an
|
||||
* abstract buffer type to a metadata::Entry corresponding to an
|
||||
* actual buffer, which is locked for exclusive use by one client.
|
||||
*/
|
||||
BuffHandle
|
||||
BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID)
|
||||
{
|
||||
metadata::Key& typeKey = meta_->get (typeID);
|
||||
metadata::Entry& entry = meta_->markLocked(typeKey, storage, implID);
|
||||
|
||||
return BuffHandle (BufferDescriptor(*this, entry), storage);
|
||||
}
|
||||
|
||||
|
||||
/** BufferProvider API: declare in advance the need for working buffers.
|
||||
* This optional call allows client code to ensure the availability of the
|
||||
* necessary working space, prior to starting the actual operations. The
|
||||
* client may reasonably assume to get the actual number of buffers, as
|
||||
* indicated by the return value. A provider may be able to handle
|
||||
* various kinds of buffers (e.g. of differing size), which are
|
||||
* distinguished by the \em type embodied into the BufferDescriptor.
|
||||
* @return maximum number of simultaneously usable buffers of this type,
|
||||
* to be retrieved later through calls to #lockBuffer.
|
||||
* @throw error::State when no buffer of this kind can be provided
|
||||
* @note the returned count may differ from the requested count.
|
||||
*/
|
||||
uint
|
||||
BufferProvider::announce (uint count, BufferDescriptor const& type)
|
||||
{
|
||||
uint actually_possible = prepareBuffers (count, type);
|
||||
if (!actually_possible)
|
||||
throw error::State ("unable to fulfil request for buffers"
|
||||
,LUMIERA_ERROR_BUFFER_MANAGEMENT);
|
||||
return actually_possible;
|
||||
}
|
||||
|
||||
|
||||
/** BufferProvider API: retrieve a single buffer for exclusive use.
|
||||
* This call actually claims a buffer of this type and marks it for
|
||||
* use by client code. The returned handle allows for convenient access,
|
||||
* but provides no automatic tracking or memory management. The client is
|
||||
* explicitly responsible to invoke #releaseBuffer (which can be done directly
|
||||
* on the BuffHandle).
|
||||
* @return a copyable handle, representing this buffer and this usage transaction.
|
||||
* @throw error::State when unable to provide this buffer
|
||||
* @note this function may be used right away, without prior announcing, but then
|
||||
* the client should be prepared for exceptions. The #announce operation allows
|
||||
* to establish a reliably available baseline.
|
||||
*/
|
||||
BuffHandle
|
||||
BufferProvider::lockBuffer (BufferDescriptor const& type)
|
||||
{
|
||||
REQUIRE (was_created_by_this_provider (type));
|
||||
|
||||
return provideLockedBuffer (type);
|
||||
} // is expected to call buildHandle() --> state transition
|
||||
|
||||
|
||||
/** BufferProvider API: state transition to \em emitted state.
|
||||
* Client code may signal a state transition through this optional operation.
|
||||
* The actual meaning of an "emitted" buffer is implementation defined; similarly,
|
||||
* some back-ends may actually do something when emitting a buffer (e.g. commit data
|
||||
* to cache), while others just set a flag or do nothing at all. This state transition
|
||||
* may be invoked at most once per locked buffer.
|
||||
* @throw error::Fatal in case of invalid state transition sequence. Only a locked buffer
|
||||
* may be emitted, and at most once.
|
||||
* @warning by convention, emitting a buffer implies that the contained data is ready and
|
||||
* might be used by other parts of the application.
|
||||
* An emitted buffer should not be modified anymore.
|
||||
*/
|
||||
void
|
||||
BufferProvider::emitBuffer (BuffHandle const& handle)
|
||||
{
|
||||
metadata::Entry& metaEntry = meta_->get (handle.entryID());
|
||||
mark_emitted (metaEntry.parentKey(), metaEntry.localKey());
|
||||
metaEntry.mark(EMITTED);
|
||||
}
|
||||
|
||||
|
||||
/** BufferProvider API: declare done and detach.
|
||||
* Client code is required to release \em each previously locked buffer eventually.
|
||||
* @warning invalidates the BuffHandle, clients mustn't access the buffer anymore.
|
||||
* Right after releasing, an access through the handle will throw;
|
||||
* yet the buffer might be re-used and the handle become valid
|
||||
* later on accidentally.
|
||||
* @note EX_FREE
|
||||
*/
|
||||
void
|
||||
BufferProvider::releaseBuffer (BuffHandle const& handle)
|
||||
try {
|
||||
metadata::Entry& metaEntry = meta_->get (handle.entryID());
|
||||
metaEntry.mark(FREE); // might invoke embedded dtor function
|
||||
detachBuffer (metaEntry.parentKey(), metaEntry.localKey());
|
||||
meta_->release (metaEntry);
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider")
|
||||
|
||||
|
||||
|
||||
/** @warning this operation locally modifies the metadata entry of a single buffer
|
||||
* to attach a TypeHandler taking ownership of an object embedded within the buffer.
|
||||
* The client is responsible for actually placement-constructing the object; moreover
|
||||
* the client is responsible for any damage done to already existing buffer content.
|
||||
* @note the buffer must be in locked state and the underlying buffer type must not define
|
||||
* an non-trivial TypeDescriptor, because there is no clean way of superseding an
|
||||
* existing TypeDescriptor, which basically is just a functor and possibly
|
||||
* could perform any operation on buffer clean-up.
|
||||
* @note EX_STRONG
|
||||
*/
|
||||
void
|
||||
BufferProvider::attachTypeHandler (BuffHandle const& target, BufferDescriptor const& reference)
|
||||
{
|
||||
metadata::Entry& metaEntry = meta_->get (target.entryID());
|
||||
metadata::Entry& refEntry = meta_->get (reference);
|
||||
REQUIRE (refEntry.isTypeKey());
|
||||
REQUIRE (!metaEntry.isTypeKey());
|
||||
if (!metaEntry.isLocked())
|
||||
throw error::Logic ("unable to attach an object because buffer isn't locked for use"
|
||||
, LUMIERA_ERROR_LIFECYCLE);
|
||||
|
||||
metaEntry.useTypeHandlerFrom (refEntry); // EX_STRONG
|
||||
}
|
||||
|
||||
|
||||
/** @internal abort normal lifecycle, reset the underlying buffer and detach from it.
|
||||
* This allows to break out of normal usage and reset the handle to \em invalid state
|
||||
* @param invokeDtor if possibly the clean-up function of an TypeHandler registered with
|
||||
* the buffer metadata should be invoked prior to resetting the metadata state.
|
||||
* Default is \em not to invoke anything
|
||||
* @note EX_FREE
|
||||
*/
|
||||
void
|
||||
BufferProvider::emergencyCleanup (BuffHandle const& target, bool invokeDtor)
|
||||
try {
|
||||
metadata::Entry& metaEntry = meta_->get (target.entryID());
|
||||
metaEntry.invalidate (invokeDtor);
|
||||
detachBuffer (metaEntry.parentKey(), metaEntry.localKey());
|
||||
meta_->release (metaEntry);
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (engine, "cleanup of buffer metadata while handling an error")
|
||||
|
||||
|
||||
|
||||
bool
|
||||
BufferProvider::was_created_by_this_provider (BufferDescriptor const& descr) const
|
||||
{
|
||||
return isSameObject (*this, *descr.provider_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === BufferDescriptor and BuffHandle === */
|
||||
|
|
@ -71,12 +249,67 @@ namespace engine {
|
|||
}
|
||||
|
||||
|
||||
size_t
|
||||
BufferDescriptor::determineBufferSize() const
|
||||
{
|
||||
return provider_->getBufferSize (*this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BuffHandle::emit()
|
||||
{
|
||||
REQUIRE (isValid());
|
||||
descriptor_.provider_->emitBuffer(*this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BuffHandle::release()
|
||||
{
|
||||
UNIMPLEMENTED ("forward buffer release call to buffer provider");
|
||||
if (pBuffer_)
|
||||
{
|
||||
REQUIRE (isValid());
|
||||
descriptor_.provider_->releaseBuffer(*this);
|
||||
pBuffer_ = 0;
|
||||
}
|
||||
ENSURE (!isValid());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
BuffHandle::emergencyCleanup()
|
||||
{
|
||||
descriptor_.provider_->emergencyCleanup(*this); // EX_FREE
|
||||
pBuffer_ = 0;
|
||||
}
|
||||
|
||||
|
||||
/** Install a standard TypeHandler for an already locked buffer.
|
||||
* This causes the dtor function to be invoked when releasing this buffer.
|
||||
* The assumption is that client code will placement-construct an object
|
||||
* into this buffer right away, and thus we're taking ownership on that object.
|
||||
* @param type a reference BufferDescriptor defining an embedded TypeHandler to use
|
||||
* A copy of this TypeHandler will be stored into the local metadata for
|
||||
* this buffer only, not altering the basic buffer type in any way
|
||||
* @throw lifecycle error when attempting to treat an buffer not in locked state
|
||||
* @throw error::Logic in case of insufficient buffer space to hold the
|
||||
* intended target object
|
||||
* @note EX_STRONG
|
||||
*/
|
||||
void
|
||||
BuffHandle::takeOwnershipFor(BufferDescriptor const& type)
|
||||
{
|
||||
if (!this->isValid())
|
||||
throw error::Logic ("attaching an object requires an buffer in locked state"
|
||||
, LUMIERA_ERROR_LIFECYCLE);
|
||||
if (this->size() < type.determineBufferSize())
|
||||
throw error::Logic ("insufficient buffer size to hold an instance of that type");
|
||||
|
||||
descriptor_.provider_->attachTypeHandler(*this, type); // EX_STRONG
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/** @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
|
||||
** 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,
|
||||
|
|
@ -45,6 +45,8 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/type-handler.hpp"
|
||||
#include "proc/engine/buffer-local-key.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
|
@ -56,7 +58,10 @@ namespace engine {
|
|||
using lib::Literal;
|
||||
|
||||
|
||||
class Metadata;
|
||||
class BufferMetadata;
|
||||
|
||||
|
||||
LUMIERA_ERROR_DECLARE (BUFFER_MANAGEMENT); ///< Problem providing working buffers
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -66,46 +71,70 @@ namespace engine {
|
|||
* - "locking" a buffer to yield a buffer handle
|
||||
* - dereferencing this smart-handle class
|
||||
*
|
||||
* @warning all of BufferProvider is assumed to run within a threadsafe environment.
|
||||
*
|
||||
* @todo as of 6/2011 buffer management within the engine is still a bit vague
|
||||
* @todo as of 11/11 thread safety within the engine remains to be clarified
|
||||
*/
|
||||
class BufferProvider
|
||||
: boost::noncopyable
|
||||
{
|
||||
scoped_ptr<Metadata> meta_;
|
||||
scoped_ptr<BufferMetadata> meta_;
|
||||
|
||||
|
||||
protected: /* === for Implementation by concrete providers === */
|
||||
|
||||
protected:
|
||||
BufferProvider (Literal implementationID);
|
||||
|
||||
virtual uint prepareBuffers (uint count, HashVal typeID) =0;
|
||||
|
||||
virtual BuffHandle provideLockedBuffer (HashVal typeID) =0;
|
||||
virtual void mark_emitted (HashVal typeID, LocalKey const&) =0;
|
||||
virtual void detachBuffer (HashVal typeID, LocalKey const&) =0;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~BufferProvider(); ///< this is an interface
|
||||
virtual ~BufferProvider(); ///< this is an ABC
|
||||
|
||||
|
||||
virtual uint announce (uint count, BufferDescriptor const&) =0;
|
||||
uint announce (uint count, BufferDescriptor const&);
|
||||
|
||||
virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0;
|
||||
virtual void releaseBuffer (BuffHandle const&) =0;
|
||||
BuffHandle lockBuffer (BufferDescriptor const&);
|
||||
void emitBuffer (BuffHandle const&);
|
||||
void releaseBuffer (BuffHandle const&);
|
||||
|
||||
template<typename BU>
|
||||
BuffHandle lockBufferFor ();
|
||||
|
||||
/** allow for attaching and owing an object within an already created buffer */
|
||||
void attachTypeHandler (BuffHandle const& target, BufferDescriptor const& reference);
|
||||
|
||||
void emergencyCleanup (BuffHandle const& target, bool invokeDtor =false);
|
||||
|
||||
|
||||
/** describe the kind of buffer managed by this provider */
|
||||
BufferDescriptor getDescriptorFor(size_t storageSize=0);
|
||||
BufferDescriptor getDescriptorFor(size_t storageSize, TypeHandler specialTreatment);
|
||||
|
||||
template<typename BU>
|
||||
BufferDescriptor getDescriptor();
|
||||
|
||||
|
||||
|
||||
|
||||
/* === API for BuffHandle internal access === */
|
||||
|
||||
bool verifyValidity (BufferDescriptor const&);
|
||||
bool verifyValidity (BufferDescriptor const&) const;
|
||||
size_t getBufferSize (HashVal typeID) const;
|
||||
|
||||
protected:
|
||||
BuffHandle buildHandle (HashVal typeID, void* storage, LocalKey const&);
|
||||
|
||||
bool was_created_by_this_provider (BufferDescriptor const&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === Implementation === */
|
||||
|
||||
/** convenience shortcut:
|
||||
|
|
@ -119,15 +148,20 @@ namespace engine {
|
|||
BuffHandle
|
||||
BufferProvider::lockBufferFor()
|
||||
{
|
||||
UNIMPLEMENTED ("convenience shortcut to announce and lock for a specific object type");
|
||||
BufferDescriptor attach_object_automatically = getDescriptor<BU>();
|
||||
return lockBuffer (attach_object_automatically);
|
||||
}
|
||||
|
||||
|
||||
/** define a "buffer type" for automatically creating
|
||||
* an instance of the template type embedded into the buffer
|
||||
* and destroying that embedded object when releasing the buffer.
|
||||
*/
|
||||
template<typename BU>
|
||||
BufferDescriptor
|
||||
BufferProvider::getDescriptor()
|
||||
{
|
||||
UNIMPLEMENTED ("build descriptor for automatically placing an object instance into the buffer");
|
||||
return getDescriptorFor (sizeof(BU), TypeHandler::create<BU>());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
129
src/proc/engine/buffhandle-attach.hpp
Normal file
129
src/proc/engine/buffhandle-attach.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
BUFFHANDLE-ATTACH.hpp - Buffer handle extension to attach objects into the buffer
|
||||
|
||||
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 buffhandle-attach.hpp
|
||||
** Extension to allow placing objects right into the buffers, taking ownership.
|
||||
** This extension is mostly helpful for writing unit-tests, and beyond that for the
|
||||
** rather unusual case where we need to place an full-blown object into the buffer,
|
||||
** instead of just plain data. A possible use case for this mechanism is to allow for
|
||||
** state pre calculation stream, feeding this local state to the individual render node
|
||||
** embedded into a "state frame". Some effect processors indeed need to maintain state
|
||||
** beyond the single frame (e.g. averaging, integrating, sound compression), which usually
|
||||
** is handled by applying an "instance" of that processor to the frames to be calculated
|
||||
** in a straight sequence.
|
||||
**
|
||||
** BuffHandle and the underlying BufferProvider standard implementation support that case
|
||||
** by attaching an object managing functor to the metadata. This way, the state can live
|
||||
** directly embedded into the frame and still be accessed like an object. To keep the
|
||||
** header and compilation footprint low, the implementation of the functions supporting
|
||||
** this special case was split out of the basic buffhandle.hpp
|
||||
**
|
||||
** @see BuffHandle
|
||||
** @see BufferProviderProtocol_test usage demonstration
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_BUFFHANDLE_ATTACH_H
|
||||
#define ENGINE_BUFFHANDLE_ATTACH_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/engine/buffer-provider.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
|
||||
|
||||
|
||||
/* === BuffHandle Implementation === */
|
||||
|
||||
|
||||
#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \
|
||||
try \
|
||||
{ \
|
||||
return *new(pBuffer_) _CTOR_; \
|
||||
} \
|
||||
catch(...) \
|
||||
{ \
|
||||
emergencyCleanup(); /* EX_FREE */ \
|
||||
pBuffer_ = 0; \
|
||||
throw; \
|
||||
}
|
||||
|
||||
/** 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.
|
||||
* @throw error::Logic in case there is already another TypeHandler registered
|
||||
* in charge of managing the buffer contents, or when the object to create
|
||||
* would not fit into this buffer.
|
||||
*/
|
||||
template<typename BU>
|
||||
inline BU&
|
||||
BuffHandle::create()
|
||||
{
|
||||
takeOwnershipFor<BU>();
|
||||
_EXCEPTION_SAFE_INVOKE (BU());
|
||||
}
|
||||
|
||||
#undef _EXCEPTION_SAFE_INVOKE
|
||||
|
||||
|
||||
|
||||
/** @internal helper to attach an TypeHandler after-the fact.
|
||||
* @note this prepares the buffer for placement-creating an embedded object.
|
||||
* It doesn't actually create an object
|
||||
* @throw error::Logic in case there is already another TypeHandler registered
|
||||
* in charge of managing the buffer contents, or when the object to create
|
||||
* would not fit into this buffer.
|
||||
*/
|
||||
template<typename BU>
|
||||
inline void
|
||||
BuffHandle::takeOwnershipFor()
|
||||
{
|
||||
BufferDescriptor howto_attach_object_automatically
|
||||
= descriptor_.provider_->getDescriptor<BU>();
|
||||
takeOwnershipFor (howto_attach_object_automatically); // EX_STRONG
|
||||
}
|
||||
|
||||
|
||||
/** convenience shortcut: access the buffer contents casted to a specific type.
|
||||
* @warning this is a \em blind cast, there is no type safety.
|
||||
* @note clients can utilise the metadata::LocalKey to keep track of some
|
||||
* specific property of the buffer, like e.g. the type of object.
|
||||
*/
|
||||
template<typename BU>
|
||||
inline BU&
|
||||
BuffHandle::accessAs()
|
||||
{
|
||||
if (!pBuffer_)
|
||||
throw error::Logic ("buffer not (yet) locked for access by clients"
|
||||
, LUMIERA_ERROR_LIFECYCLE);
|
||||
return *reinterpret_cast<BU*> (pBuffer_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
@ -21,9 +21,9 @@
|
|||
*/
|
||||
|
||||
/** @file buffhandle.hpp
|
||||
** Various bits needed to support the buffer management within the render nodes.
|
||||
** A front-end 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
|
||||
** needs several input and output buffers. These may be allocated and provided by various
|
||||
** 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
|
||||
|
|
@ -31,13 +31,19 @@
|
|||
** 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
|
||||
** These buffer handles are based on a buffer descriptor record, which is opaque as far
|
||||
** as the client is concerned. BufferDescriptor acts as a representation of the type or
|
||||
** kind of buffer. The only way to obtain such a BufferDescriptor is from a concrete
|
||||
** BufferProvider implementation. A back-link to this owning and managing provider is
|
||||
** embedded into the BufferDescriptor, allowing to retrieve an buffer handle, corresponding
|
||||
** to an actual buffer provided and managed behind the scenes. There is no automatic
|
||||
** resource management; clients are responsible to invoke BuffHandle#release when done.
|
||||
**
|
||||
** @see BufferProvider
|
||||
** @see BufferProviderProtocol_test usage demonstration
|
||||
** @see OutputSlot
|
||||
** @see bufftable.hpp storage for the buffer table
|
||||
** @see engine::RenderInvocation
|
||||
*/
|
||||
|
||||
|
|
@ -52,6 +58,12 @@
|
|||
|
||||
namespace engine {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
using error::LUMIERA_ERROR_LIFECYCLE;
|
||||
|
||||
typedef size_t HashVal; ////////////TICKET #722
|
||||
|
||||
class BuffHandle;
|
||||
class BufferProvider;
|
||||
|
||||
|
||||
|
|
@ -64,54 +76,36 @@ namespace engine {
|
|||
* @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
|
||||
*/
|
||||
class BufferDescriptor
|
||||
{
|
||||
protected:
|
||||
BufferProvider* provider_;
|
||||
uint64_t subClassification_;
|
||||
HashVal subClassification_;
|
||||
|
||||
BufferDescriptor(BufferProvider& manager, uint64_t detail)
|
||||
BufferDescriptor(BufferProvider& manager, HashVal detail)
|
||||
: provider_(&manager)
|
||||
, subClassification_(detail)
|
||||
{ }
|
||||
|
||||
friend class BufferProvider;
|
||||
friend class BuffHandle;
|
||||
|
||||
public:
|
||||
// using standard copy operations
|
||||
|
||||
bool verifyValidity() const;
|
||||
size_t determineBufferSize() const;
|
||||
|
||||
operator HashVal() const { return subClassification_; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ProcNode;
|
||||
typedef ProcNode* PNode;
|
||||
|
||||
|
||||
struct ChannelDescriptor ///////TODO really need to define that here? it is needed for node wiring only
|
||||
{
|
||||
const lumiera::StreamType * bufferType; /////////////////////////////////////////TICKET #828
|
||||
};
|
||||
|
||||
struct InChanDescriptor : ChannelDescriptor
|
||||
{
|
||||
PNode dataSrc; ///< the ProcNode to pull this input from
|
||||
uint srcChannel; ///< output channel to use on the predecessor node
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
|
@ -127,15 +121,16 @@ namespace engine {
|
|||
|
||||
/** @internal a buffer handle may be obtained by "locking"
|
||||
* a buffer from the corresponding BufferProvider */
|
||||
BuffHandle(BufferDescriptor const& typeInfo, PBuff storage = 0)
|
||||
BuffHandle(BufferDescriptor const& typeInfo, void* storage = 0)
|
||||
: descriptor_(typeInfo)
|
||||
, pBuffer_(storage)
|
||||
, pBuffer_(static_cast<PBuff>(storage))
|
||||
{ }
|
||||
|
||||
// using standard copy operations
|
||||
|
||||
|
||||
|
||||
void emit();
|
||||
void release();
|
||||
|
||||
|
||||
|
|
@ -146,6 +141,8 @@ namespace engine {
|
|||
BU& accessAs();
|
||||
|
||||
|
||||
//////////////////////////////////////////TICKET #249 this operator looks obsolete. The Buff type is a placeholder type,
|
||||
//////////////////////////////////////////TODO it should never be accessed directly from within Lumiera engine code
|
||||
Buff&
|
||||
operator* () const
|
||||
{
|
||||
|
|
@ -160,42 +157,27 @@ namespace engine {
|
|||
&& descriptor_.verifyValidity();
|
||||
}
|
||||
|
||||
HashVal
|
||||
entryID() const
|
||||
{
|
||||
return HashVal(descriptor_);
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
UNIMPLEMENTED ("forward to the buffer provider for storage size diagnostics");
|
||||
return descriptor_.determineBufferSize();
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename BU>
|
||||
void takeOwnershipFor();
|
||||
void takeOwnershipFor(BufferDescriptor const& type);
|
||||
|
||||
void emergencyCleanup();
|
||||
};
|
||||
|
||||
|
||||
/* === 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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/channel-descriptor.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
|
@ -70,6 +70,7 @@ namespace engine {
|
|||
PBu inBuff;
|
||||
};
|
||||
|
||||
class BufferDescriptor;
|
||||
|
||||
/** Obsolete, to be rewritten /////TICKET #826 */
|
||||
class BuffTableStorage
|
||||
|
|
|
|||
75
src/proc/engine/channel-descriptor.hpp
Normal file
75
src/proc/engine/channel-descriptor.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
CHANNEL-DESCRIPTOR.hpp - Channel / Buffer type representation for the engine
|
||||
|
||||
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 channel-descriptor.hpp
|
||||
** Representation of the Media type of a data channel used within the engine.
|
||||
**
|
||||
** @todo as it stands (11/2011) this file is obsoleted and needs to be refactored,
|
||||
** alongside with adapting the node invocation to the new BufferProvider interface.
|
||||
**
|
||||
** @see nodewiring-def.hpp
|
||||
** @see nodeoperation.hpp
|
||||
** @see bufftable-obsolete.hpp storage for the buffer table
|
||||
** @see engine::RenderInvocation
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_CHANNEL_DESCRIPTOR_H
|
||||
#define ENGINE_CHANNEL_DESCRIPTOR_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/streamtype.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
using error::LUMIERA_ERROR_LIFECYCLE;
|
||||
|
||||
typedef size_t HashVal; ////////////TICKET #722
|
||||
|
||||
class BuffHandle;
|
||||
class BufferProvider;
|
||||
class BufferDescriptor;
|
||||
|
||||
|
||||
|
||||
class ProcNode;
|
||||
typedef ProcNode* PNode;
|
||||
|
||||
|
||||
struct ChannelDescriptor ///////TODO really need to define that here? it is needed for node wiring only
|
||||
{
|
||||
const lumiera::StreamType * bufferType; /////////////////////////////////////////TICKET #828
|
||||
};
|
||||
|
||||
struct InChanDescriptor : ChannelDescriptor
|
||||
{
|
||||
PNode dataSrc; ///< the ProcNode to pull this input from
|
||||
uint srcChannel; ///< output channel to use on the predecessor node
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
@ -22,18 +22,10 @@
|
|||
|
||||
|
||||
#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;
|
||||
|
||||
#include "proc/engine/tracking-heap-block-provider.hpp"
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
|
@ -43,115 +35,10 @@ namespace engine {
|
|||
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
|
||||
: pImpl_()
|
||||
{ }
|
||||
|
||||
DiagnosticBufferProvider::~DiagnosticBufferProvider() { }
|
||||
|
|
@ -174,13 +61,13 @@ namespace engine {
|
|||
return diagnostics();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DiagnosticBufferProvider::HeapMemProvider&
|
||||
|
||||
|
||||
|
||||
TrackingHeapBlockProvider&
|
||||
DiagnosticBufferProvider::reset()
|
||||
{
|
||||
pImpl_.reset(new HeapMemProvider());
|
||||
pImpl_.reset(new TrackingHeapBlockProvider());
|
||||
return *pImpl_;
|
||||
}
|
||||
|
||||
|
|
@ -189,9 +76,9 @@ namespace engine {
|
|||
{
|
||||
return &implInstance == pImpl_.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === diagnostic API === */
|
||||
|
|
@ -199,21 +86,21 @@ namespace engine {
|
|||
bool
|
||||
DiagnosticBufferProvider::buffer_was_used (uint bufferID) const
|
||||
{
|
||||
return pImpl_->access_or_create(bufferID).was_used();
|
||||
return pImpl_->access_emitted(bufferID).was_used();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DiagnosticBufferProvider::buffer_was_closed (uint bufferID) const
|
||||
{
|
||||
return pImpl_->access_or_create(bufferID).was_closed();
|
||||
return pImpl_->access_emitted(bufferID).was_closed();
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
DiagnosticBufferProvider::accessMemory (uint bufferID) const
|
||||
{
|
||||
return pImpl_->access_or_create(bufferID).accessMemory();
|
||||
return pImpl_->access_emitted(bufferID).accessMemory();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
|
||||
/** @file diagnostic-buffer-provider.hpp
|
||||
** An facility for writing unit-tests targetting the BufferProvider interface.
|
||||
** An facility for writing unit-tests targeting the BufferProvider interface.
|
||||
**
|
||||
** @see buffer-provider-protocol-test.cpp
|
||||
*/
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/singleton.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "proc/engine/type-handler.hpp"
|
||||
#include "proc/engine/buffer-provider.hpp"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
|
@ -44,6 +45,13 @@ namespace engine {
|
|||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
/**
|
||||
* simple BufferProvider implementation
|
||||
* with additional allocation tracking
|
||||
*/
|
||||
class TrackingHeapBlockProvider;
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Helper for unit tests: Buffer provider reference implementation.
|
||||
*
|
||||
|
|
@ -53,18 +61,11 @@ namespace engine {
|
|||
: boost::noncopyable
|
||||
{
|
||||
|
||||
/**
|
||||
* simple BufferProvider implementation
|
||||
* with additional allocation tracking
|
||||
*/
|
||||
class HeapMemProvider;
|
||||
|
||||
|
||||
boost::scoped_ptr<HeapMemProvider> pImpl_;
|
||||
boost::scoped_ptr<TrackingHeapBlockProvider> pImpl_;
|
||||
static lib::Singleton<DiagnosticBufferProvider> diagnostics;
|
||||
|
||||
|
||||
HeapMemProvider& reset();
|
||||
TrackingHeapBlockProvider& reset();
|
||||
bool isCurrent (BufferProvider const&);
|
||||
|
||||
|
||||
|
|
@ -98,22 +99,6 @@ namespace engine {
|
|||
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:
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace engine{
|
|||
CalcStream
|
||||
EngineService::calculate(ModelPort mPort,
|
||||
Timings nominalTimings,
|
||||
OutputConnection output,
|
||||
OutputConnection& output,
|
||||
Quality serviceQuality)
|
||||
{
|
||||
UNIMPLEMENTED ("build a standard calculation stream");
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ namespace engine{
|
|||
CalcStream
|
||||
calculate(ModelPort mPort,
|
||||
Timings nominalTimings,
|
||||
OutputConnection output,
|
||||
OutputConnection& output,
|
||||
Quality serviceQuality =QoS_DEFAULT);
|
||||
|
||||
CalcStream
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
|
||||
#include "proc/state.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/channel-descriptor.hpp"
|
||||
#include "proc/engine/bufftable-obsolete.hpp"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
#include "proc/state.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/channel-descriptor.hpp"
|
||||
#include "proc/engine/bufftable-obsolete.hpp"
|
||||
#include "proc/engine/nodeinvocation.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "proc/state.hpp"
|
||||
#include "proc/asset/proc.hpp"
|
||||
#include "proc/mobject/parameter.hpp"
|
||||
#include "proc/engine/channel-descriptor.hpp"
|
||||
#include "lib/frameid.hpp"
|
||||
#include "lib/ref-array.hpp"
|
||||
|
||||
|
|
|
|||
325
src/proc/engine/tracking-heap-block-provider.cpp
Normal file
325
src/proc/engine/tracking-heap-block-provider.cpp
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
TrackingHeapBlockProvider - plain heap allocating BufferProvider implementation for tests
|
||||
|
||||
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/bool-checkable.hpp"
|
||||
#include "lib/scoped-ptrvect.hpp"
|
||||
#include "lib/scoped-holder.hpp"
|
||||
#include "lib/util-foreach.hpp"
|
||||
|
||||
#include "proc/engine/tracking-heap-block-provider.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
//using util::for_each;
|
||||
using util::and_all;
|
||||
using std::vector;
|
||||
using lib::ScopedHolder;
|
||||
using lib::ScopedPtrVect;
|
||||
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
namespace { // implementation helpers...
|
||||
|
||||
using diagn::Block;
|
||||
|
||||
/** helper to find Block entries
|
||||
* based on their raw memory address */
|
||||
inline bool
|
||||
identifyBlock (Block const& inQuestion, void* storage)
|
||||
{
|
||||
return storage == &inQuestion;
|
||||
}
|
||||
|
||||
/** build a searching predicate */
|
||||
inline function<bool(Block const&)>
|
||||
search_for_block_using_this_storage (void* storage)
|
||||
{
|
||||
return bind (identifyBlock, _1, storage);
|
||||
}
|
||||
|
||||
template<class VEC>
|
||||
inline Block*
|
||||
pick_Block_by_storage (VEC& vec, void* blockLocation)
|
||||
{
|
||||
typename VEC::iterator pos
|
||||
= std::find_if (vec.begin(),vec.end()
|
||||
,search_for_block_using_this_storage(blockLocation));
|
||||
if (pos!=vec.end())
|
||||
return &(*pos);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace diagn {
|
||||
|
||||
typedef ScopedPtrVect<Block> PoolVec;
|
||||
typedef ScopedHolder<PoolVec> PoolHolder;
|
||||
|
||||
/**
|
||||
* @internal Pool of allocated buffer Blocks of a specific size.
|
||||
* Helper for implementing a Diagnostic BufferProvider; actually does
|
||||
* just heap allocations for the Blocks, but keeps a collection of
|
||||
* allocated Blocks around. Individual entries can be retrieved
|
||||
* and thus removed from the responsibility of BlockPool.
|
||||
*
|
||||
* The idea is that each buffer starts its lifecycle within some pool
|
||||
* and later gets "emitted" to an output sequence, where it remains for
|
||||
* later investigation and diagnostics.
|
||||
*/
|
||||
class BlockPool
|
||||
: public lib::BoolCheckable<BlockPool>
|
||||
{
|
||||
uint maxAllocCount_;
|
||||
size_t memBlockSize_;
|
||||
PoolHolder blockList_;
|
||||
|
||||
public:
|
||||
BlockPool()
|
||||
: maxAllocCount_(0) // unlimited by default
|
||||
, memBlockSize_(0)
|
||||
, blockList_()
|
||||
{ }
|
||||
|
||||
void
|
||||
initialise (size_t blockSize)
|
||||
{
|
||||
blockList_.create();
|
||||
memBlockSize_ = blockSize;
|
||||
}
|
||||
// standard copy operations are valid, but will
|
||||
// raise an runtime error, once BlockPool is initialised.
|
||||
|
||||
~BlockPool()
|
||||
{
|
||||
if (!verify_all_children_idle())
|
||||
ERROR (test, "Block actively in use while shutting down BufferProvider "
|
||||
"allocation pool. This might lead to Segfault and memory leaks.");
|
||||
}
|
||||
|
||||
|
||||
uint
|
||||
prepare_for (uint number_of_expected_buffers)
|
||||
{
|
||||
if (maxAllocCount_ &&
|
||||
maxAllocCount_ < blockList_->size() + number_of_expected_buffers)
|
||||
{
|
||||
ASSERT (maxAllocCount_ >= blockList_->size());
|
||||
return maxAllocCount_ - blockList_->size();
|
||||
}
|
||||
// currently no hard limit imposed
|
||||
return number_of_expected_buffers;
|
||||
}
|
||||
|
||||
|
||||
Block&
|
||||
createBlock()
|
||||
{
|
||||
return blockList_->manage (new Block(memBlockSize_));
|
||||
}
|
||||
|
||||
|
||||
Block*
|
||||
find (void* blockLocation)
|
||||
{
|
||||
return pick_Block_by_storage (*blockList_, blockLocation);
|
||||
}
|
||||
|
||||
|
||||
Block*
|
||||
transferResponsibility (Block* allocatedBlock)
|
||||
{
|
||||
return blockList_->detach (allocatedBlock);
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
return blockList_->size();
|
||||
}
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return blockList_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool
|
||||
verify_all_children_idle()
|
||||
{
|
||||
try {
|
||||
if (blockList_)
|
||||
return and_all (*blockList_, is_in_sane_state);
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (test, "State verification of diagnostic BufferProvider allocation pool");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
is_in_sane_state (Block const& block)
|
||||
{
|
||||
return !block.was_used()
|
||||
|| block.was_closed();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace { // Details of allocation and accounting
|
||||
|
||||
const uint MAX_BUFFERS = 50;
|
||||
|
||||
diagn::Block emptyPlaceholder(0);
|
||||
|
||||
} // (END) Details of allocation and accounting
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @internal create a memory tracking BufferProvider,
|
||||
*/
|
||||
TrackingHeapBlockProvider::TrackingHeapBlockProvider()
|
||||
: BufferProvider ("Diagnostic_HeapAllocated")
|
||||
, pool_(new diagn::PoolTable)
|
||||
, outSeq_()
|
||||
{ }
|
||||
|
||||
TrackingHeapBlockProvider::~TrackingHeapBlockProvider()
|
||||
{
|
||||
INFO (proc_mem, "discarding %zu diagnostic buffer entries", outSeq_.size());
|
||||
}
|
||||
|
||||
|
||||
/* ==== Implementation of the BufferProvider interface ==== */
|
||||
|
||||
uint
|
||||
TrackingHeapBlockProvider::prepareBuffers(uint requestedAmount, HashVal typeID)
|
||||
{
|
||||
diagn::BlockPool& responsiblePool = getBlockPoolFor (typeID);
|
||||
return responsiblePool.prepare_for (requestedAmount);
|
||||
}
|
||||
|
||||
|
||||
BuffHandle
|
||||
TrackingHeapBlockProvider::provideLockedBuffer(HashVal typeID)
|
||||
{
|
||||
diagn::BlockPool& blocks = getBlockPoolFor (typeID);
|
||||
diagn::Block& newBlock = blocks.createBlock();
|
||||
return buildHandle (typeID, newBlock.accessMemory(), &newBlock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackingHeapBlockProvider::mark_emitted (HashVal typeID, LocalKey const& implID)
|
||||
{
|
||||
diagn::Block* block4buffer = locateBlock (typeID, implID);
|
||||
if (!block4buffer)
|
||||
throw error::Logic ("Attempt to emit a buffer not known to this BufferProvider"
|
||||
, LUMIERA_ERROR_BUFFER_MANAGEMENT);
|
||||
diagn::BlockPool& pool = getBlockPoolFor (typeID);
|
||||
outSeq_.manage (pool.transferResponsibility (block4buffer));
|
||||
}
|
||||
|
||||
|
||||
/** mark a buffer as officially discarded */
|
||||
void
|
||||
TrackingHeapBlockProvider::detachBuffer (HashVal typeID, LocalKey const& implID)
|
||||
{
|
||||
diagn::Block* block4buffer = locateBlock (typeID, implID);
|
||||
REQUIRE (block4buffer, "releasing a buffer not allocated through this provider");
|
||||
block4buffer->markReleased();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ==== Implementation details ==== */
|
||||
|
||||
size_t
|
||||
TrackingHeapBlockProvider::emittedCnt() const
|
||||
{
|
||||
return outSeq_.size();
|
||||
}
|
||||
|
||||
diagn::Block&
|
||||
TrackingHeapBlockProvider::access_emitted (uint bufferID)
|
||||
{
|
||||
if (!withinOutputSequence (bufferID))
|
||||
return emptyPlaceholder;
|
||||
else
|
||||
return outSeq_[bufferID];
|
||||
}
|
||||
|
||||
bool
|
||||
TrackingHeapBlockProvider::withinOutputSequence (uint bufferID) const
|
||||
{
|
||||
if (bufferID >= MAX_BUFFERS)
|
||||
throw error::Fatal ("hardwired internal limit for test buffers exceeded");
|
||||
|
||||
return bufferID < outSeq_.size();
|
||||
}
|
||||
|
||||
diagn::BlockPool&
|
||||
TrackingHeapBlockProvider::getBlockPoolFor (HashVal typeID)
|
||||
{
|
||||
diagn::BlockPool& pool = (*pool_)[typeID];
|
||||
if (!pool)
|
||||
pool.initialise(getBufferSize(typeID));
|
||||
return pool;
|
||||
}
|
||||
|
||||
diagn::Block*
|
||||
TrackingHeapBlockProvider::locateBlock (HashVal typeID, void* storage)
|
||||
{
|
||||
diagn::BlockPool& pool = getBlockPoolFor (typeID);
|
||||
diagn::Block* block4buffer = pool.find (storage);
|
||||
return block4buffer? block4buffer
|
||||
: searchInOutSeqeuence (storage);
|
||||
}
|
||||
|
||||
diagn::Block*
|
||||
TrackingHeapBlockProvider::searchInOutSeqeuence (void* blockLocation)
|
||||
{
|
||||
return pick_Block_by_storage (outSeq_, blockLocation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
187
src/proc/engine/tracking-heap-block-provider.hpp
Normal file
187
src/proc/engine/tracking-heap-block-provider.hpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
TRACKING-HEAP-BLOCK-PROVIDER.hpp - plain heap allocating BufferProvider implementation for tests
|
||||
|
||||
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 tracking-heap-block-provider.hpp
|
||||
** Dummy implementation of the BufferProvider interface to support writing unit tests.
|
||||
** This BufferProvider is especially straight forward and brain dead: it just claims
|
||||
** more and more heap blocks and never releases any memory dynamically. This allows
|
||||
** to investigate additional tracking status flags for each allocated block after
|
||||
** the fact.
|
||||
**
|
||||
** The allocated buffers are numbered with a simple ascending sequence of integers,
|
||||
** used as LocalKey (see BufferMetadata). Clients can just request a Buffer with the
|
||||
** given number, causing that block to be allocated. There is a "backdoor", allowing
|
||||
** to access any allocated block, even if it is considered "released" by the terms
|
||||
** of the usual lifecycle. Only when the provider object itself gets destroyed,
|
||||
** all allocated blocks will be discarded.
|
||||
**
|
||||
** @see DiagnosticOutputSlot
|
||||
** @see DiagnosticBufferProvider
|
||||
** @see buffer-provider-protocol-test.cpp
|
||||
*/
|
||||
|
||||
#ifndef PROC_ENGINE_TRACKING_HEAP_BLOCK_PROVIDER_H
|
||||
#define PROC_ENGINE_TRACKING_HEAP_BLOCK_PROVIDER_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/engine/buffer-provider.hpp"
|
||||
#include "lib/scoped-ptrvect.hpp"
|
||||
|
||||
#include <tr1/unordered_map>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
using lib::ScopedPtrVect;
|
||||
|
||||
namespace diagn {
|
||||
|
||||
using boost::scoped_ptr;
|
||||
using boost::scoped_array;
|
||||
|
||||
|
||||
/**
|
||||
* Helper for a diagnostic BufferProvider:
|
||||
* A block of heap allocated storage, with the capability
|
||||
* to store some additional tracking information.
|
||||
*/
|
||||
class Block
|
||||
: boost::noncopyable
|
||||
{
|
||||
scoped_array<char> storage_;
|
||||
|
||||
bool was_released_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
Block(size_t bufferSize)
|
||||
: storage_(bufferSize? new char[bufferSize] : NULL)
|
||||
, was_released_(false)
|
||||
{ }
|
||||
|
||||
bool
|
||||
was_used() const
|
||||
{
|
||||
return bool(storage_);
|
||||
}
|
||||
|
||||
bool
|
||||
was_closed() const
|
||||
{
|
||||
return was_released_;
|
||||
}
|
||||
|
||||
void*
|
||||
accessMemory() const
|
||||
{
|
||||
REQUIRE (storage_, "Block was never prepared for use");
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
void
|
||||
markReleased()
|
||||
{
|
||||
was_released_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
class BlockPool;
|
||||
|
||||
typedef std::tr1::unordered_map<HashVal,BlockPool> PoolTable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* simple BufferProvider implementation with additional allocation tracking.
|
||||
* @internal used as PImpl by DiagnosticBufferProvider and DiagnosticOutputSlot.
|
||||
*
|
||||
* This dummy implementation of the BufferProvider interface uses a linearly growing
|
||||
* table of heap allocated buffer blocks, which will never be discarded, unless the object
|
||||
* is discarded as a whole. There is an additional testing/diagnostics API to access the
|
||||
* tracked usage information, even when blocks are already marked as "released".
|
||||
*/
|
||||
class TrackingHeapBlockProvider
|
||||
: public BufferProvider
|
||||
{
|
||||
scoped_ptr<diagn::PoolTable> pool_;
|
||||
ScopedPtrVect<diagn::Block> outSeq_;
|
||||
|
||||
public:
|
||||
/* === BufferProvider interface === */
|
||||
|
||||
virtual uint prepareBuffers (uint count, HashVal typeID);
|
||||
virtual BuffHandle provideLockedBuffer (HashVal typeID);
|
||||
virtual void mark_emitted (HashVal entryID, LocalKey const&);
|
||||
virtual void detachBuffer (HashVal entryID, LocalKey const&);
|
||||
|
||||
public:
|
||||
TrackingHeapBlockProvider();
|
||||
virtual ~TrackingHeapBlockProvider();
|
||||
|
||||
size_t emittedCnt() const;
|
||||
|
||||
diagn::Block& access_emitted (uint bufferID);
|
||||
|
||||
template<typename TY>
|
||||
TY& accessAs (uint bufferID);
|
||||
|
||||
private:
|
||||
bool withinOutputSequence (uint bufferID) const;
|
||||
diagn::BlockPool& getBlockPoolFor (HashVal typeID);
|
||||
diagn::Block* locateBlock (HashVal typeID, void*);
|
||||
diagn::Block* searchInOutSeqeuence (void* storage);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** convenience shortcut: access the buffer with the given number,
|
||||
* then try to convert the raw memory to the templated type.
|
||||
* @throw error::Invalid if the required fame number is beyond
|
||||
* the number of buffers marked as "emitted"
|
||||
* @throw error::Fatal if conversion is not possible or the
|
||||
* conversion path chosen doesn't work (which might
|
||||
* be due to RTTI indicating an incompatible type).
|
||||
*/
|
||||
template<typename TY>
|
||||
TY&
|
||||
TrackingHeapBlockProvider::accessAs (uint bufferID)
|
||||
{
|
||||
if (!withinOutputSequence (bufferID))
|
||||
throw error::Invalid ("Buffer with the given ID not yet emitted");
|
||||
|
||||
diagn::Block& memoryBlock = access_emitted (bufferID);
|
||||
TY* converted = reinterpret_cast<TY*> (memoryBlock.accessMemory());
|
||||
|
||||
REQUIRE (converted);
|
||||
return *converted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
182
src/proc/engine/type-handler.hpp
Normal file
182
src/proc/engine/type-handler.hpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
TYPE-HANDLER.hpp - a functor pair for setup and destruction
|
||||
|
||||
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 type-handler.hpp
|
||||
** Helper holding a pair of type-build-up and destruction functors.
|
||||
** Basically these two functors embody all type specific knowledge required
|
||||
** to place an object into some buffer space and to clean up later. They may even
|
||||
** be used in a more unspecific way, e.g. just to "prepare" a buffer or frame and to
|
||||
** "clean up" after usage.
|
||||
**
|
||||
** Within the Lumiera Engine, the BufferProvider default implementation utilises instances
|
||||
** of TypeHandler to \em describe specific buffer types capable of managing an attached object,
|
||||
** or requiring some other kind of special treatment of the memory area used for the buffer.
|
||||
** This BufferDescriptor is embodied into the BufferMetadata::Key and used later on to invoke
|
||||
** the contained ctor / dtor functors, passing a concrete buffer (memory area).
|
||||
**
|
||||
** @see buffer-metadata.hpp
|
||||
** @see buffer-provider.hpp
|
||||
** @see BufferMetadataKey_test#verifyTypeHandler unit-test
|
||||
*/
|
||||
|
||||
#ifndef PROC_ENGINE_TYPE_HANDLER_H
|
||||
#define PROC_ENGINE_TYPE_HANDLER_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/functor-util.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
|
||||
namespace engine {
|
||||
|
||||
using lib::HashVal;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::function;
|
||||
using std::tr1::placeholders::_1;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
namespace { // (optional) helpers to build an object embedded into a 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 a buffer.
|
||||
* TypeHandler describes how to outfit the buffer in a specific way.
|
||||
* Special convenience builder function(s) are provided to create a
|
||||
* TypeHandler performing placement-new into a buffer given on invocation.
|
||||
* @note engine::BufferMetadata uses a TypeHandler to represent any
|
||||
* special treatment of a buffer space. When defined, the buffer
|
||||
* will be prepared on locking and cleanup will be invoked
|
||||
* automatically when releasing.
|
||||
* @warning comparison and hash values rely on internals of the
|
||||
* tr1::function implementation and might not be 100% accurate
|
||||
*/
|
||||
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 engine
|
||||
#endif
|
||||
|
|
@ -90,7 +90,7 @@ namespace test {
|
|||
string
|
||||
display(Symbol name) const
|
||||
{
|
||||
static format fmt("%s(ID=%4d)");
|
||||
static format fmt("%s(ID=%03d)");
|
||||
return boost::str(fmt % name % this->id_);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
212
src/proc/play/output-slot-connection.hpp
Normal file
212
src/proc/play/output-slot-connection.hpp
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
OUTPUT-SLOT-CONNECTION.hpp - implementation API for concrete output slots
|
||||
|
||||
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 output-slot-connection.hpp
|
||||
** Interface for concrete output implementations to talk to the OutputSlot frontend.
|
||||
** The OutputSlot concept helps to decouple the render engine implementation from the details
|
||||
** of handling external output connections. For this to work, a concrete implementation of such
|
||||
** an external output needs to integrate with the generic OutputSlot frontend, as used by the
|
||||
** engine. This generic frontend uses a PImpl, pointing to a ConnectionState object, which embodies
|
||||
** the actual implementation. Moreover, this actual implementation is free to use specifically crafted
|
||||
** OutputSlot::Connection elements to handle the ongoing output for individual channels. The latter
|
||||
** thus becomes the central implementation side API for providing actual output capabilities.
|
||||
**
|
||||
** @see OutputSlotProtocol_test
|
||||
** @see diagnostic-output-slot.hpp ////TODO
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROC_PLAY_OUTPUT_SLOT_CONNECTION_H
|
||||
#define PROC_PLAY_OUTPUT_SLOT_CONNECTION_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/play/output-slot.hpp"
|
||||
#include "lib/handle.hpp"
|
||||
//#include "lib/time/timevalue.hpp"
|
||||
//#include "proc/engine/buffer-provider.hpp"
|
||||
//#include "proc/play/timings.hpp"
|
||||
#include "lib/iter-source.hpp"
|
||||
#include "lib/iter-adapter-stl.hpp"
|
||||
//#include "lib/sync.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
//#include <string>
|
||||
//#include <vector>
|
||||
//#include <tr1/memory>
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace play {
|
||||
|
||||
using ::engine::BuffHandle;
|
||||
//using ::engine::BufferProvider;
|
||||
//using lib::time::Time;
|
||||
//using std::string;
|
||||
using lib::transform;
|
||||
using lib::iter_stl::eachElm;
|
||||
|
||||
//using std::vector;
|
||||
//using std::tr1::shared_ptr;
|
||||
using boost::scoped_ptr;
|
||||
|
||||
|
||||
|
||||
|
||||
/** @internal represents the \em active
|
||||
* point in each of the per-channel connections
|
||||
* used when this OutputSlot is operational.
|
||||
*
|
||||
* \par OutputSlot Core API
|
||||
*
|
||||
* Actually, this extension point towards the implementation
|
||||
* of the actual output handling carries the core API of OutputSlot.
|
||||
* Thus, the task of actually implementing an OutputSlot boils down
|
||||
* to implementing this interface and providing a ConnectionState.
|
||||
* - \c lock() announces this FrameID and the corresponding buffer
|
||||
* to be in exclusive use by the client from now on
|
||||
* - \c transfer() ends the client sided processing and initiates
|
||||
* the outputting of the data found in the corresponding buffer.
|
||||
* - \c pushout() actually pushes the denoted buffer to the output.
|
||||
* Typically, \c pushout() is called from the \c transfer()
|
||||
* implementation; yet it may as well be called from a separate
|
||||
* service thread or some kind of callback.
|
||||
* @note the meaning of FrameID is implementation defined.
|
||||
*/
|
||||
class OutputSlot::Connection
|
||||
{
|
||||
public:
|
||||
virtual ~Connection();
|
||||
|
||||
virtual BuffHandle claimBufferFor(FrameID) =0;
|
||||
virtual bool isTimely (FrameID, TimeValue) =0;
|
||||
virtual void transfer (BuffHandle const&) =0;
|
||||
virtual void pushout (BuffHandle const&) =0;
|
||||
virtual void discard (BuffHandle const&) =0;
|
||||
virtual void shutDown () =0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Extension point for Implementation.
|
||||
* The ConnectionState is where the concrete output
|
||||
* handling implementation is expected to reside.
|
||||
* OutputSlot is a frontend and accesses
|
||||
* ConnectionState in the way of a PImpl.
|
||||
*/
|
||||
class OutputSlot::ConnectionState
|
||||
: public OutputSlot::Allocation
|
||||
, boost::noncopyable
|
||||
{
|
||||
public:
|
||||
virtual ~ConnectionState() { }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base class for the typical implementation approach.
|
||||
* Using this class is \em not mandatory. But obviously,
|
||||
* we'd get to manage a selection of Connection objects
|
||||
* representing the "active points" in several media channels
|
||||
* connected through this OutputSlot. These Connection subclasses
|
||||
* are what is referenced by the DataSink smart-ptrs handed out
|
||||
* to the client code. As ConnectionState implements the Allocation
|
||||
* API, it has the liability to create these DataSink smart-ptrs,
|
||||
* which means to wire them appropriately and also provide an
|
||||
* deleter function (here #shutdownConnection) to be invoked
|
||||
* when the last copy of the smart-handle goes out of scope.
|
||||
*
|
||||
* The typical standard/base implementation provided here
|
||||
* manages a collection of active Connection subclass objects.
|
||||
*/
|
||||
template<class CON>
|
||||
class ConnectionStateManager
|
||||
: public OutputSlot::ConnectionState
|
||||
, public vector<CON>
|
||||
{
|
||||
|
||||
typedef OutputSlot::OpenedSinks OpenedSinks;
|
||||
|
||||
|
||||
/* == Allocation Interface == */
|
||||
|
||||
OpenedSinks
|
||||
getOpenedSinks()
|
||||
{
|
||||
REQUIRE (this->isActive());
|
||||
return transform (eachElm(*this), connectOutputSink);
|
||||
}
|
||||
|
||||
bool
|
||||
isActive()
|
||||
{
|
||||
return 0 < vector<CON>::size();
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
ConnectionStateManager()
|
||||
{ }
|
||||
|
||||
virtual
|
||||
~ConnectionStateManager()
|
||||
{ }
|
||||
|
||||
|
||||
void
|
||||
init (uint numChannels)
|
||||
{
|
||||
for (uint i=0; i<numChannels; ++i)
|
||||
push_back(buildConnection());
|
||||
}
|
||||
|
||||
|
||||
/** factory function to build the actual
|
||||
* connection handling objects per channel */
|
||||
virtual CON buildConnection() =0;
|
||||
|
||||
|
||||
private: // Implementation details
|
||||
|
||||
static DataSink
|
||||
connectOutputSink (CON& connection)
|
||||
{
|
||||
DataSink newSink;
|
||||
newSink.activate(&connection, shutdownConnection);
|
||||
return newSink;
|
||||
}
|
||||
|
||||
static void
|
||||
shutdownConnection (OutputSlot::Connection* toClose)
|
||||
{
|
||||
REQUIRE (toClose);
|
||||
toClose->shutDown();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace proc::play
|
||||
#endif
|
||||
|
|
@ -21,13 +21,21 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/play/output-slot.hpp"
|
||||
#include "proc/play/output-slot-connection.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace proc {
|
||||
namespace play {
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
|
||||
namespace { // hidden local details of the service implementation....
|
||||
|
|
@ -36,8 +44,17 @@ namespace play {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
OutputSlot::~OutputSlot() { } // emit VTables here....
|
||||
|
||||
OutputSlot::Allocation::~Allocation() { }
|
||||
|
||||
OutputSlot::Connection::~Connection() { }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** whether this output slot is occupied
|
||||
|
|
@ -47,9 +64,53 @@ namespace play {
|
|||
bool
|
||||
OutputSlot::isFree() const
|
||||
{
|
||||
UNIMPLEMENTED ("connection state");
|
||||
return ! this->state_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
OutputSlot::Allocation&
|
||||
OutputSlot::allocate()
|
||||
{
|
||||
if (!isFree())
|
||||
throw error::Logic ("Attempt to open/allocate an OutputSlot already in use.");
|
||||
|
||||
UNIMPLEMENTED ("internal interface to determine the number of channel-connections");
|
||||
|
||||
state_.reset (this->buildState());
|
||||
return *state_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OutputSlot::disconnect()
|
||||
{
|
||||
if (!isFree())
|
||||
state_.reset(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* === DataSink frontend === */
|
||||
|
||||
BuffHandle
|
||||
DataSink::lockBufferFor(FrameID frameNr)
|
||||
{
|
||||
return impl().claimBufferFor(frameNr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DataSink::emit (FrameID frameNr, BuffHandle const& data2emit, TimeValue currentTime)
|
||||
{
|
||||
OutputSlot::Connection& connection = impl();
|
||||
if (connection.isTimely(frameNr,currentTime))
|
||||
connection.transfer(data2emit);
|
||||
else
|
||||
connection.discard(data2emit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@
|
|||
//#include "lib/sync.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
//#include <string>
|
||||
//#include <vector>
|
||||
//#include <tr1/memory>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
|
||||
|
||||
namespace proc {
|
||||
|
|
@ -56,29 +56,18 @@ namespace play {
|
|||
|
||||
using ::engine::BuffHandle;
|
||||
using ::engine::BufferProvider;
|
||||
using lib::time::Time;
|
||||
using lib::time::TimeValue;
|
||||
//using std::string;
|
||||
|
||||
//using std::vector;
|
||||
//using std::tr1::shared_ptr;
|
||||
//using boost::scoped_ptr;
|
||||
using boost::scoped_ptr;
|
||||
|
||||
|
||||
/** established output channel */
|
||||
class Connection;
|
||||
|
||||
typedef int64_t FrameNr;
|
||||
|
||||
|
||||
class DataSink
|
||||
: public lib::Handle<Connection>
|
||||
{
|
||||
|
||||
public:
|
||||
BuffHandle lockBufferFor(FrameNr);
|
||||
void emit(FrameNr);
|
||||
};
|
||||
class DataSink;
|
||||
|
||||
typedef int64_t FrameID;
|
||||
|
||||
|
||||
|
||||
|
|
@ -90,32 +79,46 @@ namespace play {
|
|||
class OutputSlot
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
/** Table to maintain connection state */
|
||||
class ConnectionState;
|
||||
|
||||
scoped_ptr<ConnectionState> state_;
|
||||
|
||||
virtual ConnectionState* buildState() =0;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~OutputSlot();
|
||||
|
||||
typedef lib::IterSource<DataSink>::iterator OpenedSinks;
|
||||
|
||||
struct Allocation
|
||||
class Allocation
|
||||
{
|
||||
OpenedSinks getOpenedSinks();
|
||||
|
||||
bool isActive();
|
||||
public:
|
||||
virtual OpenedSinks getOpenedSinks() =0;
|
||||
virtual bool isActive() =0;
|
||||
|
||||
/////TODO add here the getters for timing constraints
|
||||
protected:
|
||||
~Allocation();
|
||||
};
|
||||
|
||||
/** established output channel */
|
||||
class Connection;
|
||||
|
||||
|
||||
/** can this OutputSlot be allocated? */
|
||||
bool isFree() const;
|
||||
|
||||
Allocation
|
||||
allocate();
|
||||
/** claim this slot for exclusive use */
|
||||
Allocation& allocate();
|
||||
|
||||
protected:
|
||||
friend class DataSink;
|
||||
|
||||
virtual void lock (FrameNr, uint channel) =0;
|
||||
virtual void transfer (FrameNr, uint channel) =0;
|
||||
virtual void pushout (FrameNr, uint channel) =0;
|
||||
/** disconnect from this OutputSlot
|
||||
* @warning may block until DataSinks are gone */
|
||||
void disconnect();
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -123,5 +126,18 @@ namespace play {
|
|||
|
||||
|
||||
|
||||
class DataSink
|
||||
: public lib::Handle<OutputSlot::Connection>
|
||||
{
|
||||
|
||||
public:
|
||||
BuffHandle lockBufferFor(FrameID);
|
||||
void emit(FrameID, BuffHandle const&, TimeValue currentTime = Time::MAX); ///////////////TICKET #855
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace proc::play
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,53 +20,31 @@
|
|||
// 1/11 - integer floor and wrap operation(s)
|
||||
// 1/11 - how to fetch the path of the own executable -- at least under Linux?
|
||||
// 10/11 - simple demo using a pointer and a struct
|
||||
// 11/11 - using the boost random number generator(s)
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/random/linear_congruential.hpp>
|
||||
|
||||
using boost::lexical_cast;
|
||||
|
||||
using std::cout;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* custom datastructure
|
||||
* holding a constant char array with "hey"
|
||||
*/
|
||||
struct MyStruct
|
||||
{
|
||||
char data_[3];
|
||||
const int length_;
|
||||
|
||||
MyStruct()
|
||||
: length_(3)
|
||||
{
|
||||
const char *tmp = "hey";
|
||||
for (int i=0; i<length_; ++i)
|
||||
data_[i] = *(tmp+i);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// define a global variable holding a MyStruct
|
||||
MyStruct theStruct;
|
||||
|
||||
|
||||
|
||||
void
|
||||
printMyStruct(MyStruct* myPointer)
|
||||
{
|
||||
for (int i=0; i < myPointer->length_; ++i)
|
||||
cout << myPointer->data_[i];
|
||||
|
||||
cout << "\n";
|
||||
}
|
||||
using std::endl;
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int, char**) //(int argc, char* argv[])
|
||||
main (int cnt, char* argv[])
|
||||
{
|
||||
printMyStruct (&theStruct);
|
||||
|
||||
int32_t seed = (2 == cnt)? lexical_cast<int32_t> (argv[1]) : 42;
|
||||
|
||||
boost::rand48 ranGen(seed);
|
||||
|
||||
cout << "seed = "<< seed << endl;
|
||||
for (uint i=0; i< 100; ++i)
|
||||
cout << ranGen() % CHAR_MAX <<"__";
|
||||
|
||||
cout << "\n.gulp.\n";
|
||||
|
||||
|
|
|
|||
|
|
@ -96,9 +96,9 @@ out: Placement.+mobject.+test.+TestSubMO1.+. use-cnt=1 .*
|
|||
out: Placement.+mobject.+test.+TestSubMO2.+. use-cnt=1 .*
|
||||
out: Placement.+mobject.+test.+TestSubMO21.+ use-cnt=2 .*
|
||||
out: Placement.+mobject.+session.+Clip.+.... use-cnt=2 .*
|
||||
out: TestSubMO1\(ID= [0-9]{3}\)
|
||||
out: TestSubMO2\(ID= [0-9]{3}\)
|
||||
out: TestSubMO21\(ID= [0-9]{3}\)
|
||||
out: TestSubMO1\(ID=[0-9]{3}\)
|
||||
out: TestSubMO2\(ID=[0-9]{3}\)
|
||||
out: TestSubMO21\(ID=[0-9]{3}\)
|
||||
out: specialAPI()
|
||||
out: pID\(\w{6,16}\)
|
||||
END
|
||||
|
|
|
|||
|
|
@ -2,16 +2,27 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en
|
|||
|
||||
|
||||
|
||||
PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END
|
||||
TEST "Test support: dummy frames" TestFrame_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
PLANNED "buffer metadata type keys" BufferMetadataKey_test <<END
|
||||
TEST "Test support: dummy buffer provider" TrackingHeapBlockProvider_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
PLANNED "buffer metadata and state transitions" BufferMetadata_test <<END
|
||||
TEST "Buffer provider diagnostics" BufferProviderProtocol_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "buffer metadata type keys" BufferMetadataKey_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "buffer metadata and state transitions" BufferMetadata_test <<END
|
||||
END
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace asset {
|
|||
* or resort to template metaprogramming tricks.
|
||||
* Just providing templated comparison operators
|
||||
* would generally override the behaviour of
|
||||
* boost::shared_ptr, which is not desirable.
|
||||
* std::shared_ptr, which is not desirable.
|
||||
* @see Asset::Ident#compare
|
||||
*/
|
||||
class OrderingOfAssets_test : public Test
|
||||
|
|
|
|||
|
|
@ -167,6 +167,13 @@ namespace test {
|
|||
|
||||
// serialise, then de-serialise into a new instance and compare both
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
twoRandomDigits()
|
||||
{
|
||||
return 10 + rand() % 90;
|
||||
}
|
||||
|
||||
|
||||
} // test-helper implementation
|
||||
|
|
@ -244,7 +251,7 @@ namespace test {
|
|||
arg3->storeTuple (tuple::make (rand() % 10, TimeVar(randTime())));
|
||||
arg4->storeTuple (tuple::make (rand() % 10, TimeVar(randTime())));
|
||||
|
||||
arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), 10 + rand() % 90));
|
||||
arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), twoRandomDigits() ));
|
||||
|
||||
CHECK (!arg5->canUndo());
|
||||
|
||||
|
|
@ -333,7 +340,7 @@ namespace test {
|
|||
|
||||
// store a set of parameter values, later to be used on invocation
|
||||
args.storeTuple (
|
||||
tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), rand() % 100));
|
||||
tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), twoRandomDigits() ));
|
||||
CHECK (!isnil (args));
|
||||
cout << args << endl;
|
||||
|
||||
|
|
@ -377,7 +384,7 @@ namespace test {
|
|||
protocol << "RESET...";
|
||||
|
||||
args.storeTuple (
|
||||
tuple::make (TTime(TimeValue(123456)), Tstr("unbelievable"), rand() %100));
|
||||
tuple::make (TTime(TimeValue(123456)), Tstr("unbelievable"), twoRandomDigits() ));
|
||||
cout << "modified: " << args << endl;
|
||||
cout << "copied : " << argsCopy << endl; // holds still the old params & memento
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ namespace test {
|
|||
using boost::str;
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
using boost::ref;
|
||||
using std::tr1::ref;
|
||||
using boost::lexical_cast;
|
||||
using util::contains;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,37 +24,25 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
//#include "lib/util-foreach.hpp"
|
||||
#include "lib/util.hpp"
|
||||
//#include "proc/play/diagnostic-output-slot.hpp"
|
||||
//#include "proc/engine/testframe.hpp"
|
||||
//#include "proc/engine/diagnostic-buffer-provider.hpp"
|
||||
#include "proc/engine/buffer-metadata.hpp"
|
||||
#include "proc/engine/testframe.hpp"
|
||||
//#include "proc/engine/buffhandle.hpp"
|
||||
//#include "proc/engine/bufftable.hpp"
|
||||
|
||||
//#include <boost/format.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <cstdlib>
|
||||
//#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
//using boost::format;
|
||||
//using std::string;
|
||||
//using std::cout;
|
||||
//using util::for_each;
|
||||
using std::strncpy;
|
||||
using boost::scoped_ptr;
|
||||
using util::isnil;
|
||||
using lib::test::randStr;
|
||||
using util::isSameObject;
|
||||
using util::isnil;
|
||||
|
||||
|
||||
namespace engine{
|
||||
namespace test {
|
||||
|
||||
|
||||
// using lib::AllocationCluster;
|
||||
// using mobject::session::PEffect;
|
||||
// using ::engine::BuffHandle;
|
||||
using lumiera::error::LUMIERA_ERROR_FATAL;
|
||||
using lumiera::error::LUMIERA_ERROR_INVALID;
|
||||
using lumiera::error::LUMIERA_ERROR_LIFECYCLE;
|
||||
|
||||
|
|
@ -66,13 +54,23 @@ namespace test {
|
|||
const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
|
||||
const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
|
||||
|
||||
const HashVal JUST_SOMETHING = 123;
|
||||
const void* const SOME_POINTER = &JUST_SOMETHING;
|
||||
// const uint TEST_SIZE = 1024*1024;
|
||||
// const uint TEST_ELMS = 20;
|
||||
|
||||
HashVal JUST_SOMETHING = 123;
|
||||
void* const SOME_POINTER = &JUST_SOMETHING;
|
||||
|
||||
}
|
||||
|
||||
template<typename TY>
|
||||
TY&
|
||||
accessAs (metadata::Entry& entry)
|
||||
{
|
||||
TY* ptr = reinterpret_cast<TY*> (entry.access());
|
||||
ASSERT (ptr);
|
||||
return *ptr;
|
||||
}
|
||||
}//(End) Test fixture and helpers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
|
|
@ -83,7 +81,7 @@ namespace test {
|
|||
class BufferMetadata_test : public Test
|
||||
{
|
||||
/** common Metadata table to be tested */
|
||||
scoped_ptr<Metadata> meta_;
|
||||
scoped_ptr<BufferMetadata> meta_;
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
|
|
@ -91,7 +89,7 @@ namespace test {
|
|||
CHECK (ensure_proper_fixture());
|
||||
verifyBasicProperties();
|
||||
verifyStandardCase();
|
||||
UNIMPLEMENTED ("cover all metadata properties");
|
||||
verifyStateMachine();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -99,7 +97,7 @@ namespace test {
|
|||
ensure_proper_fixture()
|
||||
{
|
||||
if (!meta_)
|
||||
meta_.reset(new Metadata("BufferMetadata_test"));
|
||||
meta_.reset(new BufferMetadata("BufferMetadata_test"));
|
||||
|
||||
return (SIZE_A != SIZE_B)
|
||||
&& (JUST_SOMETHING != meta_->key(SIZE_A))
|
||||
|
|
@ -112,11 +110,11 @@ namespace test {
|
|||
verifyBasicProperties()
|
||||
{
|
||||
// retrieve some type keys
|
||||
Metadata::Key key = meta_->key(SIZE_A);
|
||||
metadata::Key key = meta_->key(SIZE_A);
|
||||
CHECK (key);
|
||||
|
||||
Metadata::Key key1 = meta_->key(SIZE_A);
|
||||
Metadata::Key key2 = meta_->key(SIZE_B);
|
||||
metadata::Key key1 = meta_->key(SIZE_A);
|
||||
metadata::Key key2 = meta_->key(SIZE_B);
|
||||
CHECK (key1);
|
||||
CHECK (key2);
|
||||
CHECK (key == key1);
|
||||
|
|
@ -133,16 +131,16 @@ namespace test {
|
|||
CHECK ( isSameObject (meta_->get(key), meta_->get(key1)));
|
||||
CHECK (!isSameObject (meta_->get(key), meta_->get(key2)));
|
||||
|
||||
// entries retrieved this far are inactive (type only) entries
|
||||
Metadata::Entry& m1 = meta_->get(key);
|
||||
// entries retrieved thus far were inactive (type only) entries
|
||||
metadata::Entry& m1 = meta_->get(key);
|
||||
CHECK (NIL == m1.state());
|
||||
CHECK (!meta_->isLocked(key));
|
||||
|
||||
VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
|
||||
VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED) );
|
||||
VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED));
|
||||
VERIFY_ERROR (LIFECYCLE, m1.mark(FREE) );
|
||||
|
||||
// now create an active (buffer) entry
|
||||
Metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER);
|
||||
metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER);
|
||||
CHECK (!isSameObject (m1,m2));
|
||||
CHECK (NIL == m1.state());
|
||||
CHECK (LOCKED == m2.state());
|
||||
|
|
@ -173,7 +171,7 @@ namespace test {
|
|||
CHECK ( meta_->isKnown(keyX));
|
||||
CHECK ( meta_->isKnown(key1));
|
||||
VERIFY_ERROR (LIFECYCLE, m2.access());
|
||||
VERIFY_ERROR (LIFECYCLE, m2.mark(LOCKED));
|
||||
VERIFY_ERROR (FATAL, m2.mark(LOCKED)); // buffer missing
|
||||
CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible
|
||||
|
||||
// release buffer...
|
||||
|
|
@ -189,18 +187,18 @@ namespace test {
|
|||
* @note to get the big picture, please refer to
|
||||
* BufferProviderProtocol_test#verifyStandardCase()
|
||||
* This testcase here performs precisely the metadata related
|
||||
* operations necessary to carry out the standard case outlined
|
||||
* in that more high level test.
|
||||
* operations necessary to carry out the standard case
|
||||
* outlined on a higher level in the mentioned test.
|
||||
*/
|
||||
void
|
||||
verifyStandardCase()
|
||||
{
|
||||
// to build a descriptor for a buffer holding a TestFrame
|
||||
TypeHandler attachTestFrame = TypeHandler::create<TestFrame>();
|
||||
Metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame);
|
||||
metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame);
|
||||
|
||||
// to build a descriptor for a raw buffer of size SIZE_B
|
||||
Metadata::Key rawBuffType = meta_->key(SIZE_B);
|
||||
metadata::Key rawBuffType = meta_->key(SIZE_B);
|
||||
|
||||
// to announce using a number of buffers of this type
|
||||
LocalKey transaction1(1);
|
||||
|
|
@ -219,23 +217,25 @@ namespace test {
|
|||
// a real-world BufferProvider would use some kind of allocator
|
||||
|
||||
// track individual buffers by metadata entries
|
||||
Metadata::Entry f0 = meta_->markLocked(bufferType1, &frames[0]);
|
||||
Metadata::Entry f1 = meta_->markLocked(bufferType1, &frames[1]);
|
||||
Metadata::Entry f2 = meta_->markLocked(bufferType1, &frames[2]);
|
||||
metadata::Entry& f0 = meta_->markLocked(bufferType1, &frames[0]);
|
||||
metadata::Entry& f1 = meta_->markLocked(bufferType1, &frames[1]);
|
||||
metadata::Entry& f2 = meta_->markLocked(bufferType1, &frames[2]);
|
||||
|
||||
metadata::Entry& r0 = meta_->markLocked(rawBuffType, &rawbuf[0]);
|
||||
metadata::Entry& r1 = meta_->markLocked(rawBuffType, &rawbuf[1]);
|
||||
|
||||
Metadata::Entry r0 = meta_->markLocked(bufferType1, &rawbuf[0]);
|
||||
Metadata::Entry r1 = meta_->markLocked(bufferType1, &rawbuf[1]);
|
||||
|
||||
CHECK (LOCKED == f0.state());
|
||||
CHECK (LOCKED == f1.state());
|
||||
CHECK (LOCKED == f2.state());
|
||||
CHECK (LOCKED == r0.state());
|
||||
CHECK (LOCKED == r1.state());
|
||||
|
||||
// for the TestFrame buffers, additionally we'd have to create/attach an object
|
||||
attachTestFrame.createAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically??
|
||||
attachTestFrame.createAttached (frames+1);
|
||||
attachTestFrame.createAttached (frames+2);
|
||||
CHECK (transaction1 == f0.localKey());
|
||||
CHECK (transaction1 == f1.localKey());
|
||||
CHECK (transaction1 == f2.localKey());
|
||||
CHECK (transaction2 == r0.localKey());
|
||||
CHECK (transaction2 == r1.localKey());
|
||||
|
||||
|
||||
CHECK (f0.access() == frames+0);
|
||||
CHECK (f1.access() == frames+1);
|
||||
|
|
@ -243,6 +243,11 @@ namespace test {
|
|||
CHECK (r0.access() == rawbuf+0);
|
||||
CHECK (r1.access() == rawbuf+1);
|
||||
|
||||
TestFrame defaultFrame;
|
||||
CHECK (defaultFrame == f0.access());
|
||||
CHECK (defaultFrame == f1.access());
|
||||
CHECK (defaultFrame == f2.access());
|
||||
|
||||
// at that point, we'd return BuffHandles to the client
|
||||
HashVal handle_f0(f0);
|
||||
HashVal handle_f1(f1);
|
||||
|
|
@ -250,14 +255,35 @@ namespace test {
|
|||
HashVal handle_r0(r0);
|
||||
HashVal handle_r1(r1);
|
||||
|
||||
// client uses the buffers
|
||||
// client uses the buffers---------------------(Start)
|
||||
accessAs<TestFrame> (f0) = testData(1);
|
||||
accessAs<TestFrame> (f1) = testData(2);
|
||||
accessAs<TestFrame> (f2) = testData(3);
|
||||
|
||||
//////////////////TODO: access the storage through the metadata-key
|
||||
//////////////////TODO: to a state transition on the metadata
|
||||
CHECK (testData(1) == frames[0]);
|
||||
CHECK (testData(2) == frames[1]);
|
||||
CHECK (testData(3) == frames[2]);
|
||||
|
||||
CHECK (TestFrame::isAlive (f0.access()));
|
||||
CHECK (TestFrame::isAlive (f1.access()));
|
||||
CHECK (TestFrame::isAlive (f2.access()));
|
||||
|
||||
strncpy (& accessAs<char> (r0), randStr(SIZE_B - 1).c_str(), SIZE_B);
|
||||
strncpy (& accessAs<char> (r1), randStr(SIZE_B - 1).c_str(), SIZE_B);
|
||||
|
||||
// client might trigger some state transitions
|
||||
f0.mark(EMITTED);
|
||||
f1.mark(EMITTED);
|
||||
f1.mark(BLOCKED);
|
||||
// client uses the buffers---------------------(End)
|
||||
|
||||
|
||||
f0.mark(FREE); // note: implicitly invoking the embedded dtor
|
||||
f1.mark(FREE);
|
||||
f2.mark(FREE);
|
||||
r0.mark(FREE);
|
||||
r1.mark(FREE);
|
||||
|
||||
attachTestFrame.destroyAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically??
|
||||
attachTestFrame.destroyAttached (frames+1);
|
||||
attachTestFrame.destroyAttached (frames+2);
|
||||
|
||||
meta_->release(handle_f0);
|
||||
meta_->release(handle_f1);
|
||||
|
|
@ -265,14 +291,106 @@ namespace test {
|
|||
meta_->release(handle_r0);
|
||||
meta_->release(handle_r1);
|
||||
|
||||
CHECK (TestFrame::isDead (&frames[0])); // was destroyed implicitly
|
||||
CHECK (TestFrame::isDead (&frames[1]));
|
||||
CHECK (TestFrame::isDead (&frames[2]));
|
||||
|
||||
// manual cleanup of test allocations
|
||||
delete[] frames;
|
||||
delete[] rawbuf;
|
||||
|
||||
CHECK (!meta_->isLocked(handle_f0));
|
||||
CHECK (!meta_->isLocked(handle_f1));
|
||||
CHECK (!meta_->isLocked(handle_f2));
|
||||
CHECK (!meta_->isLocked(handle_r0));
|
||||
CHECK (!meta_->isLocked(handle_r1));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyStateMachine()
|
||||
{
|
||||
// start with building a type key....
|
||||
metadata::Key key = meta_->key(SIZE_A);
|
||||
CHECK (NIL == meta_->get(key).state());
|
||||
CHECK (meta_->get(key).isTypeKey());
|
||||
CHECK (!meta_->isLocked(key));
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
|
||||
VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(LOCKED) );
|
||||
VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(EMITTED));
|
||||
VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(BLOCKED));
|
||||
VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(FREE) );
|
||||
VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(NIL) );
|
||||
|
||||
// now build a concrete buffer entry
|
||||
metadata::Entry& entry = meta_->markLocked(key, SOME_POINTER);
|
||||
CHECK (LOCKED == entry.state());
|
||||
CHECK (!entry.isTypeKey());
|
||||
|
||||
CHECK (SOME_POINTER == entry.access());
|
||||
|
||||
VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); // invalid state transition
|
||||
VERIFY_ERROR (FATAL, entry.mark(NIL) );
|
||||
|
||||
entry.mark (EMITTED); // valid transition
|
||||
CHECK (EMITTED == entry.state());
|
||||
CHECK (entry.isLocked());
|
||||
|
||||
VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
|
||||
VERIFY_ERROR (FATAL, entry.mark(EMITTED));
|
||||
VERIFY_ERROR (FATAL, entry.mark(NIL) );
|
||||
CHECK (EMITTED == entry.state());
|
||||
|
||||
entry.mark (FREE);
|
||||
CHECK (FREE == entry.state());
|
||||
CHECK (!entry.isLocked());
|
||||
CHECK (!entry.isTypeKey());
|
||||
|
||||
VERIFY_ERROR (LIFECYCLE, entry.access() );
|
||||
VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
|
||||
VERIFY_ERROR (FATAL, entry.mark(EMITTED));
|
||||
VERIFY_ERROR (FATAL, entry.mark(BLOCKED));
|
||||
VERIFY_ERROR (FATAL, entry.mark(FREE) );
|
||||
VERIFY_ERROR (FATAL, entry.mark(NIL) );
|
||||
|
||||
// re-use buffer slot, start new lifecycle
|
||||
void* OTHER_LOCATION = this;
|
||||
entry.lock (OTHER_LOCATION);
|
||||
CHECK (LOCKED == entry.state());
|
||||
CHECK (entry.isLocked());
|
||||
|
||||
VERIFY_ERROR (LIFECYCLE, entry.lock(SOME_POINTER));
|
||||
|
||||
entry.mark (BLOCKED); // go directly to the blocked state
|
||||
CHECK (BLOCKED == entry.state());
|
||||
VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
|
||||
VERIFY_ERROR (FATAL, entry.mark(EMITTED) );
|
||||
VERIFY_ERROR (FATAL, entry.mark(BLOCKED) );
|
||||
VERIFY_ERROR (FATAL, entry.mark(NIL) );
|
||||
|
||||
CHECK (OTHER_LOCATION == entry.access());
|
||||
|
||||
entry.mark (FREE);
|
||||
CHECK (!entry.isLocked());
|
||||
VERIFY_ERROR (LIFECYCLE, entry.access() );
|
||||
|
||||
meta_->lock(key, SOME_POINTER);
|
||||
CHECK (entry.isLocked());
|
||||
|
||||
entry.mark (EMITTED);
|
||||
entry.mark (BLOCKED);
|
||||
CHECK (BLOCKED == entry.state());
|
||||
CHECK (SOME_POINTER == entry.access());
|
||||
|
||||
// can't discard metadata, need to free first
|
||||
VERIFY_ERROR (LIFECYCLE, meta_->release(entry) );
|
||||
CHECK (meta_->isKnown(entry));
|
||||
CHECK (entry.isLocked());
|
||||
|
||||
entry.mark (FREE);
|
||||
meta_->release(entry);
|
||||
CHECK (!meta_->isKnown(entry));
|
||||
CHECK ( meta_->isKnown(key));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,29 +24,25 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/test/testdummy.hpp"
|
||||
#include "lib/util-foreach.hpp"
|
||||
//#include "proc/play/diagnostic-output-slot.hpp"
|
||||
#include "proc/engine/testframe.hpp"
|
||||
#include "proc/engine/diagnostic-buffer-provider.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/buffhandle-attach.hpp"
|
||||
#include "proc/engine/bufftable.hpp"
|
||||
|
||||
//#include <boost/format.hpp>
|
||||
//#include <iostream>
|
||||
|
||||
//using boost::format;
|
||||
//using std::string;
|
||||
//using std::cout;
|
||||
using util::isSameObject;
|
||||
using util::for_each;
|
||||
|
||||
|
||||
namespace engine{
|
||||
namespace test {
|
||||
|
||||
// using lib::AllocationCluster;
|
||||
// using mobject::session::PEffect;
|
||||
using lib::test::Dummy;
|
||||
|
||||
using ::engine::BuffHandle;
|
||||
using lumiera::error::LUMIERA_ERROR_LIFECYCLE;
|
||||
using error::LUMIERA_ERROR_LOGIC;
|
||||
using error::LUMIERA_ERROR_LIFECYCLE;
|
||||
|
||||
|
||||
namespace { // Test fixture
|
||||
|
|
@ -64,21 +60,26 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* @test verify the OutputSlot interface and base implementation
|
||||
* by performing full data exchange cycle. This is a
|
||||
* kind of "dry run" for documentation purposes,
|
||||
* both the actual OutputSlot implementation
|
||||
* as the client using this slot are Mocks.
|
||||
/******************************************************************************
|
||||
* @test verify and demonstrate the usage cycle of data buffers for the engine
|
||||
* based on the BufferProvider interface. This is kind of a "dry run"
|
||||
* for documentation purposes, because the BufferProvider implementation
|
||||
* used here is just a diagnostics facility, allowing to investigate
|
||||
* the state of individual buffers even after "releasing" them.
|
||||
*
|
||||
* This test should help understanding the sequence of buffer management
|
||||
* operations performed at various stages while passing an calculation job
|
||||
* through the render engine.
|
||||
*/
|
||||
class BufferProviderProtocol_test : public Test
|
||||
{
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
UNIMPLEMENTED ("build a diagnostic buffer provider and perform a full lifecycle");
|
||||
verifySimpleUsage();
|
||||
verifyStandardCase();
|
||||
verifyObjectAttachment();
|
||||
verifyObjectAttachmentFailure();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -93,11 +94,12 @@ namespace test {
|
|||
BuffHandle buff = provider.lockBufferFor<TestFrame>();
|
||||
CHECK (buff.isValid());
|
||||
CHECK (sizeof(TestFrame) <= buff.size());
|
||||
buff.create<TestFrame>() = testData(0);
|
||||
buff.accessAs<TestFrame>() = testData(0);
|
||||
|
||||
TestFrame& storage = buff.accessAs<TestFrame>();
|
||||
CHECK (testData(0) == storage);
|
||||
TestFrame& content = buff.accessAs<TestFrame>();
|
||||
CHECK (testData(0) == content);
|
||||
|
||||
buff.emit();
|
||||
buff.release();
|
||||
CHECK (!buff.isValid());
|
||||
VERIFY_ERROR (LIFECYCLE, buff.accessAs<TestFrame>() );
|
||||
|
|
@ -105,8 +107,6 @@ namespace test {
|
|||
DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider);
|
||||
CHECK (checker.buffer_was_used (0));
|
||||
CHECK (checker.buffer_was_closed (0));
|
||||
CHECK (checker.object_was_attached<TestFrame> (0));
|
||||
CHECK (checker.object_was_destroyed<TestFrame> (0));
|
||||
|
||||
CHECK (testData(0) == checker.accessMemory (0));
|
||||
}
|
||||
|
|
@ -147,6 +147,83 @@ namespace test {
|
|||
CHECK (checker.all_buffers_released());
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyObjectAttachment()
|
||||
{
|
||||
BufferProvider& provider = DiagnosticBufferProvider::build();
|
||||
BufferDescriptor type_A = provider.getDescriptorFor(sizeof(TestFrame));
|
||||
BufferDescriptor type_B = provider.getDescriptorFor(sizeof(int));
|
||||
BufferDescriptor type_C = provider.getDescriptor<int>();
|
||||
|
||||
BuffHandle handle_A = provider.lockBuffer(type_A);
|
||||
BuffHandle handle_B = provider.lockBuffer(type_B);
|
||||
BuffHandle handle_C = provider.lockBuffer(type_C);
|
||||
|
||||
CHECK (handle_A);
|
||||
CHECK (handle_B);
|
||||
CHECK (handle_C);
|
||||
|
||||
CHECK (sizeof(TestFrame) == handle_A.size());
|
||||
CHECK (sizeof( int ) == handle_B.size());
|
||||
CHECK (sizeof( int ) == handle_C.size());
|
||||
|
||||
TestFrame& embeddedFrame = handle_A.create<TestFrame>();
|
||||
CHECK (isSameObject (*handle_A, embeddedFrame));
|
||||
CHECK (embeddedFrame.isAlive());
|
||||
CHECK (embeddedFrame.isSane());
|
||||
|
||||
VERIFY_ERROR (LOGIC, handle_B.create<TestFrame>()); // too small to hold a TestFrame
|
||||
VERIFY_ERROR (LIFECYCLE, handle_C.create<int>()); // has already an attached TypeHandler (creating an int)
|
||||
|
||||
handle_A.release();
|
||||
handle_B.release();
|
||||
handle_C.release();
|
||||
|
||||
CHECK (embeddedFrame.isDead());
|
||||
CHECK (embeddedFrame.isSane());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyObjectAttachmentFailure()
|
||||
{
|
||||
BufferProvider& provider = DiagnosticBufferProvider::build();
|
||||
BufferDescriptor type_D = provider.getDescriptorFor(sizeof(Dummy));
|
||||
|
||||
Dummy::checksum() = 0;
|
||||
BuffHandle handle_D = provider.lockBuffer(type_D);
|
||||
CHECK (0 == Dummy::checksum()); // nothing created thus far
|
||||
|
||||
handle_D.create<Dummy>();
|
||||
CHECK (0 < Dummy::checksum());
|
||||
|
||||
handle_D.release();
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
BuffHandle handle_DD = provider.lockBuffer(type_D);
|
||||
|
||||
CHECK (0 == Dummy::checksum());
|
||||
Dummy::activateCtorFailure();
|
||||
|
||||
CHECK (handle_DD.isValid());
|
||||
try
|
||||
{
|
||||
handle_DD.create<Dummy>();
|
||||
NOTREACHED ("Dummy ctor should fail");
|
||||
}
|
||||
catch (int val)
|
||||
{
|
||||
CHECK (!handle_DD.isValid());
|
||||
|
||||
CHECK (0 < Dummy::checksum());
|
||||
CHECK (val == Dummy::checksum());
|
||||
}
|
||||
|
||||
VERIFY_ERROR (LIFECYCLE, handle_DD.accessAs<Dummy>() );
|
||||
VERIFY_ERROR (LIFECYCLE, handle_DD.create<Dummy>() );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ namespace test {
|
|||
ModelPort port(pipe);
|
||||
|
||||
OutputSlot& oSlot = DiagnosticOutputSlot::build();
|
||||
Allocation output = oSlot.allocate();
|
||||
Allocation& output = oSlot.allocate();
|
||||
Timings timings; /////////TODO
|
||||
|
||||
// Invoke test subject...
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include "proc/engine/nodefactory.hpp"
|
||||
#include "proc/engine/nodewiring.hpp"
|
||||
#include "proc/engine/stateproxy.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/channel-descriptor.hpp"
|
||||
#include "proc/mobject/session/effect.hpp"
|
||||
#include "lib/allocation-cluster.hpp"
|
||||
|
||||
|
|
|
|||
208
tests/components/proc/engine/testframe-test.cpp
Normal file
208
tests/components/proc/engine/testframe-test.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
TestFrame(Test) - verify proper operation of dummy data frames
|
||||
|
||||
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/test/run.hpp"
|
||||
#include "proc/engine/testframe.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <limits.h>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
using test::Test;
|
||||
using std::rand;
|
||||
using util::isSameObject;
|
||||
using boost::scoped_ptr;
|
||||
|
||||
|
||||
namespace engine{
|
||||
namespace test {
|
||||
|
||||
namespace { // used internally
|
||||
|
||||
const uint CHAN_COUNT = 30; // independent families of test frames to generate
|
||||
const uint NUM_FRAMES = 1000; // number of test frames in each of these families
|
||||
|
||||
|
||||
void
|
||||
corruptMemory(void* base, uint offset, uint count)
|
||||
{
|
||||
char* accessor = reinterpret_cast<char*> (base);
|
||||
while (count--)
|
||||
accessor[offset+count] = rand() % CHAR_MAX;
|
||||
}
|
||||
} // (End) internal defs
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* @test verify test helper for engine tests: a dummy data frame.
|
||||
* TestFrame instances can be created right away, without any
|
||||
* external library dependencies. A test frame is automatically
|
||||
* filled with random data; multiple frames are arranged in
|
||||
* sequences and channels, causing the random data to be
|
||||
* reproducible yet different in each frame.
|
||||
*
|
||||
* To ease writing unit tests, TestFrame provides comparison
|
||||
* and assignment and tracks lifecycle automatically. As tests
|
||||
* regarding the engine typically have to deal with buffer
|
||||
* management, an arbitrary memory location can be interpreted
|
||||
* as TestFrame and checked for corruption.
|
||||
*/
|
||||
class TestFrame_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
verifyBasicProperties();
|
||||
verifyFrameLifecycle();
|
||||
verifyFrameSeries();
|
||||
useFrameTable();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyBasicProperties()
|
||||
{
|
||||
CHECK (1024 < sizeof(TestFrame));
|
||||
|
||||
TestFrame frameA;
|
||||
TestFrame frameB;
|
||||
TestFrame frameC(5);
|
||||
|
||||
CHECK (frameA == frameB);
|
||||
CHECK (frameA != frameC);
|
||||
CHECK (frameB != frameC);
|
||||
|
||||
CHECK (frameA.isAlive());
|
||||
CHECK (frameB.isAlive());
|
||||
CHECK (frameC.isAlive());
|
||||
|
||||
CHECK (frameA.isSane());
|
||||
CHECK (frameB.isSane());
|
||||
CHECK (frameC.isSane());
|
||||
|
||||
void * frameMem = &frameB;
|
||||
|
||||
CHECK (frameA == frameMem);
|
||||
corruptMemory(frameMem,20,5);
|
||||
CHECK (!frameB.isSane());
|
||||
|
||||
frameB = frameC;
|
||||
|
||||
CHECK (frameB.isSane());
|
||||
CHECK (frameA != frameB);
|
||||
CHECK (frameA != frameC);
|
||||
CHECK (frameB == frameC);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyFrameLifecycle()
|
||||
{
|
||||
CHECK (!TestFrame::isDead (this));
|
||||
CHECK (!TestFrame::isAlive (this));
|
||||
|
||||
TestFrame* onHeap = new TestFrame(23);
|
||||
CHECK ( TestFrame::isAlive (onHeap));
|
||||
CHECK (!onHeap->isDead());
|
||||
CHECK (onHeap->isAlive());
|
||||
CHECK (onHeap->isSane());
|
||||
|
||||
delete onHeap;
|
||||
CHECK ( TestFrame::isDead (onHeap));
|
||||
CHECK (!TestFrame::isAlive (onHeap));
|
||||
}
|
||||
|
||||
|
||||
/** @test build sequences of test frames,
|
||||
* organised into multiple families (channels).
|
||||
* Verify that adjacent frames hold differing data
|
||||
*/
|
||||
void
|
||||
verifyFrameSeries()
|
||||
{
|
||||
scoped_ptr<TestFrame> thisFrames[CHAN_COUNT];
|
||||
scoped_ptr<TestFrame> prevFrames[CHAN_COUNT];
|
||||
|
||||
for (uint i=0; i<CHAN_COUNT; ++i)
|
||||
thisFrames[i].reset (new TestFrame(0, i));
|
||||
|
||||
for (uint nr=1; nr<NUM_FRAMES; ++nr)
|
||||
for (uint i=0; i<CHAN_COUNT; ++i)
|
||||
{
|
||||
thisFrames[i].swap (prevFrames[i]);
|
||||
thisFrames[i].reset (new TestFrame(nr, i));
|
||||
CHECK (thisFrames[i]->isSane());
|
||||
CHECK (prevFrames[i]->isSane());
|
||||
CHECK (prevFrames[i]->isAlive());
|
||||
|
||||
CHECK (*thisFrames[i] != *prevFrames[i]); // differs from predecessor within the same channel
|
||||
|
||||
for (uint j=0; j<i; ++j)
|
||||
{
|
||||
ENSURE (j!=i);
|
||||
CHECK (*thisFrames[i] != *thisFrames[j]); // differs from frames in other channels at this point
|
||||
CHECK (*thisFrames[i] != *prevFrames[j]); // differs cross wise from predecessors in other channels
|
||||
} } }
|
||||
|
||||
|
||||
/** @test the table of test frames
|
||||
* computed on demand */
|
||||
void
|
||||
useFrameTable()
|
||||
{
|
||||
TestFrame& frX = testData(3,50);
|
||||
TestFrame& frY = testData(3,25);
|
||||
TestFrame& frZ = testData(3,50);
|
||||
|
||||
CHECK (frX.isSane());
|
||||
CHECK (frY.isSane());
|
||||
CHECK (frZ.isSane());
|
||||
|
||||
CHECK (frX != frY);
|
||||
CHECK (frX == frZ);
|
||||
CHECK (frY != frZ);
|
||||
|
||||
CHECK (isSameObject (frX, frZ));
|
||||
|
||||
corruptMemory(&frZ,40,20);
|
||||
CHECK (!frX.isSane());
|
||||
CHECK (!testData(3,50).isSane());
|
||||
CHECK ( testData(3,51).isSane());
|
||||
CHECK ( testData(3,49).isSane());
|
||||
|
||||
resetTestFrames();
|
||||
|
||||
CHECK ( testData(3,50).isSane());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (TestFrame_test, "unit engine");
|
||||
|
||||
|
||||
|
||||
}} // namespace engine::test
|
||||
292
tests/components/proc/engine/testframe.cpp
Normal file
292
tests/components/proc/engine/testframe.cpp
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
TestFrame - test data frame (stub) for checking Render engine 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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "proc/engine/testframe.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include <boost/random/linear_congruential.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <limits.h>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace engine {
|
||||
namespace test {
|
||||
|
||||
using std::vector;
|
||||
using std::memcpy;
|
||||
|
||||
typedef boost::rand48 PseudoRandom;
|
||||
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
namespace { // hidden local support facilities....
|
||||
|
||||
/** @internal helper for generating unique test frames.
|
||||
* This "discriminator" is used as a random seed when
|
||||
* filling the test frame data buffers. It is generated
|
||||
* to be different on adjacent frames of the same series,
|
||||
* as well as to differ to all near by neighbouring channels.
|
||||
* @param seq the sequence number of the frame within the channel
|
||||
* @param family the channel this frame belongs to
|
||||
*/
|
||||
uint64_t
|
||||
generateDistinction(uint seq, uint family)
|
||||
{
|
||||
// random offset, but fixed per executable run
|
||||
static uint base(10 + rand() % 990);
|
||||
|
||||
// use the family as stepping
|
||||
return (seq+1) * (base+family);
|
||||
}
|
||||
|
||||
|
||||
TestFrame&
|
||||
accessAsTestFrame (void* memoryLocation)
|
||||
{
|
||||
REQUIRE (memoryLocation);
|
||||
return *reinterpret_cast<TestFrame*> (memoryLocation);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal table to hold test data frames.
|
||||
* These frames are built on demand, but retained thereafter.
|
||||
* Some tests might rely on the actual memory locations, using the
|
||||
* test frames to simulate a real input frame data stream.
|
||||
* @param CHA the maximum number of channels to expect
|
||||
* @param FRA the maximum number of frames to expect per channel
|
||||
* @warning choose the maximum number parameters wisely.
|
||||
* We're allocating memory to hold a table of test frames
|
||||
* e.g. sizeof(TestFrame) * 20channels * 100frames ≈ 2 MiB
|
||||
* The table uses vectors, and thus will grow on demand,
|
||||
* but this might cause existing frames to be relocated in memory;
|
||||
* some tests might rely on fixed memory locations. Just be cautious!
|
||||
*/
|
||||
template<uint CHA, uint FRA>
|
||||
struct TestFrameTable
|
||||
: vector<vector<TestFrame> >
|
||||
{
|
||||
typedef vector<vector<TestFrame> > VECT;
|
||||
|
||||
TestFrameTable()
|
||||
: VECT(CHA)
|
||||
{
|
||||
for (uint i=0; i<CHA; ++i)
|
||||
at(i).reserve(FRA);
|
||||
}
|
||||
|
||||
TestFrame&
|
||||
getFrame (uint seqNr, uint chanNr=0)
|
||||
{
|
||||
if (chanNr >= this->size())
|
||||
{
|
||||
WARN (test, "Growing table of test frames to %d channels, "
|
||||
"which is > the default (%d)", chanNr, CHA);
|
||||
resize(chanNr+1);
|
||||
}
|
||||
ENSURE (chanNr < this->size());
|
||||
vector<TestFrame>& channel = at(chanNr);
|
||||
|
||||
if (seqNr >= channel.size())
|
||||
{
|
||||
WARN_IF (seqNr >= FRA, test,
|
||||
"Growing channel #%d of test frames to %d elements, "
|
||||
"which is > the default (%d)", chanNr, seqNr, FRA);
|
||||
for (uint i=channel.size(); i<=seqNr; ++i)
|
||||
channel.push_back (TestFrame (i,chanNr));
|
||||
}
|
||||
ENSURE (seqNr < channel.size());
|
||||
|
||||
return channel[seqNr];
|
||||
}
|
||||
};
|
||||
|
||||
const uint INITIAL_CHAN = 20;
|
||||
const uint INITIAL_FRAMES = 100;
|
||||
|
||||
typedef TestFrameTable<INITIAL_CHAN,INITIAL_FRAMES> TestFrames;
|
||||
|
||||
boost::scoped_ptr<TestFrames> testFrames;
|
||||
|
||||
|
||||
TestFrame&
|
||||
accessTestFrame (uint seqNr, uint chanNr)
|
||||
{
|
||||
if (!testFrames) testFrames.reset (new TestFrames);
|
||||
|
||||
return testFrames->getFrame(seqNr,chanNr);
|
||||
}
|
||||
|
||||
} // (End) hidden impl details
|
||||
|
||||
|
||||
|
||||
|
||||
TestFrame&
|
||||
testData (uint seqNr)
|
||||
{
|
||||
return accessTestFrame (seqNr, 0);
|
||||
}
|
||||
|
||||
TestFrame&
|
||||
testData (uint chanNr, uint seqNr)
|
||||
{
|
||||
return accessTestFrame (seqNr,chanNr);
|
||||
}
|
||||
|
||||
void
|
||||
resetTestFrames()
|
||||
{
|
||||
testFrames.reset(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ===== TestFrame class ===== */
|
||||
|
||||
TestFrame::~TestFrame()
|
||||
{
|
||||
stage_ = DISCARDED;
|
||||
}
|
||||
|
||||
|
||||
TestFrame::TestFrame(uint seq, uint family)
|
||||
: distinction_(generateDistinction (seq,family))
|
||||
, stage_(CREATED)
|
||||
{
|
||||
ASSERT (0 < distinction_);
|
||||
buildData();
|
||||
}
|
||||
|
||||
|
||||
TestFrame::TestFrame (TestFrame const& o)
|
||||
: distinction_(o.distinction_)
|
||||
, stage_(CREATED)
|
||||
{
|
||||
memcpy (data_, o.data_, BUFFSIZ);
|
||||
}
|
||||
|
||||
TestFrame&
|
||||
TestFrame::operator= (TestFrame const& o)
|
||||
{
|
||||
if (DISCARDED == stage_)
|
||||
throw new error::Logic ("target TestFrame is already dead");
|
||||
if (this != &o)
|
||||
{
|
||||
distinction_ = o.distinction_;
|
||||
stage_ = CREATED;
|
||||
memcpy (data_, o.data_, BUFFSIZ);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @note performing an unchecked conversion of the given
|
||||
* memory location to be accessed as TestFrame.
|
||||
* The sanity of the data found at that location
|
||||
* is checked as well, not only the lifecycle flag.
|
||||
*/
|
||||
bool
|
||||
TestFrame::isAlive (void* memLocation)
|
||||
{
|
||||
TestFrame& candidate (accessAsTestFrame (memLocation));
|
||||
return candidate.isSane()
|
||||
&& candidate.isAlive();
|
||||
}
|
||||
|
||||
bool
|
||||
TestFrame::isDead (void* memLocation)
|
||||
{
|
||||
TestFrame& candidate (accessAsTestFrame (memLocation));
|
||||
return candidate.isSane()
|
||||
&& candidate.isDead();
|
||||
}
|
||||
|
||||
bool
|
||||
TestFrame::operator== (void* memLocation) const
|
||||
{
|
||||
TestFrame& candidate (accessAsTestFrame (memLocation));
|
||||
return candidate.isSane()
|
||||
&& candidate == *this;
|
||||
}
|
||||
|
||||
bool
|
||||
TestFrame::contentEquals (TestFrame const& o) const
|
||||
{
|
||||
for (uint i=0; i<BUFFSIZ; ++i)
|
||||
if (data_[i] != o.data_[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestFrame::verifyData() const
|
||||
{
|
||||
PseudoRandom gen(distinction_);
|
||||
for (uint i=0; i<BUFFSIZ; ++i)
|
||||
if (data_[i] != (gen() % CHAR_MAX))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TestFrame::buildData()
|
||||
{
|
||||
PseudoRandom gen(distinction_);
|
||||
for (uint i=0; i<BUFFSIZ; ++i)
|
||||
data_[i] = (gen() % CHAR_MAX);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TestFrame::isAlive() const
|
||||
{
|
||||
return (CREATED == stage_)
|
||||
|| (EMITTED == stage_);
|
||||
}
|
||||
|
||||
bool
|
||||
TestFrame::isDead() const
|
||||
{
|
||||
return (DISCARDED == stage_);
|
||||
}
|
||||
|
||||
bool
|
||||
TestFrame::isSane() const
|
||||
{
|
||||
return ( (CREATED == stage_)
|
||||
||(EMITTED == stage_)
|
||||
||(DISCARDED == stage_))
|
||||
&& verifyData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // namespace engine::test
|
||||
|
|
@ -25,21 +25,14 @@
|
|||
#define PROC_ENGINE_TESTFRAME_H
|
||||
|
||||
|
||||
//#include "lib/time/timevalue.hpp"
|
||||
|
||||
//#include <string>
|
||||
|
||||
|
||||
//using std::tr1::shared_ptr;
|
||||
//using std::string;
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace engine {
|
||||
namespace test {
|
||||
|
||||
|
||||
//class TestPlacement;
|
||||
|
||||
/**
|
||||
* Mock data frame for simulated rendering.
|
||||
* A test frame can be created and placed instead of a real data frame.
|
||||
|
|
@ -47,52 +40,80 @@ namespace test {
|
|||
* Placeholder functions are provided for assignment (simulating the actual
|
||||
* calculations); additional diagnostic functions allow to verify the
|
||||
* performed operations after-the fact
|
||||
*
|
||||
* @todo WIP-WIP-WIP 9/11
|
||||
*
|
||||
* Each TestFrame is automatically filled with pseudo random data;
|
||||
* multiple frames are arranged in sequences and channels, causing the random data
|
||||
* to be reproducible yet different within each frame. TestFrame's lifecycle is
|
||||
* tracked and marked in an embedded state field. Moreover, the contents of the
|
||||
* data block can be verified, because the sequence of bytes is reproducible,
|
||||
* based on the channel and sequence number of the test frame.
|
||||
*
|
||||
* @see TestFrame_test
|
||||
* @see OutputSlotProtocol_test
|
||||
*
|
||||
*/
|
||||
class TestFrame
|
||||
{
|
||||
enum StageOfLife {
|
||||
CREATED, EMITTED, DISCARDED
|
||||
};
|
||||
|
||||
static const size_t BUFFSIZ = 1024;
|
||||
|
||||
uint64_t distinction_;
|
||||
StageOfLife stage_;
|
||||
|
||||
char data_[BUFFSIZ];
|
||||
|
||||
public:
|
||||
~TestFrame();
|
||||
TestFrame (uint seq=0, uint family=0);
|
||||
TestFrame (TestFrame const&);
|
||||
TestFrame& operator= (TestFrame const&);
|
||||
|
||||
bool
|
||||
operator== (void* memLocation)
|
||||
{
|
||||
UNIMPLEMENTED ("verify contents of an arbitrary memory location");
|
||||
}
|
||||
/** Helper to verify that a given memory location holds
|
||||
* an active TestFrame instance (created, not yet destroyed)
|
||||
* @return true if the TestFrame datastructure is intact and
|
||||
* marked as still alive.
|
||||
*/
|
||||
static bool isAlive (void* memLocation);
|
||||
|
||||
friend bool
|
||||
operator== (TestFrame const& f1, TestFrame const& f2)
|
||||
{
|
||||
UNIMPLEMENTED ("equality of test data frames");
|
||||
}
|
||||
/** Helper to verify a given memory location holds
|
||||
* an already destroyed TestFrame instance */
|
||||
static bool isDead (void* memLocation);
|
||||
|
||||
friend bool
|
||||
operator!= (TestFrame const& f1, TestFrame const& f2)
|
||||
{
|
||||
return !(f1 == f2);
|
||||
}
|
||||
bool isAlive() const;
|
||||
bool isDead() const;
|
||||
bool isSane() const;
|
||||
|
||||
bool operator== (void* memLocation) const;
|
||||
|
||||
friend bool operator== (TestFrame const& f1, TestFrame const& f2) { return f1.contentEquals(f2); }
|
||||
friend bool operator!= (TestFrame const& f1, TestFrame const& f2) { return !f1.contentEquals(f2); }
|
||||
|
||||
private:
|
||||
bool contentEquals (TestFrame const& o) const;
|
||||
bool verifyData() const;
|
||||
void buildData ();
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline TestFrame
|
||||
testData (uint seqNr)
|
||||
{
|
||||
UNIMPLEMENTED ("build, memorise and expose test data frames on demand");
|
||||
}
|
||||
/** Helper to access a specific frame of test data at a fixed memory location.
|
||||
* The series of test frames is generated on demand, but remains in memory thereafter,
|
||||
* similar to real data accessible from some kind of source stream. Each of these generated
|
||||
* test frames filled with different yet reproducible pseudo random data.
|
||||
* Client code is free to access and corrupt this data.
|
||||
*/
|
||||
TestFrame& testData (uint seqNr);
|
||||
|
||||
inline TestFrame
|
||||
testData (uint chanNr, uint seqNr)
|
||||
{
|
||||
UNIMPLEMENTED ("build, memorise and expose test data frames on demand (multi-channel)");
|
||||
}
|
||||
TestFrame& testData (uint chanNr, uint seqNr);
|
||||
|
||||
|
||||
|
||||
/* == some test data to check == */
|
||||
// extern const lib::time::Duration LENGTH_TestClip;
|
||||
/** discards all the TestFrame instances and
|
||||
* initialises an empty table of test frames */
|
||||
void resetTestFrames();
|
||||
|
||||
|
||||
|
||||
}} // namespace engine::test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,222 @@
|
|||
/*u4
|
||||
TrackingHeapBlockProvider(Test) - verify a support facility for diagnostic/test purposes
|
||||
|
||||
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 "lib/test/run.hpp"
|
||||
#include "proc/engine/tracking-heap-block-provider.hpp"
|
||||
#include "proc/engine/buffhandle-attach.hpp"
|
||||
#include "proc/engine/testframe.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
using std::rand;
|
||||
|
||||
|
||||
namespace engine{
|
||||
namespace test {
|
||||
|
||||
|
||||
|
||||
namespace { // Test fixture
|
||||
|
||||
const size_t TEST_ELM_SIZE = sizeof(uint);
|
||||
const uint MAX_ELMS = 50;
|
||||
|
||||
std::vector<uint> testNumbers(MAX_ELMS);
|
||||
|
||||
|
||||
bool
|
||||
has_expectedContent (uint nr, diagn::Block& memoryBlock)
|
||||
{
|
||||
void* mem = memoryBlock.accessMemory();
|
||||
uint data = *static_cast<uint*> (mem);
|
||||
|
||||
return data == testNumbers[nr];
|
||||
}
|
||||
|
||||
bool
|
||||
verifyUsedBlock (uint nr, diagn::Block& memoryBlock)
|
||||
{
|
||||
return memoryBlock.was_used()
|
||||
&& memoryBlock.was_closed()
|
||||
&& has_expectedContent (nr, memoryBlock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* @test verify a test support facility, used to write mock components
|
||||
* to test the lumiera engine. The TrackingHeapBlockProvider is a
|
||||
* braindead implementation of the BufferProvider interface: it just
|
||||
* claims new heap blocks and never de-allocates them, allowing other
|
||||
* test and mock objects to verify allocated buffers after the fact.
|
||||
*/
|
||||
class TrackingHeapBlockProvider_test : public Test
|
||||
{
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
simpleExample();
|
||||
verifyStandardCase();
|
||||
verifyTestProtocol();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
simpleExample()
|
||||
{
|
||||
TrackingHeapBlockProvider provider;
|
||||
|
||||
BuffHandle testBuff = provider.lockBufferFor<TestFrame>();
|
||||
CHECK (testBuff);
|
||||
CHECK (testBuff.accessAs<TestFrame>().isSane());
|
||||
|
||||
uint dataID = 1 + rand() % 29;
|
||||
testBuff.accessAs<TestFrame>() = testData(dataID);
|
||||
|
||||
provider.emitBuffer (testBuff);
|
||||
provider.releaseBuffer(testBuff);
|
||||
|
||||
diagn::Block& block0 = provider.access_emitted(0);
|
||||
CHECK (testData(dataID) == block0.accessMemory());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyStandardCase()
|
||||
{
|
||||
TrackingHeapBlockProvider provider;
|
||||
|
||||
BufferDescriptor buffType = provider.getDescriptorFor(TEST_ELM_SIZE);
|
||||
uint numElms = provider.announce(MAX_ELMS, buffType);
|
||||
CHECK (0 < numElms);
|
||||
CHECK (numElms <= MAX_ELMS);
|
||||
|
||||
for (uint i=0; i<numElms; ++i)
|
||||
{
|
||||
BuffHandle buff = provider.lockBuffer(buffType);
|
||||
buff.accessAs<uint>() = testNumbers[i] = rand() % 100000;
|
||||
provider.emitBuffer (buff);
|
||||
provider.releaseBuffer(buff);
|
||||
}
|
||||
|
||||
for (uint nr=0; nr<numElms; ++nr)
|
||||
{
|
||||
CHECK (verifyUsedBlock (nr, provider.access_emitted(nr)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verifyTestProtocol()
|
||||
{
|
||||
TrackingHeapBlockProvider provider;
|
||||
|
||||
BufferDescriptor buffType = provider.getDescriptorFor(TEST_ELM_SIZE);
|
||||
|
||||
BuffHandle bu1 = provider.lockBuffer (buffType);
|
||||
BuffHandle bu2 = provider.lockBuffer (buffType);
|
||||
BuffHandle bu3 = provider.lockBuffer (buffType);
|
||||
BuffHandle bu4 = provider.lockBuffer (buffType);
|
||||
BuffHandle bu5 = provider.lockBuffer (buffType);
|
||||
|
||||
// buffers are locked,
|
||||
// but still within the per-type allocation pool
|
||||
// while the output sequence is still empty
|
||||
CHECK (!provider.access_emitted(0).was_used());
|
||||
CHECK (!provider.access_emitted(1).was_used());
|
||||
CHECK (!provider.access_emitted(2).was_used());
|
||||
CHECK (!provider.access_emitted(3).was_used());
|
||||
CHECK (!provider.access_emitted(4).was_used());
|
||||
|
||||
// can use the buffers for real
|
||||
bu1.accessAs<uint>() = 1;
|
||||
bu2.accessAs<uint>() = 2;
|
||||
bu3.accessAs<uint>() = 3;
|
||||
bu4.accessAs<uint>() = 4;
|
||||
bu5.accessAs<uint>() = 5;
|
||||
|
||||
CHECK (0 == provider.emittedCnt());
|
||||
|
||||
// now emit buffers in shuffled order
|
||||
provider.emitBuffer (bu3);
|
||||
provider.emitBuffer (bu1);
|
||||
provider.emitBuffer (bu5);
|
||||
provider.emitBuffer (bu4);
|
||||
provider.emitBuffer (bu2);
|
||||
|
||||
CHECK (5 == provider.emittedCnt());
|
||||
|
||||
CHECK (3 == provider.accessAs<uint>(0));
|
||||
CHECK (1 == provider.accessAs<uint>(1));
|
||||
CHECK (5 == provider.accessAs<uint>(2));
|
||||
CHECK (4 == provider.accessAs<uint>(3));
|
||||
CHECK (2 == provider.accessAs<uint>(4));
|
||||
|
||||
CHECK ( provider.access_emitted(0).was_used());
|
||||
CHECK ( provider.access_emitted(1).was_used());
|
||||
CHECK ( provider.access_emitted(2).was_used());
|
||||
CHECK ( provider.access_emitted(3).was_used());
|
||||
CHECK ( provider.access_emitted(4).was_used());
|
||||
|
||||
CHECK (!provider.access_emitted(0).was_closed());
|
||||
CHECK (!provider.access_emitted(1).was_closed());
|
||||
CHECK (!provider.access_emitted(2).was_closed());
|
||||
CHECK (!provider.access_emitted(3).was_closed());
|
||||
CHECK (!provider.access_emitted(4).was_closed());
|
||||
|
||||
bu5.release();
|
||||
CHECK (!provider.access_emitted(0).was_closed());
|
||||
CHECK (!provider.access_emitted(1).was_closed());
|
||||
CHECK ( provider.access_emitted(2).was_closed());
|
||||
CHECK (!provider.access_emitted(3).was_closed());
|
||||
CHECK (!provider.access_emitted(4).was_closed());
|
||||
|
||||
bu2.release();
|
||||
bu2.release();
|
||||
bu5.release();
|
||||
CHECK (!provider.access_emitted(0).was_closed());
|
||||
CHECK (!provider.access_emitted(1).was_closed());
|
||||
CHECK ( provider.access_emitted(2).was_closed());
|
||||
CHECK (!provider.access_emitted(3).was_closed());
|
||||
CHECK ( provider.access_emitted(4).was_closed());
|
||||
|
||||
CHECK (!bu2);
|
||||
CHECK (bu3);
|
||||
|
||||
bu1.release();
|
||||
bu3.release();
|
||||
bu4.release();
|
||||
|
||||
CHECK (5 == provider.emittedCnt());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (TrackingHeapBlockProvider_test, "unit player");
|
||||
|
||||
|
||||
|
||||
}} // namespace engine::test
|
||||
|
|
@ -34,11 +34,10 @@
|
|||
#include "lib/query.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/ref.hpp>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
using boost::ref;
|
||||
using std::tr1::ref;
|
||||
using std::tr1::placeholders::_1;
|
||||
using util::isSameObject;
|
||||
using util::and_all;
|
||||
|
|
|
|||
|
|
@ -32,15 +32,19 @@
|
|||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "proc/play/output-slot.hpp"
|
||||
#include "proc/play/output-slot-connection.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/tracking-heap-block-provider.hpp"
|
||||
#include "lib/iter-source.hpp" ////////////TODO really going down that path...?
|
||||
#include "proc/engine/testframe.hpp"
|
||||
//#include "lib/sync.hpp"
|
||||
|
||||
//#include <boost/noncopyable.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
//#include <string>
|
||||
//#include <vector>
|
||||
//#include <tr1/memory>
|
||||
#include <tr1/memory>
|
||||
//#include <boost/scoped_ptr.hpp>
|
||||
|
||||
|
||||
|
|
@ -48,14 +52,105 @@ namespace proc {
|
|||
namespace play {
|
||||
|
||||
//using std::string;
|
||||
using ::engine::BufferDescriptor;
|
||||
using ::engine::test::TestFrame;
|
||||
using ::engine::TrackingHeapBlockProvider;
|
||||
|
||||
//using std::vector;
|
||||
//using std::tr1::shared_ptr;
|
||||
using std::tr1::shared_ptr;
|
||||
//using boost::scoped_ptr;
|
||||
|
||||
|
||||
|
||||
class TrackingInMemoryBlockSequence
|
||||
: public OutputSlot::Connection
|
||||
{
|
||||
|
||||
shared_ptr<TrackingHeapBlockProvider> buffProvider_;
|
||||
BufferDescriptor bufferType_;
|
||||
|
||||
|
||||
/* === Connection API === */
|
||||
|
||||
BuffHandle
|
||||
claimBufferFor(FrameID frameNr)
|
||||
{
|
||||
return buffProvider_->lockBuffer (bufferType_);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
isTimely (FrameID frameNr, TimeValue currentTime)
|
||||
{
|
||||
if (Time::MAX == currentTime)
|
||||
return true;
|
||||
|
||||
UNIMPLEMENTED ("find out about timings");
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
transfer (BuffHandle const& filledBuffer)
|
||||
{
|
||||
pushout (filledBuffer);
|
||||
}
|
||||
|
||||
void
|
||||
pushout (BuffHandle const& data4output)
|
||||
{
|
||||
buffProvider_->emitBuffer (data4output);
|
||||
buffProvider_->releaseBuffer(data4output);
|
||||
}
|
||||
|
||||
void
|
||||
discard (BuffHandle const& superseededData)
|
||||
{
|
||||
buffProvider_->releaseBuffer (superseededData);
|
||||
}
|
||||
|
||||
void
|
||||
shutDown ()
|
||||
{
|
||||
buffProvider_.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
TrackingInMemoryBlockSequence()
|
||||
: buffProvider_(new TrackingHeapBlockProvider())
|
||||
, bufferType_(buffProvider_->getDescriptor<TestFrame>())
|
||||
{
|
||||
INFO (engine_dbg, "building in-memory diagnostic output sequence");
|
||||
}
|
||||
|
||||
virtual
|
||||
~TrackingInMemoryBlockSequence()
|
||||
{
|
||||
INFO (engine_dbg, "releasing diagnostic output sequence");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SimulatedOutputSequences
|
||||
: public ConnectionStateManager<TrackingInMemoryBlockSequence>
|
||||
, boost::noncopyable
|
||||
{
|
||||
TrackingInMemoryBlockSequence
|
||||
buildConnection()
|
||||
{
|
||||
return TrackingInMemoryBlockSequence();
|
||||
}
|
||||
|
||||
public:
|
||||
SimulatedOutputSequences (uint numChannels)
|
||||
{
|
||||
init (numChannels);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Helper for unit tests: Mock output sink.
|
||||
*
|
||||
|
|
@ -64,6 +159,17 @@ namespace play {
|
|||
class DiagnosticOutputSlot
|
||||
: public OutputSlot
|
||||
{
|
||||
|
||||
static const uint MAX_CHANNELS = 5;
|
||||
|
||||
/* === hook into the OutputSlot frontend === */
|
||||
ConnectionState*
|
||||
buildState()
|
||||
{
|
||||
return new SimulatedOutputSequences(MAX_CHANNELS);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
/** build a new Diagnostic Output Slot instance,
|
||||
* discard the existing one. Use the static query API
|
||||
|
|
@ -105,28 +211,28 @@ namespace play {
|
|||
|
||||
|
||||
bool
|
||||
buffer_was_used (uint channel, FrameNr frame)
|
||||
buffer_was_used (uint channel, FrameID frame)
|
||||
{
|
||||
UNIMPLEMENTED ("determine if the denoted buffer was indeed used");
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
buffer_unused (uint channel, FrameNr frame)
|
||||
buffer_unused (uint channel, FrameID frame)
|
||||
{
|
||||
UNIMPLEMENTED ("determine if the specified buffer was never touched/locked for use");
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
buffer_was_closed (uint channel, FrameNr frame)
|
||||
buffer_was_closed (uint channel, FrameID frame)
|
||||
{
|
||||
UNIMPLEMENTED ("determine if the specified buffer was indeed closed properly");
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
emitted (uint channel, FrameNr frame)
|
||||
emitted (uint channel, FrameID frame)
|
||||
{
|
||||
UNIMPLEMENTED ("determine if the specivied buffer was indeed handed over for emitting output");
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue