Invocation: complement NodeBase_test with simple example

While initially intended as introductory test, it meanwhile
focuses on intricate technical details on the level of
basic building blocks, notably the `FeedManifold`

Now I have added a simple end-to-end demonstration example
how a Render Node is built from scratch, leaving out all
technical details and all convenience front-ends like
the `NodeBuilder` — just one dummy port invoked directly.
This commit is contained in:
Fischlurch 2025-02-19 01:30:54 +01:00
parent 8a4060861f
commit 6c2761b337
3 changed files with 133 additions and 28 deletions

View file

@ -2,7 +2,7 @@ TESTING "Component Test Suite: Render Engine parts" ./test-suite --group=node
PLANNED "Proc Node basics" NodeBase_test <<END
TEST "Proc Node basics" NodeBase_test <<END
END

View file

@ -19,6 +19,7 @@
#include "lib/test/run.hpp"
#include "lib/iter-zip.hpp"
#include "lib/meta/function.hpp"
#include "lib/several-builder.hpp"
#include "steam/engine/proc-node.hpp"
#include "steam/engine/turnout.hpp"
#include "steam/engine/turnout-system.hpp"
@ -27,17 +28,14 @@
#include "steam/engine/diagnostic-buffer-provider.hpp"
#include "steam/engine/buffhandle-attach.hpp"
#include "lib/test/test-helper.hpp"
//#include "lib/format-cout.hpp"
#include "lib/test/diagnostic-output.hpp"/////////////////////TODO
#include "lib/format-util.hpp"///////////////////////////////TODO
#include "lib/util.hpp"
//using std::string;
using std::tuple;
using std::array;
using util::isSameAdr;
using lib::test::showType;
using lib::makeSeveral;
using lib::izip;
@ -46,14 +44,25 @@ namespace engine{
namespace test {
namespace { // Test fixture
/**
*/
}
/***************************************************************//**
* @test basic render node properties and behaviour.
/******************************************************//**
* @test basic render node structure and building blocks.
* This test documents and verifies some fundamental
* Render Node structures, looking at intricate technical
* details, which are usually hidden below the NodeBuidler.
* - #verify_NodeStructure is a demonstration example
* to show fundamentals of node construction and
* invocation, using a dummy implementation.
* - the following cases cover extremely technical details
* of the FeedManifold, which serves as junction point
* between Render Node and external library functions.
* - in a similar style, \ref NodeFeed_test covers the
* various parameter- and data connections of Nodes
* in a »clean-room« setting
* - much more high-level is NodeLink_test, covering
* the construction of a Render Node network
* - NodeBuilder_test focuses on aspects of node
* generation, as packaged into the NodeBuilder.
*/
class NodeBase_test : public Test
{
@ -62,12 +71,20 @@ namespace test {
{
seedRand();
verify_TurnoutSystem();
verify_NodeStructure();
verify_FeedManifold();
verify_FeedPrototype();
UNIMPLEMENTED ("build a simple render node and then activate it");
}
/** @test the TurnoutSystem as transient coordinator for node invocation
/** @test the TurnoutSystem as transient connection hub for node invocation
* - for most invocations, just the nominal timeline time and an
* arbitrary process indentification-key is placed into fixed
* «slots« within the TurnoutSystem, from where these parameters
* can be retrieved by actual processing functions;
* - for some special cases however, additional storage blocks
* can be chained up, to allow accessing arbitrary parameters
* through the TurnoutSystem as front-end.
*/
void
verify_TurnoutSystem()
@ -82,14 +99,14 @@ namespace test {
// Demonstrate extension-block to TurnoutSystem
// Used to setup elaborate parameter-nodes.
// Used to setup elaborate parameter-nodes...
double someVal = defaultGen.uni(); // some param value, computed by »elaborate logic«
auto spec = buildParamSpec()
.addValSlot (someVal); // declare a parameter slot for an extension data block
auto acc0 = spec.makeAccessor<0>(); // capture an accessor-functor for later use
{// Build and connect extension storage block
auto dataBlock =
auto dataBlock = // ...typically placed locally into a nested stack frame
spec.makeBlockBuilder()
.buildParamDataBlock(invoker);
@ -97,10 +114,98 @@ namespace test {
CHECK (invoker.get(acc0) == someVal); // now able to retrieve data from extension block
invoker.detachChainBlock (dataBlock);
}
// base block continues to be usable...
CHECK (invoker.getNomTime() == nomTime);
}
/** @test the FeedManifold as adapter between Engine and processing library
/** @test very basic structure of a Render Node.
* - All render processing happens in \ref Port implementations
* - here we use a dummy port, which just picks up a parameter
* from the TurnoutSystem and writes it into the output buffer;
* no further recursive call happens so this is a source node.
* - To _incorporate_ this Port implementation into a Render Node,
* the _connectivity_ of the node network must be defined:
* + each node has a list of »Leads« (predecessor nodes)
* + and an array of port implementation (here just one port)
* - note that data placement relies on lib::Several, which can
* be configured to use a custom allocator to manage storage
* - furthermore, a node gets some ID descriptors, which are used
* to generate processing metadata (notably a hash key for caching)
* - for the actual invocation, foremost we need a _buffer provider_
* - and we need to supply the most basic parameters, like the
* nominal timeline time and a proccess-Key. These will be
* embedded into the TurnoutSystem, to be accessible throughout
* the complete recursive node-pull invocation.
* - This test verifies that the actual invocation indeed happened
* and placed a random parameter-value into the output buffer.
* @remark In reality, processing operations are delegated to a
* media-processing library, which requires elaborate buffer handling
* and typically entails recursive calls to predecessor nodes. This
* intricate logic is handled by the typical Port implementation
* known as \ref MediaWeavingPattern; notably the processing will
* rely on a transient data structure called \ref FeedManifold, which
* is verified in much more detail [below](\ref #verify_FeedManifold)
*/
void
verify_NodeStructure()
{
class DummyProcessing
: public Port
{
public:
DummyProcessing (ProcID& id)
: Port{id}
{ }
/** Entrance point to the next recursive step of media processing. */
BuffHandle
weave (TurnoutSystem& turnoutSystem, OptionalBuff outBuffer) override
{// do something deeply relevant, like feeding a dummy parameter...
outBuffer->accessAs<long>() = turnoutSystem.getProcKey();
return *outBuffer;
}
};
// Prepare Connectivity for the Node
auto leadNodes = makeSeveral<ProcNodeRef>(); // empty, no predecessor nodes
auto nodePorts = makeSeveral<Port>() // build the port implementation object(s)
.emplace<DummyProcessing> (ProcID::describe ("TestDummy","live(long)"));
// Build a Render Node
ProcNode theNode{Connectivity{nodePorts.build()
,leadNodes.build()
}};
// Inspect Node metadata...
CHECK (watch(theNode).isSrc());
CHECK (watch(theNode).leads().size() == 0);
CHECK (watch(theNode).ports().size() == 1);
CHECK (watch(theNode).getNodeSpec () == "TestDummy-◎"_expect );
CHECK (watch(theNode).getPortSpec(0) == "TestDummy.live(long)"_expect );
// prepare for invoking the node....
BufferProvider& provider = DiagnosticBufferProvider::build();
BuffHandle buff = provider.lockBufferFor<long> (-55);
CHECK (-55 == buff.accessAs<long>()); // allocated some data buffer for the result, with a marker-value
Time nomTime{Time::ZERO};
ProcessKey key = 1 + rani(100); // here we »hide« some data value in the ProcessKey
uint port{0}; // we will pull port-#0 of the node
// Trigger Node invocation...
buff = theNode.pull (port, buff, nomTime, key);
CHECK (key == buff.accessAs<uint>()); // DummyProcessing port placed ProcessKey into the output-buffer
buff.release();
}
/** @test the FeedManifold as adapter between Engine and processing library...
* - bind local λ with various admissible signatures
* - construct specifically tailored FeedManifold types
* - use the DiagnosticBufferProvider for test buffers

View file

@ -91765,19 +91765,19 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1713821236750" ID="ID_1655113761" MODIFIED="1713823539175" TEXT="testgetriebener Aufbau">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1713821706444" ID="ID_1035135639" MODIFIED="1733525811029" TEXT="NodeBase_test">
<icon BUILTIN="hourglass"/>
<node COLOR="#338800" CREATED="1713821706444" ID="ID_1035135639" MODIFIED="1739924540014" TEXT="NodeBase_test">
<icon BUILTIN="button_ok"/>
<node CREATED="1733525753053" ID="ID_1646783261" MODIFIED="1733525804171" TEXT="Demonstriert die Grundelemente"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1719282799123" ID="ID_605400011" MODIFIED="1719282822229" TEXT="mu&#xdf; ich erst einmal gr&#xf6;&#xdf;tenteils auskommentieren">
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1713823489509" ID="ID_738439301" MODIFIED="1719282837986" TEXT="idealerweise nebenbei neu mit aufbauen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1713823489509" ID="ID_738439301" MODIFIED="1739924537767" TEXT="idealerweise nebenbei neu mit aufbauen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1733577482085" ID="ID_932666491" MODIFIED="1733577589840" TEXT="Turnout-System: Funktionsweise testen">
<node COLOR="#338800" CREATED="1733577482085" ID="ID_932666491" MODIFIED="1739919405096" TEXT="Turnout-System: Funktionsweise testen">
<linktarget COLOR="#406cd3" DESTINATION="ID_932666491" ENDARROW="Default" ENDINCLINATION="-128;9;" ID="Arrow_ID_983808840" SOURCE="ID_494911945" STARTARROW="None" STARTINCLINATION="-353;-26;"/>
<icon BUILTIN="pencil"/>
<node CREATED="1733580248780" ID="ID_1698079544" MODIFIED="1736045190074" TEXT="mit nominal Time erzeugen">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1733580248780" ID="ID_1698079544" MODIFIED="1739919407685" TEXT="mit nominal Time erzeugen">
<arrowlink COLOR="#482eb7" DESTINATION="ID_855223653" ENDARROW="Default" ENDINCLINATION="-595;39;" ID="Arrow_ID_342943763" STARTARROW="None" STARTINCLINATION="-65;-160;"/>
<linktarget COLOR="#4f70b3" DESTINATION="ID_1698079544" ENDARROW="Default" ENDINCLINATION="100;348;" ID="Arrow_ID_822714910" SOURCE="ID_1699433498" STARTARROW="None" STARTINCLINATION="356;24;"/>
<node CREATED="1733580331065" ID="ID_1781432590" MODIFIED="1733584790502" TEXT="Testen mit Time-Grid!">
@ -91830,11 +91830,11 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<arrowlink COLOR="#5c6a7a" DESTINATION="ID_1291306906" ENDARROW="Default" ENDINCLINATION="-289;17;" ID="Arrow_ID_1279473915" STARTARROW="None" STARTINCLINATION="177;10;"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1739914036725" ID="ID_568175538" MODIFIED="1739914070848" TEXT="Zugriff auf die Basisparameter zeigen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1739914036725" ID="ID_568175538" MODIFIED="1739919401094" TEXT="Zugriff auf die Basisparameter zeigen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1739914046380" ID="ID_487791127" MODIFIED="1739914070848" TEXT="direkt zeigen da&#xdf; ein Chain-Block an/abgekoppelt werden kann">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1739914046380" ID="ID_487791127" MODIFIED="1739919403060" TEXT="direkt zeigen da&#xdf; ein Chain-Block an/abgekoppelt werden kann">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1734310118603" ID="ID_1954508996" MODIFIED="1734634792828" TEXT="Feed-Manifold: Eigenschaften demonstrieren">