From 8c78e50730ccf9b19d0a6496cefae382d8261712 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 21 Jun 2023 03:55:09 +0200 Subject: [PATCH] Job-Planning: extended deadline integration test - allow to configure the expected job runtime in the test spec - remove link to EngineConfig and hard-wire the engine latency for now ... extended integration testing reveals two further bugs ;-) ... document deadline calculation --- src/steam/engine/exit-node.hpp | 23 +- src/steam/engine/job-planning.hpp | 21 +- src/steam/play/timings.cpp | 25 +- src/steam/play/timings.hpp | 8 +- src/vault/engine/engine-config.cpp | 4 +- src/vault/engine/engine-config.hpp | 3 + tests/47engine.tests | 9 +- .../engine/job-planning-pipeline-test.cpp | 22 +- tests/core/steam/engine/job-planning-test.cpp | 11 +- tests/core/steam/engine/mock-dispatcher.hpp | 10 +- wiki/renderengine.html | 38 ++-- wiki/thinkPad.ichthyo.mm | 215 +++++++++++------- 12 files changed, 236 insertions(+), 153 deletions(-) diff --git a/src/steam/engine/exit-node.hpp b/src/steam/engine/exit-node.hpp index 2bc137973..c1a643431 100644 --- a/src/steam/engine/exit-node.hpp +++ b/src/steam/engine/exit-node.hpp @@ -37,6 +37,7 @@ #include "lib/time/timevalue.hpp" #include "vault/engine/job.h" //////////////////////////////////////////////////////////////////TICKET #1295 : rather need a way to retrieve a real JobFunctor building block from the ProcNode +#include #include using lib::HashVal; @@ -52,6 +53,10 @@ namespace engine { using vault::engine::JobFunctor; + namespace {// hard wired placeholder config.... + const Duration DUMMY_JOB_RUNTIME{FSecs{1,50}}; + } + /** * A top-level point in the render node network where data generation can be driven. @@ -68,23 +73,33 @@ namespace engine { : util::Cloneable { HashVal pipelineIdentity_; //////////////////////////////////////////////////////////TICKET #1293 : Hash-Chaining for invocation-ID... derive from ProcNode wiring - ExitNodes prerequisites_; ///////////////////////////////////////////////////////////TICKET #1306 : actual access to low-level-Model (ProcNode) - JobFunctor* action_{nullptr}; ////////////////////////////////////////////////////////////TICKET #1295 : link to actual implementation action in low-level-Model + Duration runtimeBound_; ///////////////////////////////////////////////////////////TICKET #1283 : integrate with dynamic runtime observation + ExitNodes prerequisites_; ////////////////////////////////////////////////////////////TICKET #1306 : actual access to low-level-Model (ProcNode) + JobFunctor* action_{nullptr}; /////////////////////////////////////////////////////////////TICKET #1295 : link to actual implementation action in low-level-Model public: ExitNode() : pipelineIdentity_{0} + , runtimeBound_{DUMMY_JOB_RUNTIME} , prerequisites_{} { } ExitNode (HashVal id - ,ExitNodes&& prereq =ExitNodes{} + ,Duration jobRuntime + ,ExitNodes&& prereq =ExitNodes{} ,JobFunctor* functor =nullptr) : pipelineIdentity_{id} + , runtimeBound_{jobRuntime} , prerequisites_{std::move (prereq)} , action_{functor} { } + explicit + ExitNode (HashVal id + ,ExitNodes&& prereq =ExitNodes{}) + : ExitNode{id, DUMMY_JOB_RUNTIME, std::move(prereq)} + { } + static ExitNode NIL; @@ -125,7 +140,7 @@ namespace engine { getUpperBoundRuntime() const { ///////////////////////////////////////////////////////////////////////////TICKET #1283 : lay foundation how to observe timing behaviour for a render pipeline - return Duration{FSecs{1,50}}; // Uh-Oh booo + return runtimeBound_; } }; diff --git a/src/steam/engine/job-planning.hpp b/src/steam/engine/job-planning.hpp index 98c503fc3..9819857ad 100644 --- a/src/steam/engine/job-planning.hpp +++ b/src/steam/engine/job-planning.hpp @@ -213,21 +213,18 @@ namespace engine { - Duration - totalLatency (Timings const& timings) - { - return jobTicket_.getExpectedRuntime() - + timings.currentEngineLatency() - + timings.outputLatency; - } - Time doCalcDeadline(Timings const& timings) { - Time anchor = isTopLevel()? timings.getTimeDue(frameNr_) - : dependentPlan_->determineDeadline (timings); ////////////////////TICKET #1310 : quadratic in the depth of the dependency chain - return anchor - - totalLatency(timings); + if (isTopLevel()) + return timings.getTimeDue(frameNr_) // anchor at timing grid + - jobTicket_.getExpectedRuntime() // deduce the presumably runtime + - timings.engineLatency // and the generic engine overhead + - timings.outputLatency; // Note: output latency only on top-level job + else + return dependentPlan_->determineDeadline (timings) ////////////////////////////////////////////TICKET #1310 : WARNING - quadratic in the depth of the dependency chain + - jobTicket_.getExpectedRuntime() + - timings.engineLatency; } #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored... diff --git a/src/steam/play/timings.cpp b/src/steam/play/timings.cpp index c219e1415..123d58e2c 100644 --- a/src/steam/play/timings.cpp +++ b/src/steam/play/timings.cpp @@ -31,20 +31,26 @@ #include "steam/play/timings.hpp" -#include "vault/engine/engine-config.hpp" #include "lib/time/formats.hpp" #include "lib/time/timequant.hpp" - +#include "lib/rational.hpp" namespace steam { namespace play { - using vault::engine::EngineConfig; using lib::time::PQuant; using lib::time::Time; using lib::time::TimeVar; using lib::time::FrameCnt; + using lib::time::FSecs; + + namespace { // Hard wired placeholder settings... + + const Duration DEFAULT_ENGINE_LATENCY = Duration{Time{10,0}}; ///////////////////////////////////////TICKET #802 : shouldn't be hard wired + const Duration DEFAULT_JOB_PLANNING_TURNOVER(FSecs(3,2)); + + }//(End)hard wired settings namespace { // hidden local details of the service implementation.... @@ -74,7 +80,8 @@ namespace play { , playbackUrgency {ASAP} , playbackSpeed {1} , scheduledDelivery{Time::NEVER} - , outputLatency {Duration::NIL} + , outputLatency{Duration::NIL} + , engineLatency{DEFAULT_ENGINE_LATENCY} //////////////////////////////////////////////////////////////TICKET #802 : derive from engine state -- but make it adjustable for unit tests!!! { ENSURE (grid_); } @@ -85,6 +92,7 @@ namespace play { , playbackSpeed {1} , scheduledDelivery{realTimeAnchor} , outputLatency {Duration::NIL} + , engineLatency{DEFAULT_ENGINE_LATENCY} { ENSURE (grid_); } @@ -190,7 +198,7 @@ namespace play { Duration Timings::getPlanningChunkDuration() const { - return EngineConfig::get().currentJobPlanningRhythm(); + UNIMPLEMENTED ("controlling the job planning rhythm"); } @@ -211,13 +219,6 @@ namespace play { } - Duration - Timings::currentEngineLatency() const - { - return EngineConfig::get().currentEngineLatency(); - } - - Timings Timings::constrainedBy (Timings additionalConditions) diff --git a/src/steam/play/timings.hpp b/src/steam/play/timings.hpp index 1c7f93194..51a8411fd 100644 --- a/src/steam/play/timings.hpp +++ b/src/steam/play/timings.hpp @@ -100,7 +100,8 @@ namespace play { PlaybackUrgency playbackUrgency; boost::rational playbackSpeed; /////////////TICKET #902 we need a more generic representation for variable speed playback Time scheduledDelivery; ///< a wall clock time corresponding to the Grid's origin. Can be Time::Never (=not time bound) - Duration outputLatency; ///////////////TICKET #1301 do we even need this information here (-> rather on some Engine state) + Duration outputLatency; ///////////////TICKET #802 this information is necessary, but it is not clear who maintains and updates the latency + Duration engineLatency; ///< reasonable guess at the scheduling and dispatch-delay of the render engine explicit Timings (FrameRate fps); @@ -172,11 +173,6 @@ namespace play { * follow-up planning job */ FrameCnt establishNextPlanningChunkStart(FrameCnt anchorFrame) const; - /** reasonable guess of the current engine working delay. - * Frame calculation deadlines will be readjusted by that value, - * to be able to deliver in time with sufficient likeliness. */ - Duration currentEngineLatency() const; - bool isOriginalSpeed() const; bool isTimebound() const; diff --git a/src/vault/engine/engine-config.cpp b/src/vault/engine/engine-config.cpp index 8f27c7120..cf575d6c8 100644 --- a/src/vault/engine/engine-config.cpp +++ b/src/vault/engine/engine-config.cpp @@ -23,6 +23,9 @@ /** @file engine-config.cpp ** implementation of engine configuration and parametrisation + ** @todo 6/2023 not clear if this is placeholder code or something substantial; + ** however, it seems not plausible; rather I'd expect some data collection + ** and information service to be connected with the RenderEnvironmentClosure */ @@ -31,7 +34,6 @@ #include "lib/rational.hpp" -using boost::rational; using lib::time::FrameRate; using lib::time::FSecs; using util::Rat; diff --git a/src/vault/engine/engine-config.hpp b/src/vault/engine/engine-config.hpp index ad15dec77..11ed388f9 100644 --- a/src/vault/engine/engine-config.hpp +++ b/src/vault/engine/engine-config.hpp @@ -23,6 +23,9 @@ /** @file engine-config.hpp ** access point to configuration of engine parameters + ** @todo 6/2023 not clear if this is placeholder code or something substantial; + ** however, it seems not plausible; rather I'd expect some data collection + ** and information service to be connected with the RenderEnvironmentClosure */ diff --git a/tests/47engine.tests b/tests/47engine.tests index e29d32e25..46b288108 100644 --- a/tests/47engine.tests +++ b/tests/47engine.tests @@ -52,11 +52,12 @@ END TEST "Render job planning calculation" JobPlanning_test < TimeVar //////////////////////////////////TICKET #1261 : transform-iterator unable to handle immutable time + .transform([&](FrameCnt frameNr) { return grid->timeOf (frameNr); }) @@ -175,10 +175,9 @@ namespace test { play::Timings timings (FrameRate::PAL); CHECK (materialise ( - treeExplore ( - dispatcher.forCalcStream(timings) - .timeRange(Time{200,0}, Time{500,0}) // Note: end point is exclusive - )) + dispatcher.forCalcStream(timings) + .timeRange(Time{200,0}, Time{500,0}) // Note: end point is exclusive + ) == "200ms-240ms-280ms-320ms-360ms-400ms-440ms-480ms"_expect); } @@ -268,10 +267,13 @@ namespace test { { MockDispatcher dispatcher{MakeRec() // define a single segment for the complete time axis .attrib("mark", 11) // the »master job« for each frame has pipeline-ID ≔ 11 + .attrib("runtime", Duration{Time{10,0}}) .scope(MakeRec() .attrib("mark",22) // a »prerequisite job« marked with pipeline-ID ≔ 22 + .attrib("runtime", Duration{Time{20,0}}) .scope(MakeRec() .attrib("mark",33) // further »recursive prerequisite« + .attrib("runtime", Duration{Time{30,0}}) .genNode()) .genNode()) .genNode()}; @@ -307,15 +309,15 @@ namespace test { return _Fmt{"J(%d|%s⧐%s)"} % mark % nominalTime % deadline; }; - CHECK (visualise(pipeline) == "J(11|200ms⧐1s148ms)"_expect); // first job in pipeline: nominal t=200ms, deadline 1s148ms - + CHECK (visualise(pipeline) == "J(11|200ms⧐1s180ms)"_expect); // first job in pipeline: nominal t=200ms, + // .... 10ms engine latency + 10ms job runtime ⟶ deadline 1s180ms CHECK (materialise( treeExplore(move(pipeline)) .transform(visualise) ) - == "J(11|200ms⧐1s148ms)-J(22|200ms⧐1s96ms)-J(33|200ms⧐1s44ms)-" - "J(11|240ms⧐1s188ms)-J(22|240ms⧐1s136ms)-J(33|240ms⧐1s84ms)-" - "J(11|280ms⧐1s228ms)-J(22|280ms⧐1s176ms)-J(33|280ms⧐1s124ms)"_expect); + == "J(11|200ms⧐1s180ms)-J(22|200ms⧐1s150ms)-J(33|200ms⧐1s110ms)-" // ... -(10+10) | -(10+10)-(10+20) | -(10+10)-(10+20)-(10+30) + "J(11|240ms⧐1s220ms)-J(22|240ms⧐1s190ms)-J(33|240ms⧐1s150ms)-" + "J(11|280ms⧐1s260ms)-J(22|280ms⧐1s230ms)-J(33|280ms⧐1s190ms)"_expect); } }; diff --git a/tests/core/steam/engine/job-planning-test.cpp b/tests/core/steam/engine/job-planning-test.cpp index be4f8b86a..10a0008dc 100644 --- a/tests/core/steam/engine/job-planning-test.cpp +++ b/tests/core/steam/engine/job-planning-test.cpp @@ -109,7 +109,7 @@ namespace test { // the following calculations are expected to happen.... Duration latency = ticket.getExpectedRuntime() - + timings.currentEngineLatency() + + timings.engineLatency + timings.outputLatency; Offset nominalOffset (timings.getFrameStartAt(0), timings.getFrameStartAt(frameNr)); @@ -143,7 +143,9 @@ namespace test { setupDependentJob() { MockDispatcher dispatcher{MakeRec() // »master job« for each frame + .attrib("runtime", Duration{Time{30,0}}) .scope(MakeRec() // a »prerequisite job« on which the »master job« depends + .attrib("runtime", Duration{Time{50,0}}) .genNode()) .genNode()}; @@ -169,14 +171,15 @@ namespace test { // the following relations are expected to hold for the prerequisite.... Duration latency = prereq.getExpectedRuntime() - + timings.currentEngineLatency() - + timings.outputLatency; + + timings.engineLatency; // Note: here only the engine, not the output latency Time expectedDeadline{masterDeadline - latency}; cout << util::_Fmt{"Prerequisite......\n" + "master deadline : %s\n" "latency : %s\n" - "deadline : %s"} + "prereq deadline : %s"} + % masterDeadline % latency % prereqDeadline << endl; diff --git a/tests/core/steam/engine/mock-dispatcher.hpp b/tests/core/steam/engine/mock-dispatcher.hpp index 347025dfd..497d85e88 100644 --- a/tests/core/steam/engine/mock-dispatcher.hpp +++ b/tests/core/steam/engine/mock-dispatcher.hpp @@ -154,7 +154,7 @@ namespace test { static ExitNode defineSimpleSpec (HashVal seed = 1+rand()) { - return ExitNode{seed + return ExitNode{seed, DUMMY_JOB_RUNTIME ,ExitNodes{} ,& MockJob::getFunctor()}; } @@ -224,6 +224,7 @@ namespace test { buildExitNodeFromSpec (GenNode const& spec) { return ExitNode{buildSeed (spec) + ,buildRuntime (spec) ,buildPrerequisites (spec) ,& MockJob::getFunctor()}; } @@ -241,6 +242,13 @@ namespace test { return seed? HashVal(*seed) : HashVal(rand() % 1000); } + Duration + buildRuntime (GenNode const& spec) + { + auto runtime = spec.retrieveAttribute ("runtime"); + return runtime? *runtime : DUMMY_JOB_RUNTIME; + } + ExitNodes buildPrerequisites (GenNode const& spec) { diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 8bc3c8d75..ec0faa4af 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2498,34 +2498,32 @@ Additionally, they may be used for resource management purposes by embedding a r #* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic) -
+
An entity within the RenderEngine, responsible for translating a logical [[calculation stream|CalcStream]] (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames. As a //service// the Dispatcher acts as //bridge// between [[»playback«|Player]] and the [[render nodes network|Rendering]].
 
 The Dispatcher works together with the [[job ticket(s)|JobTicket]] and the [[Scheduler]]; actually these are the //core abstractions//&nbsp; the process of ''job planning'' relies on. While the actual scheduler implementation lives within the Vault, the job tickets and the dispatcher are located within the [[Segmentation]], which is the backbone of the [[low-level model|LowLevelModel]]. More specifically, the dispatcher interface is //implemented//&nbsp; by a set of &rarr; [[dispatcher tables|DispatcherTables]] within the segmentation.
 
-{{red{stalled since 2014}}} -- development on this (important) topic has been postponed. Moreover, some rough edges remain within the Design &rarr; see [[some notes...|AboutMonads]]
-
 !Collaborations
-The purpose of this interface is to support the planning of new jobs, for a given CalcStream. From time to time, a chunk of new RenderJob entries will be prepared for the [[Scheduler]]. Each job knows his frame number and the actual ProcNode to operate. So, to start planning jobs, we need to translate time &rarr; frame number &rarr; segment &rarr; real exit node.
-
+The purpose of this interface is to support the planning of new jobs, for a given [[stream-of-calculations|CalcStream]]. From time to time, a chunk of new [[frame rendering jobs|RenderJob]] entries will be prepared for the [[Scheduler]]. Each job knows his frame number and the actual [[render node|ProcNode]] to operate. So, to start planning jobs, we need to translate...
+⧐ time &rarr; frame number &rarr; segment &rarr; real exit node.
 !!!Invocation situation
 * our //current starting point//&nbsp; is given as ''anchor frame'', related to the [[Timings]] of a [[render/playback process|PlayProcess]]
-* we want to address a specific frame, addressed by an //absolute frame-number// -- interpreted relative to the //frame grid// of these //playback {{{Timings}}}//
-* &rArr; the dispatcher establishes the //fundamental coordinates// of that frame (''frame coordinates''), comprised of
+* we want to address a specific frame, denominated by an //absolute frame-number// -- interpreted relative to the //frame grid// of these //playback {{{Timings}}}//
+* ⟹ the dispatcher establishes the //fundamental characteristics// of that frame, comprised of
 ** the absolute nominal time
 ** the absolute frame number
 ** the ModelPort to draw data from
-** focussed context information:
+** additional focussed context information:
 *** the relevant [[segment|Segmentation]] responsible for producing this frame
 *** the corresponding JobTicket to use at this point
 *** a concrete ExitNode to pull for this frame
-*** possibly a real-time ''deadline''
+*** possibly a real-time related [[deadline|JobDeadline]]
 
 !!!Planning Chunks
-In fact, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' and starting point for the next partial evaluation. On the implementation level, each CalcStream maintains a {{{RenderDrive}}} (see below), which acts both as //planning pipeline// and JobFunctor for the next planning chunk to come. Moreover, the associated [[Timings]] record establishes a definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation.
+As such, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' and starting point for the next partial evaluation. On the implementation level, each CalcStream maintains a {{{RenderDrive}}} (see below), which acts both as //planning pipeline// and JobFunctor for the next planning chunk to come. Moreover, the associated [[Timings]] record establishes a definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation.
 
 !!!related timelines and the frame grid
-The frame dispatch step joins and combines multiple time axes. Through the process of //scheduling,// the output generation is linked to real ''wall clock time'' and the dispatch step establishes the deadlines, taking the ''engine latency'' into account. As such, any render or playback process establishes an ''output time grid'', linking frame numbers to nominal output time or timecode, based on the ''output frame rate'' -- where both the framerate and the actual progression (speed) might be changed dynamically. But beyond all of this there is a third, basically independent temporal structure involved: the actual content to render, the ''effective session timeline''. While encoded in nominal, absolute, internal time values not necessarily grid aligned, in practice at least the //breaking points,// the temporal location where the content or structure of the pipeline changes, are aligned //to a grid used while creating the edit.// Yet this session timing structure need not be related in any way to the playback grid, nor is it necessarily the same as the ''source grid'' defined by the media data used to feed the pipeline.
+The frame dispatch step joins and combines multiple time axes. Through the process of //scheduling,// the output generation is linked to real ''wall clock time'' and the dispatch step establishes the [[deadlines|JobDeadline]], taking the ''engine latency'' into account. As such, any render or playback process establishes an ''output time grid'', linking frame numbers to nominal output time or timecode, based on the ''output frame rate'' -- where both the framerate and the actual progression (speed) might be changed dynamically. But beyond all of this there is a third, basically independent temporal structure involved: the actual content to render, the ''effective session timeline''. While encoded in nominal, absolute, internal time values not necessarily grid aligned, in practice at least the //breaking points,// the temporal location where the content or structure of the pipeline changes, are aligned //to an implicit grid used while creating the edit.// Yet this ''edit timing'' structure need not be related in any way to the ''playback grid'', nor is it necessarily the same as the ''source grid'' defined by the media data used to feed the pipeline.
 
 These complex relationships are reflected in the invocation structure leading to an individual frame job. The [[calculation stream|CalcStream]] provides the [[render/playback timings|Timings]], while the actual implementation of the dispatcher, backed by the [[Fixture]] and thus linked to the session models, gets to relate the effective nominal time, the frame number, the exit node and the //processing function.//
 
@@ -4261,9 +4259,21 @@ Thus an invocation trail represents one specific path leading to the invocation
 ''Note'': {{red{future plans and visions -- no clear and distinct meaning -- as of 4/21}}}
 
-
+
+
//Point at the real wall-clock time axis, at which a [[render job|RenderJob]] must have delivered its action.//
+After that point, further pursuing the evaluation of such a job, or any of its dependencies is futile and can be abandoned.
+
+!External constraints and Latency
+For the typical calculation of media data frames, several dependent actions have to be carried out in proper order. The »top-level job« serves the purpose to deliver the complete media frame into some external //data sink.// When the render engine operates //time-bound// (as for real-time playback), delivery of data is acceptable only during a limited //time window// -- it starts when the data buffer (from a double buffering or round-robin scheme) becomes „open“ and ready to receive data, and it ends with the next »frame clock tick« when this data must be posted to the external hardware output or receiver. Working backward from these external constraints, it is clear that the job actually must be dispatched earlier by some margin, since moving forward all these interactions requires some processing time. This additional time margin is known as ''Latency'' -- further structured into the //output latency// inherent to the presentation mechanism, and the //engine latency// due to the overhead of coordinating the render operations. In addition to the latency, also the actual ''calculation effort'' must be accounted for, and combining these figures gives the ''schedule deadline'', which is the latest point in (real wall-clock) time where a calculation may still possibly reach its goal.
+
+Most calculations are not monolithic and can not be performed in-memory in a single run -- rather, some ''prerequisites'' must be prepared, like e.g. loading source media data from disk and decoding this data through an external library. Often prerequisites are //I/O-bound// and //intermittent// in nature, and will thus be organised as separate ''prerequisite jobs''. Deadline calculations for these are coordinated by a similar scheme and lined up backwards to the dependend job's schedule deadline.
+!!!Computation of Deadlines
+Pre-planning this chain of schedules is only necessary for {{{TIMEBOUND}}} playback -- in the other cases, for //best effort// or //background// computations, just the sequence of dependencies must be observed, starting the next step only after all prerequisites are ready. And while the [[Scheduler]] actually has the capability to react on such logical dependencies, for precise timing the schedule deadlines will be prepared in the JobPlanningPipeline. Working backwards from the »top-level« frame job, prerequisites will be discovered incrementally by a recursive depth-first »tree exploration«. Defined by a //exploration function// within an iterator-pipeline construct, further layers of child dependencies are discovered and pushed onto a stack. At some point, a »leaf prerequisite« is discovered -- and at that point the complete chain of dependencies resides within this stack, each represented as a {{{JobPlanning}}} data record. The computation is arranges in a way to link these dependent planning contexts together, allowing to determine the dependent scheduling deadlines by recurring to the parent {{{JobPlanning}}} record, all the way up to the root record, which aligns to an time gird point externally set by the playback timings.
+
+
+
//Depth-first evaluation pipeline used in the FrameDispatcher to generate the next chunk of [[render jobs|RenderJob]]//
-This is an implementation structure backed and established by the [[Dispatcher|FrameDispatcher]] and operated by the RenderDrive core of each CalcStream, where it is assembled by a //builder notation// -- backed by the {{{TreeExplorer}}} iterator pipeline framework; besides the typical filter and transform operations, the latter offers an »expansion« mechanism to integrate a //monadic exhaustive depth-first search,// allowing to pick up all prerequisites of a given calculation step by step within a local planning context.
+This is an implementation structure backed and established by the [[Dispatcher|FrameDispatcher]] and operated by the RenderDrive core of each CalcStream, where it is assembled by a //builder notation// -- backed by the {{{TreeExplorer}}} iterator pipeline framework; besides the typical filter and transform operations, the latter offers an »expansion« mechanism to integrate a //monadic exhaustive depth-first search,// allowing to pick up all prerequisites of a given calculation, proceeding step by step within a local planning context.
 
 !Stages of the Job-planning pipeline
 ;frame-tick
@@ -4276,7 +4286,7 @@ This is an implementation structure backed and established by the [[Dispatcher|F
 ;job-planning
 :collect the complete planning context and determine time frame
 :*especially add a {{{DataSink}}} specification to the JobFunctor closure
-:*and evaluate the dependency structure from the //exploration step// to establish Job deadlines
+:*and evaluate the dependency structure from the //exploration step// to establish Job [[deadlines|JobDeadline]].
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8cfdda9da..3b3d45916 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -69960,7 +69960,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -70383,8 +70383,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -70542,9 +70542,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + @@ -70746,6 +70746,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + @@ -73873,10 +73876,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + @@ -73920,8 +73923,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -73951,7 +73954,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -73962,11 +73965,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

- +
+ +
- + @@ -73987,6 +73992,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + +
@@ -75578,71 +75586,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - - - - - - - - - - - - - - - - -

- ...damit, daß das JobPlanning bereits in der Mitte der Pipeline erzeugt wird... -

- -
-
- - - - - - -

- hier spielt auch mit, daß man das expandPrerequisites() stets weglassen kann (das folgt schon aus den Eigenschaften einer solchen Expander-Funktion — sie muß auch ohne Expansion funktionieren) -

- -
-
- - - -
- - - - - - -

- Vorläufig: einen Transformer vorsehen, der das JobPlanning noch manipulieren kann -

- -
- - - - - -

- Wichtig: dieser Transformer muß explizit JobPlanning& als Ergebnistyp deklarieren: damit bekommt man die »Referenz-Variante« vom ItemWrapper — leider aber auch eine zusätzliche Indirektion, die nicht wegoptimiert werden kann (weil sie je nach darunter liegendem Expander woanders hin zeigt) -

- -
- -
+ + + @@ -75828,7 +75777,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -75906,7 +75855,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +
@@ -76561,7 +76510,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -76572,11 +76521,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - - + + + @@ -76587,8 +76536,33 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

- + + + + + + + + + +

+ ...aktuell denke ich, die Details der Timings kommen aus der RenderEnvironmentClosure (aber was das heißt sei dahingestellt....) +

+ +
+ +
+
+ + + + + + +
+ +
@@ -77370,6 +77344,77 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ ...damit, daß das JobPlanning bereits in der Mitte der Pipeline erzeugt wird... +

+ +
+
+ + + + + + +

+ hier spielt auch mit, daß man das expandPrerequisites() stets weglassen kann (das folgt schon aus den Eigenschaften einer solchen Expander-Funktion — sie muß auch ohne Expansion funktionieren) +

+ +
+
+ + + +
+ + + + + + +

+ Vorläufig: einen Transformer vorsehen, der das JobPlanning noch manipulieren kann +

+ +
+ + + + + +

+ Wichtig: dieser Transformer muß explizit JobPlanning& als Ergebnistyp deklarieren: damit bekommt man die »Referenz-Variante« vom ItemWrapper — leider aber auch eine zusätzliche Indirektion, die nicht wegoptimiert werden kann (weil sie je nach darunter liegendem Expander woanders hin zeigt) +

+ +
+ +
+
+