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 "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

View file

@ -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 > > > >

View file

@ -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
{