From 991f0a31f4de27f475d866b27ce3a5dde7fc02ab Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 14 Dec 2024 03:50:10 +0100 Subject: [PATCH] =?UTF-8?q?Invocation:=20this=20=C2=BBweaving-pattern?= =?UTF-8?q?=C2=AB=20evolves=20into=20a=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting from a prototypical implementation, where each »slot« in the function is directly connected to the corresponding lead / port, the implementation of the `SimpleWeavingPattern` (as it was called previously) could be augmented and adapted gradually — and seems well suited to cover most standard cases of ''media processing'' So a name change is mandated, and the code is also extracted and relocated, possibly even to be combined with the code of the `InvocationAdapter`, thereby hopefully making the implementation more accessible Generally speaking, ''weaving patterns'' take on the role of the prime extension point regarding `Port` implementation. --- src/steam/engine/turnout.hpp | 120 ++------- src/steam/engine/weaving-pattern-builder.hpp | 6 +- src/steam/engine/weaving-pattern.hpp | 220 +---------------- tests/core/steam/engine/node-base-test.cpp | 1 + wiki/thinkPad.ichthyo.mm | 242 +++++++++++++++---- 5 files changed, 216 insertions(+), 373 deletions(-) diff --git a/src/steam/engine/turnout.hpp b/src/steam/engine/turnout.hpp index 0a5102846..c648b0862 100644 --- a/src/steam/engine/turnout.hpp +++ b/src/steam/engine/turnout.hpp @@ -54,29 +54,14 @@ #include "steam/common.hpp" #include "steam/engine/proc-node.hpp" #include "steam/engine/turnout-system.hpp" -#include "steam/engine/feed-manifold.hpp" -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation -//#include "vault/gear/job.h" -//#include "steam/engine/exit-node.hpp" -//#include "lib/time/timevalue.hpp" -//#include "lib/linked-elements.hpp" -#include "lib/several.hpp" -//#include "lib/util-foreach.hpp" -//#include "lib/iter-adapter.hpp" -#include "lib/meta/function.hpp" -//#include "lib/itertools.hpp" -//#include "lib/util.hpp" ////////OOO wegen manifoldSiz() #include -#include -//#include namespace steam { namespace engine { using std::forward; - using lib::Several; /** @@ -97,98 +82,19 @@ namespace engine { } + template + constexpr bool + _verify_usable_as_WeavingPattern() + { + using Feed = typename PAT::Feed; + ASSERT_MEMBER_FUNCTOR (&PAT::mount, Feed()); + ASSERT_MEMBER_FUNCTOR (&PAT::pull, void(Feed&, TurnoutSystem&)); + ASSERT_MEMBER_FUNCTOR (&PAT::shed, void(Feed&, OptionalBuff)); + ASSERT_MEMBER_FUNCTOR (&PAT::weft, void(Feed&)); + ASSERT_MEMBER_FUNCTOR (&PAT::fix, BuffHandle(Feed&)); + return sizeof(PAT); + } - /** - * Standard implementation for a _Weaving Pattern_ to connect - * the input and output data feeds (buffers) into a processing function. - * @tparam INVO a configuration / policy base class to _adapt for invocation_ - * @note assumptions made regarding the overall structure - * - `INVO::Feed` defines an _invocation adapter_ for the processing function - * - `INVO::buildFeed()` is a functor to (repeatedly) build `Feed` instances - * - the _invocation adapter_ in turn embeds a `FeedManifold` to hold - * + an array of input buffer pointers - * + an array of output buffer pointers - * + `INVO::MAX_SIZ` limits both arrays - */ - template - struct SimpleWeavingPattern - : INVO - { - using Feed = typename INVO::Feed; - - static_assert (_verify_usable_as_InvocationAdapter()); - - Several leadPort; - Several outTypes; - - uint resultSlot{0}; - - /** forwarding-ctor to provide the detailed input/output connections */ - template - SimpleWeavingPattern (Several&& pr - ,Several&& dr - ,uint resultIdx - ,ARGS&& ...args) - : INVO{forward(args)...} - , leadPort{move(pr)} - , outTypes{move(dr)} - , resultSlot{resultIdx} - { } - - - Feed - mount() - { - return INVO::buildFeed(); - } - - void - pull (Feed& feed, TurnoutSystem& turnoutSys) - { - for (uint i=0; i()); + using Feed = typename PAT::Feed; public: diff --git a/src/steam/engine/weaving-pattern-builder.hpp b/src/steam/engine/weaving-pattern-builder.hpp index bc8a916a8..3bbe9354d 100644 --- a/src/steam/engine/weaving-pattern-builder.hpp +++ b/src/steam/engine/weaving-pattern-builder.hpp @@ -26,7 +26,7 @@ ** because the given _functor_ is constructed within a plug-in tailored to a specific ** media processing library (e.g. FFmpeg) and thus can be a lambda to forward to the ** actual function. - ** @note steam::engine::Turnout mixes-in the steam::engine::SimpleWeavingPattern, which in turn + ** @note steam::engine::Turnout mixes-in the steam::engine::MediaWeavingPattern, which in turn ** inherits from an *Invocation Adapter* given as template parameter. So this constitutes ** an *extension point* where other, more elaborate invocation schemes could be integrated. ** @@ -97,8 +97,8 @@ //#include "vault/gear/job.h" #include "lib/several-builder.hpp" #include "steam/engine/proc-id.hpp" -#include "steam/engine/turnout.hpp" #include "steam/engine/engine-ctx.hpp" +#include "steam/engine/weaving-pattern.hpp" #include "steam/engine/buffer-provider.hpp" #include "steam/engine/buffhandle-attach.hpp" /////////////////OOO why do we need to include this? we need the accessAs() template function #include "lib/test/test-helper.hpp" ////////////////////////////OOO TODO added for test @@ -347,7 +347,7 @@ namespace engine { template - using SimpleDirectInvoke = SimpleWeavingPattern>; + using SimpleDirectInvoke = MediaWeavingPattern>; /** diff --git a/src/steam/engine/weaving-pattern.hpp b/src/steam/engine/weaving-pattern.hpp index a715fe739..cfe80ce53 100644 --- a/src/steam/engine/weaving-pattern.hpp +++ b/src/steam/engine/weaving-pattern.hpp @@ -51,7 +51,7 @@ #define STEAM_ENGINE_WEAVING_PATTERN_H #include "steam/common.hpp" -#include "steam/engine/proc-node.hpp" +#include "steam/engine/turnout.hpp" #include "steam/engine/turnout-system.hpp" #include "steam/engine/feed-manifold.hpp" /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation @@ -77,174 +77,6 @@ namespace engine { using std::forward; using lib::Several; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1367 : Rebuild the Node Invocation - /** - * Adapter to shield the ProcNode from the actual buffer management, - * allowing the processing function within ProcNode to use logical - * buffer IDs. StateAdapter is created on the stack for each pull() - * call, using setup/wiring data preconfigured by the builder. - * Its job is to provide the actual implementation of the Cache - * push / fetch and recursive downcall to render the source frames. - */ - class StateAdapter - : public StateClosure - { - protected: - StateClosure& parent_; - StateClosure& current_; - - StateAdapter (StateClosure& callingProcess) - : parent_ (callingProcess), - current_(callingProcess.getCurrentImplementation()) - { } - - virtual StateClosure& getCurrentImplementation () { return current_; } - - - - public: /* === proxying the StateClosure interface === */ - - virtual void releaseBuffer (BuffHandle& bh) { current_.releaseBuffer (bh); } - - virtual void is_calculated (BuffHandle const& bh) { current_.is_calculated (bh); } - - virtual BuffHandle fetch (FrameID const& fID) { return current_.fetch (fID); } - - virtual BuffTableStorage& getBuffTableStorage() { return current_.getBuffTableStorage(); } - - // note: allocateBuffer() is chosen specifically based on the actual node wiring - - }; - - - - - - /** - * Invocation context state. - * A ref to this type is carried through the chain of NEXT::step() functions - * which form the actual invocation sequence. The various operations in this sequence - * access the context via the references in this struct, while also using the inherited - * public State interface. The object instance actually used as Invocation is created - * on the stack and parametrised according to the necessities of the invocation sequence - * actually configured. Initially, this real instance is configured without FeedManifold, - * because the invocation may be short-circuited due to Cache hit. Otherwise, when - * the invocation sequence actually prepares to call the process function of this - * ProcNode, a buffer table chunk is allocated by the StateProxy and wired in. - */ - struct Invocation - : StateAdapter - { - Connectivity const& wiring; - const uint outNr; - - FeedManifold* feedManifold; - - protected: - /** creates a new invocation context state, without FeedManifold */ - Invocation (StateClosure& callingProcess, Connectivity const& w, uint o) - : StateAdapter(callingProcess), - wiring(w), outNr(o), - feedManifold(0) - { } - - public: - uint nrO() const { return wiring.nrO; } - uint nrI() const { return wiring.nrI; } - uint buffTabSize() const { return nrO()+nrI(); } - - /** setup the link to an externally allocated buffer table */ - void setBuffTab (FeedManifold* b) { this->feedManifold = b; } - - bool - buffTab_isConsistent () - { - return (feedManifold) - && (0 < buffTabSize()) - && (nrO()+nrI() <= buffTabSize()) - && (feedManifold->inBuff == &feedManifold->outBuff[nrO()] ) - && (feedManifold->inHandle == &feedManifold->outHandle[nrO()]) - ; - } - - - public: - /** specialised version filling in the additional information, i.e - * the concrete node id and the channel number in question */ - virtual FrameID const& - genFrameID () - { - return current_.genFrameID(wiring.nodeID, outNr); - } - - virtual FrameID const& - genFrameID (NodeID const& nID, uint chanNo) - { - return current_.genFrameID (nID,chanNo); - } - - }; - - - ////////////TICKET #249 this strategy should better be hidden within the BuffHandle ctor (and type-erased after creation) - struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations - : Invocation - { - AllocBufferFromParent (StateClosure& sta, Connectivity const& w, const uint outCh) - : Invocation(sta, w, outCh) {} - - virtual BuffHandle - allocateBuffer (const lumiera::StreamType* ty) { return parent_.allocateBuffer(ty); } ////////////TODO: actually implement the "allocate from parent" logic! - }; - - struct AllocBufferFromCache ///< using the global current StateClosure, which will delegate to Cache - : Invocation - { - AllocBufferFromCache (StateClosure& sta, Connectivity const& w, const uint outCh) - : Invocation(sta, w, outCh) {} - - virtual BuffHandle - allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); } - }; - - - /** - * The real invocation context state implementation. It is created - * by the NodeWiring (Connectivity) of the processing node which - * is pulled by this invocation, hereby using the internal configuration - * information to guide the selection of the real call sequence - * - * \par assembling the call sequence implementation - * Each ProcNode#pull() call creates such a StateAdapter subclass on the stack, - * with a concrete type according to the Connectivity of the node to pull. - * This concrete type encodes a calculation Strategy, which is assembled - * as a chain of policy templates on top of OperationBase. For each of the - * possible configurations we define such a chain (see bottom of nodeoperation.hpp). - * The WiringFactory defined in nodewiring.cpp actually drives the instantiation - * of all those possible combinations. - */ - template - class ActualInvocationProcess - : public BufferProvider - , private Strategy - { - public: - ActualInvocationProcess (StateClosure& callingProcess, Connectivity const& w, const uint outCh) - : BufferProvider(callingProcess, w, outCh) - { } - - /** contains the details of Cache query and recursive calls - * to the predecessor node(s), eventually followed by the - * ProcNode::process() callback - */ - BuffHandle retrieve () - { - return Strategy::step (*this); - } - }; -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1367 : Rebuild the Node Invocation -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation /** * Standard implementation for a _Weaving Pattern_ to connect @@ -259,7 +91,7 @@ namespace engine { * + `INVO::MAX_SIZ` limits both arrays */ template - struct SimpleWeavingPattern + struct MediaWeavingPattern : INVO { using Feed = typename INVO::Feed; @@ -273,10 +105,10 @@ namespace engine { /** forwarding-ctor to provide the detailed input/output connections */ template - SimpleWeavingPattern (Several&& pr - ,Several&& dr - ,uint resultIdx - ,ARGS&& ...args) + MediaWeavingPattern (Several&& pr + ,Several&& dr + ,uint resultIdx + ,ARGS&& ...args) : INVO{forward(args)...} , leadPort{move(pr)} , outTypes{move(dr)} @@ -339,46 +171,6 @@ namespace engine { }; - /** - * Processing structure to activate a Render Node and produce result data. - * @tparam PAT a _Weaving Pattern,_ which defines in detail how data is retrieved, - * combined and processed to yield the results; actually this implementation - * is assembled from several building blocks, in accordance to the specific - * situation as established by the _Builder_ for a given render node. - */ - template - class Turnout - : public Port - , public PAT -// , util::MoveOnly - { - using Feed = typename PAT::Feed; - - public: - template - Turnout (ProcID& id, INIT&& ...init) - : Port{id} - , PAT{forward (init)...} - { } - - /** - * Entrance point to the next recursive step of media processing. - * @param turnoutSys anchor context with parameters and services - * @return a BuffHandle exposing the generated result data - */ - BuffHandle - weave (TurnoutSystem& turnoutSys, OptionalBuff outBuff =std::nullopt) override - { - Feed feed = PAT::mount(); - PAT::pull(feed, turnoutSys); - PAT::shed(feed, outBuff); - PAT::weft(feed); - return PAT::fix (feed); - } - }; - - - }}// namespace steam::engine #endif /*STEAM_ENGINE_WEAVING_PATTERN_H*/ diff --git a/tests/core/steam/engine/node-base-test.cpp b/tests/core/steam/engine/node-base-test.cpp index 9e3f7733f..fece463ad 100644 --- a/tests/core/steam/engine/node-base-test.cpp +++ b/tests/core/steam/engine/node-base-test.cpp @@ -18,6 +18,7 @@ #include "lib/test/run.hpp" //#include "lib/test/test-helper.hpp" +#include "lib/meta/function.hpp" #include "steam/engine/proc-node.hpp" #include "steam/engine/turnout.hpp" #include "steam/engine/turnout-system.hpp" diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index fcee81483..5dd62af62 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -23090,9 +23090,7 @@ - - - +

...nochmal zusammengefaßt @@ -23471,9 +23469,7 @@ - - - +

direkten Bezug auf die Timeline vermeiden! @@ -24234,9 +24230,7 @@ - - - +

Das Schöne an diesem Ansatz wäre, daß er für den User komplett natürlich wirkt; solange man gleichartige Clips in einer Timeline liegen hat, würde sich dieses Konzept überaupt nicht auffällig bemerkbar machen; und ein weiterer Vorteil wäre, daß man es als Weiterentwicklung des 1. Lösungsansatzes betrachten kann... @@ -24880,9 +24874,7 @@ - - - +

Clips dürften die häufigsten Entitäten in der Timeline-Anzeige werden. Es müssen tausende bis zehntausende Clips performant gehandhabt werden @@ -26085,9 +26077,7 @@ - - - +

denn, wie sich herausgestellt hat, muß das Interface ViewHook hierarchisch heruntergebrochen werden @@ -27828,9 +27818,7 @@ - - - +

geprüft mit Screenshot in Gimp. @@ -30375,9 +30363,7 @@ - - - +

  • @@ -31429,9 +31415,7 @@ - - - +

    ...welches aber lokal auf die Anwendung angewendet wird, mit höherer Priorität @@ -34578,9 +34562,7 @@ - - - +

    UI-Bus gilt nur für globale Belange @@ -36846,9 +36828,7 @@ - - - +

    ...dann kann sich das Sytem erst mal beruhigen @@ -37952,9 +37932,7 @@ - - - +

    ...und dadurch ensteht hier ein "linke-Tasche-rechte-Tasche-Spiel". @@ -38848,9 +38826,7 @@ - - - +

    ...die konkrete Interpretation der Mausbewegung @@ -39063,9 +39039,7 @@ - - - +

    nein.... nicht wirklich, weil man ja doch noch die Position korrigieren muß @@ -39319,9 +39293,7 @@ - - - +

    Die Gesten-Controller sollen später einmal Teile eines umfangreicheren Frameworks werden; im Besonderen wollen wir abstrahierte Gesten, die verschiedene Eingabesysteme übergreifen können. Für dieses Ziel muß der konkrete Gesten-Controller soweit schematisiert sein, daß man im Zuge der weiteren Entwicklung sich ergebende Erweiterungspunkte einführen kann, auch in schon bestehende Implementierungen. Als naheliegendes Schema bietet sich die State-Machine an, da die Gesten-Erkennung auf theoretischer Ebene ohnehin ein (ggfs nichdeterministischer) FSA ist. @@ -82974,7 +82946,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

    - ...d.h. die Planung steigt für einen Port ein, findet für dieses in Proc-Asset in einem bereits vorliegenden Prototypen der Connectivity, und kann für dieses Proc-Asset alle benötigten Inputs identifizieren und jeden von diesen einem anderen, ebenfalls bereits bekannten Vorläufer Proc-Asset zuordnen; diese Zuordnung wäre dann die Belegung eines Port, und das Proc-Asset würde zu einer geplanten Node. Im Besonderen ist durch diese Vorraussetzung festgelegt, daß alle diese Belegungen bereits eindeutig und entscheidbar sind; Zweideutigeiten und Unmöglichkeiten sind in den vorausgehenden Verarbeitungsschritten bereits aussortiert worden... + ...d.h. die Planung steigt für einen Port ein, findet für dieses ein Proc-Asset in einem bereits vorliegenden Prototypen der Connectivity, und kann für dieses Proc-Asset alle benötigten Inputs identifizieren und jeden von diesen einem anderen, ebenfalls bereits bekannten Vorläufer Proc-Asset zuordnen; diese Zuordnung wäre dann die Belegung eines Port, und das Proc-Asset würde zu einer geplanten Node. Im Besonderen ist durch diese Vorraussetzung festgelegt, daß alle diese Belegungen bereits eindeutig und entscheidbar sind; Zweideutigeiten und Unmöglichkeiten sind in den vorausgehenden Verarbeitungsschritten bereits aussortiert worden...

    @@ -83189,7 +83161,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

    - Das bedeutet: wenn man auf dem default-konstruierten Builder die build()-Metode ausführt, dann entsteht eine Node, die zwar alle erwarteten Ports hat, aber alle diese Ports liefern bei Aufruf ein NULL-Handle des jeweils eingesetzten BufferProviders liefert. (Selbstverständlich wäre es viel schöner, an dieser Stelle einen leeren Frame zu liefern, aber das ist nicht möglich, ohne das Format zu kennen, welches jedoch von der Domain-Façade geleistet werden muß) + Das bedeutet: wenn man auf dem default-konstruierten Builder die build()-Metode ausführt, dann entsteht eine Node, die zwar alle erwarteten Ports hat, aber alle diese Ports liefern bei Aufruf ein NULL-Handle des jeweils eingesetzten BufferProviders. (Selbstverständlich wäre es viel schöner, an dieser Stelle einen leeren Frame zu liefern, aber das ist nicht möglich, ohne das Format zu kennen, welches jedoch von der Domain-Façade geleistet werden muß)

    @@ -89820,7 +89792,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - + @@ -89829,8 +89801,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

    +
    - + @@ -89854,7 +89827,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    dann brauchen wir sowiso und stets irgend eine Struktur auf dem Stack, in dem die BuffHandles liegen; möglicherweise sogar über mehrere Stack-Frames hinweg durchgereicht
  • - andererseits kann eine Library ganz gewiß nix mit Lumiera-BuffHandles anfangen, sondern wird ein spezielles Datenformat wollen; das bedeutet, man muß das BuffHandle in jedem Fall belegen, dann dereferenzieren und den resultierenden Pointer irgendwohin kopieren, um die Zielfunktion aufrufen zu können (das war auch die Idee hinter der »BuffTable« im ersten Entwurf) + andererseits kann eine Library ganz gewiß nix mit Lumiera-BuffHandles anfangen, sondern wird ein spezielles Datenformat wollen; das bedeutet, man muß das BuffHandle in jedem Fall belegen, dann dereferenzieren und den resultierenden Pointer irgendwohin kopieren, um die Zielfunktion aufrufen zu können (das war auch die Idee hinter der »BuffTable« im ersten Entwurf)
@@ -89994,14 +89967,39 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

- man bekommt von jeder Kopie den gleichen Zielpuffer, und auch die Lebenszyklus-Metoden lassen sich von jedem Handle aufrufen + man bekommt von jeder Kopie den gleichen Zielpuffer, und auch die Lebenszyklus-Methoden lassen sich von jedem Handle aufrufen

- + + + + + +

+ ...während ein Ausgabepuffer explizit im shed()-Schritt belegt wird, kurz vor dem Aufruf der Processing-Function +

+ +
+
+ + + + +

+ ...während Ausgabepuffer zwar an der Stelle committed werden, aber »der« Ausgabepuffer wird noch nicht freigegeben, denn er ist ja Eingabepuffer der Nachfolger-Node +

+ + +
+
+ + + +
@@ -91334,6 +91332,54 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + +

+ Wert ist wichtig — keine Referenzen, keine Pointer. Das ist eine einfache Regel um Parameter von Buffern unterscheiden zu können, und ansonsten keinerlei weitere Einschränkungen zu machen +

+ +
+
+ + +
+
+
+ + + + + + + + + + +
+ + + + + + + + +
@@ -92679,6 +92725,42 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ sollte »im Prinzip« auf das bestehende Schema aufgepflantzt werden +

+ + +
+ + + + + + + + + + + + + + + + + + + + +
+
@@ -94019,6 +94101,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + +
@@ -94185,6 +94277,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + @@ -94217,7 +94327,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -94275,6 +94386,14 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + @@ -96899,6 +97018,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + @@ -97073,6 +97197,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ somit geht es vor allem darum, +

+

+ Folgeschäden eines redundanten Aufrufs zu bedenken +

+ + +
+