From bb3d56543643454599095f564ef179d8f5cb30f5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 5 May 2024 15:12:23 +0200 Subject: [PATCH] Invocation: Reassessment of existing code ...which seems to be basically fine thus far ...beyond some renaming and rearranging ''it turns out that the final, crucial links, necessary to tie all together, are yet to be developed'' --- src/steam/engine/nodeinvocation.hpp | 2 +- src/steam/engine/nodeoperation.hpp | 6 +- src/steam/engine/proc-node.hpp | 8 +- src/steam/engine/render-invocation.hpp | 2 +- src/steam/engine/rendergraph.cpp | 2 +- src/steam/engine/rendergraph.hpp | 2 +- .../{state.hpp => engine/state-closure.hpp} | 10 +- src/steam/engine/stateproxy.hpp | 2 +- src/steam/mobject/builder/renderstate.hpp | 2 +- wiki/renderengine.html | 19 +- wiki/thinkPad.ichthyo.mm | 644 +++++++++++++++++- 11 files changed, 659 insertions(+), 40 deletions(-) rename src/steam/{state.hpp => engine/state-closure.hpp} (94%) diff --git a/src/steam/engine/nodeinvocation.hpp b/src/steam/engine/nodeinvocation.hpp index 324363ad2..43ca21f58 100644 --- a/src/steam/engine/nodeinvocation.hpp +++ b/src/steam/engine/nodeinvocation.hpp @@ -55,8 +55,8 @@ #define ENGINE_NODEINVOCATION_H -#include "steam/state.hpp" #include "steam/engine/proc-node.hpp" +#include "steam/engine/state-closure.hpp" #include "steam/engine/channel-descriptor.hpp" #include "steam/engine/bufftable-obsolete.hpp" diff --git a/src/steam/engine/nodeoperation.hpp b/src/steam/engine/nodeoperation.hpp index 15fd98982..fca9b5ff4 100644 --- a/src/steam/engine/nodeoperation.hpp +++ b/src/steam/engine/nodeoperation.hpp @@ -57,8 +57,8 @@ #define ENGINE_NODEOPERATION_H -#include "steam/state.hpp" #include "steam/engine/proc-node.hpp" +#include "steam/engine/state-closure.hpp" #include "steam/engine/channel-descriptor.hpp" #include "steam/engine/bufftable-obsolete.hpp" #include "steam/engine/nodeinvocation.hpp" @@ -282,8 +282,8 @@ namespace config { ///////////////////////////: but BufferProvider selection is going to be solved differently anyway, see Ticket #249 template struct SelectBuffProvider { typedef AllocBufferFromParent Type; }; - template - struct SelectBuffProvider< Config> { typedef AllocBufferFromCache Type; }; + template + struct SelectBuffProvider< Config> { typedef AllocBufferFromCache Type; }; template diff --git a/src/steam/engine/proc-node.hpp b/src/steam/engine/proc-node.hpp index 9ba5fa300..e12f38f1d 100644 --- a/src/steam/engine/proc-node.hpp +++ b/src/steam/engine/proc-node.hpp @@ -37,14 +37,14 @@ ** @see operationpoint.hpp */ -#ifndef ENGINE_PROCNODE_H -#define ENGINE_PROCNODE_H +#ifndef ENGINE_PROC_NODE_H +#define ENGINE_PROC_NODE_H #include "lib/error.hpp" #include "steam/common.hpp" -#include "steam/state.hpp" #include "steam/asset/proc.hpp" #include "steam/mobject/parameter.hpp" +#include "steam/engine/state-closure.hpp" #include "steam/engine/channel-descriptor.hpp" #include "lib/frameid.hpp" #include "lib/ref-array.hpp" @@ -185,4 +185,4 @@ namespace engine { }} // namespace steam::engine -#endif +#endif /*ENGINE_PROC_NODE_H*/ diff --git a/src/steam/engine/render-invocation.hpp b/src/steam/engine/render-invocation.hpp index 37d2853c8..a8fdd7321 100644 --- a/src/steam/engine/render-invocation.hpp +++ b/src/steam/engine/render-invocation.hpp @@ -36,7 +36,7 @@ #define ENGINE_RENDER_INVOCATION_H -//#include "steam/state.hpp" +//#include "steam/engine/state-closure.hpp" #include "steam/engine/proc-node.hpp" #include "steam/engine/buffhandle.hpp" //#include "steam/engine/bufftable-obsolete.hpp" diff --git a/src/steam/engine/rendergraph.cpp b/src/steam/engine/rendergraph.cpp index c6c6b4cf7..831fb6e05 100644 --- a/src/steam/engine/rendergraph.cpp +++ b/src/steam/engine/rendergraph.cpp @@ -27,8 +27,8 @@ #include "steam/engine/rendergraph.hpp" +#include "steam/engine/state-closure.hpp" #include "lib/frameid.hpp" -#include "steam/state.hpp" namespace lumiera { diff --git a/src/steam/engine/rendergraph.hpp b/src/steam/engine/rendergraph.hpp index 185564076..6b1e7706d 100644 --- a/src/steam/engine/rendergraph.hpp +++ b/src/steam/engine/rendergraph.hpp @@ -30,7 +30,7 @@ #define STEAM_ENGINE_RENDERGRAPH_H #include "steam/common.hpp" -#include "steam/state.hpp" +#include "steam/engine/state-closure.hpp" #include "lib/time/timevalue.hpp" diff --git a/src/steam/state.hpp b/src/steam/engine/state-closure.hpp similarity index 94% rename from src/steam/state.hpp rename to src/steam/engine/state-closure.hpp index 537608062..1f66e282c 100644 --- a/src/steam/state.hpp +++ b/src/steam/engine/state-closure.hpp @@ -1,5 +1,5 @@ /* - STATE.hpp - Key Interface representing a render process and encapsulating state + STATE-CLOSURE.hpp - Key Interface representing a render process and encapsulating state Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -21,13 +21,13 @@ */ -/** @file state.hpp +/** @file state-closure.hpp ** Access point to an ongoing render's processing state. */ -#ifndef STEAM_INTERFACE_STATE_H -#define STEAM_INTERFACE_STATE_H +#ifndef STEAM_ENGINE_STATE_CLOSURE_H +#define STEAM_ENGINE_STATE_CLOSURE_H #include "lib/error.hpp" @@ -125,4 +125,4 @@ namespace proc_interface { } // namespace proc_interface -#endif +#endif /*STEAM_ENGINE_STATE_CLOSURE_H*/ diff --git a/src/steam/engine/stateproxy.hpp b/src/steam/engine/stateproxy.hpp index 4b5438cfd..0673b253e 100644 --- a/src/steam/engine/stateproxy.hpp +++ b/src/steam/engine/stateproxy.hpp @@ -37,7 +37,7 @@ #define ENGINE_STATEPROXY_H -#include "steam/state.hpp" +#include "steam/engine/state-closure.hpp" namespace steam { diff --git a/src/steam/mobject/builder/renderstate.hpp b/src/steam/mobject/builder/renderstate.hpp index 0b3fcd3d6..5a493233e 100644 --- a/src/steam/mobject/builder/renderstate.hpp +++ b/src/steam/mobject/builder/renderstate.hpp @@ -33,7 +33,7 @@ #ifndef MOBJECT_BUILDER_RENDERSTATE_H #define MOBJECT_BUILDER_RENDERSTATE_H -#include "steam/state.hpp" +#include "steam/engine/state-closure.hpp" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index dbb906736..991e2b2dd 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -5104,7 +5104,7 @@ Thus the mapping is a copyable value object, using an associative array. It may First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit. -
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (Vault, Stage), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -5117,12 +5117,12 @@ In order to be usable as //output sink,// an output slot needs to be //allocated
 Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames are assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these [[timings|Timings]] when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Based on this information, the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
 
 !!!usage and implementation
-Clients retrieve just a reference to an output slot by asking a suitable OutputManager for an output possibility supporting a specific format. Usually, they just "claim" this slot by invoking {{{allocate()}}}, which behind the scenes causes building of the actual output connections and mechanisms. For each such connection -- corresponding to a single channel within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The concrete ~OutputSlot implementation performs operations quite specific to the kind of output and external interface in question. All tese specific handling is embodied within the concrete connection implementation used by the concrete ~OutputSlot
+Clients retrieve a reference to an output slot by asking a suitable OutputManager for an output possibility supporting a specific format. Usually, they just "claim" this slot by invoking {{{allocate()}}}, which behind the scenes causes building of the actual output connections and mechanisms. For each such connection -- corresponding to a single channel within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The concrete ~OutputSlot implementation performs operations quite specific to the kind of output and external interface in question -- thereby calling directly into a specific connection implementation.
 
 !!!timing expectations
-Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These [[Timings]] are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+Besides the sink handles, allocation of an output slot defines some timing constraints, to which the client must comply. These [[Timings]] are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
 
-The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is assumed to be a job running within the engine scheduler and thus can be configured to run //after// another job has finished, and to run within certain time limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is assumed to be a job running within the engine scheduler and thus can be configured to run //after// another job has finished, and to run within certain time limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. This enables the output slot implementation to work out from this time specification if the call is timely or overdue -- and react accordingly.
 
 !!!output modes
 some concrete output connections and drivers embody a specific operation mode (e.g. sample rate or number of channels). The decision and setup of these operational configuration is initiated together with the [[resolution|OutputMapping]] of an OutputDesignation within the OutputManager, finally leading to an output slot (reference), which can be assumed to be suitably configured, before the client allocates this slot for active use. Moreover, an individual output sink (corresponding to a single channel) may just remain unused -- until there is an {{{emit()}}} call and successful data handover, this channel will just feature silence or remain black. (More flexible system, e.g. Jack, allow to generate an arbitrary number of output pins -- Lumiera will support this by allowing to set up additional output slots and attach this information to the current session &rarr; SessionConfigurationAttachment)
@@ -8135,8 +8135,11 @@ Shutdown is initiated by sending a message to the dispatcher loop. This causes t
 
{{red{killme}}}
-
-
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack {{red{Really on the stack? 9/11}}} for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
+
+
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack {{red{Really on the stack? 9/11}}} for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
+
+{{red{WIP 4/2024: the interface will be called {{{StateClosure}}} -- not sure to what degree there will be different implementations ...
+right now about to work towards a first integration of render node invocation}}}
An Object representing a //Render Process// and containing associated state information.
@@ -11029,12 +11032,12 @@ These Viewer (or Monitor) elements play an enabling role for any output generati
 
 When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
-
+
for showing output, three entities are involved
 * the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
 * by connecting to a viewer component (&rarr; ViewConnection), an actual output target is established
 * the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
-
+{{red{4/24 maybe better call this {{{ModelPlayActivation}}} ?}}}
 !the viewer connection
 A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputSlot registered with the OutputManager &mdash; again an output mapping step.
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 7d9bcab76..111508253 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -72000,7 +72000,34 @@
 
 
 
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 
 
 
@@ -78325,9 +78352,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + - + + @@ -78335,10 +78364,53 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + +

+ Das war ein theoretisches Konstrukt, das auf den ersten Blick (auf einer abstrakten Ebene) unglaublich plausibel erscheint; tatsächlich gibt es aber nirgends eine Domäne, in der eine solche Entität oder Daten-Gruppierung eine Rolle spielt. Aufgrund dieser Einsicht wurde das Konzept im Sommer 2023 aufgegeben. +

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

+ bestimmende Freiheitsgrade +

+

+ für die Job-Invocation +

+ +
+ + + + + + + + + + + + + + + + + + + + + @@ -78350,7 +78422,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -78363,6 +78435,30 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + +

+ was darüberhinaus benötigt wird, ist noch nicht wirklich klar +

+ +
+
+ + + + + + @@ -78628,6 +78724,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+ @@ -78972,10 +79069,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + - + @@ -79739,7 +79837,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79773,7 +79871,201 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ ...also sowas wie Lösung-4, mit einem strukturierten Term; leider kostet ein solcher Term zwangsläufig viel Platz (weil er offen sein muß) und läuft damit stets auf eine Heap-Allokation hinaus; da müssen wir sehr vorsichtig sein, bedingt durch den Hebel aufgrund der großen Zahl an Segmenten +

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

+ ...allerdings nur unter der Annahme, daß die DataSink jeweils komplett unique ist (was tatsächlich nicht zu erwarten ist); wenn man allerdings im API von DataSink noch etwas unterbringt, um auf eine Stream-Type-ID zuzugreifen, dann wäre CalcStram bzw. InvocationInstanceID tatsächlich redundant und könnte aus den anderen Angaben errechnet werden. +

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

+ ...will sagen, eine »ExitNode« muß nicht eine besondere Art von Node sein, denn der Zwek von Nodes und der/den ExitNodes überschneidet sich überhaupt nicht +

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

+ CalcStream ≡ eine aktiv laufende Folge von Berechnungen für einen  Ausgabekanal +

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

+ für jeden Feed gibt es einen ModelPort (ggfs. eine Ausprägung) +

+

+ und eine top-Level-Node in jedem Segment +

+ +
+
@@ -79834,8 +80126,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -79900,6 +80192,21 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + @@ -80006,12 +80313,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + @@ -80185,7 +80499,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -80211,9 +80525,146 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + +

+ favour composition over inheritance +

+ +
+
+
+ + + + + + + + + +
    +
  • + DataSink ist verwoben mit dem Buffer-Protokoll; dieses muß von der Node-Invocation im Job selber durchgespielt werden +
  • +
  • + der pull()-Call passiert als ein Teilschritt in demselben; hier werden OutputBuffer vorrausgesetzt +
  • +
+ + +
+ +
+ + + + + + + + + + + + + + +

+ Rolle der chanNo im bestehenden Invocation-Code +

+ + +
+ + + + +

+ ...welche ihrerseits ein komplexes, widersprüchliches und auf lange Sicht unbestimmtes Thema ist +

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

+ Medien-Erzeugung (Sound, Bild) kann durchaus mehrere, gleichberechtigte Aspekte oder Dimensionen erzeugen, und es kann sehr wohl sein, daß man einen Teil davon oder alle auch weiterverarbeitet. Beispielsweise kann eine Sound-Synthese verschiedene und miteinander verwobene Abstrahlungen nachbilden, beispielsweise in Anlehnung an ein Blasinstrument, das eine Abstrahlung am Mund hat, eine Abstrahlung des Körpers und eine Abstrahlung vom Trichter. Und man möchte diese jeweils eigens in einen virtuellen Raum einbinden können. Oder eine Bild-Analyse, die ein interpoliertes Bildelement, sowie einen Bewegungsvektor desselben ausgibt +

+ + +
+
+ + + + +

+ In diese Kategorie fallen alle Verarbeitungsschritte, die sich nicht  auf komplett isolierte Primitiv-Operationen auf Einzelkanälen faktorisieren lassen. Beispiel wäre ein Resonanz- oder Reflexionsvorgang in einem Schallfeld, oder eine Überblende- oder Overlay-Operation mit mehreren Masken-Ebenen, die neben dem Basis-Farbwert sowohl Transparenz alsauch Luminanz umfaßt (also das Problem, das pre-multiplied Alpha nicht wirklich lösen kann). +

+ + +
+
+ + + + +

+ Alle »de-Coder« fallen tatsächlich in diese Kategorie, im Besonderen beim Extrahieren von Einzelkanälen zur getrennten Weiterverarbeitung, aber auch beim decodieren von komprimierten Strömen in Roh-Datenframes über eine Zeitspanne hinweg +

+ + +
+
+
+ + + + +

+ die eigentliche Frage ist: wie übersetzt sich das in Topologie? +

+ + +
+ + + +

+ diese Frage muß wohl eigens gestellt werden — und das wird leider meistens versäumt +

+ + +
+
+
+ + + + + + + + + + @@ -80226,12 +80677,140 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + +
    +
  • + Daten können aus dem Cache kommen, oder frisch aus einer Quelle +
  • +
  • + per Parametrisierung können manche Verbindungen deaktiviert sein +
  • +
+ + +
+
+ + + + + + + + + + +

+ jede ProcNode kann gleichermaßen gePULLt werden. +

+

+ Insofern braucht es im NodeGraph selber hier keinerlei spezielle Funktionalität, sondern jeweils nur eine top-level-Node, deren Output zur DataSink für den pull-Vorgang paßt. All die Services, die wir von einer ExitNode erwarten, finden auf einem Level höher statt, nämlich in der Job-Planung; dort brauchen wir diverse ID-Auflösungen und Informationsdienste +

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

+ ...was natürlich KISS ist; in einem zweiten Schritt repariert man diese Verschwendung durch ein mehr- oder weniger trickreiches Caching +

+ + +
+
+ + + + +

+ bei dieser Variante ist der entscheidende Aspekt, daß diese Adaptierung automatisch in der Verschaltung realisiert wird; ebenso sorgt die Verschaltung dafür, ggfs den Cache mit anzusteuern +

+ + +
+ +
+ + + + +

+ ...sie werden en bloc in seine Liste der Inputs übernommen; erst in einem internen Adaptions-Schritt (FeedManifold) wird ggfs daraus ausgewählt +

+ + +
+
+ + + + +

+ in dieser Variante wird die spezielle Adaptierung als separate Node explizit gemacht; somit werden Mehrfach-Verbindungen explizit zugelassen, und für ein solches Verbindungs-Bündel ist nur ein einziger Pull-Aufruf notwendig, wobei die Routing-Node dann sofort alle Outputs auf einmal bekommt — allerdings hat die Routing-Node stets nur einen Vorläufer +

+ +
+ +
+
+ + + + + + + +
+ + + + + + + + + @@ -80249,6 +80828,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + @@ -80513,7 +81099,33 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + + + + + + +

+ Fazit: gar nicht +

+ +
+ + + +
+
+ + +
@@ -127219,6 +127831,10 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo + + + +