WIP finally got the design straight?
This commit is contained in:
parent
48780bef74
commit
cb8ac94dcf
3 changed files with 195 additions and 156 deletions
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "common/error.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/procnode.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <vector>
|
||||
|
|
@ -61,49 +62,6 @@ namespace engine {
|
|||
};
|
||||
|
||||
|
||||
class BuffTableStorage;
|
||||
class WiringDescriptor;
|
||||
|
||||
/**
|
||||
* to be allocated on the stack while evaluating a ProcNode#pull() call.
|
||||
* The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
|
||||
* which can be used to crate such chunks. The claiming and releasing of
|
||||
* slots in the BuffTableStorage is automatically tied to BuffTableChunk
|
||||
* object's lifecycle.
|
||||
*/
|
||||
class BuffTableChunk
|
||||
: public BuffTable,
|
||||
boost::noncopyable
|
||||
{
|
||||
const uint siz_;
|
||||
BuffTable::Chunk tab_;
|
||||
BuffTableStorage& sto_;
|
||||
|
||||
public:
|
||||
BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
|
||||
: siz_(wd.getNrI() + wd.getNrO()),
|
||||
tab_(storage.claim (siz_)),
|
||||
sto_(storage)
|
||||
{
|
||||
const uint nrO(wd.getNrO());
|
||||
|
||||
// Setup the public visible table locations
|
||||
this->outHandle = &tab_.first[ 0 ];
|
||||
this->inHandle = &tab_.first[nrO];
|
||||
this->outBuff = &tab_.second[ 0 ];
|
||||
this->inBuff = &tab_.second[nrO];
|
||||
}
|
||||
|
||||
~BuffTableChunk ()
|
||||
{
|
||||
sto_.release (siz_);
|
||||
ASSERT ( sto_.level_check (tab_),
|
||||
"buffer management logic broken.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class BuffTableStorage
|
||||
{
|
||||
vector<BuffHandle> hTab_;
|
||||
|
|
@ -158,5 +116,47 @@ namespace engine {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* to be allocated on the stack while evaluating a ProcNode#pull() call.
|
||||
* The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
|
||||
* which can be used to crate such chunks. The claiming and releasing of
|
||||
* slots in the BuffTableStorage is automatically tied to BuffTableChunk
|
||||
* object's lifecycle.
|
||||
*/
|
||||
class BuffTableChunk
|
||||
: public BuffTable,
|
||||
boost::noncopyable
|
||||
{
|
||||
const uint siz_;
|
||||
BuffTable::Chunk tab_;
|
||||
BuffTableStorage& sto_;
|
||||
|
||||
public:
|
||||
BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
|
||||
: siz_(wd.getNrI() + wd.getNrO()),
|
||||
tab_(storage.claim (siz_)),
|
||||
sto_(storage)
|
||||
{
|
||||
const uint nrO(wd.getNrO());
|
||||
|
||||
// Setup the public visible table locations
|
||||
this->outHandle = &tab_.first[ 0 ];
|
||||
this->inHandle = &tab_.first[nrO];
|
||||
this->outBuff = &tab_.second[ 0 ];
|
||||
this->inBuff = &tab_.second[nrO];
|
||||
}
|
||||
|
||||
~BuffTableChunk ()
|
||||
{
|
||||
sto_.release (siz_);
|
||||
ASSERT ( sto_.level_check (tab_),
|
||||
"buffer management logic broken.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace engine
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,16 +73,6 @@ namespace engine {
|
|||
* call, using setup/wiring data preconfigured by the builder.
|
||||
* Its job is to provide the actual implementation of the Cache
|
||||
* push / fetch and recursive downcall to render the source frames.
|
||||
*
|
||||
* \par assembling the implementation
|
||||
* This is the abstract base class of all concrete StateAdapter types.
|
||||
* Each ProcNode#pull() call creates such a StateAdapter on the stack,
|
||||
* with a concrete type according to the WiringDescriptor of the node
|
||||
* to pull. The concrete type is assembled by a chain of policy templates
|
||||
* on top of InvocationStateBase. For each of the possible configuratons
|
||||
* we define such a chain (see bottom of this header). The WiringFactory
|
||||
* defined in nodewiring.cpp actually drives the instantiation of all
|
||||
* those possible combinations
|
||||
*/
|
||||
class StateAdapter
|
||||
: public State
|
||||
|
|
@ -97,6 +87,8 @@ namespace engine {
|
|||
{ }
|
||||
|
||||
virtual State& getCurrentImplementation () { return current_; }
|
||||
|
||||
typedef State& (StateAdapter::*BufferProvider);
|
||||
|
||||
|
||||
public: /* === proxying the State interface === */
|
||||
|
|
@ -110,10 +102,28 @@ namespace engine {
|
|||
|
||||
// note: allocateBuffer() is choosen specifically based on the actual node wiring
|
||||
|
||||
|
||||
protected: /* === API for use by the node operation pull() call === */
|
||||
|
||||
BufferDescriptor
|
||||
getBufferDescriptor (uint chan)
|
||||
{
|
||||
TODO ("determine BufferDescriptor based on WiringDescriptor channel info");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Invocation context state.
|
||||
* A ref to this type is carried through the chain of NEXT::step() functions
|
||||
* which form the actual invocation sequence. The various operations in this sequence
|
||||
* access the context via the references in this struct, and also using the inherited
|
||||
* public State intercace. The object instance actually used as Invocation is created
|
||||
* on the stack and parametrized according to the necessities of the invocation
|
||||
* sequence actually configured. This real object instance comes in two flavours:
|
||||
* - a intended/partial invocation (which may be short-circuited due to Cache hit)
|
||||
* - a complete invocation, including storage for managing the buffer handles.
|
||||
*/
|
||||
struct Invocation
|
||||
: StateAdapter
|
||||
{
|
||||
|
|
@ -122,43 +132,98 @@ namespace engine {
|
|||
const uint outNr;
|
||||
|
||||
protected:
|
||||
Invocation (State& callingProcess, WiringDescriptor const& w, BuffTable& b, uint o)
|
||||
/** creates a new invocation context state,
|
||||
* configuring it as intended/partial invocation without BuffTable */
|
||||
Invocation (State& callingProcess, WiringDescriptor const& w, BufferProvider buffSrc, uint o)
|
||||
: StateAdapter(callingProcess),
|
||||
wiring(w),
|
||||
buffTab(b),
|
||||
outNr(o)
|
||||
wiring(w), outNr(o),
|
||||
bufferProvider_(buffSrc)
|
||||
{ }
|
||||
|
||||
/** used to build a complete invocation, including a buffer table,
|
||||
based on an existing intended/partial invocation */
|
||||
Invocation (Invocation const& currInvocation, BuffTable& localBuffTab)
|
||||
: StateAdapter(currInvocation),
|
||||
wiring(currInvocation.wiring),
|
||||
buffTab(localBuffTab),
|
||||
outNr(currInvocation.outNr),
|
||||
bufferProvider_(currInvocation.bufferProvider_)
|
||||
{ }
|
||||
|
||||
/** configuration of the actual entity in charge of allocating Buffers.
|
||||
* depending on the configured call sequence, this may be a parent StateAdapter,
|
||||
* or the backend in case we want to fade the results into the Cache */
|
||||
const BufferProvider bufferProvider_;
|
||||
|
||||
public:
|
||||
BuffHandle const&
|
||||
allocateBuffer (uint outChannel)
|
||||
{
|
||||
return this->*bufferProvider_.allocateBuffer(
|
||||
this->getBufferDescriptor(outChannel));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<uint SZ> /////////////////TODO: rather template on the BufferProvider
|
||||
class InvocationImpl
|
||||
: private BuffTableStorage<SZ>,
|
||||
public Invocation
|
||||
/**
|
||||
* The real invocation context state implementation. It is created
|
||||
* by the NodeWiring (WiringDescriptor) of the processing node which
|
||||
* is pulled by this invocation, hereby using the internal configuration
|
||||
* information to guide the selecton of the real call sequence
|
||||
*
|
||||
* \par assembling the call sequence implementation
|
||||
* Each ProcNode#pull() call creates such a StateAdapter subclass on the stack,
|
||||
* with a concrete type according to the WiringDescriptor of the node to pull.
|
||||
* This concrete type encodes a calculation Strategy, which is assembled
|
||||
* as a chain of policy templates on top of OperationBase. For each of the
|
||||
* possible configuratons we define such a chain (see bottom of this header).
|
||||
* The WiringFactory defined in nodewiring.cpp actually drives the instantiation
|
||||
* of all those possible combinations.
|
||||
*/
|
||||
template<class Strategy, StateAdapter::BufferProvider buffPro>
|
||||
class ActualInvocationOrder
|
||||
: public Invocation
|
||||
{
|
||||
|
||||
protected:
|
||||
public:
|
||||
InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh)
|
||||
: BuffTableStorage<SZ>(w),
|
||||
Invocation(callingProcess, w, static_cast<BuffTable&>(*this), outCh)
|
||||
: Invocation(callingProcess, w, buffPro, outCh)
|
||||
{ }
|
||||
|
||||
/** contains the details of Cache query and recursive calls
|
||||
* to the predecessor node(s), eventually followed by the
|
||||
* ProcNode::process() callback
|
||||
*/
|
||||
template<class Strategy>
|
||||
BuffHandle retrieve ()
|
||||
{
|
||||
return Strategy::step (*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Extends the invocation context state by a table of buffer pointers.
|
||||
* This extension is used whenever the call sequence goes into actually
|
||||
* invoking a process() function, which necessiates to build a array of
|
||||
* buffer pointers and to allocate buffers to hold the data to be worked on.
|
||||
* It is implemented as decorating an already created "partial invocation",
|
||||
* just adding the allocation of a Table Chunk for organizing the buffers.
|
||||
*/
|
||||
class ProcessInvocation
|
||||
: protected BuffTableChunk,
|
||||
public Invocation
|
||||
{
|
||||
public:
|
||||
ProcessInvocation (Invocation const& currInvocation)
|
||||
: BuffTableChunk(currInvocation.wiring, currInvocation.getBuffTableStorage()),
|
||||
Invocation(currInvocation, static_cast<BuffTable&>(*this))
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Collection of functions used to build up the invocation sequence.
|
||||
*/
|
||||
class OperationBase
|
||||
{
|
||||
|
||||
|
|
@ -170,9 +235,8 @@ namespace engine {
|
|||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
{
|
||||
BuffHandle fetched = this->current_.fetch (
|
||||
this->genFrameID (
|
||||
this->requiredOutputNr));
|
||||
BuffHandle fetched = ivo.fetch (
|
||||
this->genFrameID (ivo));
|
||||
if (fetched)
|
||||
return fetched;
|
||||
else
|
||||
|
|
@ -185,25 +249,25 @@ namespace engine {
|
|||
struct PullInput : NEXT
|
||||
{
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
step (Invocation& started_invocation)
|
||||
{
|
||||
this->createBuffTable();
|
||||
ProcessInvocation ivo (started_invocation);
|
||||
|
||||
ASSERT (this->buffTab);
|
||||
ASSERT (0 < this->buffTabSize());
|
||||
ASSERT (this->nrO == this->nrI );
|
||||
ASSERT (this->nrO+this->nrI <= this->buffTabSize());
|
||||
ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]);
|
||||
BuffHandle *inH = this->buffTab->inHandles;
|
||||
BuffHandle::PBuff *inBuff = this->buffTab->inBuffs;
|
||||
ASSERT (ivo.buffTab);
|
||||
ASSERT (0 < ivo.buffTabSize());
|
||||
ASSERT (ivo.nrO() == ivo.nrI() );
|
||||
ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
|
||||
ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
|
||||
BuffHandle *inH = ivo.buffTab->inHandles;
|
||||
BuffHandle::PBuff *inBuff = ivo.buffTab->inBuffs;
|
||||
|
||||
for (uint i = 0; i < this->nrI; ++i )
|
||||
for (uint i = 0; i < ivo.nrI(); ++i )
|
||||
{
|
||||
inBuff[i] =
|
||||
*(inH[i] = this->pullPredecessor(i)); // invoke predecessor
|
||||
*(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor
|
||||
// now Input #i is ready...
|
||||
}
|
||||
return NEXT::step (ivo);
|
||||
return NEXT::step (ivo); // note: passing down a ref to the ProcessInvocation
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -212,23 +276,23 @@ namespace engine {
|
|||
struct ReadSource : NEXT
|
||||
{
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
step (Invocation& started_invocation)
|
||||
{
|
||||
this->createBuffTable();
|
||||
ProcessInvocation ivo (started_invocation);
|
||||
|
||||
ASSERT (this->buffTab);
|
||||
ASSERT (0 < this->buffTabSize());
|
||||
ASSERT (this->nrO+this->nrI <= this->buffTabSize());
|
||||
ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]);
|
||||
BuffHandle *inH = this->buffTab->inHandles;
|
||||
BuffHandle *outH = this->buffTab->handles;
|
||||
BuffHandle::PBuff *inBuff = this->buffTab->inBuffs;
|
||||
BuffHandle::PBuff *outBuff = this->buffTab->buffers;
|
||||
ASSERT (ivo.buffTab);
|
||||
ASSERT (0 < ivo.buffTabSize());
|
||||
ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
|
||||
ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
|
||||
BuffHandle *inH = ivo.buffTab->inHandles;
|
||||
BuffHandle *outH = ivo.buffTab->handles;
|
||||
BuffHandle::PBuff *inBuff = ivo.buffTab->inBuffs;
|
||||
BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
|
||||
|
||||
for (uint i = 0; i < this->nrI; ++i )
|
||||
for (uint i = 0; i < ivo.nrI(); ++i )
|
||||
{
|
||||
inBuff[i] = outBuff[i] =
|
||||
*(inH[i] = outH[i] = this->getSource(i)); // TODO: how to access source nodes???
|
||||
*(inH[i] = outH[i] = this->getSource(ivo,i));
|
||||
// now Input #i is ready...
|
||||
}
|
||||
return NEXT::step (ivo);
|
||||
|
|
@ -236,97 +300,71 @@ namespace engine {
|
|||
};
|
||||
|
||||
|
||||
template<class NEXT, class BUFFSRC>
|
||||
struct AllocOutput
|
||||
template<class NEXT>
|
||||
struct AllocOutput : NEXT
|
||||
{
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
{
|
||||
ASSERT (this->buffTab);
|
||||
ASSERT (this->nrO < this->buffTabSize());
|
||||
BuffHandle *outH = this->buffTab->handles;
|
||||
BuffHandle::PBuff *outBuff = this->buffTab->buffers;
|
||||
ASSERT (ivo.buffTab);
|
||||
ASSERT (ivo.nrO() < ivo.buffTabSize());
|
||||
BuffHandle *outH = ivo.buffTab->handles;
|
||||
BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
|
||||
|
||||
for (uint i = 0; i < this->nrO; ++i )
|
||||
for (uint i = 0; i < ivo.nrO(); ++i )
|
||||
{
|
||||
outBuff[i] =
|
||||
*(outH[i] = allocateBuffer (i));
|
||||
*(outH[i] = ivo.allocateBuffer (i));
|
||||
// now Output buffer for channel #i is available...
|
||||
}
|
||||
return NEXT::step (ivo);
|
||||
}
|
||||
|
||||
private:
|
||||
BuffHandle const&
|
||||
allocateBuffer (uint outCh)
|
||||
{
|
||||
BUFFSRC::getBufferProvider(this).allocateBuffer(
|
||||
this->getBufferDescriptor(outCh));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum OutBuffProvider { PARENT, CACHE };
|
||||
|
||||
template<OutBuffProvider>
|
||||
struct OutBuffSource ;
|
||||
|
||||
template<>
|
||||
struct OutBuffSource<PARENT>
|
||||
{
|
||||
static State& getBufferProvider (Invocation& thisState) { return thisState.parent_; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OutBuffSource<CACHE>
|
||||
{
|
||||
static State& getBufferProvider (Invocation& thisState) { return thisState.current_; }
|
||||
};
|
||||
|
||||
|
||||
template<class NEXT>
|
||||
struct ProcessData
|
||||
struct ProcessData : NEXT
|
||||
{
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
{
|
||||
ASSERT (this->buffTab);
|
||||
ASSERT (this->nrO+this->nrI <= this->buffTabSize());
|
||||
ASSERT (this->validateBuffers());
|
||||
ASSERT (ivo.buffTab);
|
||||
ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
|
||||
ASSERT (this->validateBuffers(ivo));
|
||||
|
||||
// Invoke our own process() function, providing the buffer array
|
||||
this->wiring_.processFunction (this->buffTab->buffers);
|
||||
ivo.wiring.processFunction (ivo.buffTab->buffers);
|
||||
|
||||
return NEXT::step (ivo);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NEXT>
|
||||
struct FeedCache
|
||||
struct FeedCache : NEXT
|
||||
{
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
{
|
||||
// declare all Outputs as finished
|
||||
this->current_.isCalculated(this->buffTab->handles,
|
||||
this->nrO);
|
||||
ivo.isCalculated(ivo.buffTab->handles,
|
||||
ivo.nrO());
|
||||
|
||||
return NEXT::step (ivo);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NEXT>
|
||||
struct ReleaseBuffers
|
||||
{
|
||||
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
|
||||
{ ///////////////// this would require BuffHandle to be a smart ref....
|
||||
BuffHandle
|
||||
step (Invocation& ivo)
|
||||
{
|
||||
// all buffers besides the required Output no longer needed
|
||||
this->current_.releaseBuffers(this->buffTab->handles,
|
||||
this->buffTabSize(),
|
||||
this->requiredOutputNr);
|
||||
this->releaseBuffers(ivo.buffTab->handles,
|
||||
ivo.buffTabSize(),
|
||||
ivo.outNr);
|
||||
|
||||
return this->buffTab->outH[this->requiredOutputNr];
|
||||
return ivo.buffTab->outH[this->requiredOutputNr];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -335,9 +373,8 @@ namespace engine {
|
|||
|
||||
/* === declare the possible Assembly of these elementary steps === */
|
||||
|
||||
template<class Config>
|
||||
struct Strategy ;
|
||||
|
||||
template<StateAdapter::BufferProvider buffPro>
|
||||
struct OutBuffSource{ static const StateAdapter::BufferProvider VALUE = buffPro; };
|
||||
|
||||
template<char CACHE_Fl=0, char INPLACE_Fl=0>
|
||||
struct SelectBuffProvider;
|
||||
|
|
@ -348,23 +385,26 @@ namespace engine {
|
|||
template<> struct SelectBuffProvider<> : OutBuffSource<PARENT>{ };
|
||||
|
||||
|
||||
template<class Config>
|
||||
struct Strategy ;
|
||||
|
||||
using lumiera::typelist::Config;
|
||||
|
||||
template<char INPLACE_Fl>
|
||||
struct Strategy< Config<CACHING,PROCESS,INPLACE_Fl> >
|
||||
template<char INPLACE>
|
||||
struct Strategy< Config<CACHING,PROCESS,INPLACE> >
|
||||
: QueryCache<
|
||||
PullInput<
|
||||
AllocOutput<SelectBuffProvider<CACHING,INPLACE_Fl>,
|
||||
AllocOutput<
|
||||
ProcessData<
|
||||
FeedCache<
|
||||
ReleaseBuffers<
|
||||
OperationBase > > > > > >
|
||||
{ };
|
||||
|
||||
template<char INPLACE_Fl>
|
||||
struct Strategy< Config<PROCESS,INPLACE_Fl> >
|
||||
template<char INPLACE>
|
||||
struct Strategy< Config<PROCESS,INPLACE> >
|
||||
: PullInput<
|
||||
AllocOutput<SelectBuffProvider<NOT_SET,INPLACE_Fl>,
|
||||
AllocOutput<
|
||||
ProcessData<
|
||||
ReleaseBuffers<
|
||||
OperationBase > > > >
|
||||
|
|
@ -383,7 +423,7 @@ namespace engine {
|
|||
template<>
|
||||
struct Strategy< Config<CACHING> >
|
||||
: ReadSource<
|
||||
AllocOutput<OutBuffSource<CACHE>,
|
||||
AllocOutput<
|
||||
ProcessData< // wiring_.processFunction is supposed to do just buffer copying here
|
||||
ReleaseBuffers<
|
||||
OperationBase > > > >
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
#include "proc/lumiera.hpp"
|
||||
#include "common/frameid.hpp"
|
||||
#include "proc/engine/buffhandle.hpp"
|
||||
#include "proc/engine/bufftable.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
|
|
@ -40,7 +39,7 @@ namespace engine {
|
|||
using lumiera::FrameID;
|
||||
|
||||
class StateAdapter;
|
||||
|
||||
class BuffTableStorage;
|
||||
|
||||
class State
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue