diff --git a/src/steam/engine/node-builder.hpp b/src/steam/engine/node-builder.hpp index 62eb282b0..93b03ce98 100644 --- a/src/steam/engine/node-builder.hpp +++ b/src/steam/engine/node-builder.hpp @@ -137,26 +137,6 @@ namespace engine { class PortBuilderRoot; - namespace { // Metaprogramming helper to pick the proper constructor... - - using lib::meta::disable_if; - using std::bool_constant; - using std::__and_; - - template - struct ArgMatch : std::false_type { }; - -// template -// struct ArgMatch&&, SizMark, XS...> : std::true_type { }; // the chaining-ctor takes a NodeBuilder and a size-mark... - template - struct ArgMatch const&, XS...> : std::true_type { }; // the chaining-ctor takes a NodeBuilder and a size-mark... - - /** Metaprogramming: prevent the var-args ctor from shadowing the chaining ctor */ - template - using disable_if_Chain = disable_if<__and_ // do not use this constructor if 3 arguments given - ,ArgMatch // and these match the signature for the chaining-constructor - >>; - } template @@ -171,17 +151,10 @@ namespace engine { DAT patternData_; public: - template//, typename = disable_if_Chain> + template NodeBuilder (INIT&& ...alloInit) : leads_{forward (alloInit)...} - { -//lib::test::TypeDebugger> muggi; -//lib::test::TypeDebugger> muggi; -// if constexpr (ArgMatch()) -// static_assert(not sizeof(POL), "YESSS!"); -// else -// static_assert(not sizeof(POL), "OH_NO"); - } + { } template NodeBuilder (NodeBuilder&& pred, SizMark, BUILD&& entryBuilder) @@ -249,6 +222,7 @@ namespace engine { + template class PortBuilderRoot : protected NodeBuilder @@ -264,7 +238,7 @@ namespace engine { /** setup standard wiring to adapt the given processing function. * @return a PortBuilder specialised to wrap the given \a FUN */ template - auto invoke (FUN&& fun); + auto invoke (FUN fun); /** specify an `InvocationAdapter` to use explicitly. */ template @@ -313,10 +287,12 @@ namespace engine { return move(*this); } + /** define the output slot to use as result + * @remark default is to use the first one */ PortBuilder asResultSlot (uint r) { - UNIMPLEMENTED ("define the output slot to use as result (default is the first one)"); + weavingBuilder_.selectResultSlot(r); return move(*this); } @@ -364,16 +340,10 @@ namespace engine { { //////////////////////////////////////////////////////////OOO need to provide all links to lead nodes here weavingBuilder_.fillRemainingBufferTypes(); - using MoThi = decltype(move(*this)); -// if constexpr (ArgMatch, void*>()) -// static_assert(not sizeof(POL), "YESSS!"); -// else -// static_assert(not sizeof(POL), "OH_NO"); -// lib::test::TypeDebugger> buggi; - return NodeBuilder{static_cast&&> (*this) //move (*this) + return NodeBuilder{static_cast&&> (*this) // slice away PortBulder subclass data ,weavingBuilder_.sizMark ,weavingBuilder_.build()}; - } // chain to builder with extended patternData + } // chain to builder with extended patternData private: template @@ -389,10 +359,10 @@ namespace engine { template template auto - PortBuilderRoot::invoke (FUN&& fun) + PortBuilderRoot::invoke (FUN fun) { using WeavingBuilder_FUN = WeavingBuilder(), FUN>; - return PortBuilder{move(*this), forward (fun)}; + return PortBuilder{move(*this), move(fun)}; } /* template diff --git a/src/steam/engine/proc-node.hpp b/src/steam/engine/proc-node.hpp index 030fdb2ab..b5725cad9 100644 --- a/src/steam/engine/proc-node.hpp +++ b/src/steam/engine/proc-node.hpp @@ -75,16 +75,17 @@ namespace engine { /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation class Port - : util::MoveOnly //////////////////////////////////////////////////OOO not clear if necessary here, and requires us to declare the ctors!!! See Turnout +// : util::MoveOnly //////////////////////////////////////////////////OOO not clear if necessary here, and requires us to declare the ctors!!! See Turnout + : util::NonCopyable //////////////////////////////////////////////////OOO this would be the perfect solution, if we manage to handle this within the builder { public: virtual ~Port(); ///< this is an interface virtual BuffHandle weave (TurnoutSystem&, OptionalBuff =std::nullopt) =0; - // compiler does not generate a move-ctor automatically due to explicit dtor - Port() = default; - Port(Port&&) = default; +// // compiler does not generate a move-ctor automatically due to explicit dtor +// Port() = default; +// Port(Port&&) = default; }; using PortRef = std::reference_wrapper; diff --git a/src/steam/engine/turnout.hpp b/src/steam/engine/turnout.hpp index cd3e7bd67..5da4aa619 100644 --- a/src/steam/engine/turnout.hpp +++ b/src/steam/engine/turnout.hpp @@ -279,14 +279,14 @@ namespace engine { /** * Standard implementation for a _Weaving Pattern_ to connect * the input and output data feeds (buffers) into a processing function. - * @tparam CONF a configuration / policy base class + * @tparam INVO a configuration / policy base class to _adapt for invocation_ * @note assumptions made regarding the overall structure - * - `CONF::Feed` defines an _invocation adapter_ for the processing function - * - `CONF::buildFeed()` is a functor to (repeatedly) build `Feed` instances + * - `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 - * + `CONF::MAX_SIZ` limits both arrays + * + `INVO::MAX_SIZ` limits both arrays */ template struct SimpleWeavingPattern @@ -386,10 +386,10 @@ namespace engine { using PAT::PAT; public: - Turnout(Turnout&& rr) ////////////////////////////////////////////OOO investigation of MoveOnly and problems with the builder logic - : Port(static_cast(rr)) - , PAT(static_cast(rr)) - { } +// Turnout(Turnout&& rr) ////////////////////////////////////////////OOO investigation of MoveOnly and problems with the builder logic +// : Port(static_cast(rr)) +// , PAT(static_cast(rr)) +// { } /** * Entrance point to the next recursive step of media processing. diff --git a/src/steam/engine/weaving-pattern-builder.hpp b/src/steam/engine/weaving-pattern-builder.hpp index 4c6441379..afb9e5db0 100644 --- a/src/steam/engine/weaving-pattern-builder.hpp +++ b/src/steam/engine/weaving-pattern-builder.hpp @@ -23,12 +23,74 @@ /** @file weaving-pattern-builder.hpp ** Construction kit to establish an invocation scheme for media calculations. + ** Adapters and configuration is provided to invoke the actual _media processing function_ + ** in accordance to a fixed _wiring scheme:_ + ** - the function takes two arguments + ** - these are an array of input and output buffer pointers + ** - buffer sizes or types are assumed to be uniform over all »slots« + ** - yet the input side my use another type than the output side + ** @todo as of 10/2024, this scheme is established as prototype to explore how processing nodes + ** can be build, connected and invoked; the expectation is however that this simple scheme + ** is suitable to adapt and handle many common cases of invoking media processing functions, + ** 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 + ** inherits from an *Invocation Adapter* given as template parameter. So this constitutes + ** an *extension point* where other, more elaborate invocation schemes could be integrated. + ** + ** # Interplay of NodeBuider, PortBuilder and WeavingBuilder + ** + ** The steam::engine::WeavingBuilder defined here serves as the low-level builder and adapter to + ** prepare the wiring and invocation. The builder-API allows to setup the wiring of input and + ** output-»slots« and control some detail aspects like caching. However, without defining any + ** connections explicitly, a simple 1:1 wiring scheme is employed + ** - each _input slot_ of the function gets an input buffer, which is filled by _pulling_ + ** (i.e. invoking) a predecessor node (a so called »lead«). + ** - for each _output slot_ a buffer is allocated for the processing function to drop off + ** the calculated media data + ** - only one of these output buffers is used as actual result, while the other buffers + ** are just discarded (but may possibly be fed to the frame cache). + ** + ** Each [Processing Node](\ref ProcNode) represents one specific processing functionality on + ** a logical level; yet such a node may be able to generate several „flavours“ of this processing, + ** which are represented as *ports* on this node. Actually, each such port stands for one specific + ** setup of a function invocation, with appropriate _wiring_ of input and output connections. + ** For example, an audio filtering function may be exposed on port-#1 for stereo sound, while + ** port-#2 may process the left, and port-#3 the right channel in isolation. It is entirely + ** up to the library-adapter-plug-in what processing functions to expose, and in which flavours. + ** The WeavingBuilder is used to generate a single \ref Turnout object, which corresponds to + ** the invocation of a single port and thus one flavour of processing. + ** + ** On the architectural level above, the \ref NodeBuilder exposes the ability to set up a + ** ProcNode, complete with several ports and connected to possibly several predecessor nodes. + ** Using several NodeBuilder invocations, the _processing node graph_ can be built up starting + ** from the source (predecessors) and moving up to the _exit nodes,_ which produce the desired + ** calculation results. The NodeBuilder offers a function to define the predecessor nodes + ** (also designated as _lead nodes_), and it offers an entrance point to descend into a + ** PortBuilder, allowing to add the port definitions for this node step by step. + ** + ** On the implementation level, the PortBuilder inherits from the NodeBuilder and embeds a + ** WeavingBuilder instance. Moreover, the actual parametrisations of the NodeBuilder template + ** are chained to create a _functional data structure._ This intricate setup is necessary because + ** the actual data structure of the node graph comprises several small descriptor arrays and + ** interconnected pointers, which are all placed into consecutive chunks of memory, using a + ** custom allocator, the AllocationCluster. The lib::Several is used as front-end to access + ** these small collections of related objects, and the associated lib::SeveralBuilder provides + ** the low-level memory allocation and object creation functionality. The purpose of this + ** admittedly quite elaborate scheme is to generate a compact data structure, with high + ** cache locality and without wasting too much memory. Since the exact number of elements + ** and the size of those elements can be concluded only after the builder-API usage has + ** been completed, the aforementioned functional datastructure is used to collect the + ** parametrisation information for all ports, while delaying the actual object creation. + ** With this technique, it is possible to generate all descriptors or entries of one + ** kind in a single run, and placed optimally and compact into the memory allocation. ** ** @see turnout.hpp ** @see node-builder.hpp ** @see NodeLinkage_test ** - ** @todo WIP-WIP-WIP as of 7/2024 prototyping how to build and invoke render nodes /////////////////////////TICKET #1367 + ** @todo WIP-WIP-WIP as of 10/2024 prototyping how to build and invoke render nodes /////////////////////////TICKET #1371 ** */ @@ -191,11 +253,14 @@ namespace engine { }; /** - * Example base configuration for a Weaving-Pattern chain: + * Typical base configuration for a Weaving-Pattern chain: * - use a simple processing function * - pass an input/output buffer array to this function * - map all »slots« directly without any re-ordering * - use a sufficiently sized FeedManifold as storage scheme + * @remark actual media handling plug-ins may choose to + * employ more elaborate _invocation adapters_ + * specifically tailored to the library's needs. */ template struct DirectFunctionInvocation @@ -207,14 +272,13 @@ namespace engine { std::function buildFeed; -// template + /** when building the Turnout, prepare the _invocation adapter_ + * @note processing function \a fun is bound by value into the closure, + * so that each invocation will create a copy of that function, + * embedded (and typically inlined) into the invocation adapter. + */ DirectFunctionInvocation(FUN fun) - : buildFeed{[=]//procFun = forward (fun)] - { -// using URGS = decltype(procFun); -// lib::test::TypeDebugger murks; - return Feed{fun}; - }} + : buildFeed{[=]{ return Feed{fun}; }} { } }; @@ -275,9 +339,12 @@ namespace engine { }; + + template using SimpleDirectInvoke = SimpleWeavingPattern>; + template struct WeavingBuilder : util::MoveOnly @@ -307,7 +374,7 @@ namespace engine { { PortRef portRef; /////////////////////////////////////OOO TODO need Accessor on ProcNode!!!!! leadPort.append (portRef); - ENSURE (leadPort.size() < N); + ENSURE (leadPort.size() <= N); return move(*this); } @@ -318,7 +385,7 @@ namespace engine { while (cnt--) buffTypes.emplace_back([](BufferProvider& provider) { return provider.getDescriptor(); }); - ENSURE (buffTypes.size() < N); + ENSURE (buffTypes.size() <= N); return move(*this); } @@ -367,7 +434,6 @@ namespace engine { ] (PortDataBuilder& portData) mutable -> void { -//lib::test::TypeDebugger uggi; portData.template emplace (move(leads) ,move(types) ,resultIdx diff --git a/tests/core/steam/engine/node-linkage-test.cpp b/tests/core/steam/engine/node-linkage-test.cpp index 69cabe034..ed9edc4c0 100644 --- a/tests/core/steam/engine/node-linkage-test.cpp +++ b/tests/core/steam/engine/node-linkage-test.cpp @@ -73,7 +73,7 @@ namespace test { { auto con = prepareNode() .preparePort() - .invoke(&dummyOp) + .invoke(dummyOp) .completePort() .build(); CHECK (isnil (con.leads)); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 924dd7298..6dbff3030 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -8842,9 +8842,7 @@ - - - +

oder man fällt auf eine mögliche Substitution zurück @@ -9521,9 +9519,7 @@ - - - +

unter der Annahme, daß wir beim Lumiera Forward Iterator - Konzept bleiben @@ -11921,9 +11917,7 @@ - - - +

Lösungen müssen @@ -15807,9 +15801,7 @@ - - - +

Allokator pro Typ @@ -19816,9 +19808,7 @@ - - - +

analog wie ein Player; während das Content-Rendering läuft muß der Clip eine Registrierung in einem zuständigen OutputManager aufrechterhalten, um die berechneten Pixmaps dann entgegenzunehmen @@ -19828,9 +19818,7 @@ - - - +

...wir speichern ganz sicher nicht ein Mini-Bild für jeden Frame eines Video, sondern brauchen hier wohl eine Art lokales Caching; das heißt, die Pixmaps für einen aktiven Bereich plus eine gewisse Umgebung sind direkt greifbar im GUI. Dieses Caching ist aber nur ein 1st-Level, denn wir wollen den (als sehr elaboriert konzipierten) globalen Frame-Cache auch für diesen Zweck mitbenutzen; schließlich dürften diese Vorschaubilder die häufigsten laufenden Rendervorgänge sein... @@ -50598,9 +50586,7 @@ - - - +

mehrere könnten für @@ -50613,9 +50599,7 @@ - - - +

....denn wir wollen ja grade @@ -50654,9 +50638,7 @@ - - - +

...und zwar zwingend, sobald @@ -50687,9 +50669,7 @@ - - - +

...vom Linken her nicht, da wir Gui gegen Proc linken @@ -50789,9 +50769,7 @@ - - - +

...da die DispatcherQueue direkt Command-Objekte (=frontend handle) speichert @@ -89618,8 +89596,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + @@ -89640,7 +89625,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -89652,6 +89637,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+ @@ -89660,7 +89646,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -89681,6 +89667,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+
@@ -89689,8 +89676,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + @@ -89720,12 +89709,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -89739,12 +89728,14 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+
- + + @@ -89771,16 +89762,18 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + - + - + + @@ -89898,7 +89891,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -89926,15 +89919,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + +
- + - + @@ -89965,7 +89960,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -90039,26 +90035,27 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +

- denn der konkrete Funktionstyp und das N (≙Manifold-Größe), sowie ggfs. ein alternativer »InvocationAdapter«-Typ sind zunächst im konkreten Typ des PortBuilders angelegt (als Typ-Kontext); das bedeutet, das Parameter-Tupel muß aus lauter Wrappern bestehen, die die konkreten Typen bereits materialisiert und virtualisiert enthalten. Für lib::Several ist das definitiv der Fall (das verweist auf einen Extent, in dem die Metadaten und das Daten-Array liegen). Auch std::function ist eine Abstraktion. Also wäre wohl noch ein weiterer Generator-Functor notwendig, in dem man den konkreten InvocationAdapter versteckt + denn der konkrete Funktionstyp und das N (≙Manifold-Größe), sowie ggfs. ein alternativer »InvocationAdapter«-Typ sind zunächst im konkreten Typ des PortBuilders angelegt (als Typ-Kontext); das bedeutet, das Parameter-Satz muß aus lauter Wrappern bestehen, die die konkreten Typen bereits materialisiert und virtualisiert enthalten. Für lib::Several ist das definitiv der Fall (das verweist auf einen Extent, in dem die Metadaten und das Daten-Array liegen). Auch std::function ist eine Abstraktion. Also wäre wohl noch ein weiterer Generator-Functor notwendig, in dem man den konkreten InvocationAdapter versteckt und damit die genaue Parametrisierung des Turnout versteckt....

- +
- + + - + @@ -90067,21 +90064,22 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+
- - + + - + - + - + @@ -90101,14 +90099,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + - - - + + + + - + @@ -90139,12 +90139,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - - + + @@ -90224,7 +90224,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

- + @@ -90254,9 +90254,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -90268,13 +90268,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
danach überhaupt erst outTypes = DataBuilder<POL, BuffDescr>  erzeugen

- - +
- + @@ -90336,7 +90335,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -90359,10 +90358,63 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + +

+ Denn std::decay macht genau das, was auch passiert, wenn man ein Argument  by-value nimmt. Und das ist hier auch aus anderen Gründen genau das, was wir brauchen: Egal ob Lambda oder Funktor order Funktions-Referenz, es wird erst mal im Builder materialisiert, und von dort weiter geschoben in die λ-closure, welche im Builder für den InvocationAdapter abgelegt ist; im Prototyp-Beispiel ist das die Klasse DirectFunctionInvocation. Das »Protokoll« für den Turnout und das SimpleWeavingPattern erwartet, daß in diesem Builder eine Fuktion oder ein Funktor buildFeed() gegeben ist. Das bedeutet, für jede Invocation wird eine Kopie der processing-Function gezogen und direkt in den code des InvocationAdapters geinlined. Typischerweise ist die processing-Function selber wiederum als Lamda definiert, und damit erfolgt ein sehr direkter und effizienter Aufruf der eigentlichen Library-Funktion +

+ + +
+
- - + +
+ + + + + + + +

+ die Zwiebelschalen werden sukzessive darüber gelegt, also ist der Anfang ganz innen +

+ +
+
+ + + + +

+ ...jeder einzelne Turnout-Builder liefert nebenbei auch (über einen compile-time-constant-Parameter) die Storage-Size des von ihm generierten Turnout ab; über eine max()-Kette finden wir die größte Anforderung, die dann auch als »spread« an den lib::SeveralBuilder übergeben wird +

+ +
+
+ + + + + + +

+ diese setzen den jeweiligen Turnout direkt per emplace() in die Ziel-Storage +

+ + +
+ +