LUMIERA.clone/src/steam/engine/nodeoperation.hpp
Ichthyostega 806db414dd Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
 * there is no entity "Lumiera.org" which holds any copyrights
 * Lumiera source code is provided under the GPL Version 2+

== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''

The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!

The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00

332 lines
10 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
NODEOPERATION.hpp - Specify how the nodes call each other and how processing is organized
Copyright (C)
2008, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
*/
/** @file nodeoperation.hpp
** Chunks of operation for invoking the rendernodes.
** This header defines part of the "glue" which holds together the render node network
** and enables to pull a result frames from the nodes. Especially, the aspect of
** buffer management and cache query is covered here. Each node has been preconfigured by
** the builder with a WiringDescriptor and a concrete type of a StateAdapter, including
** a specific Configuration, because the node can be built to
** - participate in the Caching or ignore the cache
** - actually process a result or just pull frames from a source
** - employ in-Place calculations or use separate in/out buffers
** Additionally, each node may have a given number of input/output pins, expecting to
** be provided with buffers holding a specific kind of data.
**
** \par composition of the invocation Strategy
** For each individual ProcNode#pull() call, the WiringAdapter#callDown() builds an Invocation state
** instance directly on the stack, holding references to the actual buffer pointers and state. Using this
** StateAdapter, the predecessor nodes are pulled. The way these operations are carried out is encoded
** in the actual type of Strategy, which is defined at the bottom of this header. Each Strategy is a chain
** of elementary operations invoking each other (\c NEXT::step(invocation) ). Notably, all those possible
** configurations are pre-built while compiling (it's a small number below 32 configuration instance).
** To be able to select the Strategy for each configuration, we need a Factory (ConfigSelector defined in
** nodewiring-config.hpp). which is actually instantiated and used in nodewiring.cpp, which is the object
** file holding all those instantiations.
**
** @see engine::ProcNode
** @see engine::Invocation
** @see engine::StateClosure
** @see engine::NodeFactory
** @see nodewiring-config.hpp
** @see nodewiring.hpp interface for building/wiring the nodes
**
*/
#ifndef ENGINE_NODEOPERATION_H
#define ENGINE_NODEOPERATION_H
//#include "steam/engine/proc-node.hpp" ///////////////////////////////TODO clarify if required further on
#include "steam/engine/connectivity-obsolete.hpp"
#include "steam/engine/state-closure-obsolete.hpp"
#include "steam/engine/channel-descriptor.hpp"
#include "steam/engine/nodeinvocation.hpp"
#include "steam/engine/feed-manifold-obsolete.hpp"
#include "lib/meta/util.hpp"
#include "lib/meta/configflags.hpp"
#include "lib/frameid.hpp"
namespace steam {
namespace engine {
namespace config {
/**
* Base class of all concrete invocation sequences.
* Provides a collection of functions used to build up the invocation sequence.
* Additionally providing a marker used to detect the existence of an concrete
* definition/specialisation for a given specific configuration.
*/
struct OperationBase
{
typedef lib::meta::Yes_t is_defined;
BuffHandle
getSource (Invocation& ivo, uint chanNo)
{
UNIMPLEMENTED ("retrieve source data provided by the vault/scheduler");
}
BuffHandle
pullPredecessor (Invocation& ivo, uint chanNo)
{
UNIMPLEMENTED ("invoke pull() on the denoted predecessor node");
}
void
releaseBuffers(BuffHandle* table, uint slotCnt, uint slot_to_retain) //////////////TODO this is going to be implemented rather by smart-handle, Ticket #249
{
UNIMPLEMENTED ("release all buffers with the exception of the desired output");
}
bool
validateBuffers (Invocation& ivo)
{
UNIMPLEMENTED ("Do a final, specifically tailored validation step on the buffers prior to invoking the process function");
}
};
template<class NEXT>
struct QueryCache : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle fetched = ivo.fetch (ivo.genFrameID());
if (fetched)
return fetched;
else
return NEXT::step (ivo);
}
};
template<class NEXT>
struct AllocBufferTable : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffTableChunk buffTab (ivo.wiring, ivo.getBuffTableStorage());
ivo.setBuffTab(&buffTab);
ASSERT (ivo.feedManifold);
ASSERT (ivo.buffTab_isConsistent());
return NEXT::step (ivo);
}
};
template<class NEXT>
struct PullInput : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle * inH = ivo.feedManifold->inHandle;
BuffHandle::PBuff *inBuff = ivo.feedManifold->inBuff;
for (uint i = 0; i < ivo.nrI(); ++i )
{
inBuff[i] =
&*(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor
// now Input #i is ready...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct ReadSource : NEXT
{
BuffHandle
step (Invocation& ivo)
{
BuffHandle *inH = ivo.feedManifold->inHandle;
BuffHandle *outH = ivo.feedManifold->outHandle;
BuffHandle::PBuff *inBuff = ivo.feedManifold->inBuff;
BuffHandle::PBuff *outBuff = ivo.feedManifold->outBuff;
ASSERT (ivo.nrO() == ivo.nrI() );
for (uint i = 0; i < ivo.nrI(); ++i )
{
inBuff[i] = outBuff[i] =
&*(inH[i] = outH[i] = this->getSource(ivo,i));
// now Input #i is ready...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct AllocOutput : NEXT
{
BuffHandle
step (Invocation& ivo)
{
ASSERT (ivo.feedManifold);
ASSERT (ivo.nrO() < ivo.buffTabSize());
BuffHandle *outH = ivo.feedManifold->outHandle;
BuffHandle::PBuff *outBuff = ivo.feedManifold->outBuff;
for (uint i = 0; i < ivo.nrO(); ++i )
{
outBuff[i] =
&*(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType));
// now Output buffer for channel #i is available...
}
return NEXT::step (ivo);
}
};
template<class NEXT>
struct ProcessData : NEXT
{
BuffHandle
step (Invocation& ivo)
{
ASSERT (ivo.feedManifold);
ASSERT (ivo.buffTab_isConsistent());
ASSERT (this->validateBuffers(ivo));
// Invoke our own process() function,
// providing the array of outBuffer+inBuffer ptrs
(*ivo.wiring.procFunction) (*ivo.feedManifold->outBuff);
return NEXT::step (ivo);
}
};
template<class NEXT>
struct FeedCache : NEXT
{
BuffHandle
step (Invocation& ivo)
{
for (uint i = 0; i < ivo.nrO(); ++i )
{
// declare all Outputs as finished
ivo.is_calculated(ivo.feedManifold->outHandle[i]);
}
return NEXT::step (ivo);
}
};
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.... --> ///TICKET #249
BuffHandle
step (Invocation& ivo)
{
// all buffers besides the required Output no longer needed
this->releaseBuffers(ivo.feedManifold->outHandle,
ivo.buffTabSize(),
ivo.outNr);
return ivo.feedManifold->outHandle[ivo.outNr];
}
};
/* =============================================================== */
/* === declare the possible Assembly of these elementary steps === */
enum Cases
{
CACHING = 1,
PROCESS,
INPLACE,
NOT_SET = 0,
NUM_Cases = INPLACE
};
using lib::meta::Config;
///////////////////////TODO: selecting this way isn't especially readable,
///////////////////////////: but BufferProvider selection is going to be solved differently anyway, see Ticket #249
template<class CONF>
struct SelectBuffProvider { typedef AllocBufferFromParent Type; };
template<uint PROC_ign, uint INPLA_ign>
struct SelectBuffProvider< Config<CACHING, PROC_ign, INPLA_ign>> { typedef AllocBufferFromCache Type; };
template<class Config>
struct Strategy ;
template<uint INPLACE_ign>
struct Strategy< Config<CACHING,PROCESS,INPLACE_ign>>
: QueryCache<
AllocBufferTable<
PullInput<
AllocOutput<
ProcessData<
FeedCache<
ReleaseBuffers<
OperationBase > > > > > > >
{ };
template<uint INPLACE_ign>
struct Strategy< Config<PROCESS,INPLACE_ign>>
: AllocBufferTable<
PullInput<
AllocOutput<
ProcessData<
ReleaseBuffers<
OperationBase > > > > >
{ };
template<>
struct Strategy< Config<> >
: AllocBufferTable<
ReadSource<
ReleaseBuffers<
OperationBase > > >
{ };
template<>
struct Strategy< Config<INPLACE> > : Strategy< Config<> > { };
template<>
struct Strategy< Config<CACHING> >
: AllocBufferTable<
ReadSource<
AllocOutput<
ProcessData< // wiring_.processFunction is supposed to do just buffer copying here
ReleaseBuffers<
OperationBase > > > > >
{ };
}}} // namespace steam::engine::config
#endif