Commit graph

6667 commits

Author SHA1 Message Date
bad6751aae (after extended break)
...caused by personal circumstances
...attempt to understand the context I was working on

 * Integration is driven by the `NodeLinkage_test`
 * the near-term goal is to ''get any node built'' — simplified
 * the outline of the `NodeBuilder` and `PortBuilder` is settled
 * the task at hand is how to fill in the definition of a `Port`
 * which in turn ''requires prototyping'' — to establish a kind of weaving-pattern
 * the immediate next thing to do is to ''build an `InvocationAdapter` within the »test-ontology«''
2024-10-09 03:48:18 +02:00
409f2f20b8 Invocation: introduce engine-context for dependency injection
...by relying on DI for some effectively global services, notably
the cache provider, the API for building and wiring render nodes
can be simplified to cover only the actual node connectivity
2024-08-03 03:21:59 +02:00
38dd3a738e Invocation: introduce optional output buffer
Doing so directly seems to be a better solution than to inject an OutputBufferProvider;
the latter will still be needed, yet will not be part of the regular weaving pattern,
but used directly at top-level to obtain the output `BuffHandle`, which is then
passed to the `Port::weave()` call
2024-07-31 19:20:44 +02:00
4bda550f68 Invocation: investigate ways to configure the output-info
...still not convinced that this is a good design,
since it seems to subvert the general design to treat one special case.
However, I can't see a good way to address this special case directly
2024-07-30 23:44:55 +02:00
a4ff2081b9 Invocation: explore variants to pass output connection
There might be one specific output result buffer at top level
for each invocation, which must be delivered into a prepared
output sink. This amounts to one special case, cross-cutting
an otherwise completely generic data flow scheme.

After considering several solutions, it seems most straight-forward
to configure a specific `OutputBufferProvider` to serve as a proxy for
the `OutputSlot` / `DataSink` provided at top-level to the Render-Job.

As an asside, this analyis reveals that the result-slot number does
not belong into the `FeedManifold`, which is dynamic (on the stack);
rather, it's a fixed value configured as part of the `WeavingPattern`
2024-07-30 20:05:48 +02:00
9a23aa773b Invocation: analyse usage of buffer metadata entries
Code clean-up: mark all buffers with a dedicated tagging type


The point in question is: if we work the LocalTag into the type-hash,
could it be possible to miss an existing entry in the metadata registry?
This could cause two entries to be locked for a single buffer address,
leading to data corruption.

As far as I can see, in the current usage this would not happen,
but unfortunately this problem can not be ruled out, since the BufferProvider
API and protocol is designed to be open for various usage patterns.

However, the same potentially disastrous pattern could also materialise
when registering two different buffer types, and then locking each
for the same buffer location.
2024-07-28 19:29:27 +02:00
72c7386435 Invocation: apply a consistent ordering on the chained hash calculation
...this seems to be a tricky aspect; we use hash-chaining to create
derived entries, which may cause the identity of an entry to depend
on the order of specialisation. Looked through the possible code paths,
but these seem to be quite complicated; I see the lurking danger of
creating a second entry (with a different hash), and then in worst case
even locking/unlocking a given buffer twice....
2024-07-27 23:53:27 +02:00
6d7a814495 Invocation: settle upon a way to mark the output buffer
...this is a surprisingly tricky issue, since it undercuts the
generic and recursive implementation of buffer handling;

fortunately I've foreseen such demands may arise down the road
and I've reserved an »Local Key« (now renamed into `LocalTag`),
whose meaning is implementation defined and interpreted by
the specific `BufferProvider`
2024-07-27 17:17:02 +02:00
ea183086ca Invocation: Prototyping to clarify buffer type marking
Requirement analysis shows that the ''actual buffer provider'' to use
constitutes yet another independent degree of freedom, which conceivably
must be handled by the Builder internals rather than by the Domain Ontology.

Thus the simple solution to use a `BuffDescr` to mark the type must be augmented
to also allow configuration of the underlying `BufferProvider`, which generates
the descriptor and can later be invoked with this descriptor to ''lock  an actual Buffer.''

In some cases, setup of the buffer types could even be more complicated and require
access to the actual (runtime) invocaton context; such extreme cases however
could be rendered as an extension of the scheme established here,
by storing the (up to now transient) constructor functors persistently.
Which leads to the decision not to care for those extremely complicated
corner cases right now, and thus to construct all buffer descriptors
in the `build()` call
2024-07-24 20:29:37 +02:00
42f8f8d5af Invocation: Prototyping how to setup invocation wiring
...still fighting to find a suitable API to define
how inputs and outputs are connected and mapped to function parameters.

The solution drafted here uses the reshaped `DataBuilder` (≙`lib::SeveralBuilder`)
to add up connections for each »slot«, disregarding the possibility of permutations.
Similar to `NodeBuilder`, a policy template is used to pass down the setup
for an actual custom allocator.
2024-07-24 03:52:44 +02:00
352ef31ab0 Invocation: further condense the allocator-selector definition
After applying all the preceding refactorings, it turns out that
the `DataBuilder` defined here ''is essentially `lib::SeveralBuilder`'',
only with a different arrangement of the type parameters, due to the
specific usage context here.

It is thus possible to replace all the interim / helper / rebinding templates
by simple templated typedefs. The only tangible difference is that for
usage in the Builder, a ''selector policy'' is passed as a simple type argument,
which in practice wires the concrete allocator information down into each
sub-builder created during the ongoing construction of a node structure.
2024-07-23 02:38:06 +02:00
1705c40dc2 Invocation: rearrange SeveralBuilder policy
redefine the policy for `lib::SeveralBuilder` to be a template-template parameter.
In fact it should have been this way from start, yet defining this kind of
very elaborate code bottom-up lets you sometime miss the wood for the trees

So to restate: `lib::SeveralBuilder` takes a ''policy template,''
which then in turn will be instantiated with the same types `I` (interface)
and `E` (element type) used on `SeveralBuilder` itself. Obviously, there can be
further types involved and thus additional type parameters may be necessary,
notably the ''Allocator'' — yet these are better injected when ''defining''
the policy template itself.

The default binding for this policy template is defined as `allo::HeapOwn`,
which causes the builder to allocate the storage extents through the standard
heap allocator, and for the created `lib::Several` to take full ownership of
embedded objects, invoking their destructors when falling out of scope.
2024-07-17 01:43:17 +02:00
2fd3629d80 Invocation: difficulties with defining a suitable DataBuilder
As a direct consequence of the insights regarding Dependency-Injection,
a ''Builder Toolkit'' is required, which can be used to adapt various
kinds of ''Weaving Patterns'' — since obviously it is not possible to
settle down on a single Pattern, and thus several ''families of builders''
will emerge, one for each ''line of construction'' for ''Weaving Patterns''.

To stress this point, what I am coding here is a prototype, aimed at
being used as part of a **Test Domain Ontology** — and other Domain Ontologies
(e.g. für FFmpeg) will certainly require other construction schemes
for their Weaving Patterns. So this is an open field, and can not be
settled once and for all.

This immediately leads to another, rather technical problem:
If we're about to work with ''delegate Builders,'' then also
a way to pass-down the allocator configuration is required.

We had settled on a preliminary solution with the helper `DataBuilder`,
yet this solution looks like it defines how `lib::SeveralBuilder`
should be used in most of the cases. So there is now a conflict
between the existing definition scheme for `lib::SeveralBuilder`,
which was achieved in a bottom-up way, and a slightly different
definition scheme ''as it should be''

Starting to attack this latter detail problem, as a first step,
the definition of `DataBuilder` can be simplified by collapsing
it with the `lib::allo::SetupSeveral`
2024-07-17 02:35:05 +02:00
fc9ff9252a Invocation: clarify role of Buffer-Descriptor and Dependency-Injection
It became clear that a secondary system of connections must be added,
running top-down from a global model context, and thus contrary to the
regular orientation of the node network, which connects upwards from
predecessor to successor, in accordance with the pull principle.

If we accept this wiring as part of the primary structure, it can be
established immediately while building the nodes, thus adding a preconfigured
''pattern of Buffer Descriptors'' to each node, since there is no further
''moving part'' — beyond the wiring to the `BufferProvider`, which thus
becomes part of a global `ModelContext`

As an immediate consequence, the storage for this configuraion should
also be switched to `lib::Several` and handled similar to the primary
node wiring in the Builder...
2024-07-15 18:52:59 +02:00
968bfb8fab Invocation: look for ways how to build the Turnout
It seems we need a `WeavingPattern`-Builder, which obviously
must be rather flexible, since those patterns are to be composed
from several layers, which should be extensible within a given ''Domain Ontology''

So this seems to lead to a builder-DSL which creates »**onion layers**«
of builders, with the ability to extend and specialise the type on each layer.

''As it will be quite challenging to get this into usable shape,
it seems best to approach this step by step through prototyping...''
2024-07-15 02:26:53 +02:00
95046489d2 Invocation: release buffer after completed calculation
Not entirely sure how to use the `emit()` call properly,
assuming that it means that data is complete in buffer,
but can still be read after that point
2024-07-15 00:36:20 +02:00
133dd9e8ce Invocation: now able to implement connecting of buffers
* at least for a simple, prototypical setup
 * and actually shifting the onerous into the Level-1 builder \\
   ''(which is precisely the intention here)''
2024-07-12 03:06:11 +02:00
1955d28087 Invocation: use introspection to setup an example
The deeper problem is that we must not engage into any premature decisions
regarding the structure or layout of the actual processing function invocation.

Thus attempting to create a kind of »firewall« of sorts, by connecting
the building blocks strictly through template parameter and preferably
figuring out any detailed knowledge locally, through ''compile-time introspection...''
2024-07-11 18:35:17 +02:00
e7b68427d3 Invocation: look for ways to build an example InvocationAdapter
...even the initial effort to stub its operation turns into a
challenge, since honestly there is near nothing we can assume safely,
without sliding into uncovered provisions regarding the ''Domain Ontology''
2024-07-11 16:31:11 +02:00
0d7d4b5afa Invocation: consider how to arrange the InvocationAdapter
- it is clear that this adaptor will be a ''Concept''
- yet it must in some way access the `FeedManifold` and also control additional storage
- a rather obvious solution is to layer it ''on top'' of the manifold
2024-07-11 02:41:33 +02:00
ec65e2b7b9 Invocation: continue draft of a simple 1:1 WeavingPattern
...which brings about various (preliminary) decisions regarding
Metadata storage in the `Turnout`-object, which acts as a guidance
and specification for the actual invocation for this specific node.

As starting point, I choose the ''KISS'' solution of embedding some
blocks of `UninitialisedStorage` directly into the `Turnout`; obviously
these blocks must be oversized, since we can not effort emitting a
dedicated template instance for each different count of input / output
feeds. Moreover, these data buffers are assumed to be filled with
valid objects by the builder ''(this is a lurking danger)''
2024-07-10 03:35:51 +02:00
0b938320ea Invocation: draft a simplified prototype for an invocation
...attempt to somehow get my foot into the door...
2024-07-09 21:06:38 +02:00
3d6515acca Invocation: further analysis of invocation structure
...turns out that the intended structure is still too fine grained
and explicit and many operational steps can be collapsed into a single
virtual scope, wherein they can be deemed implementation detail...
2024-07-09 18:03:13 +02:00
d09b061434 Invocation: add nested builder to configure a port
...which brings us right into the middle of the task of building a Turnout...
2024-07-08 19:24:03 +02:00
d3344e7dd3 Invocation: improve notation by using a wrapper
...so the solution is to build up the working data as `lib::SeveralBuilder`;
however, a more concise notation can be achieved with a suitably configured
wrapping subclass; together with the cross-builder trick, this allows
to write the allocation configuration in a clearly libelled way,
while the field definition and the builder constructor hides the
complexities of picking up the extension point and passing on the
wiring to the allocator instance.
2024-07-08 05:41:31 +02:00
d291853174 Invocation: add cross-builder to inject a specific (node) allocator
...using the same pattern here as was successful also for the underlying lib::SeveralBuilder;
even if it may seem logically backwards, it just reads much better and
is more understandable, and has the added benefit of providing a dedicated
definition scope, which can be kept separate from the constructor definition
of the actual builder

{{{
  prepareNode()
    .withAllocator<XYZ>()
    .addLead(predecessor)
    .build()
}}}
2024-07-08 04:45:16 +02:00
cedb1830dc Invocation: work out solution for builder initialisation
...turns out to be surprisingly tricky, since the nested
lib::SeveralBuilder instances require parametrisation by a
''policy template,'' which in turn relies on the actual allocator.
And we want to provide the allocator as a constructor parameter,
including the ability to pick up a custom specialisation for
some specific allocator (notably AllocationCluster requires
to hook into this kind of extension point, to be able to
employ its dedicated API for dynamic allocation adjustment)
2024-07-08 03:56:38 +02:00
b01fc6e350 Invocation: adjustments to lib::Several to prepare for allocator use
* conduct analysis regarding allocator handling in the Builder
 * turns out we'll have to keep around two different allocators while building
 * ⟹ establish the goal to confine usage of the Node allocator to the lower Levels
 * consequently must open up the `lib::SeveralBuilder` to be usable
   as an intermediary data structure, while building up the target data
 * in the initial design, the `SeveralBuilder` was kept opaque, since
   contents can be expected to be re-located frequently and thus exposing
   elements and taking references could be dangerous — yet this is also
   true for `std::vector` however, so people are assumed to know
   when they want to shoot themselves into their own foot
2024-07-07 16:12:22 +02:00
58a955a879 Invocation: first draft of the node builder invocation 2024-07-06 21:31:03 +02:00
7c554caf08 Invocation: clarify further requirements for the Level-2 builder
...especially what is necessary to represent at this level and what information
is implicit; notably there will be an implicit default wiring, but we allow
for case-by-case deviations
2024-07-06 04:37:36 +02:00
1f7ddbe5ec Invocation: draft possible syntactic structure based on these conjectures
The Builder will have to perform several passes, gradually refining
the model into the low-level Render Node network. Right now, some
guesses regarding the last steps of this process are possible,
thus defining the lowest level of a model builder structure
 * Level-3 : mapping data flow paths
 * Level-2 : detailed configuration of data buffer passing
 * Level-1 : build the actual parameter structures for invocation

In the current »Vertical Slice« we're able to fully define Level-1
and maybe Level-2
2024-07-06 01:28:18 +02:00
ce9bf7f143 Invocation: conjectures pertaining an implementation of Node-Graph generation
To escape a possible deadlock in analysis, I resort to developing
some kind of free-wheeling presupposition how the **Builder** could
be implemented — a centrepiece of the Lumiera architecture envisioned
thus far — which ''unfortunately'' can only be planned and developed
in a more solid way ''after'' the current »Vertical Slice« is completed.

Thus I find myself in the uncomfortable situation of having to work towards
a core piece, which can not yet be built, since it relies heavily on
the very structures to be built...
2024-07-06 01:13:23 +02:00
604c4b580b Invocation: painstaking analysis of calculation requirements
...the complexity of details is a nightmare
...still fighting to grasp a generic structure allowing to ''fold down''
   the details into the specific ''domain ontologies'' for the media libraries
2024-07-03 04:34:04 +02:00
8c536fc637 Invocation: consider what is required to setup a FeedManifold
...and this line of analysis brings us deep into the ''Buffer Provider''
concept developed in 2012 — which appears to be very well to the point
and stands the test of time.

Adding some ''variadic arguments'' at the right place surprisingly leads
to an ''extension point'' — which in turn directly taps into the
still quite uncharted territory interfacing to a **Domain Ontology**;
the latter is assumed to define how to deal with entities and relationships
defined by some media handling library like e.g. FFmpeg.
So what we're set to do here is actually ''ontology mapping....''
2024-06-29 04:22:23 +02:00
717af81986 Invocation: Identify parts relevant for a node builder
The immediate next step is to build some render nodes directly
in a test setting, without using any kind of ''node factory.''
Getting ahead with this task requires to identify the constituents
to be represented on the first code layer for the reworked code
(here ''first layer'' means any part that are ''not'' supplied
by generic, templated building blocks).

Notably we need to build a descriptor for the `FeedManifold` —
which in turn implies we have to decide on some fundamental aspects
of handling buffers in the render process.

To allow rework of the `ProcNode` connectivity, a lot of presumably obsoleted
draft code from 2011 has to be detached, to be able to keep it in-tree
for further reference (until the rework and refactoring is settled).
2024-06-25 04:54:39 +02:00
9f233f1e90 Invocation: Detail-planning of node invocation
* consider which operations to provide where
 * collect components to be built for a basic node wiring
 * define an entrance point for node invocation
2024-06-23 19:40:43 +02:00
17dcb7495f Invocation: establish a concept for the rework
As outlined in #1367, the integration effort requires some rework
of existing code, which will be driven ahead by the `NodeLinkage_test`
 * redefine Node Connectivity
 * build simple `ProcNode` directly in scope
 * create an `TurnoutSystem` instance
 * perform a ''dummy Node-Invocation''
2024-06-21 16:22:58 +02:00
f632701f48 Library: lib::Several complete and tested (see #473)
As a replacement for the `RefArray` a new generic container
has been implemented and tested, in interplay with `AllocationCluster`
 * the front-end container `lib::Several<I>` exposes only a reference
   to the ''interface type'' `I`, while hiding any storage details
 * data can only be populated through the `lib::SeveralBuilder`
 * a lot of flexibility is allowed for the actual element data types
 * element storage is maintained in a storage extent, managed through
   a custom allocator (defaulting to `std::allocator` ⟹ heap storage)
2024-06-19 19:40:03 +02:00
cf6abf6a3b Library: observe allocator limits on exponential expansion
The `SeveralBuilder` employs the same tactic as `std::vector`,
by over-allocating a reserve buffer, which grows in exponential
increments, to amortise better the costs of re-allocation.

This tactic does not play well with space limited allocators
like `AllocationCluster` however; it is thus necessary to provide
an extension point where the actuall allocator's limitation can be
queried, allowing to use what is available as reserve, but not more.

With these adaptations, a full usage cycle backed by `AllocationCluster`
can be demonstrated, including variations of dynamic allocation adjustment.
2024-06-19 17:35:46 +02:00
39e9ecd90e Library: AllocationCluster and SeveralBuilder logic tweaks
...identified as part of bug investigation

 * make clear that reserve() prepares for an absolute capacity
 * clarify that, to the contrary, ensureStorageCapaciy() means the delta

Moreover, it turns out that the assertion regarding storage limits
triggers frequently while writing the test code; so we can conclude
that the `AllocationCluster` interface lures into allocating without
previous check. Consequently, this check now throws a runtime exception.

As an aside, the size limitation should be accessible on the interface,
similar to `std::vector::max_size()`
2024-06-19 15:45:12 +02:00
9709309186 Library: setup adaptor for dynamic adjustments
By means of the extension point, which produces a dedicated policy
for use with `AllocationCluster`, it becomes possible to use the
specialised API to adjust the latest allocation in the cluster.
When this is not actually usable, the policy will fall back
on the standard implementation (which is wasteful when
applied to `AllocationCluster`, since memory for the
obsoleted, smaller blocks not de-allocated then...
2024-06-19 03:26:36 +02:00
7d066a85ee Library: now use AllocationCluster as custom allocator
* this validates usage of the extension point
 * however, there is no special treatment yet,
   and thus a re-alloc leves the previoius block as waste
2024-06-19 01:29:46 +02:00
aacea3c10a Library: lib::Several container now passes test with TrackingAllocator
- decided to allow creating empty lib::Several;
  no need to be overly rigid in this point,
  since it is move-assignable anyway...

- populate with enough elements to provoke several reallocations
  with copying over the existing elements
- precisely calculate and verify the expected allocation size
- verify the use-count due to dedicated allocator instances
  being embedded into both the builder and hidden in the deleter
- move-assign data
- all checksums go to zero at end
2024-06-18 19:09:21 +02:00
50306db164 Library: more stringent deleter logic
The setup for `ArrayBucket` is special, insofar it shell de-allocate itself,
which creates the danger of re-entrant calls, or to the contrary, the danger
to invoke this clean-up function without actually invoking the destructor.

These problems become relevant once the destructor function itself is statefull,
as is the case when embedding a non-trivial, instance bound allocator
to be used for the clean-up work. Using the new `lib::TrackingAllocator`
highlighted this potential problem, since the allocator maintains a use-count.

Thus I decided to move the »destruction mechanics« one level down into
a dedicated and well encapsulated base class; invoking ArrayBucket's destructor
thereby becomes the only way to trigger the clean-up, and even ElementFactory::destroy()
can now safely check if the destructor was already invoked, and otherwise
re-invoke itself through this embedded destructor function. Moreover,
as an additional safety measure, the actual destructor function is now
moved into the local stack frame of the object's destructor call, removing
any possibility for the de-allocation to interfere with the destructor
invokation itself
2024-06-18 18:15:58 +02:00
31c24e0017 Library: investigate discrepancy in allocator
part of the observed deviation stems form bugs in logging and checksum calculation;
but there seems to be a real problem hidden in the allocator usage of the
new component, since the use-cnt of the handle does not drop to zero
2024-06-18 17:20:23 +02:00
09c8c2a29f Library: better handle the alignment issues explicitly
While there might be the possibility to use the magic of the standard library,
it seems prudent rather to handle this insidious problem explicitly,
to make clear what is going on here.

To allow for such explicit alignment handling, I have now changed the
scheme of the storage definition; the actual buffer now starts ''behind''
the `ArrayBucket<I>` object, which thereby becomes a metadata managing header.

__To summarise the problem__: since we are maintaining a dynamically sized buffer,
and since we do not want to expose the actual element type through the
front-end object, we're necessarily bound to perform a raw-memory allocation.
This is denoted in bytes, and thus the allocator can no longer manage
the proper alignment automatically. Rather, we get a storage buffer with
just ''some accidental'' alignment, and we must care to request a sufficient
overhead to be able to shift the actual storage area forward to the next
proper alignment boundary. Obviously this also implies that we must
store this individual padding adjustment somewhere in the metadata,
in order to be able to report the correct size of the block later
on de-allocation.
2024-06-18 03:16:26 +02:00
dc6c8e0858 Library: investigate alignment issues
The solution implemented thus far turns out to be not sufficient
for ''over-aligned-data'', as the raw-allocator can not perform the
''magic work'' because we're exposing only `std::byte` data.
2024-06-17 16:58:07 +02:00
055df59dde Library: tracking diagnostic allocator now complete and tested 2024-06-17 01:55:49 +02:00
10edc31eac Library: build adaptor for automated unique-ownership
This adaptor works in concert with the generic allocator
building blocks (prospective ''Concepts'') and automatically
registers a either static or dynamic back-link to the factory
for clean-up.

Use this wrapper fore more in-depth test of the new `TrackingAllocator`
and verify proper behaviour through the `EventLog`
2024-06-16 19:31:16 +02:00
be3cf61111 Library: verify fundamental properties of TrackingAllocator
* implement some further statistic and diagnostic helpers
 * explicitly create and discard a base allocation for test
2024-06-16 15:44:43 +02:00