From 8069c874f164fad17fe3cb341610ea1793204ab3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Dec 2024 21:55:48 +0100 Subject: [PATCH] Invocation: develop a concept for handling parameter data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...as part of the rendering process, executed on top of the low-level-model (Render Node network) as conceived thus far. Parameter handling could be ''encoded'' into render nodes altogether, or, at the other extreme, an explicit parameter handling could be specified as part of the Render Node execution. As both extremes will lead to some unfavourable consequences, I am aiming at a middle ground: largely, the ''automation computation'' will be encoded and hidden within the network, implying that this topic remains to be addressed as part of defining the Builder semantics and implementation. Yet in part the required processing structure can be foreseen at an abstract level, and thus the essential primitive operations are specified explicitly as part of the Render Node definition. Notably the ''standard Weaving Pattern'' will include a ''parameter tuple'' into each `FeedManifold` and require a binding function, which accepts this tuple as first argument. Moreover — at implementation level, a library facility must be provided to support handling of ''arbitrary heterogeneous data values'' embedded directly into stack frame memory, together with a type-safe compile-time overlay, which allows the builder to embed specific ''accessor handles'' into functor bindings, even while the actual storage location is not yet known at that time (obviously, as being located on the stack). __Note__: a recurring topic is how to return descriptor objects from builder functions; for this purpose, I am adjusting the semantics of `lib/nocopy.hpp` to be more specific... --- src/lib/allocation-cluster.hpp | 14 + src/lib/hetero-data.hpp | 94 ++++++ src/lib/iter-stack.hpp | 2 +- src/lib/nocopy.hpp | 27 +- src/steam/engine/exit-node.hpp | 4 +- src/steam/engine/turnout-system.hpp | 9 +- src/steam/engine/turnout.hpp | 4 +- tests/15library.tests | 5 + tests/library/hetero-data-test.cpp | 66 ++++ wiki/thinkPad.ichthyo.mm | 452 ++++++++++++++++++++++++---- 10 files changed, 610 insertions(+), 67 deletions(-) create mode 100644 src/lib/hetero-data.hpp create mode 100644 tests/library/hetero-data-test.cpp diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index e71752ced..c15a72947 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -21,6 +21,17 @@ ** pattern. Optionally it is even possible to skip invocation of object ** destructors, making de-allocation highly efficient (typically the ** memory pages are already cache-cold when about to discarded). + ** \par base allocation + ** The actual allocation of storage extents uses heap memory expanded in blocks + ** of AllocationCluster::EXTENT_SIZ. While the idea is to perform allocations + ** mostly at start and then hold and use the memory, the allocation is never + ** actually _closed_ — implying that further allocations can be added during + ** the whole life time, which may possibly even trigger a further base allocation + ** if storage space in the last Extent is exhausted. In theory, it would be possible + ** to use a custom allocation (in the AllocationCluster::StorageManager::Extents, + ** which is a lib::LinkedElements and could be parametrised with a allocator template). + ** Allocations are never discarded, and thus any alloted memory will be kept until + ** the whole AllocationCluster is destroyed as a compound. ** \par using as STL allocator ** AllocationCluster::Allocator is an adapter to expose the interface ** expected by std::allocator_traits (and thus usable by all standard compliant @@ -28,6 +39,9 @@ ** including the invocation of their destructors, while relying on the allocator ** to allot and discard bare memory. However, to avoid invoking any destructors, ** the container itself can be created with AllocationCluster::createDisposable. + ** This causes the container (front-end) to be emplaced itself into the used + ** AllocationCluster — and since the container's destructor will not be invoked + ** in this arrangement, the container will not be able to invoke element dtors. ** \par dynamic adjustments ** Under controlled conditions, it is possible to change the size of the latest ** raw allocation handed out, within the limits of the available reserve in the diff --git a/src/lib/hetero-data.hpp b/src/lib/hetero-data.hpp new file mode 100644 index 000000000..652e846ec --- /dev/null +++ b/src/lib/hetero-data.hpp @@ -0,0 +1,94 @@ +/* + HETERO-DATA.hpp - handle chain of heterogeneous data blocks + + Copyright (C) + 2024, Hermann Vosseler + +  **Lumiera** is free software; you can redistribute it and/or modify it +  under the terms of the GNU General Public License as published by the +  Free Software Foundation; either version 2 of the License, or (at your +  option) any later version. See the file COPYING for further details. + +*/ + + +/** @file hetero-data.hpp + ** Maintain a chained sequence of heterogeneous data blocks without allocation. + ** This building block for low-level memory management allows to build up a collection + ** of entirely arbitrary data placed into existing and possibly distributed storage. + ** The safety of storage and lifetime must be ensured by other means, since data access + ** proceeds without further bound checks. However, a type-safe compile-time overlay of + ** accessor marker types is provided, allowing to integrate such a storage layout into + ** an overall memory safe arrangement. + ** + ** A usage scenario would be gradually to build up an assortment of data elements directly + ** in local automatic storage within an elaborate recursive call stack unfolding recursively. + ** Notably the accessor marker types can be assembled independently from the provision of + ** actual storage, as the connection between accessor and actual storage address is + ** _established late,_ on actual _data access._ Obviously, data access in such an arrangement + ** requires traversal in several steps, which, on the other hand, can be justified by a good + ** cache locality of recently used stack frames, thereby avoiding heap allocations altogether. + ** + ** @todo WIP-WIP this is the draft of a design sketch regarding the render node network, + ** which seems to be still pretty much in flux as of 12/2024 + ** @see HeteroData_test + ** @see steam::engine::TurnoutSystem (use case) + ** + */ + + + +#ifndef LIB_HETERO_DATA_H +#define LIB_HETERO_DATA_H + + +//#include "lib/error.hpp" +//#include "lib/symbol.hpp" +#include "lib/nocopy.hpp" +#include "lib/linked-elements.hpp" +#include "lib/meta/typelist.hpp" + +//#include +//#include + + +namespace lib { + + struct StorageLoc + { + StorageLoc* next{nullptr}; + }; + + template + struct StorageFrame + : StorageLoc + , std::tuple + { + + }; + + /** + */ + template + class HeteroData + { + + }; + + template + class HeteroData,TAIL>> + : protected StorageFrame + { + public: + size_t constexpr size(); + + template + struct Navigator + { + + }; + }; + + +} // namespace lib +#endif /*LIB_HETERO_DATA_H*/ diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp index cc5fad9e4..22cc4cbd7 100644 --- a/src/lib/iter-stack.hpp +++ b/src/lib/iter-stack.hpp @@ -242,7 +242,7 @@ namespace lib { * to prepare and pre-fill a sequence */ struct Builder - : util::Cloneable + : util::NonCopyable { Builder(IterQueue& initialElements) : queue_(initialElements) diff --git a/src/lib/nocopy.hpp b/src/lib/nocopy.hpp index 0a2494cb4..ca9691efc 100644 --- a/src/lib/nocopy.hpp +++ b/src/lib/nocopy.hpp @@ -72,19 +72,32 @@ namespace util { }; /** - * Types marked with this mix-in may be created by - * copy-construction (or move construction), - * but may be not reassigned thereafter. - * @remark especially this allows returning - * by-value from a builder function, - * while prohibiting any further copy + * Types marked with this mix-in may be created and moved + * liberally at construction, while any further assignment + * to object instances is prohibited thereafter. + */ + class NonAssign + { + protected: + ~NonAssign() = default; + NonAssign() = default; + NonAssign (NonAssign&&) = default; + NonAssign (NonAssign const&) = default; + NonAssign& operator= (NonAssign&&) = delete; + NonAssign& operator= (NonAssign const&) = delete; + }; + + /** + * Types marked with this mix-in may be duplicated + * by copy-construction, yet may not be moved or + * transferred any further after creation. */ class Cloneable { protected: ~Cloneable() = default; Cloneable() = default; - Cloneable (Cloneable&&) = default; + Cloneable (Cloneable&&) = delete; Cloneable (Cloneable const&) = default; Cloneable& operator= (Cloneable&&) = delete; Cloneable& operator= (Cloneable const&) = delete; diff --git a/src/steam/engine/exit-node.hpp b/src/steam/engine/exit-node.hpp index bf73ebb72..5cad3e46e 100644 --- a/src/steam/engine/exit-node.hpp +++ b/src/steam/engine/exit-node.hpp @@ -58,10 +58,10 @@ namespace engine { * @warning ExitNode should ideally be NonCopyable, since it is referred by the JobTicket * However, we need to clone-and-remould Segments (Split-Splice-Algo), and this implies * that the render nodes can be shared among multiple Segments. If all these assessments - * are correct can only be decided when the actual memory management is settled. + * are correct after all can only be decided once actual memory management is settled. */ class ExitNode - : util::Cloneable + : util::NonAssign { HashVal pipelineIdentity_; //////////////////////////////////////////////////////////TICKET #1293 : Hash-Chaining for invocation-ID... derive from ProcNode wiring Duration runtimeBound_; ///////////////////////////////////////////////////////////TICKET #1283 : integrate with dynamic runtime observation diff --git a/src/steam/engine/turnout-system.hpp b/src/steam/engine/turnout-system.hpp index 12621155e..2573f5323 100644 --- a/src/steam/engine/turnout-system.hpp +++ b/src/steam/engine/turnout-system.hpp @@ -39,6 +39,7 @@ #include "steam/engine/state-closure.hpp" /////////////////////OOO will take on a different role (if any) /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1367 : Rebuild the Node Invocation +#include "lib/nocopy.hpp" #include "lib/time/timevalue.hpp" @@ -46,7 +47,13 @@ namespace steam { namespace engine { using lib::time::Time; - + + template + class ParamStorageFrame + : util::NonCopyable + { + + }; /** diff --git a/src/steam/engine/turnout.hpp b/src/steam/engine/turnout.hpp index b77948bbd..f57c21702 100644 --- a/src/steam/engine/turnout.hpp +++ b/src/steam/engine/turnout.hpp @@ -337,7 +337,7 @@ namespace engine { void weft (Feed& feed) { - feed.invoke(); + feed.invoke(); // process data } BuffHandle @@ -349,7 +349,7 @@ namespace engine { } for (uint i=0; i + +  **Lumiera** is free software; you can redistribute it and/or modify it +  under the terms of the GNU General Public License as published by the +  Free Software Foundation; either version 2 of the License, or (at your +  option) any later version. See the file COPYING for further details. + +* *****************************************************************/ + +/** @file del-stash-test.cpp + ** unit test \ref HeteroData_test + */ + + +#include "lib/test/run.hpp" +#include "lib/hetero-data.hpp" + + + +namespace lib { +namespace test{ + + namespace { // probe victims + + }//(End) test data + + + + + /*************************************************************//** + * @test maintain a sequence of data tuples in local storage, + * providing pre-configured type-safe data access. + * + * @see lib::HeteroData + * @see NodeBase_test::verify_TurnoutSystem() + */ + class HeteroData_test : public Test + { + + virtual void + run (Arg) + { +// seedRand(); +// checksum = 0; + + checkSingleKill(); + } + + + void + checkSingleKill () + { + + } + }; + + + /** Register this test class... */ + LAUNCHER (HeteroData_test, "unit common"); + + +}} // namespace lib::test diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index e891656a7..e377e536b 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -20985,9 +20985,7 @@ - - - +

man schreibt die konkrete Implementierung direkt bei der Implementierung des Zieltyps mit @@ -21333,9 +21331,7 @@ - - - +

...also wenn wir ViewHook für einen anderen Kinder-Typ brauchen @@ -21966,9 +21962,7 @@ - - - +

Entscheidung: NonCopoyable @@ -22761,9 +22755,7 @@ - - - +

latürnich @@ -23493,9 +23485,7 @@ - - - +

woher soll man das wissen? @@ -25514,9 +25504,7 @@ - - - +

wie in Kopf und Rumpf injizieren @@ -28888,9 +28876,7 @@ - - - +

nämlich mit minimalem Admin-Overhead, @@ -33761,9 +33747,7 @@ - - - +

...und dieser muß deshalb auch schon eine Funktion getAnchorHook() auf dem API bieten @@ -37741,9 +37725,7 @@ - - - +

die Konsequenz aus Lösung-1 ist aber nicht wirklich abwegig @@ -39565,9 +39547,7 @@ - - - +

einfachers Dragging mit der Maus? oder kommt zum Abschluß eine spezielle Taste? oder eine spezielle Mausgeste? @@ -40443,9 +40423,7 @@ - - - +

Resultat: @@ -40856,9 +40834,7 @@ - - - +

denn dabei wird gerundet, um die exakte Pixel-Zahl zu erhalten @@ -41135,9 +41111,7 @@ - - - +

denn für x ≔ I/S  ergibt sich x/I = 1/s @@ -41595,9 +41569,7 @@ - - - +

weil es direkt in das Feld px_per_second_ geht @@ -87412,8 +87384,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Das muß so sein aus Gründen der logischen Konsistenz: Der Invocation-Mechanismus der Render-Engine ist generisch, und das bedeutet, er kann nichts implizit über das zu rendernde Modell wissen; zwar wird für den Build-Vorgang in absolute Placements  reduziert, aber diese beziehen sich immer noch auf eine bestimmte Timeline — ebenso wie der Render-Vorgang, der auf einer Timeline  abläuft. Das bedeutet, für den Rendervorgang ist das Koordinatensystem implizit, und er gibt nur eine absolute nominal Time relativ dazu an; jedoch wird dieser implizite Kontext in der Job-Planung übersetzt in den Zugriff auf eine bestimmte konkrete Exit-Node. Insofern kann dann ein Job komplett generisch auf der Render-Engine laufen, denn er tarnsportiert sowohl die absolute nominal Time, alsauch die konkrete ExitNode. Das Turnout-System selber ist ebenfalls generisch, und das heißt, es wird nur sinnvoll in Verbindung mit einer ExitNode zur Aufführung gebracht

- -
+
@@ -87468,8 +87439,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
und diese ist massiv (Millionen Zyklen, allerdings in Speicher, der dann im Cache liegt)

- - + @@ -87491,8 +87461,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...oder werden sie von der Medien-Berechnung verdrängt?

- - +
@@ -87526,8 +87495,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - +
@@ -87537,8 +87505,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
....die intrusive linked List habe ich nämlich fertig und getestet, zudem ist der Code dafür sehr robust zu verwenden, wenn man ohnehin einen privatenTyp für ein Bucket definiert. Dem gegenüber müßte man die Index-Tabellen-Lösung erst mal konzipieren und austesten, und zudem wäre die wohl eine Spezial-Implementierung und daher auch jedes Mal wieder zu verstehen. Damit ist die Entscheidung klar, man muß wirklich erst mal zeigen daß ein Problem besteht...

- -
+
@@ -87548,8 +87515,382 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ der Build-Prozeß belegt sukzessiv mehrere abstrakte Slots +

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

+ Eine compiletime-Lösung setzt zwingend vorraus, daß der Code zur Belegung mehr oder weniger explizit und fest verdrahtet ist +

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

+ Ich hab ein ungutes Gefühl, wenn man jetzt sagt, das muß dann halt im Einzelfall programmiert werden. Denn es ist nicht so, daß es hier einen »User« oder »Client« gäbe, wie bei einer klassischen Library. Vielmehr muß später, in einer weiteren Runde, ein Builder implementiert werden, der seinerseits wieder generisch sein soll. Demgegenüber kann das Library-Plug-in zwar Adapter bereitstellen, aber diese Adapter können keine Implementierung einer Parameter-Automation bereitstellen, denn das ist ein zur Medien-Berechnung komplett disjunkter Belang — das heißt, genauso wie es für eine Film-Timeline schon mal eine Video- und eine Audio-Renderpipeline geben wird, wird es zusätzlich auch noch eine Automations-Pipeline geben. Nur daß diese nun auch noch mit der Audio / Videoverarbeitung quer-verknüpft sein muß. .... hoppla — das ist eine neue Einsicht +

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

+ und dann gibt es eben doch eine Render-Engine Domain-Ontologie +

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

+ ganz analog wie ebenda gefordert werden kann: brauche N Eingabepuffer mit Format F₁ und M Ausgabepuffer mit Format F₂ +

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

+ das ist die Systematik die auf Level-2 gebraucht wird +

+ +
+
+ + + + + + +

+ ...das wäre fatal, denn dann würde die Abstraktion zusammenbrechen; etweder der Builder, oder (noch schlimmer) das Library-Plug-in müßte Render-Engine-Internals instrumentieren +

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

+ explizit ausgeführte +

+

+ Parameterbehandlung +

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

+ Es geht um die Verständlichkeit und Wartbarkeit des Codes. Wenn ein derart zentrales Thema überhaupt keinen Niederschlag in konkreten Strukturen findet, sondern nur über eine Konvention abgebildet wird, ist das verwirrend und fehleranfällig. Umso mehr, als hier nun auch noch auf transiente Datenpuffer verwiesen wird. +

+ +
+
+ + + + + +

+ Damit wäre nämlich eine relativ sichere Storage auf dem Stack gegeben, und die Gegenwart der Parameter wäre trotzdem stets eindeutig dokumentiert. Auch im Hinblick darauf, daß vermutlich sehr häufig irgend welche Parameter fest gesetzt werden müssen (aber nicht per Automation bestimmt) +

+ + +
+
+ + + + +

+ die explizite Behandlung würde damit in das konkrete Weaving-Pattern verlegt +

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

+ in diesem Fall hätten alle Bindings die gleiche Port-Subklasse, würden aber beim Zugriff auf die Parameter einen virtual call machen +

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

+ wichtigster Vorteil: es ist noch nicht festgelegt +

+ +
+
+ + + + + + + +

+ Ich wollte mehr, und deshalb halte ich die Stelle für das TurnoutSystem offen — obwohl auf gegenwärtigem Stand seine verbleibende Funktionalität komplett in die interne Mechanik integriert werden könnte. Auf diesem gegenwärtigen Stand kann ich die Vorstellung noch nicht weiter entwickeln, weil mir der klare Blick auf den realen Gebrauch in den tatsächlichen Proportionen fehlt — aber ich hoffe, daß sich dann aus dem Einsatz eines Baukasten-Systems irgendwann klarere Muster codifizieren lassen +

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

+ Fazit: zurück zum ersten Konzept +

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

+ einen Satz Parameter an anderer Stelle platzieren und anhängen +

+ + +
+
+ + + + + + +

+ Der Zugriff erfolgt unchecked, aber ein typsicherer Zugriff soll durch einen compile-time-Overlay gewährleistet sein. Essentiell ist, daß die typsicheren Accessoren erzeugt werden können bevor die konkrete Storage-Adressen bekannt sind +

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + @@ -95016,6 +95357,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + +
@@ -96849,8 +97194,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
per Default ist stets eine absolute nominal Time gegeben

- - +