WIP finally got the design straight?

This commit is contained in:
Fischlurch 2008-07-18 21:21:46 +02:00
parent 48780bef74
commit cb8ac94dcf
3 changed files with 195 additions and 156 deletions

View file

@ -27,6 +27,7 @@
#include "common/error.hpp" #include "common/error.hpp"
#include "proc/engine/buffhandle.hpp" #include "proc/engine/buffhandle.hpp"
#include "proc/engine/procnode.hpp"
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <vector> #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 class BuffTableStorage
{ {
vector<BuffHandle> hTab_; 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 } // namespace engine
#endif #endif

View file

@ -73,16 +73,6 @@ namespace engine {
* call, using setup/wiring data preconfigured by the builder. * call, using setup/wiring data preconfigured by the builder.
* Its job is to provide the actual implementation of the Cache * Its job is to provide the actual implementation of the Cache
* push / fetch and recursive downcall to render the source frames. * 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 class StateAdapter
: public State : public State
@ -98,6 +88,8 @@ namespace engine {
virtual State& getCurrentImplementation () { return current_; } virtual State& getCurrentImplementation () { return current_; }
typedef State& (StateAdapter::*BufferProvider);
public: /* === proxying the State interface === */ public: /* === proxying the State interface === */
@ -110,10 +102,28 @@ namespace engine {
// note: allocateBuffer() is choosen specifically based on the actual node wiring // 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 struct Invocation
: StateAdapter : StateAdapter
{ {
@ -122,34 +132,68 @@ namespace engine {
const uint outNr; const uint outNr;
protected: 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), : StateAdapter(callingProcess),
wiring(w), wiring(w), outNr(o),
buffTab(b), bufferProvider_(buffSrc)
outNr(o)
{ } { }
/** 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 * The real invocation context state implementation. It is created
class InvocationImpl * by the NodeWiring (WiringDescriptor) of the processing node which
: private BuffTableStorage<SZ>, * is pulled by this invocation, hereby using the internal configuration
public Invocation * 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
{ {
public:
protected:
InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh) InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh)
: BuffTableStorage<SZ>(w), : Invocation(callingProcess, w, buffPro, outCh)
Invocation(callingProcess, w, static_cast<BuffTable&>(*this), outCh)
{ } { }
/** contains the details of Cache query and recursive calls /** contains the details of Cache query and recursive calls
* to the predecessor node(s), eventually followed by the * to the predecessor node(s), eventually followed by the
* ProcNode::process() callback * ProcNode::process() callback
*/ */
template<class Strategy>
BuffHandle retrieve () BuffHandle retrieve ()
{ {
return Strategy::step (*this); return Strategy::step (*this);
@ -157,8 +201,29 @@ namespace engine {
}; };
/**
* 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 class OperationBase
{ {
@ -170,9 +235,8 @@ namespace engine {
BuffHandle BuffHandle
step (Invocation& ivo) step (Invocation& ivo)
{ {
BuffHandle fetched = this->current_.fetch ( BuffHandle fetched = ivo.fetch (
this->genFrameID ( this->genFrameID (ivo));
this->requiredOutputNr));
if (fetched) if (fetched)
return fetched; return fetched;
else else
@ -185,25 +249,25 @@ namespace engine {
struct PullInput : NEXT struct PullInput : NEXT
{ {
BuffHandle BuffHandle
step (Invocation& ivo) step (Invocation& started_invocation)
{ {
this->createBuffTable(); ProcessInvocation ivo (started_invocation);
ASSERT (this->buffTab); ASSERT (ivo.buffTab);
ASSERT (0 < this->buffTabSize()); ASSERT (0 < ivo.buffTabSize());
ASSERT (this->nrO == this->nrI ); ASSERT (ivo.nrO() == ivo.nrI() );
ASSERT (this->nrO+this->nrI <= this->buffTabSize()); ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]); ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
BuffHandle *inH = this->buffTab->inHandles; BuffHandle *inH = ivo.buffTab->inHandles;
BuffHandle::PBuff *inBuff = this->buffTab->inBuffs; BuffHandle::PBuff *inBuff = ivo.buffTab->inBuffs;
for (uint i = 0; i < this->nrI; ++i ) for (uint i = 0; i < ivo.nrI(); ++i )
{ {
inBuff[i] = inBuff[i] =
*(inH[i] = this->pullPredecessor(i)); // invoke predecessor *(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor
// now Input #i is ready... // 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 struct ReadSource : NEXT
{ {
BuffHandle BuffHandle
step (Invocation& ivo) step (Invocation& started_invocation)
{ {
this->createBuffTable(); ProcessInvocation ivo (started_invocation);
ASSERT (this->buffTab); ASSERT (ivo.buffTab);
ASSERT (0 < this->buffTabSize()); ASSERT (0 < ivo.buffTabSize());
ASSERT (this->nrO+this->nrI <= this->buffTabSize()); ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]); ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
BuffHandle *inH = this->buffTab->inHandles; BuffHandle *inH = ivo.buffTab->inHandles;
BuffHandle *outH = this->buffTab->handles; BuffHandle *outH = ivo.buffTab->handles;
BuffHandle::PBuff *inBuff = this->buffTab->inBuffs; BuffHandle::PBuff *inBuff = ivo.buffTab->inBuffs;
BuffHandle::PBuff *outBuff = this->buffTab->buffers; 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] = 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... // now Input #i is ready...
} }
return NEXT::step (ivo); return NEXT::step (ivo);
@ -236,97 +300,71 @@ namespace engine {
}; };
template<class NEXT, class BUFFSRC> template<class NEXT>
struct AllocOutput struct AllocOutput : NEXT
{ {
BuffHandle BuffHandle
step (Invocation& ivo) step (Invocation& ivo)
{ {
ASSERT (this->buffTab); ASSERT (ivo.buffTab);
ASSERT (this->nrO < this->buffTabSize()); ASSERT (ivo.nrO() < ivo.buffTabSize());
BuffHandle *outH = this->buffTab->handles; BuffHandle *outH = ivo.buffTab->handles;
BuffHandle::PBuff *outBuff = this->buffTab->buffers; BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
for (uint i = 0; i < this->nrO; ++i ) for (uint i = 0; i < ivo.nrO(); ++i )
{ {
outBuff[i] = outBuff[i] =
*(outH[i] = allocateBuffer (i)); *(outH[i] = ivo.allocateBuffer (i));
// now Output buffer for channel #i is available... // now Output buffer for channel #i is available...
} }
return NEXT::step (ivo); 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> template<class NEXT>
struct ProcessData struct ProcessData : NEXT
{ {
BuffHandle BuffHandle
step (Invocation& ivo) step (Invocation& ivo)
{ {
ASSERT (this->buffTab); ASSERT (ivo.buffTab);
ASSERT (this->nrO+this->nrI <= this->buffTabSize()); ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
ASSERT (this->validateBuffers()); ASSERT (this->validateBuffers(ivo));
// Invoke our own process() function, providing the buffer array // 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); return NEXT::step (ivo);
} }
}; };
template<class NEXT> template<class NEXT>
struct FeedCache struct FeedCache : NEXT
{ {
BuffHandle BuffHandle
step (Invocation& ivo) step (Invocation& ivo)
{ {
// declare all Outputs as finished // declare all Outputs as finished
this->current_.isCalculated(this->buffTab->handles, ivo.isCalculated(ivo.buffTab->handles,
this->nrO); ivo.nrO());
return NEXT::step (ivo); return NEXT::step (ivo);
} }
}; };
template<class NEXT> 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 BuffHandle
step (Invocation& ivo) step (Invocation& ivo)
{ {
// all buffers besides the required Output no longer needed // all buffers besides the required Output no longer needed
this->current_.releaseBuffers(this->buffTab->handles, this->releaseBuffers(ivo.buffTab->handles,
this->buffTabSize(), ivo.buffTabSize(),
this->requiredOutputNr); 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 === */ /* === declare the possible Assembly of these elementary steps === */
template<class Config> template<StateAdapter::BufferProvider buffPro>
struct Strategy ; struct OutBuffSource{ static const StateAdapter::BufferProvider VALUE = buffPro; };
template<char CACHE_Fl=0, char INPLACE_Fl=0> template<char CACHE_Fl=0, char INPLACE_Fl=0>
struct SelectBuffProvider; struct SelectBuffProvider;
@ -348,23 +385,26 @@ namespace engine {
template<> struct SelectBuffProvider<> : OutBuffSource<PARENT>{ }; template<> struct SelectBuffProvider<> : OutBuffSource<PARENT>{ };
template<class Config>
struct Strategy ;
using lumiera::typelist::Config; using lumiera::typelist::Config;
template<char INPLACE_Fl> template<char INPLACE>
struct Strategy< Config<CACHING,PROCESS,INPLACE_Fl> > struct Strategy< Config<CACHING,PROCESS,INPLACE> >
: QueryCache< : QueryCache<
PullInput< PullInput<
AllocOutput<SelectBuffProvider<CACHING,INPLACE_Fl>, AllocOutput<
ProcessData< ProcessData<
FeedCache< FeedCache<
ReleaseBuffers< ReleaseBuffers<
OperationBase > > > > > > OperationBase > > > > > >
{ }; { };
template<char INPLACE_Fl> template<char INPLACE>
struct Strategy< Config<PROCESS,INPLACE_Fl> > struct Strategy< Config<PROCESS,INPLACE> >
: PullInput< : PullInput<
AllocOutput<SelectBuffProvider<NOT_SET,INPLACE_Fl>, AllocOutput<
ProcessData< ProcessData<
ReleaseBuffers< ReleaseBuffers<
OperationBase > > > > OperationBase > > > >
@ -383,7 +423,7 @@ namespace engine {
template<> template<>
struct Strategy< Config<CACHING> > struct Strategy< Config<CACHING> >
: ReadSource< : ReadSource<
AllocOutput<OutBuffSource<CACHE>, AllocOutput<
ProcessData< // wiring_.processFunction is supposed to do just buffer copying here ProcessData< // wiring_.processFunction is supposed to do just buffer copying here
ReleaseBuffers< ReleaseBuffers<
OperationBase > > > > OperationBase > > > >

View file

@ -30,7 +30,6 @@
#include "proc/lumiera.hpp" #include "proc/lumiera.hpp"
#include "common/frameid.hpp" #include "common/frameid.hpp"
#include "proc/engine/buffhandle.hpp" #include "proc/engine/buffhandle.hpp"
#include "proc/engine/bufftable.hpp"
#include <cstddef> #include <cstddef>
@ -40,7 +39,7 @@ namespace engine {
using lumiera::FrameID; using lumiera::FrameID;
class StateAdapter; class StateAdapter;
class BuffTableStorage;
class State class State
{ {