WIP some further musing about how to do a simple invocation for unit testing ProcNode

This commit is contained in:
Fischlurch 2009-09-04 01:59:44 +02:00
parent 83067aa318
commit 391181e699
11 changed files with 136 additions and 84 deletions

View file

@ -41,6 +41,7 @@
#include "lib/error.hpp"
#include "lib/streamtype.hpp"
#include "lib/bool-checkable.hpp"
namespace engine {
@ -52,22 +53,36 @@ namespace engine {
* The real buffer pointer can be retrieved by dereferencing this smart-handle class.
*/
struct BuffHandle
: lib::BoolCheckable<BuffHandle>
{
typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
typedef Buff* PBuff;
PBuff
operator->() const
operator->() const
{
return pBuffer_;
}
Buff&
operator* () const
operator* () const
{
ENSURE (pBuffer_);
return *pBuffer_;
}
bool
isValid() const
{
return pBuffer_;
}
//////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> Ticket 249
BuffHandle()
: pBuffer_(0),
sourceID_(0)
{ }
private:
PBuff pBuffer_;
long sourceID_;

View file

@ -203,6 +203,7 @@ namespace config {
}
};
template<class NEXT>
struct FeedCache : NEXT
{
@ -219,6 +220,7 @@ namespace config {
}
};
template<class NEXT>
struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
{ ///////////////// this would require BuffHandle to be a smart ref....

View file

@ -133,7 +133,7 @@ namespace engine {
/** create and configure a concrete wiring descriptor to tie
* a ProcNode to its predecessor nodes. This includes selecting
* the actual StateProxy type, configuring it out of some operation
* the actual StateAdapter type, configuring it out of some operation
* control templates (policy classes). Compiling this operator function
* actually drives the necessary template instantiations for all cases
* encountered while building the node network.

View file

@ -83,10 +83,7 @@ namespace engine {
setup.makeInDescriptor(),
setup.resolveProcessingFunction(),
setup.createNodeID())
{
nrO = out.size();
nrI = in.size();
}
{ }
friend class WiringFactory;

View file

@ -90,7 +90,10 @@ namespace engine {
: out(o), in(i),
procFunction(pFunc),
nodeID(nID)
{ }
{
nrO = out.size();
nrI = in.size();
}
/** the wiring-dependent part of the node operation.
* Includes the creation of a one-way state object on the stack
@ -129,6 +132,9 @@ namespace engine {
public:
/** output channel count */
uint nrO() { return wiringConfig_.nrO; }
/** Engine Core operation: render and pull output from this node.
* On return, currentProcess will hold onto output buffer(s)
* containing the calculated result frames. In case this node

View file

@ -22,6 +22,8 @@
#include "proc/engine/render-invocation.hpp"
#include "proc/engine/stateproxy.hpp"
namespace engine {
@ -35,51 +37,21 @@ namespace engine {
// using mobject::session::Effect;
/** @internal */
/** @note this builds a one-way off invocation state context
* and then forwards the call; this may or may not cause
* actual calculations, depending on the cache.
* @todo for real use within the engine, the pull()-call should be
* dispatched through the scheduler; of course then the call semantics
* would be completely different. Maybe this invocation will be test only?
*/
BuffHandle
RenderInvocation::allocateBuffer (BufferDescriptor const&)
RenderInvocation::operator[] (size_t channel)
{
UNIMPLEMENTED ("allocate a suitable buffer to hold a frame of the denoted type");
}
void
RenderInvocation::releaseBuffer (BuffHandle& bh)
{
UNIMPLEMENTED ("free a buffer");
}
BuffHandle
RenderInvocation::fetch (FrameID const& fID)
{
UNIMPLEMENTED ("fetch a buffer with input data");
}
void
RenderInvocation::is_calculated (BuffHandle const& bh)
{
UNIMPLEMENTED ("declare a buffer as fully calculated and done");
}
FrameID const&
RenderInvocation::genFrameID (NodeID const&, uint chanNo)
{
UNIMPLEMENTED ("derive/generate an ID to denote this specific fame+Node position in the graph");
}
BuffTableStorage&
RenderInvocation::getBuffTableStorage() /////////////TODO need somehow to denote the specific storage requirements
{
UNIMPLEMENTED ("allocate a chunk of storage suitable for holding the buffer pointer tables");
REQUIRE (theNode_);
REQUIRE (channel < size());
StateProxy invocationState;
return theNode_->pull(invocationState, channel);
}

View file

@ -34,9 +34,9 @@
#define ENGINE_RENDER_INVOCATION_H
#include "proc/state.hpp"
//#include "proc/engine/procnode.hpp"
//#include "proc/engine/buffhandle.hpp"
//#include "proc/state.hpp"
#include "proc/engine/procnode.hpp"
#include "proc/engine/buffhandle.hpp"
//#include "proc/engine/bufftable.hpp"
@ -49,34 +49,23 @@ namespace engine {
* TODO: type comment
*/
class RenderInvocation
: public State
{
protected:
// State& parent_;
// State& current_;
ProcNode* theNode_;
RenderInvocation()
{ }
public:
RenderInvocation (ProcNode* exitNode)
: theNode_(exitNode)
{
REQUIRE (theNode_);
}
size_t size() { return theNode_->nrO(); }
/** pull calculated data from the N-th node output channel */
BuffHandle operator[] (size_t channel);
private: /* === top-level implementation of the State interface === */
BuffHandle allocateBuffer (BufferDescriptor const&);
void releaseBuffer (BuffHandle& bh);
BuffHandle fetch (FrameID const& fID);
void is_calculated (BuffHandle const& bh);
FrameID const& genFrameID (NodeID const&, uint chanNo);
BuffTableStorage& getBuffTableStorage();
virtual State& getCurrentImplementation () { return *this; }
};
///////////////////////////////////TODO: currently just fleshing out the API

View file

@ -25,8 +25,63 @@
namespace engine {
/** */
namespace { // Details...
} // (END) Details...
// using mobject::Placement;
// using mobject::session::Effect;
/** @internal */
BuffHandle
StateProxy::allocateBuffer (BufferDescriptor const&)
{
UNIMPLEMENTED ("allocate a suitable buffer to hold a frame of the denoted type");
}
void
StateProxy::releaseBuffer (BuffHandle& bh)
{
UNIMPLEMENTED ("free a buffer");
}
BuffHandle
StateProxy::fetch (FrameID const& fID)
{
UNIMPLEMENTED ("fetch a buffer with input data");
}
void
StateProxy::is_calculated (BuffHandle const& bh)
{
UNIMPLEMENTED ("declare a buffer as fully calculated and done");
}
FrameID const&
StateProxy::genFrameID (NodeID const&, uint chanNo)
{
UNIMPLEMENTED ("derive/generate an ID to denote this specific fame+Node position in the graph");
}
BuffTableStorage&
StateProxy::getBuffTableStorage() /////////////TODO need somehow to denote the specific storage requirements
{
UNIMPLEMENTED ("allocate a chunk of storage suitable for holding the buffer pointer tables");
}
} // namespace engine

View file

@ -35,7 +35,21 @@ namespace engine {
class StateProxy
: public proc_interface::State
{
protected:
private: /* === top-level implementation of the State interface === */
BuffHandle allocateBuffer (BufferDescriptor const&);
void releaseBuffer (BuffHandle& bh);
BuffHandle fetch (FrameID const& fID);
void is_calculated (BuffHandle const& bh);
FrameID const& genFrameID (NodeID const&, uint chanNo);
BuffTableStorage& getBuffTableStorage();
virtual State& getCurrentImplementation () { return *this; }
};

View file

@ -26,7 +26,8 @@
//#include "lib/util.hpp"
#include "proc/engine/nodefactory.hpp"
#include "proc/engine/nodewiring.hpp"
#include "proc/engine/render-invocation.hpp"
#include "proc/engine/stateproxy.hpp"
#include "proc/engine/buffhandle.hpp"
#include "proc/mobject/session/effect.hpp"
#include "lib/allocationcluster.hpp"
@ -54,7 +55,7 @@ namespace test {
* happening in correct order.
*/
class TestContext
: public RenderInvocation
: public StateProxy
{
//////////////TODO: facility to verify the right access operations get called
@ -83,8 +84,9 @@ namespace test {
ASSERT (pNode);
TestContext simulatedInvocation;
pNode->pull(simulatedInvocation, 0);
BuffHandle result = pNode->pull(simulatedInvocation, 0);
ASSERT (result);
// ASSERT we got calculated data in the result buffer
}
};

View file

@ -2237,12 +2237,12 @@ So, when creating a clip out of such a compound media asset, the clip has to be
In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
</pre>
</div>
<div title="NodeOperationProtocol" modifier="Ichthyostega" modified="200807072147" created="200806010251" tags="Rendering dynamic" changecount="15">
<div title="NodeOperationProtocol" modifier="Ichthyostega" modified="200909031217" created="200806010251" tags="Rendering dynamic" changecount="16">
<pre>The [[nodes|ProcNode]] are wired to form a &quot;Directed Acyclic Graph&quot;; each node knows its predecessor(s), but not its successor(s). The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. There is no such thing as an &quot;engine object&quot; calling nodes iteratively or table driven, rather, the nodes themselves issue recursive calls to their predecessor(s). For this to work, we need the nodes to adhere to a specific protocol:
# Node is pulled, with a StateProxy object as parameter (encapsulating the access to the frames or buffers)
# Node may now access current parameter values, using the state accessible via the StateProxy
# using it's //input-output and wiring descriptor,// the Node creates a StateAdapter wrapping the StateProxy for allocating buffers and accessing the required input
# StateAdapter first tries to get the output frames from the Cache in the Backend. In case of failure, a {{{process()}}} call is prepared by generating {{{pull()}}} call(s) for the input
# StateAdapter might first try to get the output frames from the Cache in the Backend. In case of failure, a {{{process()}}} call is prepared by generating {{{pull()}}} call(s) for the input
# as late as possible, typically on return, these recursive pull-calls have allocated a buffer containing the input data.
# when input is ready prior to the {{{process()}}} call, output buffers will be allocated, either from the cache, or (if not caching) from the &quot;parent&quot; StateAdapter up the callstack.
# after all buffers are available, the StateAdapter issues the {{{process()}}} call back to the originating node, which now may dereference the frame pointers and do its calculations