re-read the code, some cleanup, define Tickets...

This commit is contained in:
Fischlurch 2009-08-29 16:13:42 +02:00
parent 592c79b839
commit 6da18f76c0
10 changed files with 290 additions and 293 deletions

View file

@ -57,9 +57,9 @@ namespace engine {
return *newNode;
/*
* Problems to be solved:
* - write a generic allocator, which later on can be augmented to do block wise bulk allocation
* - the allocator needs to keep track of the objects; actually he owns the objects
* Problems to be solved: /////////////TODO: see Ticket #247
* - write a generic allocator, which later on can be augmented to do block wise bulk allocation /////////////DONE: AllocationCluster
* - the allocator needs to keep track of the objects; actually he owns the objects /////////////DONE
* - we need to access the wiring descriptor of the predecessor nodes. Which means, the code
* must be in the body of NodeFactory (because the latter is friend of ProcNode and can access the wiring descriptor)
* - btw reconsider if this level of protection is necessary, or if the const WiringDescriptor cant be just on the public interface of ProcNode

View file

@ -41,14 +41,14 @@
** of elementary operations invoking each other (\c NEXT::step(invocation) ). Notably, all those possible
** configurations are pre-built while compiling (it's a small number below 32 configuration instance).
** To be able to select the Strategy for each configuration, we need a Factory (ConfigSelector defined in
** nodewiringconfig.hpp). which is actually instantiated and used in nodewiring.cpp, which is the object
** nodewiring-config.hpp). which is actually instantiated and used in nodewiring.cpp, which is the object
** file holding all those instantiations.
**
** @see engine::ProcNode
** @see engine::Invocation
** @see engine::State
** @see engine::NodeFactory
** @see nodewiringconfig.hpp
** @see nodewiring-config.hpp
** @see nodewiring.hpp interface for building/wiring the nodes
**
*/

View file

@ -0,0 +1,181 @@
/*
NODEWIRING-CONFIG.hpp - Helper for representing and selecting the wiring case
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 nodewiring-config.hpp
** Sometimes we need to choose a different implementation for dealing with
** some special cases. While for simple cases, just testing a flag or using a
** switch statement will do the job, matters get more difficult when we have to
** employ a completely different execution path for each of the different cases,
** while using a set of common building blocks.
** \par
** In the Lumiera render engine, right on the critical path, we need some glue code
** for invoking the predecessor nodes when pulling a given processing node. The actual
** sequence is quite dependent on the specific situation each node is wired up, regarding
** buffer allocation, cache querying and the possible support for GPU processing and
** render farms. The solution is to define specialisations of a Strategy template
** using the specific configuration as template argument. Based on these, we can
** create a collection of factories, which in turn will build the internal wiring
** for the individual ProcNode instances in accordance to the situation determined
** for this node, expressed as a set of flags. As a net result, each node has an
** individual configuration (as opposed to creating a lot of hand-written individual
** ProcNode subclasses), but parts of this configuration assembly is done already at
** compile time, allowing for additional checks by typing and for possible optimisation.
**
** @see proc::engine::config::Strategy
** @see nodewiring.hpp
** @see configflags.hpp
**
*/
#ifndef ENGINE_NODEWIRING_CONFIG_H
#define ENGINE_NODEWIRING_CONFIG_H
#include "lib/util.hpp"
#include "lib/meta/configflags.hpp"
#include <tr1/functional>
#include <bitset>
#include <map>
namespace engine {
namespace config {
using lumiera::typelist::FlagInfo;
using util::contains;
using lumiera::typelist::CONFIG_FLAGS_MAX;
/**
* Helper for fabricating ProcNode Wiring configurations.
* This object builds a table of factories, holding one factory
* for each possible node configuration. Provided with the desired
* configuration encoded as bits, the related factory can be invoked,
* thus producing a product object for the given configuration.
*
* \par implementation notes
* The actual factory class is templated, so it will be defined
* at the use site of ConfigSelector. Moreover, this factory
* usually expects an ctor argument, which will be fed through
* when creating the ConfigSelector instance. This is one of
* the reasons why we go through all these complicated factory
* building: this ctor argument usually brings in a reference
* to the actual memory allocator. Thus we have to rebuild the
* ConfigSelector each time we switch and rebuild the ProcNode
* factories, which in turn happens each time we use a new
* bulk allocation memory block -- typically for each separate
* segment of the Timeline and processing node graph.
*
* Now the selection of the possible flag configurations, for which
* Factory instances are created in the table, is governed by the
* type parameter of the ConfigSelector ctor. This type parameter
* needs to be a Typelist of Typelists, each representing a flag
* configuration. The intention is to to drive this selection by
* the use of template metaprogramming for extracting all
* currently defined StateProxy object configurations.
* @todo as the factories live only within the enclosed table (map)
* we could allocate them in-place. Unfortunately this is
* non-trivial, because the STL containers employ
* value semantics and thus do a copy even on insert.
* Thus, for now we use a shared_ptr to hold the factory
* heap allocated. ---> see Ticket #248
*/
template< template<class CONF> class Factory
, typename FUNC ///< common function type of all Factory instances
, typename PAR ///< ctor parameter of the Factories
>
class ConfigSelector
{
typedef std::tr1::function<FUNC> FacFunction;
template<class FAC>
struct FactoryHolder ///< impl type erasure
: private FAC,
public FacFunction
{
FactoryHolder(PAR p)
: FAC(p),
FacFunction( static_cast<FAC&>(*this))
{ }
};
typedef std::tr1::shared_ptr<FacFunction> PFunc;
typedef std::map<size_t, PFunc> ConfigTable;
ConfigTable possibleConfig_; ///< Table of factories
/** Helper: a visitor usable with FlagInfo */
struct FactoryTableBuilder
{
PAR ctor_param_;
ConfigTable& factories_;
FactoryTableBuilder (ConfigTable& tab, PAR p)
: ctor_param_(p),
factories_(tab) { }
/* === visitation interface === */
typedef void Ret;
template<class CONF>
void
visit (size_t code)
{
PFunc pFactory (new FactoryHolder<Factory<CONF> > (ctor_param_));
factories_[code] = pFactory;
}
void done() {}
};
public:
template<class CONFS>
ConfigSelector(CONFS const&, PAR factory_ctor_param)
{
FactoryTableBuilder buildTable(this->possibleConfig_,
factory_ctor_param );
// store a new Factory instance
// for each possible Flag-Configuration
FlagInfo<CONFS>::accept (buildTable);
}
FacFunction&
operator[] (size_t configFlags) ///< retrieve the factory corresponding to the given config
{
if (contains (possibleConfig_, configFlags))
return *possibleConfig_[configFlags];
else
throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="
+std::bitset<CONFIG_FLAGS_MAX>(configFlags).to_string());
}
};
}} // namespace engine::config
#endif

View file

@ -24,7 +24,7 @@
#include "proc/engine/procnode.hpp"
#include "proc/engine/nodewiring.hpp"
#include "proc/engine/nodeoperation.hpp"
#include "proc/engine/nodewiringconfig.hpp"
#include "proc/engine/nodewiring-config.hpp"
#include "lib/meta/typelist-util.hpp"

View file

@ -1,183 +0,0 @@
/*
NODEWIRINGCONFIG.hpp - Helper for representing and selecting the wiring case
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 nodewiringconfig.hpp
** Sometimes we need to choose a different implementation for dealing with
** some special cases. While for simple cases, just testing a flag or using a
** switch statement will do the job, matters get more difficult when we have to
** employ a completely different execution path for each of the different cases,
** while using a set of common building blocks.
** \par
** In the Lumiera render engine, right on the critical path, we need some glue code
** for invoking the predecessor nodes when pulling a given processing node. The actual
** sequence is quite dependent on the specific situation each node is wired up, regarding
** buffer allocation, cache querying and the possible support for GPU processing and
** render farms. The solution is to define specialisations of a Strategy template
** using the specific configuration as template argument. Based on these, we can
** create a collection of factories, which in turn will build the internal wiring
** for the individual ProcNode instances in accordance to the situation determined
** for this node, expressed as a set of flags. As a net result, each node has an
** individual configuration (as opposed to creating a lot of hand-written individual
** ProcNode subclasses), but parts of this configuration assembly is done already at
** compile time, allowing for additional checks by typing and for possible optimisation.
**
** @see proc::engine::config::Strategy
** @see nodewiring.hpp
** @see configflags.hpp
**
*/
#ifndef ENGINE_NODEWIRINGCONFIG_H
#define ENGINE_NODEWIRINGCONFIG_H
#include "lib/util.hpp"
#include "lib/meta/configflags.hpp"
#include <tr1/functional>
#include <bitset>
#include <map>
namespace engine {
namespace config {
using lumiera::typelist::FlagInfo;
using util::contains;
using lumiera::typelist::CONFIG_FLAGS_MAX;
/**
* Helper for fabricating ProcNode Wiring configurations.
* This object builds a table of factories, holding one factory
* for each possible node configuration. Provided with the desired
* configuration encoded as bits, the related factory can be invoked,
* thus producing a product object for the given configuration.
*
* \par implementation notes
* The actual factory class is templated, so it will be defined
* at the use site of ConfigSelector. Moreover, this factory
* usually expects an ctor argument, which will be fed through
* when creating the ConfigSelector instance. This is one of
* the reasons why we go through all these complicated factory
* building: this ctor argument usually brings in a reference
* to the actual memory allocator. Thus we have to rebuild the
* ConfigSelector each time we switch and rebuild the ProcNode
* factories, which in turn happens each time we use a new
* bulk allocation memory block -- typically for each separate
* segment of the Timeline and processing node graph.
*
* Now the selection of the possible flag configurations, for which
* Factory instances are created in the table, is governed by the
* type parameter of the ConfigSelector ctor. This type parameter
* needs to be a Typelist of Typelists, each representing a flag
* configuration. The intention is to to drive this selection by
* the use of template metaprogramming for extracting all
* currently defined StateProxy object configurations.
* @todo as the factories live only within the enclosed table (map)
* we could allocate them in-place. Unfortunately this is
* non-trivial, because the STL containers employ
* value semantics and thus do a copy even on insert.
* Thus, for now we use a shared_ptr to hold the factory
* heap allocated.
*/
template< template<class CONF> class Factory
, typename FUNC ///< common function type of all Factory instances
, typename PAR ///< ctor parameter of the Factories
>
class ConfigSelector
{
typedef std::tr1::function<FUNC> FacFunction;
template<class FAC>
struct FactoryHolder ///< impl type erasure
: private FAC,
public FacFunction
{
FactoryHolder(PAR p)
: FAC(p),
FacFunction( static_cast<FAC&>(*this))
{ }
};
typedef std::tr1::shared_ptr<FacFunction> PFunc;
typedef std::map<size_t, PFunc> ConfigTable;
ConfigTable possibleConfig_; ///< Table of factories
/** Helper: a visitor usable with FlagInfo */
struct FactoryTableBuilder
{
PAR ctor_param_;
ConfigTable& factories_;
FactoryTableBuilder (ConfigTable& tab, PAR p)
: ctor_param_(p),
factories_(tab) { }
/* === visitation interface === */
typedef void Ret;
template<class CONF>
void
visit (size_t code)
{
PFunc pFactory (new FactoryHolder<Factory<CONF> > (ctor_param_));
factories_[code] = pFactory;
}
void done() {}
};
public:
template<class CONFS>
ConfigSelector(CONFS const&, PAR factory_ctor_param)
{
FactoryTableBuilder buildTable(this->possibleConfig_,
factory_ctor_param );
// store a new Factory instance
// for each possible Flag-Configuration
FlagInfo<CONFS>::accept (buildTable);
}
FacFunction&
operator[] (size_t configFlags) ///< retrieve the factory corresponding to the given config
{
if (contains (possibleConfig_, configFlags))
return *possibleConfig_[configFlags];
else
throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="
+std::bitset<CONFIG_FLAGS_MAX>(configFlags).to_string());
}
};
} // namespace config
} // namespace engine
#endif

View file

@ -31,7 +31,7 @@
**
** By using the builder interface, concrete node and wiring descriptor classes are created,
** based on some templates. These concrete classes form the "glue" to tie the node network
** together and contain much of the operation beahviour in a hard wired fashion.
** together and contain much of the operation behaviour in a hard wired fashion.
**
** @see nodefactory.hpp
** @see operationpoint.hpp
@ -130,16 +130,16 @@ namespace engine {
public:
/** Engine Core operation: render and pull output from this node.
* On return, currentProcess will hold onto output buffer(s)
* On return, currentProcess will hold onto output buffer(s)
* containing the calculated result frames. In case this node
* calculates a multichannel output, only one channel can be
* calculates a multichannel output, only one channel can be
* retrieved by such a \c pull() call, but you can expect data
* of the other channels to be processed and fed to cache.
* @param currentProcess the current processing state for
* managing buffers and accessing current parameter values
* @param requestedOutputNr the output channel requested
* (in case this node delivers more than one output channel)
* @return handle to the buffer containing the calculated result.
* @return handle to the buffer containing the calculated result.
*/
BuffHandle
pull (State& currentProcess, uint requestedOutputNr=0) const

View file

@ -22,73 +22,73 @@
#include "proc/mobject/builder/toolfactory.hpp"
#include "common/util.hpp"
#include "lib/util.hpp"
//#include <boost/ptr_container/ptr_vector.hpp>
namespace mobject {
namespace builder {
using util::isnil;
struct BuildProcessState
{
session::Fixture & fixedTimeline_;
std::auto_ptr<engine::RenderGraph> procSegment_;
boost::scoped_ptr<SegementationTool> segmentation_;
boost::scoped_ptr<NodeCreatorTool> fabrication_;
BuildProcessState (session::Fixture& theTimeline)
: fixedTimeline_(theTimeline),
procSegment_(new engine::RenderGraph())
{ }
};
ToolFactory::ToolFactory (session::Fixture& theTimeline)
: state_(new BuildProcessState (theTimeline))
namespace builder {
using util::isnil;
using std::auto_ptr;
using boost::scoped_ptr;
struct BuildProcessState
{
ENSURE (state_->fixedTimeline_.isValid());
ENSURE (state_->procSegment_);
}
SegmentationTool &
ToolFactory::configureSegmentation ()
{
REQUIRE (state_->fixedTimeline_.isValid());
REQUIRE (state_->procSegment_);
state->segmentation_.reset (new SegmentationTool (state->fixedTimeline_));
return *(state->segmentation_);
}
NodeCreatorTool &
ToolFactory::configureFabrication () //////////////////////TODO: should iterate in some way!
{
REQUIRE (state_->procSegment_);
REQUIRE (!isnil (*(state_>segmentation)));
session::Fixture & fixedTimeline_;
auto_ptr<engine::RenderGraph> procSegment_;
state->fabrication_.reset (new NodeCreatorTool(*this));
return *(state->fabrication_);
}
scoped_ptr<SegementationTool> segmentation_;
scoped_ptr<NodeCreatorTool> fabrication_;
BuildProcessState (session::Fixture& theTimeline)
: fixedTimeline_(theTimeline),
procSegment_(new engine::RenderGraph())
{ }
};
ToolFactory::ToolFactory (session::Fixture& theTimeline)
: state_(new BuildProcessState (theTimeline))
{
ENSURE (state_->fixedTimeline_.isValid());
ENSURE (state_->procSegment_);
}
SegmentationTool &
ToolFactory::configureSegmentation ()
{
REQUIRE (state_->fixedTimeline_.isValid());
REQUIRE (state_->procSegment_);
state->segmentation_.reset (new SegmentationTool (state->fixedTimeline_));
return *(state->segmentation_);
}
NodeCreatorTool &
ToolFactory::configureFabrication () //////////////////////TODO: should iterate in some way!
{
REQUIRE (state_->procSegment_);
REQUIRE (!isnil (*(state_>segmentation)));
std::auto_ptr<engine::RenderGraph>
ToolFactory::getProduct ()
{
state_->segmentation_.reset(0);
state_->fabrication_.reset(0);
return state_->procSegment_;
}
} // namespace mobject::builder
} // namespace mobject
state->fabrication_.reset (new NodeCreatorTool(*this));
return *(state->fabrication_);
}
auto_ptr<engine::RenderGraph>
ToolFactory::getProduct ()
{
state_->segmentation_.reset(0);
state_->fabrication_.reset(0);
return state_->procSegment_;
}
}} // namespace mobject::builder

View file

@ -31,44 +31,43 @@
#include "proc/engine/rendergraph.hpp"
#include <boost/scoped_ptr.hpp>
#include <memory>
namespace mobject {
namespace builder {
struct BuildProcessState;
/**
* provides the builder with the necessary, preconfigured tools.
* Dedicated to a single build process, it holds the internal state
* of this process and thus serves to coordinate and link together all
* the individual parts fabricated by using the various tools.
*/
class ToolFactory
{
boost::scoped_ptr<BuildProcessState> state_;
public:
/** prepare a builder tool kit for dealing with the given Fixture,
* which is a snapshot of some timeline made explicit. */
ToolFactory (session::Fixture&);
/** prepare a tool for properly segmenting the Fixture */
SegmentationTool & configureSegmentation ();
/** prepare a tool for building the render engine (graph) for a single segment */
NodeCreatorTool & configureFabrication ();
/** receive the finished product of the build process; effectively
* releases any other builder tool object */
std::auto_ptr<engine::RenderGraph> getProduct ();
};
} // namespace mobject::builder
} // namespace mobject
namespace builder {
struct BuildProcessState;
/**
* provides the builder with the necessary, preconfigured tools.
* Dedicated to a single build process, it holds the internal state
* of this process and thus serves to coordinate and link together all
* the individual parts fabricated by using the various tools.
*/
class ToolFactory
{
boost::scoped_ptr<BuildProcessState> state_;
public:
/** prepare a builder tool kit for dealing with the given Fixture,
* which is a snapshot of some timeline made explicit. */
ToolFactory (session::Fixture&);
/** prepare a tool for properly segmenting the Fixture */
SegmentationTool & configureSegmentation ();
/** prepare a tool for building the render engine (graph) for a single segment */
NodeCreatorTool & configureFabrication ();
/** receive the finished product of the build process; effectively
* releases any other builder tool object */
std::auto_ptr<engine::RenderGraph> getProduct ();
};
}} // namespace mobject::builder
#endif

View file

@ -32,7 +32,7 @@
**
** @see configflags.hpp
** @see typelistmanip.hpp
** @see nodewiringconfig.hpp real world usage example
** @see nodewiring-config.hpp real world usage example
**
*/
@ -43,7 +43,7 @@
#include "lib/meta/typelist-util.hpp"
#include "lib/meta/configflags.hpp"
#include "meta/typelist-diagnostics.hpp"
#include "proc/engine/nodewiringconfig.hpp"
#include "proc/engine/nodewiring-config.hpp"
#include "lib/util.hpp"
#include <boost/format.hpp>

View file

@ -33,7 +33,7 @@
**
** @see typelist-test.cpp
** @see typelist-util.hpp
** @see nodewiringconfig.hpp real world usage example
** @see nodewiring-config.hpp real world usage example
**
*/