Invocation: provide a fake engine context setup

The purpose of this test is to invoke a Render Node graph
using the full service environment as present in the Render Engine.
However, for sake of clarity, I do not want to pull up the Engine subsystem,
but rather use a local / mocked setup of all the services involved.

The node invocation is performed from within a worker thread,
to demonstrate integration with the thread-local memory pools.

The first simple test case builds and then pulls a source node.
This commit is contained in:
Fischlurch 2026-06-24 23:48:15 +02:00
parent f3dcbfe8b9
commit 70bef00324
6 changed files with 195 additions and 11 deletions

View file

@ -63,9 +63,7 @@ namespace engine {
static lib::Depend<EngineCtx> access;
private:
EngineCtx();
friend class lib::DependencyFactory<EngineCtx>;
};

View file

@ -68,7 +68,7 @@ namespace engine{
BufferProvider&
EngineFacilities::setupCacheProvider() ////////////////////////////////////////////////////////////////TICKET #1223 : in what ways does client code need to interact with the Frame Cache / Management?
{
return getNaiveBufferManager();
return setupBufferProvider();
}

View file

@ -26,7 +26,7 @@ TEST "Proc Node metadata key" NodeMeta_test <<END
END
PLANNED "Proc Node operation modes" NodeOpera_test <<END
TEST "Proc Node operation modes" NodeOpera_test <<END
END

View file

@ -17,16 +17,67 @@
#include "test/run.hpp"
#include "vault/mem/buffer-provider-setup.hpp"
#include "vault/mem/engine-buffer-metadata.hpp"
#include "vault/mem/engine-buffer-manager.hpp"
#include "vault/mem/local-buffer-stage.hpp"
#include "vault/mem/local-buffer-store.hpp"
#include "steam/engine/engine-facilities.hpp"
#include "steam/engine/node-builder.hpp"
#include "lib/depend-inject.hpp"
#include "lib/thread.hpp"
//#include "lib/util.hpp"
#include "test/diagnostic-output.hpp"///////////////TODO
//using std::string;
using lib::Thread;
using std::this_thread::yield;
using std::this_thread::sleep_for;
using std::chrono_literals::operator ""us;
namespace steam {
namespace engine{
namespace test {
namespace { // ========== Test Setup ==========
using vault::mem::BufferProviderSetup;
using vault::mem::LocalBufferStage;
using vault::mem::LocalBufferStore;
using vault::mem::EngineBufferMetadata;
using vault::mem::EngineBufferManager;
class TestEngineBufferSetup
: public BufferProviderSetup
, public EngineFacilities
{
public:
TestEngineBufferSetup()
: BufferProviderSetup{*this}
{ }
auto buildStage() { return std::make_unique<LocalBufferStage>("NodeOpera_test"); }
auto buildStore() { return std::make_unique<LocalBufferStore>(); }
BufferProvider& setupBufferProvider() override { return *this; }
};
struct TestEngineCtx
{
// install mock instances for the relevant services
lib::DependInject<EngineBufferMetadata>::Local<> metaHub;
lib::DependInject<EngineBufferManager>::Local<> globalPool;
// install a rigged access front-end for the buffer management
lib::DependInject<EngineFacilities>::Local<TestEngineBufferSetup> bufferSetup;
lib::DependInject<EngineCtx>::Local<EngineCtx> frontEndCtx;
};
}
@ -45,7 +96,79 @@ namespace test {
virtual void
run (Arg)
{
UNIMPLEMENTED ("operate some render nodes as linked together");
seedRand();
invokeSimpleNode();
}
/** @test directly invoke a simple source node.
* - use the node builder to create a simple node
* immediately into the current stack frame
* - the processing function feeds a fixed random number
* into the given output buffer
* - then launch a worker thread to pull this node
* - create an output buffer directly from the worker thread
* - invoke the single port of the node, which is visible
* since it was created before starting the local worker thread
* - after the invocation, the fixed random number should have been
* placed into the output buffer
* - after the worker thread has finished, a single buffer allocation
* should have happened; this allocation was sent back automatically
* and can be claimed and released by the global buffer manager.
*/
void
invokeSimpleNode()
{
int randNum{rani()};
auto fun = [&](int* buff){ *buff = randNum; };
TestEngineCtx mockEngine;
ProcNode node{prepareNode("Test")
.preparePort()
.invoke("fun()", fun)
.completePort()
.build()};
Thread testWorker{[&] /* === directly perform a node invocation === */
{
BuffHandle buff = EngineCtx::access().mem.lockBufferFor<int> (-55);
Time nomTime{Time::ZERO};
ProcessKey key{0};
uint port{0};
CHECK (-55 == buff.accessAs<int>());
// Trigger Node invocation...
buff = node.pull (port, buff, nomTime, key);
CHECK (randNum == buff.accessAs<int>());
buff.release();
}};
while (testWorker)
yield(); // wait for worker to finish
// the central services were indeed requested / created
CHECK (mockEngine.bufferSetup);
CHECK (mockEngine.globalPool);
CHECK (mockEngine.metaHub);
// at least two buffer types registered (one generic for int)
CHECK (2 <= mockEngine.metaHub->cntEntries());
// one buffer was requested...
CHECK (1 == watch(*mockEngine.globalPool).numAllocs());
sleep_for (500us); // additional delay for the local pool's destructor
// (which is invoked after the thread has terminated)
// process and close remaining allocations
// sent back by the local pool's destructor...
mockEngine.globalPool->processPendingRequests();
// now there should be no memory leaks.......
CHECK (0 == watch(*mockEngine.globalPool).bytesLeased());
}
};

View file

@ -303,7 +303,7 @@ namespace test {
* - the prime goal is to demonstrate correctness, balancing memory demand
* mostly through lock-free queues, without loosing any allocation.
*/
class BufferProviderLoad_test : public Test
class BufferProviderLoad_test : public Test
{
virtual void

View file

@ -89082,7 +89082,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
<node CREATED="1685050895873" ID="ID_1305413793" MODIFIED="1686789885547" TEXT="JobTicket &#xd83e;&#xdc32; JobFunctor"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685050903817" ID="ID_1951219397" MODIFIED="1714778577842">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685050903817" ID="ID_1951219397" MODIFIED="1782318913646">
<richcontent TYPE="NODE"><html>
<head/>
<body>
@ -89095,6 +89095,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
<linktarget COLOR="#3e6184" DESTINATION="ID_1951219397" ENDARROW="Default" ENDINCLINATION="-729;116;" ID="Arrow_ID_1252149896" SOURCE="ID_1316148159" STARTARROW="None" STARTINCLINATION="427;24;"/>
<linktarget COLOR="#4151b6" DESTINATION="ID_1951219397" ENDARROW="Default" ENDINCLINATION="-224;272;" ID="Arrow_ID_1424052882" SOURCE="ID_426502930" STARTARROW="None" STARTINCLINATION="530;22;"/>
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1714777535544" ID="ID_1292334278" MODIFIED="1714777603974" TEXT="nominell-zeitliche Koordinate">
<icon BUILTIN="flag-yellow"/>
@ -91082,9 +91083,10 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node CREATED="1718840198722" ID="ID_821758494" MODIFIED="1736101710239" TEXT="Fazit (1:N)">
<node CREATED="1718840198722" ID="ID_821758494" MODIFIED="1782318901343" TEXT="Fazit (1:N)">
<linktarget COLOR="#77274c" DESTINATION="ID_821758494" ENDARROW="Default" ENDINCLINATION="-254;66;" ID="Arrow_ID_474260214" SOURCE="ID_811413379" STARTARROW="None" STARTINCLINATION="811;-49;"/>
<linktarget COLOR="#6c5499" DESTINATION="ID_821758494" ENDARROW="Default" ENDINCLINATION="-305;510;" ID="Arrow_ID_1100764489" SOURCE="ID_290743732" STARTARROW="None" STARTINCLINATION="891;35;"/>
<linktarget COLOR="#462f81" DESTINATION="ID_821758494" ENDARROW="Default" ENDINCLINATION="-497;447;" ID="Arrow_ID_1332141638" SOURCE="ID_426502930" STARTARROW="None" STARTINCLINATION="827;34;"/>
<node CREATED="1718840201860" ID="ID_589597825" MODIFIED="1718840211116" TEXT="die ExitNode bekommt eine neue Rolle"/>
<node CREATED="1718840212128" ID="ID_815017645" MODIFIED="1718840220877" TEXT="...n&#xe4;mlich genau dieses Mapping zu leisten">
<node CREATED="1718840222671" ID="ID_965571589" MODIFIED="1718840253430" TEXT="high-Level ggfs strukturierte Model-Ports"/>
@ -98868,6 +98870,21 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</html></richcontent>
</node>
<node CREATED="1782263611891" ID="ID_714205556" LINK="#ID_462733128" MODIFIED="1782263644973" TEXT="werde wohl daf&#xfc;r einen Unit-Test zum Aufbau brauchen"/>
<node CREATED="1782318712259" ID="ID_426502930" MODIFIED="1782319131757" TEXT="f&#xfc;r die Job-Invocation notwendige Parameter / Freiheitsgrade beachten">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Hier besteht ein &#187;bottleneck&#171; &#8212; insofern wir sparsam mit dem Speicher in den Segmenten und der Job-Activity-Language umgehen m&#252;ssen; m&#246;glicherweise lassen sich gewisse (theoretische) Freiheitsgrade zusammenfalten in eine Processing-ID, an die ein Mapping gebunden wird (&#10230; Stream-Types, Ausf&#252;hrungs-Zustand, Switchboard). Letztlich wird das aber &#252;ber mehrere Stufen heruntergebrochen, so da&#223; beim Einstieg in den jeweiligen Node-Pull eigentlich alle Freiheitsgrade und Parameter disjunkt vorliegen sollten...
</p>
</body>
</html>
</richcontent>
<arrowlink COLOR="#4151b6" DESTINATION="ID_1951219397" ENDARROW="Default" ENDINCLINATION="-224;272;" ID="Arrow_ID_1424052882" STARTARROW="None" STARTINCLINATION="530;22;"/>
<arrowlink COLOR="#462f81" DESTINATION="ID_821758494" ENDARROW="Default" ENDINCLINATION="-497;447;" ID="Arrow_ID_1332141638" STARTARROW="None" STARTINCLINATION="827;34;"/>
</node>
</node>
<node CREATED="1782263447684" ID="ID_271895917" MODIFIED="1782263470602" TEXT="mu&#xdf; durch Prototyping herausfinden was der &#xbb;darunterliegende Code&#xab; braucht"/>
<node CREATED="1782263496601" ID="ID_304797502" MODIFIED="1782263511901" TEXT="mu&#xdf; diese Bestandteile im Test-Setup zum Laufen bekommen"/>
@ -116661,9 +116678,52 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1780347617296" ID="ID_1297498308" MODIFIED="1780347628941" TEXT="Ausgabe per OutputSlot"/>
<node CREATED="1780347660957" ID="ID_664296230" MODIFIED="1780347671228" TEXT="Nodes vorher per NodeBuilder aufbauen"/>
</node>
<node CREATED="1780347599733" ID="ID_1443629697" MODIFIED="1780347610216" TEXT="aber grunds&#xe4;tzlich noch die Test-Variante"/>
<node CREATED="1780347599733" ID="ID_1443629697" MODIFIED="1782339989101" TEXT="aber Services direkt instantiieren (nicht die &#xbb;echte Engine&#xab;)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Das m&#246;chte ich so halten zur Dokumentation der Zusammenh&#228;nge; hier geht es um Komponenten-Integration &#8212; nicht um einen Systemtest. Teils werden jedoch bereits die gleichen Komponenten verwendet, wie in der &#187;echten Engine&#171;, vor allem der BufferProvider mit lokalen Pools. Andererseits werden die RenderNodes noch direkt erzeugt.
</p>
</body>
</html>
</richcontent>
<node COLOR="#2a7b8f" CREATED="1782348968435" ID="ID_1373429853" MODIFIED="1782349068032" TEXT="Trick: verwende spezielle EngineFacilities">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
diese Subklasse ist zugleich ein BufferProviderSetup, und etabliert damit auch das Schema mit den local-mem-pools
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1782349073329" ID="ID_1059252137" MODIFIED="1782349264675" TEXT="mu&#xdf; daf&#xfc;r aber den EngineCtx als Mock instantiieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...ich wollte diese direkten Referenz-Felder, um im produktiven Einsatz mir jeweils eine Indirektion einzusparen &#10233; das hat die Konsequenz, da&#223; ich auch eine neue Instanz des EngineCtx (als front-End) erzeugen mu&#223;, um eine spezielle lokale Instanz des BufferProviderSetup injizieren zu k&#246;nnen. Deshalb mu&#223; EngineCtx dann doch public konstruierbar sein (und nicht private mit fried f&#252;r die DependencyFactory). Schade, aber letztlich irrelevant.
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="smily_bad"/>
</node>
</node>
</node>
<node CREATED="1733527376977" ID="ID_506164017" MODIFIED="1733527393014" TEXT="typische Kollaborations-Schemata">
<node COLOR="#338800" CREATED="1782348916436" ID="ID_1779017226" MODIFIED="1782348932939" TEXT="erst mal eine einfache source-Node aufrufen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1742172669058" ID="ID_1246173489" MODIFIED="1742172669058" TEXT="Job ruft 2-Node-Chain auf (incl Buffer-Management)">
<linktarget COLOR="#01c3fe" DESTINATION="ID_1246173489" ENDARROW="Default" ENDINCLINATION="-1287;-72;" ID="Arrow_ID_1281025448" SOURCE="ID_1063052148" STARTARROW="None" STARTINCLINATION="-777;45;"/>
<icon BUILTIN="idea"/>
@ -116673,13 +116733,16 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node COLOR="#338800" CREATED="1782263969797" ID="ID_1061860304" MODIFIED="1782263998735" TEXT="Prototyping des Node-Pull (&#xd83e;&#xdc32; NodeLink_test)">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1782264001438" ID="ID_803942955" MODIFIED="1782264027626" TEXT="diesen Test mit einem tats&#xe4;chlichen Buffer-Manager betreiben">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1782264001438" ID="ID_803942955" MODIFIED="1782348836995" TEXT="diesen Test mit einem tats&#xe4;chlichen Buffer-Manager betreiben">
<icon BUILTIN="yes"/>
<node CREATED="1782264029058" ID="ID_1859274644" MODIFIED="1782264212337" TEXT="Vorbild: BufferProviderLoad_test">
<linktarget COLOR="#6d7983" DESTINATION="ID_1859274644" ENDARROW="Default" ENDINCLINATION="1027;88;" ID="Arrow_ID_1323120640" SOURCE="ID_200114754" STARTARROW="None" STARTINCLINATION="1041;-37;"/>
</node>
<node CREATED="1782264230355" ID="ID_599492347" MODIFIED="1782264244009" TEXT="definiert Setup mit Worker-Thread im Test"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1782348878612" ID="ID_639195897" MODIFIED="1782348912089" TEXT="dann einen Node-Tree aufrufen &#x2014; mit DiagnosticOutputSlot">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1782264256180" ID="ID_901821600" MODIFIED="1782264427850" TEXT="daraus dann die Bestandteile der NodeInvocation extrahieren">
<arrowlink COLOR="#535988" DESTINATION="ID_462733128" ENDARROW="Default" ENDINCLINATION="-21;-42;" ID="Arrow_ID_161274584" STARTARROW="None" STARTINCLINATION="-46;2;"/>
<icon BUILTIN="hourglass"/>