Skip to content

Commit

Permalink
Simplified Executable nodes.
Browse files Browse the repository at this point in the history
I originally suggested to Lucio that he develop the Executable nodes using multiple inheritance of an Executable interface class, so that nodes could be both say a SceneNode and also Executable. That turned out to be a bad idea because the class hierarchy was rather confusing and it put the onus on developers to remember to call various methods to add plugs, test inputs were accepted etc. This led to problems like the crash bug in GafferHQ#681, and was just generally a bit more awkward than necessary.

This commit simplifies things so there is now just a single ExecutableNode base class which fits in nicely with the DependencyNode and ComputeNode base classes. The implementations for the various derived classes are now a fair bit simpler, and the bindings are performed with a new ExecutableNodeClass which matches nicely with the NodeClass and DependencyNodeClass we already have.
  • Loading branch information
johnhaddon committed May 13, 2014
1 parent 6e0f3e4 commit 2911183
Show file tree
Hide file tree
Showing 27 changed files with 389 additions and 651 deletions.
29 changes: 14 additions & 15 deletions include/Gaffer/Despatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,16 @@
#include "boost/signals.hpp"
#include "IECore/RunTimeTyped.h"
#include "Gaffer/TypeIds.h"
#include "Gaffer/Executable.h"
#include "Gaffer/ExecutableNode.h"

namespace Gaffer
{

IE_CORE_FORWARDDECLARE( Node )
IE_CORE_FORWARDDECLARE( Despatcher )
IE_CORE_FORWARDDECLARE( CompoundPlug )

/// Pure virtual class that specifies the interface for objects that
/// know how to run Executable nodes. They are also used after
/// know how to run ExecutableNodes. They are also used after
/// construction of Execution nodes to add custom plugs that
/// can tweak how they operate, for example, farm parameters.
class Despatcher : public IECore::RunTimeTyped
Expand All @@ -67,7 +66,7 @@ class Despatcher : public IECore::RunTimeTyped

IE_CORE_DECLARERUNTIMETYPEDEXTENSION( Gaffer::Despatcher, DespatcherTypeId, IECore::RunTimeTyped );

typedef boost::signal<void (const Despatcher *, const std::vector< NodePtr > &)> DespatchSignal;
typedef boost::signal<void (const Despatcher *, const std::vector<ExecutableNodePtr> &)> DespatchSignal;

/// @name Despatch signals
/// These signals are emitted on despatch events for any registered
Expand All @@ -80,7 +79,7 @@ class Despatcher : public IECore::RunTimeTyped
static DespatchSignal &postDespatchSignal();

/// Triggers the preDespatch signal and calls doDespatch() function.
void despatch( const std::vector< NodePtr > &nodes ) const;
void despatch( const std::vector<ExecutableNodePtr> &nodes ) const;

/// Registration function for despatchers.
static void registerDespatcher( std::string name, DespatcherPtr despatcher );
Expand All @@ -93,39 +92,39 @@ class Despatcher : public IECore::RunTimeTyped

protected :

friend class Executable;
friend class ExecutableNode;

/// Despatches the execution of the given array of Executable nodes (and possibly their requirements).
virtual void doDespatch( const std::vector< NodePtr > &nodes ) const = 0;
/// Despatches the execution of the given array of ExecutableNodes (and possibly their requirements).
virtual void doDespatch( const std::vector<ExecutableNodePtr> &nodes ) const = 0;

/// This function is called to add custom Despatcher plugs during Executable node construction.
/// This function is called to add custom Despatcher plugs during ExecutableNode construction.
static void addAllPlugs( CompoundPlug *despatcherPlug );

/// Function called by addAllPlugs. Despatchers have a chance to create custom plugs on Executable nodes.
/// Function called by addAllPlugs. Despatchers have a chance to create custom plugs on ExecutableNodes.
/// The function must accept situations where the node already has the plugs (nodes loaded from a scene).
virtual void addPlugs( CompoundPlug *despatcherPlug ) const = 0;

/// Representation of a Executable task plus it's requirements as other tasks.
/// Representation of a ExecutableNode task plus it's requirements as other tasks.
struct TaskDescription
{
Executable::Task task;
std::set<Executable::Task> requirements;
ExecutableNode::Task task;
std::set<ExecutableNode::Task> requirements;
};

/// Utility function that recursivelly collects all nodes and their execution requirements and flattens into
/// a list of unique Tasks (with unique executionHash) and their requirements. For nodes that don't compute (executionHash
/// returns default hash), this function will consider a separate task for each unique set of requirements. For all the other nodes
/// they will be grouped by executionHash and their final requirements will be the union of all the requirements under that same
/// hash.
static void uniqueTasks( const Executable::Tasks &tasks, std::vector< TaskDescription > &uniqueTasks );
static void uniqueTasks( const ExecutableNode::Tasks &tasks, std::vector< TaskDescription > &uniqueTasks );

private :

typedef std::map< std::string, DespatcherPtr > DespatcherMap;
static DespatcherMap g_despatchers;

typedef std::map< IECore::MurmurHash, std::vector< size_t > > TaskSet;
static const Executable::Task &uniqueTask( const Executable::Task &task, std::vector< Despatcher::TaskDescription > &uniqueTasks, TaskSet &seenTasks );
static const ExecutableNode::Task &uniqueTask( const ExecutableNode::Task &task, std::vector< Despatcher::TaskDescription > &uniqueTasks, TaskSet &seenTasks );

static DespatchSignal g_preDespatchSignal;
static DespatchSignal g_postDespatchSignal;
Expand Down
117 changes: 0 additions & 117 deletions include/Gaffer/Executable.h

This file was deleted.

71 changes: 62 additions & 9 deletions include/Gaffer/ExecutableNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,82 @@
#ifndef GAFFER_EXECUTABLENODE_H
#define GAFFER_EXECUTABLENODE_H

#include "Gaffer/Executable.h"
#include "Gaffer/Node.h"

namespace Gaffer
{

/// Base class for Executable Nodes.
/// This class is particularly useful for the python bindings, as the base class for python Executable nodes.
/// The python bindings for this class adds a static function bool isExecutable() that can be used to detect
/// Executable instances and/or classes.
class ExecutableNode : public Node, public Executable
IE_CORE_FORWARDDECLARE( Context )
IE_CORE_FORWARDDECLARE( ExecutableNode )
IE_CORE_FORWARDDECLARE( ArrayPlug )

/// A base class for nodes with external side effects such as the creation of files,
/// rendering, etc. ExecutableNodes can be chained together with other Executable nodes
/// to define a required execution order. Typically Executable nodes should be executed
/// by Despatcher classes that can query the required execution order and schedule it
/// appropriately.
class ExecutableNode : public Node
{

public :

/// A Task defines the execution of an Executable node in a specific Context.
/// It's used in Executable nodes to describe the requirements and in Despatchers
/// to represent what they are supposed to execute.
/// The comparison and hash methods can be used for building sets of unique Tasks.
class Task
{
public :

Task();
Task( const Task &t );
Task( ExecutableNodePtr n, ContextPtr c );
IECore::MurmurHash hash() const;
bool operator == ( const Task &rhs ) const;
bool operator < ( const Task &rhs ) const;

ExecutableNodePtr node;
ContextPtr context;
};

typedef std::vector<Task> Tasks;
typedef std::vector<ConstContextPtr> Contexts;

IE_CORE_DECLARERUNTIMETYPEDEXTENSION( Gaffer::ExecutableNode, ExecutableNodeTypeId, Node );

ExecutableNode( const std::string &name=defaultName<ExecutableNode>() );

virtual ~ExecutableNode();
};

IE_CORE_DECLAREPTR( ExecutableNode )
ArrayPlug *requirementsPlug();
const ArrayPlug *requirementsPlug() const;

Plug *requirementPlug();
const Plug *requirementPlug() const;

/// Fills requirements with all tasks that must be completed before execute()
/// can be called. The default implementation declares requirements defined
/// by the inputs to the requirementsPlug().
virtual void executionRequirements( const Context *context, Tasks &requirements ) const;

/// Returns a hash that uniquely represents the side effects (files created etc) of
/// calling execute with the given context. If the node returns the default hash it
/// means this node does not compute anything.
virtual IECore::MurmurHash executionHash( const Context *context ) const = 0;

/// Executes this node for all the specified contexts in sequence.
virtual void execute( const Contexts &contexts ) const = 0;

protected :

/// Implemented to deny inputs to requirementsPlug() which do not come from
/// the requirementPlug() of another ExecutableNode.
virtual bool acceptsInput( const Plug *plug, const Plug *inputPlug ) const;

private :

static size_t g_firstPlugIndex;

};

} // namespace Gaffer

Expand Down
9 changes: 2 additions & 7 deletions include/Gaffer/ExecutableOpHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#ifndef GAFFER_EXECUTABLEOPHOLDER_H
#define GAFFER_EXECUTABLEOPHOLDER_H

#include "Gaffer/Executable.h"
#include "Gaffer/ParameterisedHolder.h"

namespace IECore
Expand All @@ -53,12 +52,12 @@ namespace Gaffer
IE_CORE_FORWARDDECLARE( ParameterHandler )

/// Node for Ops that can be executed on their own in the farm or in a separate process.
class ExecutableOpHolder : public ParameterisedHolderNode, public Executable
class ExecutableOpHolder : public ParameterisedHolderExecutableNode
{

public :

IE_CORE_DECLARERUNTIMETYPEDEXTENSION( Gaffer::ExecutableOpHolder, ExecutableOpHolderTypeId, ParameterisedHolderNode );
IE_CORE_DECLARERUNTIMETYPEDEXTENSION( Gaffer::ExecutableOpHolder, ExecutableOpHolderTypeId, ParameterisedHolderExecutableNode );

ExecutableOpHolder( const std::string &name=defaultName<ExecutableOpHolder>() );

Expand All @@ -70,13 +69,9 @@ class ExecutableOpHolder : public ParameterisedHolderNode, public Executable
IECore::OpPtr getOp( std::string *className = 0, int *classVersion = 0 );
IECore::ConstOpPtr getOp( std::string *className = 0, int *classVersion = 0 ) const;

virtual void executionRequirements( const Context *context, Tasks &requirements ) const;
virtual IECore::MurmurHash executionHash( const Context *context ) const;
virtual void execute( const Contexts &contexts ) const;

protected :

virtual bool acceptsInput( const Plug *plug, const Plug *inputPlug ) const;
};

IE_CORE_DECLAREPTR( ExecutableOpHolder )
Expand Down
3 changes: 3 additions & 0 deletions include/Gaffer/ParameterisedHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#define GAFFER_PARAMETERISEDHOLDER_H

#include "Gaffer/ComputeNode.h"
#include "Gaffer/ExecutableNode.h"

namespace IECore
{
Expand Down Expand Up @@ -121,10 +122,12 @@ class ParameterisedHolder : public BaseType
typedef ParameterisedHolder<Node> ParameterisedHolderNode;
typedef ParameterisedHolder<DependencyNode> ParameterisedHolderDependencyNode;
typedef ParameterisedHolder<ComputeNode> ParameterisedHolderComputeNode;
typedef ParameterisedHolder<ExecutableNode> ParameterisedHolderExecutableNode;

IE_CORE_DECLAREPTR( ParameterisedHolderNode )
IE_CORE_DECLAREPTR( ParameterisedHolderDependencyNode )
IE_CORE_DECLAREPTR( ParameterisedHolderComputeNode )
IE_CORE_DECLAREPTR( ParameterisedHolderExecutableNode )

} // namespace Gaffer

Expand Down
1 change: 1 addition & 0 deletions include/Gaffer/TypeIds.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ enum TypeId
BackdropTypeId = 110069,
SwitchComputeNodeTypeId = 110070,
SwitchDependencyNodeTypeId = 110071,
ParameterisedHolderExecutableNodeTypeId = 110072,
LastTypeId = 110200,

};
Expand Down
Loading

0 comments on commit 2911183

Please sign in to comment.