From 0933d2bba8fcd3d1f572f595f61c767704f6f752 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 11 Jun 2023 18:31:00 +0200 Subject: [PATCH] Dispatcher-Pipeline: simplify JobTicket and remove channel differentiation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing implementation of the Player from 2012~2015 inclduded an additional differentiation by media channel (for multichannel media) and would build a separate CalcStream for each channel. The in-depth analysis conducted for the ongoing »Vertical Slice« effort revealed that this differentiation is besides the point and would never be materialised: Since -- by definition -- all media processing has to be done by the engine, also the generation of the final output format including any channel multiplexing will happen in render nodes. The only exception would be when only a single channel of multichannel media is extracted -- yet this case would then translate into a dedicated ModelPort. Based on this reasoning, a lot of complexity (and some contradictions) within the JobTicket implementation can be removed -- together with some further leftovers of the fist attempt to build JobTickets always from a Mock specification (we now use construction by the Segment, based on an ExitNode, which is the expected actual implementation for production setup) --- src/steam/engine/dispatcher.hpp | 2 +- src/steam/engine/job-ticket.cpp | 72 ++-------- src/steam/engine/job-ticket.hpp | 118 ++++++----------- tests/core/steam/engine/mock-dispatcher.hpp | 4 - .../steam/fixture/fixture-segment-test.cpp | 2 +- wiki/thinkPad.ichthyo.mm | 123 ++++++++++++------ 6 files changed, 132 insertions(+), 189 deletions(-) diff --git a/src/steam/engine/dispatcher.hpp b/src/steam/engine/dispatcher.hpp index 8c6cfc2df..2eaf1ee49 100644 --- a/src/steam/engine/dispatcher.hpp +++ b/src/steam/engine/dispatcher.hpp @@ -292,7 +292,7 @@ namespace engine { this->expandAll([](TicketDepend& currentLevel) { JobTicket const* parent = currentLevel.second; - return lib::transformIterator (parent->getPrerequisites(0) + return lib::transformIterator (parent->getPrerequisites() ,[&parent](JobTicket const& prereqTicket) { // parent shifted up to first pos return TicketDepend{parent, &prereqTicket}; diff --git a/src/steam/engine/job-ticket.cpp b/src/steam/engine/job-ticket.cpp index 8749fb468..870f892d2 100644 --- a/src/steam/engine/job-ticket.cpp +++ b/src/steam/engine/job-ticket.cpp @@ -44,69 +44,19 @@ namespace engine { } // (END) Details... -// using mobject::Placement; -// using mobject::session::Effect; - using vault::engine::JobParameter; using vault::engine::JobClosure; - using lib::HashVal; - using lib::unConst; - class FrameJobClosure - : public JobClosure - { - // data members? - - private: /* === JobClosure Interface === */ - - JobKind - getJobKind() const - { - return CALC_JOB; - } - - bool - verify (Time nominalTime, InvocationInstanceID invoKey) const - { - UNIMPLEMENTED ("access the underlying JobTicket and verify the given job time is within the relevant timeline segment"); - return false; - } - - InvocationInstanceID - buildInstanceID (HashVal seed) const override - { - UNIMPLEMENTED ("systematically generate an invoKey, distinct for the nominal time"); - } - - size_t - hashOfInstance (InvocationInstanceID invoKey) const override - { - UNIMPLEMENTED ("interpret the invoKey and create a suitable hash"); - } - - void - invokeJobOperation (JobParameter parameter) override - { - UNIMPLEMENTED ("representation of the job functor"); - } - - - void - signalFailure (JobParameter parameter, JobFailureReason reason) override - { - UNIMPLEMENTED ("what needs to be done when a job cant be invoked?"); - } - - public: - - - }; /** special »do nothing« JobTicket marker */ const JobTicket JobTicket::NOP{}; //////////////////////////////////////////////////////////////TICKET #725 : do we actually need that for the final data structure? + + JobTicket::JobTicket() + : provision_{nopFunctor(), ExitNode::NIL} + { } /** @@ -129,10 +79,8 @@ namespace engine { else { REQUIRE (this->isValid(), "Attempt to generate render job for incomplete or unspecified render plan."); - REQUIRE (coordinates.channelNr < provision_.size(), "Inconsistent Job planning; channel beyond provision"); - Provision const& provision = provision_[coordinates.channelNr]; - JobClosure& functor = static_cast (unConst(provision.jobFunctor)); ////////////////TICKET #1287 : fix actual interface down to JobFunctor (after removing C structs) - InvocationInstanceID invoKey{timeHash (nominalTime, provision.invocationSeed)}; + JobClosure& functor = static_cast (unConst(provision_.jobFunctor)); ////////////////TICKET #1287 : fix actual interface down to JobFunctor (after removing C structs) + InvocationInstanceID invoKey{timeHash (nominalTime, provision_.invocationSeed)}; return Job(functor, invoKey, nominalTime); } @@ -166,12 +114,8 @@ namespace engine { bool JobTicket::verifyInstance (JobFunctor& functor, InvocationInstanceID const& invoKey, Time nominalTime) const { - for (Provision const& p : provision_) - if (util::isSameObject (p.jobFunctor, functor) - and invoKey == timeHash (nominalTime, p.invocationSeed) - ) - return true; - return false; + return util::isSameObject (provision_.jobFunctor, functor) + and invoKey == timeHash (nominalTime, provision_.invocationSeed); } diff --git a/src/steam/engine/job-ticket.hpp b/src/steam/engine/job-ticket.hpp index e66936a6a..2c1edfdb3 100644 --- a/src/steam/engine/job-ticket.hpp +++ b/src/steam/engine/job-ticket.hpp @@ -69,8 +69,6 @@ using lib::OrientationIndicator; using util::isnil; using lib::HashVal; using lib::LUID; -// -//class ExitNode; /** @@ -86,20 +84,19 @@ using lib::LUID; * Job tickets are created by a classical recursive descent call on the exit node, * which figures out everything to be done for generating data from this node. * To turn a JobTicket into an actual job, we need the additional information - * regarding the precise frame number (=nominal time) and the channel number - * to calculate (in case the actual feed is multichannel, which is the default). - * This way, the JobTicket acts as _higher order function:_ a function - * generating on invocation another, specific function (= the job). + * regarding the precise frame number (=nominal time) and a handle for the + * DataSink exposing buffers to output generated data. Thus effectively + * the JobTicket acts as _higher order function:_ a function generating + * on invocation another, specific function (= the job). * * @todo 4/23 WIP-WIP-WIP defining the invocation sequence and render jobs - * @todo maybe the per-channel specialisation can be elided altogether...? */ class JobTicket : util::NonCopyable { struct Prerequisite { - Prerequisite* next{nullptr}; + Prerequisite* next{nullptr}; // for intrusive list JobTicket const& prereqTicket; template @@ -109,14 +106,14 @@ using lib::LUID; }; using Prerequisites = LinkedElements; + /** what handling this task entails */ struct Provision { - Provision* next{nullptr}; - JobFunctor& jobFunctor; - ExitNode const& exitNode; + JobFunctor& jobFunctor; + ExitNode const& exitNode; InvocationInstanceID invocationSeed; - Prerequisites requirements{}; + Prerequisites requirements{}; Provision (JobFunctor& func, ExitNode const& node, HashVal seed =0) : jobFunctor{func} @@ -125,25 +122,15 @@ using lib::LUID; { } /////////////////TICKET #1293 : Hash-Chaining for invocation-ID... size_t hash? or a LUID? }; - - LinkedElements provision_; //////////////////////////////////////////////////TICKET #1297 : retract differentiation into channels here (instead use ModelPorts in the Segment) + /// @internal reference to all information required for actual Job creation + Provision provision_; - template - static LinkedElements buildProvisionSpec (IT); + JobTicket(); ///< @internal as NIL marker, a JobTicket can be empty template - static LinkedElements buildProvisionSpec (ExitNode const&, ALO&); + static Provision buildProvisionSpec (ExitNode const&, ALO&); - private: - JobTicket() { } ///< @internal as NIL marker, a JobTicket can be empty - - protected: - template - JobTicket(IT featureSpec_perChannel) - : provision_{buildProvisionSpec (featureSpec_perChannel)} - { } - public: template JobTicket (ExitNode const& exitNode, ALO& allocator) @@ -151,32 +138,33 @@ using lib::LUID; { } static const JobTicket NOP; - +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete class ExplorationState; friend class ExplorationState; ExplorationState startExploration() const; ////////////////////////////TICKET #1276 : likely to become obsolete ExplorationState discoverPrerequisites (uint channelNr =0) const; ////////////////////////////TICKET #1276 : likely to become obsolete +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete + + Job createJobFor (FrameCoord coordinates) const; auto - getPrerequisites (uint slotNr =0) const + getPrerequisites () const { - return lib::transformIterator (this->empty()? Prerequisites::iterator() - : provision_[slotNr].requirements.begin() - ,[](Prerequisite& prq) -> JobTicket const& + return lib::transformIterator (this->empty()? Prerequisites::const_iterator() + : provision_.requirements.begin() + ,[](Prerequisite const& prq) -> JobTicket const& { return prq.prereqTicket; }); } - Job createJobFor (FrameCoord coordinates) const; - bool empty() const { - return isnil (provision_); + return isnil (provision_.exitNode); } bool @@ -195,6 +183,7 @@ using lib::LUID; }; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete class JobTicket::ExplorationState { typedef Prerequisites::iterator SubTicketSeq; @@ -288,14 +277,17 @@ using lib::LUID; } } }; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete /** @internal prepare and assemble the working data structure to build a JobTicket. - * @tparam IT iterator to yield a sequence of specifications for each channel, - * given as `std::pair` of a JobFunctor and a further Sequence of - * JobTicket prerequisites. + * @tparam ALO type of an allocator front-end for generating prerequisite JobTicket(s) + * @param exitNode a (possibly recursive) tree of ExitNode, detailing points where to + * pull and process data from the render nodes network; these can refer + * to nested ExitNodes(s), which need to be processed beforehand, as + * prerequisite for invoking the given (dependent) ExitNode. * @return the final wired instance of the data structure to back the new JobTicket * @remark Note especially that those data structures linked together for use by the * JobTicket are themselves allocated "elsewhere", and need to be attached @@ -306,55 +298,22 @@ using lib::LUID; * frequently after each strike of edit operations, yet traversed * and evaluated on a sub-second scale for ongoing playback. */ - template - inline LinkedElements - JobTicket::buildProvisionSpec (IT featureSpec) - { /* ---- validate parameter type ---- */ - static_assert (lib::meta::can_IterForEach::value); // -- can be iterated - using Spec = typename IT::value_type; - static_assert (lib::meta::is_Tuple()); // -- payload of iterator is a tuple - using Node = typename std::tuple_element<0, Spec>::type; - using Seed = typename std::tuple_element<1, Spec>::type; - using Func = typename std::tuple_element<2, Spec>::type; - using Preq = typename std::tuple_element<3, Spec>::type; - static_assert (lib::meta::is_basically()); // -- first component refers to the ExitNode in the model - static_assert (lib::meta::is_basically()); // -- second component provides a specific seed for Invocation-IDs - static_assert (lib::meta::is_basically()); // -- third component specifies the JobFunctor to use - static_assert (lib::meta::can_IterForEach::value); // -- fourth component is again an iterable sequence - static_assert (std::is_same()); // -- which yields JobTicket prerequisites - REQUIRE (not isnil (featureSpec) - ,"require at least specification for one channel"); - - LinkedElements provisionSpec; //////////////////////////////////////////////////TICKET #1292 : need to pass in Allocator as argument - for ( ; featureSpec; ++featureSpec) ///////////////////////////////////////////////////TICKET #1297 : this additional iteration over channels will go away - { - ExitNode& node = std::get<0> (*featureSpec); - HashVal invoSeed = std::get<1> (*featureSpec); - JobFunctor& func = std::get<2> (*featureSpec); - auto& provision = provisionSpec.emplace (func, node, invoSeed); - for (Preq pre = std::get<3> (*featureSpec); pre; ++pre) - provision.requirements.emplace (*pre); - } - provisionSpec.reverse(); // retain order of given definitions per channel ////////////TICKET #1297 : obsolete; instead we differentiate by OutputSlot in the Segment - ENSURE (not isnil (provisionSpec)); - return provisionSpec; - } template - inline LinkedElements + inline JobTicket::Provision JobTicket::buildProvisionSpec (ExitNode const& exitNode, ALO& allocTicket) { REQUIRE (not isnil (exitNode)); // has valid functor - LinkedElements provisionSpec; HashVal invoSeed = exitNode.getPipelineIdentity(); JobFunctor& func = exitNode.getInvocationFunctor(); - auto& provision = provisionSpec.emplace (func, exitNode, invoSeed); + Provision provisionSpec{func, exitNode, invoSeed}; for (ExitNode const& preNode: exitNode.getPrerequisites()) - provision.requirements.emplace(preNode, allocTicket); ////////////////////////////////////TICKET #1292 : need to pass in generic Allocator as argument + provisionSpec.requirements.emplace(preNode, allocTicket); //////////////////////////////////////////TICKET #1292 : need to pass in generic Allocator as argument return provisionSpec; } - + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete /// @deprecated : could be expendable ... likely incurred solely by the use of Monads as design pattern inline JobTicket::ExplorationState JobTicket::startExploration() const @@ -373,13 +332,10 @@ using lib::LUID; inline JobTicket::ExplorationState JobTicket::discoverPrerequisites (uint channelNr) const { - REQUIRE (channelNr < provision_.size() or not isValid()); - - return isnil (provision_)? ExplorationState() - : ExplorationState (provision_[channelNr].requirements); + return empty()? ExplorationState() + : ExplorationState (util::unConst(provision_).requirements); } - - +/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete }} // namespace steam::engine diff --git a/tests/core/steam/engine/mock-dispatcher.hpp b/tests/core/steam/engine/mock-dispatcher.hpp index 329618ed9..df0575e1b 100644 --- a/tests/core/steam/engine/mock-dispatcher.hpp +++ b/tests/core/steam/engine/mock-dispatcher.hpp @@ -138,10 +138,6 @@ namespace test { : JobTicket{defineSimpleSpec (seed), allocator()} { } -// template -// MockJobTicket (HashVal seed, IT&& prereq) -// : JobTicket{defineSpec (seed, std::forward (prereq))} -// { } /* ===== Diagnostics ===== */ diff --git a/tests/core/steam/fixture/fixture-segment-test.cpp b/tests/core/steam/fixture/fixture-segment-test.cpp index c7833e073..9b117b54f 100644 --- a/tests/core/steam/fixture/fixture-segment-test.cpp +++ b/tests/core/steam/fixture/fixture-segment-test.cpp @@ -53,7 +53,7 @@ namespace test { /*****************************************************************************//** * @test Verify properties and behaviour of a single Segment in the Segmentation * - construction of a mocked Segment - * - TODO + * - on-demand allocation of a JobTicket for a ModelPort(index) * - TODO * @see steam::fixture::Segment * @see JobPlanningSetup_test diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 065826904..4a52e7867 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -74102,13 +74102,20 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - - - - + + + + + + + + + + + + + + @@ -74118,14 +74125,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - - + + + + + - + @@ -74240,12 +74249,21 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + +
- - + + + + + + + + @@ -74279,12 +74297,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - - - + + + @@ -74301,7 +74319,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -74341,16 +74359,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + + - + @@ -74359,7 +74378,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -74468,7 +74487,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -74483,8 +74503,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -74496,8 +74516,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -74543,22 +74563,22 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + - + - + - + - + - + @@ -74587,6 +74607,33 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +

+ ursprünglich dachte ich, im Ticket würde intern noch nach einer Channel-ID (für Mehrkanal-Medien) differenziert; die Analyse im Detail ergab jedoch, daß dies stets unter dem Konstrukt »ModelPort« subsummiert werden kann — viele interne Komplexitäten fallen damit weg +

+ +
+ + + + + + + + + + + + + + + + @@ -74938,7 +74985,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +