diff --git a/tests/steam/engine/node-link-test.cpp b/tests/steam/engine/node-link-test.cpp index 61db5c044..ff25bf80c 100644 --- a/tests/steam/engine/node-link-test.cpp +++ b/tests/steam/engine/node-link-test.cpp @@ -39,7 +39,7 @@ using lib::Symbol; using util::isnil; using util::isSameObject; using test::TestFrame; -using test::testRand; +using test::testRand; namespace ont = test::ont; namespace steam { @@ -362,7 +362,7 @@ namespace test { // Prepare a precomputed parameter for the complete tree - auto selectFrameNo = [&](TurnoutSystem& tuSys){ return quantSecs(tuSys.getNomTime()); }; + auto selectFrameNo = [&](TurnoutSystem& turSys){ return quantSecs(turSys.getNomTime()); }; auto paramSpec = buildParamSpec() .addSlot (selectFrameNo); @@ -373,8 +373,8 @@ namespace test { auto stepMixer = [] (FrameCnt id)-> ont::Factr { return util::limited (0, + id, 50) / 50.0; }; // note: binds the accessor for the precomputed FrameNo-parameter - auto autoFilter = [=](TurnoutSystem& tuSys){ return stepFilter (tuSys.get (accFrameNo)); }; - auto autoMixer = [=](TurnoutSystem& tuSys){ return stepMixer (tuSys.get (accFrameNo)); }; + auto autoFilter = [=](TurnoutSystem& turSys){ return stepFilter (turSys.get (accFrameNo)); }; + auto autoMixer = [=](TurnoutSystem& turSys){ return stepMixer (turSys.get (accFrameNo)); }; // A Node with two (source) ports diff --git a/tests/steam/engine/node-opera-test.cpp b/tests/steam/engine/node-opera-test.cpp index 8da6b2e33..395c19889 100644 --- a/tests/steam/engine/node-opera-test.cpp +++ b/tests/steam/engine/node-opera-test.cpp @@ -17,6 +17,8 @@ #include "test/run.hpp" +#include "lib/allocation-cluster.hpp" +#include "test/test-rand-ontology.hpp" #include "vault/mem/buffer-provider-setup.hpp" #include "vault/mem/engine-buffer-metadata.hpp" #include "vault/mem/engine-buffer-manager.hpp" @@ -24,8 +26,12 @@ #include "vault/mem/local-buffer-store.hpp" #include "steam/engine/engine-facilities.hpp" #include "steam/engine/node-builder.hpp" +#include "steam/asset/meta/time-grid.hpp" +#include "lib/time/timequant.hpp" +#include "lib/time/timecode.hpp" #include "lib/depend-inject.hpp" #include "lib/thread.hpp" +//#include "lib/symbol.hpp" //#include "lib/util.hpp" #include "test/diagnostic-output.hpp"///////////////TODO @@ -36,6 +42,19 @@ using std::this_thread::yield; using std::this_thread::sleep_for; using std::chrono_literals::operator ""us; +using lib::time::Time; +using lib::time::QuTime; +using lib::time::FrameNr; +using lib::time::FrameCnt; +using lib::time::FrameRate; +using lib::time::PGrid; +using steam::asset::meta::TimeGrid; +//using lib::Symbol; +//using util::isnil; +//using util::isSameObject; +using test::TestFrame; +using test::testRand; +namespace ont = test::ont; namespace steam { namespace engine{ @@ -99,6 +118,7 @@ namespace test { seedRand(); invokeSimpleNode(); + invokeNodeTree(); } @@ -170,6 +190,176 @@ namespace test { // now there should be no memory leaks....... CHECK (0 == watch(*mockEngine.globalPool).bytesLeased()); } + + + + + /** @test perform a RenderInvocation to pull from a complex node tree into an output sink. + * - use a test helper class to build a node topology similar to NodeLink_test + * - the ProcNodes and all backing structures are managed by an AllocationCluster + */ + void + invokeNodeTree() + { + TestEngineCtx mockEngine; + RenderPipeline pipeline; + + Thread testWorker{[&] /* === perform a render invocation === */ + { + BuffHandle buff = EngineCtx::access().mem.lockBufferFor(); + Time nomTime{Time::ZERO}; + ProcessKey key{0}; + uint port{0}; + + TestFrame& result = buff.accessAs(); + CHECK (result.isPristine()); +SHOW_EXPR(result.getChecksum()) + + // Trigger Node invocation... + buff = pipeline.exitNode.pull (port, buff, nomTime, key); + + CHECK (not result.isPristine()); + CHECK (result.isValid()); +SHOW_EXPR(result.getChecksum()) + buff.release(); + }}; + + while (testWorker) + yield(); // wait for worker to finish + sleep_for (500us); + + mockEngine.globalPool->processPendingRequests(); + // now there should be no memory leaks....... + CHECK (0 == watch(*mockEngine.globalPool).bytesLeased()); + } + + + /** + * Helper context to build and manage a processing chain of Render Nodes. + * In the real Render Engine, these processing chains are generated by the Builder, + * and organised into »Segments« of the timeline, where each segment features a + * distinct node connectivity (i.e. typically for one clip). + * For this test, a single top-level _exit node_ is exposed. + * @note this helper context is constructed in _RAII style_, + * and the embedded AllocationCluster unwinds automatically. + * @see \ref NodeLink_test for a detailed breakdown of a similar topology + */ + struct RenderPipeline + : util::NonCopyable + { + static constexpr ont::Flavr SRC_A = 10; ///< »chain-A« arbitrary source frame marker + static constexpr ont::Flavr SRC_B = 20; ///< similar for »chain-B« + + ProcNode& exitNode{buildTree()}; + + HashVal + expectedChecksum (Time nomTime) + { + UNIMPLEMENTED ("recompute"); + } + + private: + /** custom allocator for this node tree */ + lib::AllocationCluster alloc_; + + PGrid secondsGrid_{TimeGrid::build (FrameRate{1})}; + + FrameCnt quantSecs (Time time) { return secondsGrid_->gridPoint (time); }; + + ont::Param stepFilter(FrameCnt id) { return util::limited (10, -10 + id, 50); }; + ont::Factr stepMixer (FrameCnt id) { return util::limited (0, + id, 50) / 50.0; }; + + /** @internal wire the processing node topology. + * This fake "render pipeline" is based on hash chaining computations, + * operating on TestFrame (with reproducible random data content). + * Two processing chains are built, each producing test frames; + * the first chain even adds a "filter". Some "parameters" are added + * to change the random data in a deterministic way. Finally, both chains + * are "mixed" together into a single result TestFrame. Furthermore, the + * parameter automation is based on a precomputed _frame number_ parameter, + * that is derived from the _absolute nominal time_ by quantisation into a + * time grid with a stepping of 1 second (-> `FrameRate{1}`). This precomputed + * frame number parameter is stored in a parameter data block on the call stack, + * prior to recursively evaluating the node chain attached below. + * @note the generated Render Nodes are placed into the AllocationCluster, + * together with all the further storage used by those nodes internally. + * The real Render Engine uses an AllocationCluster per Segment of the timeline. + */ + ProcNode& + buildTree() + { + // Prepare a precomputed parameter for the complete tree + auto selectFrameNo = [&](TurnoutSystem& turSys){ return quantSecs (turSys.getNomTime()); }; + auto paramSpec = buildParamSpec() + .addSlot (selectFrameNo); + auto accFrameNo = paramSpec.makeAccessor<0>(); + + // Automation functions using the accessor for the precomputed FrameNo-parameter + auto autoFilter = [&](TurnoutSystem& turSys){ return stepFilter (turSys.get (accFrameNo)); }; + auto autoMixer = [&](TurnoutSystem& turSys){ return stepMixer (turSys.get (accFrameNo)); }; + + // use processing functions from the »TestRand Ontology« + auto testGen = testRand().setupGenerator(); + auto testMan = testRand().setupManipulator(); + auto testMix = testRand().setupCombinator(); + + // A source node to produce random test frames + auto& n1s = alloc_.create( + prepareNode("srcA") + .preparePort() + .invoke(testGen.procID(), testGen.makeFun()) // params(frameNo, flavour) + .closeParam<1>(SRC_A) // --> fix the flavour ≔ SRC_A + .retrieveParam(accFrameNo) + .completePort() + .build()); + + // A node to »filter« the data in chain-A + auto& n1f = alloc_.create( + prepareNode("filterA") + .preparePort() + .invoke(testMan.procID(), testMan.makeFun()) + .attachParamFun(autoFilter) // filter-param <-- autoFilter(frameNo) + .connectLead(n1s) + .completePort() + .build()); + + + // A secondary source Node for the »chain-B« + auto& n2s = alloc_.create( + prepareNode("srcB") + .preparePort() + .invoke(testGen.procID(), testGen.makeFun()) // params(frameNo, flavour) + .closeParam<1>(SRC_B) // --> fix the flavour ≔ SRC_B + .retrieveParam(accFrameNo) + .completePort() + .build()); + + + // A mixing Node to combine both chains + auto& mix = alloc_.create( + prepareNode("mix") + .preparePort() + .invoke(testMix.procID(), testMix.makeFun()) + .attachParamFun(autoMixer) // mixer-param <-- autoMixer(frameNo) + .connectLead(n1f) + .connectLead(n2s) + .completePort() + .build()); + + + // Place a »Param-Agent«-Node on top to pre-compute the FrameNo + auto& parNode = alloc_.create( + prepareNode("Param") + .preparePort() + .computeParam(paramSpec) + .delegateLead(mix) + .completePort() + .build()); + + // here the »Param-Agent« also acts as the Exit-Node + return parNode; + } + }; }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 5ea211881..0b79a1370 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -116727,6 +116727,25 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + +

+ sollte exakt die gleiche Kalkulation direkt wiederholen (genauso wie in NodeLink_test) und die Checksumme berechnen... +

+ + +
+
+