From cb8ac94dcff6efa7e707c39818cd138691f67659 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 18 Jul 2008 21:21:46 +0200 Subject: [PATCH] WIP finally got the design straight? --- src/proc/engine/bufftable.hpp | 86 +++++----- src/proc/engine/nodeoperation.hpp | 262 +++++++++++++++++------------- src/proc/state.hpp | 3 +- 3 files changed, 195 insertions(+), 156 deletions(-) diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp index 21ada3c88..ac1329265 100644 --- a/src/proc/engine/bufftable.hpp +++ b/src/proc/engine/bufftable.hpp @@ -27,6 +27,7 @@ #include "common/error.hpp" #include "proc/engine/buffhandle.hpp" +#include "proc/engine/procnode.hpp" #include #include @@ -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 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 diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index f99dedbcb..6c0092edd 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -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 /////////////////TODO: rather template on the BufferProvider - class InvocationImpl - : private BuffTableStorage, - 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 ActualInvocationOrder + : public Invocation { - - protected: + public: InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh) - : BuffTableStorage(w), - Invocation(callingProcess, w, static_cast(*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 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(*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 - struct AllocOutput + template + 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 - struct OutBuffSource ; - - template<> - struct OutBuffSource - { - static State& getBufferProvider (Invocation& thisState) { return thisState.parent_; } - }; - - template<> - struct OutBuffSource - { - static State& getBufferProvider (Invocation& thisState) { return thisState.current_; } }; template - 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 - 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 - 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 - struct Strategy ; - + template + struct OutBuffSource{ static const StateAdapter::BufferProvider VALUE = buffPro; }; template struct SelectBuffProvider; @@ -348,23 +385,26 @@ namespace engine { template<> struct SelectBuffProvider<> : OutBuffSource{ }; + template + struct Strategy ; + using lumiera::typelist::Config; - template - struct Strategy< Config > + template + struct Strategy< Config > : QueryCache< PullInput< - AllocOutput, + AllocOutput< ProcessData< FeedCache< ReleaseBuffers< OperationBase > > > > > > { }; - template - struct Strategy< Config > + template + struct Strategy< Config > : PullInput< - AllocOutput, + AllocOutput< ProcessData< ReleaseBuffers< OperationBase > > > > @@ -383,7 +423,7 @@ namespace engine { template<> struct Strategy< Config > : ReadSource< - AllocOutput, + AllocOutput< ProcessData< // wiring_.processFunction is supposed to do just buffer copying here ReleaseBuffers< OperationBase > > > > diff --git a/src/proc/state.hpp b/src/proc/state.hpp index 84732029d..cd98a39b8 100644 --- a/src/proc/state.hpp +++ b/src/proc/state.hpp @@ -30,7 +30,6 @@ #include "proc/lumiera.hpp" #include "common/frameid.hpp" #include "proc/engine/buffhandle.hpp" -#include "proc/engine/bufftable.hpp" #include @@ -40,7 +39,7 @@ namespace engine { using lumiera::FrameID; class StateAdapter; - + class BuffTableStorage; class State {